Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
52 changes: 52 additions & 0 deletions src/custom_types.c
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,18 @@
#define F_REGEXP_LIKE_TEXT_TEXT 6263
#endif

/* Pre-PG14 compat for function OIDs used in new translations. */
#if PG_VERSION_NUM < 140000
#define F_SPLIT_PART 2088
#define F_REGEXP_REPLACE_TEXT_TEXT_TEXT 2284
#define F_REGEXP_REPLACE_TEXT_TEXT_TEXT_TEXT 2285
#define F_CONCAT_WS 3059
#define F_ARRAY_TO_STRING_ANYARRAY_TEXT 395
#define F_ARRAY_TO_STRING_ANYARRAY_TEXT_TEXT 384
#define F_TO_CHAR_TIMESTAMP_TEXT 2049
#define F_TO_CHAR_TIMESTAMPTZ_TEXT 1770
#endif

#define STR_STARTS_WITH(str, sub) strncmp(str, sub, strlen(sub)) == 0
#define STR_EQUAL(a, b) strcmp(a, b) == 0

Expand Down Expand Up @@ -202,6 +214,14 @@ chfdw_check_for_custom_function(Oid funcid)
case F_MD5_BYTEA:
case F_MD5_TEXT:
case F_TO_TIMESTAMP_FLOAT8:
case F_SPLIT_PART:
case F_REGEXP_REPLACE_TEXT_TEXT_TEXT:
case F_REGEXP_REPLACE_TEXT_TEXT_TEXT_TEXT:
case F_CONCAT_WS:
case F_ARRAY_TO_STRING_ANYARRAY_TEXT:
case F_ARRAY_TO_STRING_ANYARRAY_TEXT_TEXT:
case F_TO_CHAR_TIMESTAMP_TEXT:
case F_TO_CHAR_TIMESTAMPTZ_TEXT:
special_builtin = true;
break;
default:
Expand Down Expand Up @@ -299,6 +319,38 @@ chfdw_check_for_custom_function(Oid funcid)
entry->paren_count = 2;
break;
}
case F_SPLIT_PART:
{
entry->cf_type = CF_SPLIT_PART;
entry->custom_name[0] = '\1';
break;
}
case F_REGEXP_REPLACE_TEXT_TEXT_TEXT:
case F_REGEXP_REPLACE_TEXT_TEXT_TEXT_TEXT:
{
entry->cf_type = CF_REGEXP_REPLACE;
entry->custom_name[0] = '\1';
break;
}
case F_CONCAT_WS:
{
entry->cf_type = CF_CONCAT_WS;
entry->custom_name[0] = '\1';
break;
}
case F_ARRAY_TO_STRING_ANYARRAY_TEXT:
case F_ARRAY_TO_STRING_ANYARRAY_TEXT_TEXT:
{
strcpy(entry->custom_name, "arrayStringConcat");
break;
}
case F_TO_CHAR_TIMESTAMP_TEXT:
case F_TO_CHAR_TIMESTAMPTZ_TEXT:
{
entry->cf_type = CF_TO_CHAR;
entry->custom_name[0] = '\1';
break;
}
}

if (special_builtin)
Expand Down
179 changes: 179 additions & 0 deletions src/deparse.c
Original file line number Diff line number Diff line change
Expand Up @@ -2351,6 +2351,164 @@ deparseSubscriptingRef(SubscriptingRef * node, deparse_expr_cxt * context)
appendStringInfoChar(buf, ')');
}

/*
* Translate a PostgreSQL to_char() format string to a ClickHouse
* formatDateTime() format string. Caller must pfree the result.
*/
static char *
translate_to_char_format(const char *pgfmt)
{
StringInfoData chfmt;

initStringInfo(&chfmt);

for (const char *p = pgfmt; *p;)
{
if (strncmp(p, "YYYY", 4) == 0)
{
appendStringInfoString(&chfmt, "%Y");
p += 4;
}
else if (strncmp(p, "HH24", 4) == 0)
{
appendStringInfoString(&chfmt, "%H");
p += 4;
}
else if (strncmp(p, "HH12", 4) == 0)
{
appendStringInfoString(&chfmt, "%I");
p += 4;
}
else if (strncmp(p, "MI", 2) == 0)
{
appendStringInfoString(&chfmt, "%i");
p += 2;
}
else if (strncmp(p, "MM", 2) == 0)
{
appendStringInfoString(&chfmt, "%m");
p += 2;
}
else if (strncmp(p, "DD", 2) == 0)
{
appendStringInfoString(&chfmt, "%d");
p += 2;
}
else if (strncmp(p, "SS", 2) == 0)
{
appendStringInfoString(&chfmt, "%S");
p += 2;
}
else
{
appendStringInfoChar(&chfmt, *p);
p++;
}
}

return chfmt.data;
}

/*
* split_part(str, delim, n) → splitByString(delim, str)[n]
*/
static void
deparseSplitPart(FuncExpr * node, deparse_expr_cxt * context)
{
StringInfo buf = context->buf;
Expr *str_arg = (Expr *) linitial(node->args);
Expr *delim_arg = (Expr *) list_nth(node->args, 1);
Const *idx_const = (Const *) list_nth(node->args, 2);
int32 idx = DatumGetInt32(idx_const->constvalue);

appendStringInfoString(buf, "splitByString(");
deparseExpr(delim_arg, context);
appendStringInfoString(buf, ", ");
deparseExpr(str_arg, context);
appendStringInfo(buf, ")[%d]", idx);
}

/*
* regexp_replace(str, pat, rep) → replaceRegexpOne(str, pat, rep)
* regexp_replace(str, pat, rep, 'g') → replaceRegexpAll(str, pat, rep)
*/
static void
deparseRegexpReplace(FuncExpr * node, deparse_expr_cxt * context)
{
StringInfo buf = context->buf;
int nargs = list_length(node->args);

if (nargs == 4)
{
Const *flags = (Const *) list_nth(node->args, 3);
char *flagstr = TextDatumGetCString(flags->constvalue);

if (strchr(flagstr, 'g') != NULL)
appendStringInfoString(buf, "replaceRegexpAll(");
else
appendStringInfoString(buf, "replaceRegexpOne(");
pfree(flagstr);
}
else
appendStringInfoString(buf, "replaceRegexpOne(");

deparseExpr((Expr *) linitial(node->args), context);
appendStringInfoString(buf, ", ");
deparseExpr((Expr *) list_nth(node->args, 1), context);
appendStringInfoString(buf, ", ");
deparseExpr((Expr *) list_nth(node->args, 2), context);
appendStringInfoChar(buf, ')');
}

/*
* concat_ws(sep, a, b, ...) →
* arrayStringConcat(arrayFilter(x -> x != '',
* [ifNull(a,''), ifNull(b,''), ...]), sep)
*/
static void
deparseConcatWs(FuncExpr * node, deparse_expr_cxt * context)
{
StringInfo buf = context->buf;
Expr *sep_arg = (Expr *) linitial(node->args);
ListCell *lc;
bool first_val = true;

appendStringInfoString(buf, "arrayStringConcat(arrayFilter(x -> x != '', [");

for_each_cell(lc, node->args, list_second_cell(node->args))
{
if (!first_val)
appendStringInfoString(buf, ", ");
appendStringInfoString(buf, "ifNull(");
deparseExpr((Expr *) lfirst(lc), context);
appendStringInfoString(buf, ", '')");
first_val = false;
}

appendStringInfoString(buf, "]), ");
deparseExpr(sep_arg, context);
appendStringInfoChar(buf, ')');
}

/*
* to_char(ts, fmt) → formatDateTime(ts, translated_fmt)
*/
static void
deparseToChar(FuncExpr * node, deparse_expr_cxt * context)
{
StringInfo buf = context->buf;
Const *fmt_const = (Const *) list_nth(node->args, 1);
char *pgfmt = TextDatumGetCString(fmt_const->constvalue);
char *chfmt = translate_to_char_format(pgfmt);

appendStringInfoString(buf, "formatDateTime(");
deparseExpr((Expr *) linitial(node->args), context);
appendStringInfo(buf, ", '%s')", chfmt);

pfree(pgfmt);
pfree(chfmt);
}

/*
* Deparse a function call.
*/
Expand Down Expand Up @@ -2498,6 +2656,27 @@ deparseFuncExpr(FuncExpr * node, deparse_expr_cxt * context)
return;
}

if (cdef && cdef->cf_type == CF_SPLIT_PART)
{
deparseSplitPart(node, context);
return;
}
else if (cdef && cdef->cf_type == CF_REGEXP_REPLACE)
{
deparseRegexpReplace(node, context);
return;
}
else if (cdef && cdef->cf_type == CF_CONCAT_WS)
{
deparseConcatWs(node, context);
return;
}
else if (cdef && cdef->cf_type == CF_TO_CHAR)
{
deparseToChar(node, context);
return;
}

appendStringInfoChar(buf, '(');
if (cdef && (cdef->cf_type == CF_TIMEZONE || cdef->cf_type == CF_MATCH))
{
Expand Down
4 changes: 4 additions & 0 deletions src/include/fdw.h
Original file line number Diff line number Diff line change
Expand Up @@ -300,6 +300,10 @@ typedef enum
CF_REGEX_NO_MATCH, /* !~ POSIX regex operator */
CF_REGEX_ICASE_MATCH, /* ~* case-insensitive regex operator */
CF_REGEX_ICASE_NO_MATCH, /* !~* case-insensitive regex operator */
CF_SPLIT_PART, /* split_part → splitByString */
CF_REGEXP_REPLACE, /* regexp_replace → replaceRegexp* */
CF_CONCAT_WS, /* concat_ws → arrayStringConcat */
CF_TO_CHAR, /* to_char → formatDateTime */
} custom_object_type;

typedef enum
Expand Down
Loading
Loading