reverse-list-special-sql-syntaxes-1.patch
text/x-diff
Filename: reverse-list-special-sql-syntaxes-1.patch
Type: text/x-diff
Part: 0
Patch
Format: unified
| File | + | − |
|---|---|---|
| src/backend/nodes/copyfuncs.c | 2 | 1 |
| src/backend/nodes/equalfuncs.c | 2 | 1 |
| src/backend/nodes/makefuncs.c | 3 | 2 |
| src/backend/nodes/outfuncs.c | 2 | 1 |
| src/backend/parser/gram.y | 102 | 29 |
| src/backend/parser/parse_clause.c | 3 | 1 |
| src/backend/parser/parse_func.c | 4 | 2 |
| src/backend/parser/parse_utilcmd.c | 1 | 0 |
| src/backend/utils/adt/ruleutils.c | 212 | 1 |
| src/include/nodes/makefuncs.h | 2 | 1 |
| src/include/nodes/parsenodes.h | 2 | 1 |
| src/include/nodes/primnodes.h | 6 | 2 |
| src/test/modules/test_rls_hooks/test_rls_hooks.c | 8 | 2 |
| src/test/regress/expected/create_view.out | 1 | 1 |
| src/test/regress/expected/timestamptz.out | 3 | 3 |
diff --git a/src/backend/nodes/copyfuncs.c b/src/backend/nodes/copyfuncs.c
index 2b4d7654cc..f14236ad3a 100644
--- a/src/backend/nodes/copyfuncs.c
+++ b/src/backend/nodes/copyfuncs.c
@@ -2682,11 +2682,12 @@ _copyFuncCall(const FuncCall *from)
COPY_NODE_FIELD(args);
COPY_NODE_FIELD(agg_order);
COPY_NODE_FIELD(agg_filter);
+ COPY_NODE_FIELD(over);
COPY_SCALAR_FIELD(agg_within_group);
COPY_SCALAR_FIELD(agg_star);
COPY_SCALAR_FIELD(agg_distinct);
COPY_SCALAR_FIELD(func_variadic);
- COPY_NODE_FIELD(over);
+ COPY_SCALAR_FIELD(funcformat);
COPY_LOCATION_FIELD(location);
return newnode;
diff --git a/src/backend/nodes/equalfuncs.c b/src/backend/nodes/equalfuncs.c
index e2d1b987bf..8985b11f8f 100644
--- a/src/backend/nodes/equalfuncs.c
+++ b/src/backend/nodes/equalfuncs.c
@@ -2377,11 +2377,12 @@ _equalFuncCall(const FuncCall *a, const FuncCall *b)
COMPARE_NODE_FIELD(args);
COMPARE_NODE_FIELD(agg_order);
COMPARE_NODE_FIELD(agg_filter);
+ COMPARE_NODE_FIELD(over);
COMPARE_SCALAR_FIELD(agg_within_group);
COMPARE_SCALAR_FIELD(agg_star);
COMPARE_SCALAR_FIELD(agg_distinct);
COMPARE_SCALAR_FIELD(func_variadic);
- COMPARE_NODE_FIELD(over);
+ COMPARE_SCALAR_FIELD(funcformat);
COMPARE_LOCATION_FIELD(location);
return true;
diff --git a/src/backend/nodes/makefuncs.c b/src/backend/nodes/makefuncs.c
index 49de285f01..ee033ae779 100644
--- a/src/backend/nodes/makefuncs.c
+++ b/src/backend/nodes/makefuncs.c
@@ -582,7 +582,7 @@ makeDefElemExtended(char *nameSpace, char *name, Node *arg,
* supply. Any non-default parameters have to be inserted by the caller.
*/
FuncCall *
-makeFuncCall(List *name, List *args, int location)
+makeFuncCall(List *name, List *args, CoercionForm funcformat, int location)
{
FuncCall *n = makeNode(FuncCall);
@@ -590,11 +590,12 @@ makeFuncCall(List *name, List *args, int location)
n->args = args;
n->agg_order = NIL;
n->agg_filter = NULL;
+ n->over = NULL;
n->agg_within_group = false;
n->agg_star = false;
n->agg_distinct = false;
n->func_variadic = false;
- n->over = NULL;
+ n->funcformat = funcformat;
n->location = location;
return n;
}
diff --git a/src/backend/nodes/outfuncs.c b/src/backend/nodes/outfuncs.c
index 530328af43..cafe19b2f0 100644
--- a/src/backend/nodes/outfuncs.c
+++ b/src/backend/nodes/outfuncs.c
@@ -2766,11 +2766,12 @@ _outFuncCall(StringInfo str, const FuncCall *node)
WRITE_NODE_FIELD(args);
WRITE_NODE_FIELD(agg_order);
WRITE_NODE_FIELD(agg_filter);
+ WRITE_NODE_FIELD(over);
WRITE_BOOL_FIELD(agg_within_group);
WRITE_BOOL_FIELD(agg_star);
WRITE_BOOL_FIELD(agg_distinct);
WRITE_BOOL_FIELD(func_variadic);
- WRITE_NODE_FIELD(over);
+ WRITE_ENUM_FIELD(funcformat, CoercionForm);
WRITE_LOCATION_FIELD(location);
}
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 480d168346..20e840ce20 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -12977,6 +12977,7 @@ a_expr: c_expr { $$ = $1; }
{
$$ = (Node *) makeFuncCall(SystemFuncName("timezone"),
list_make2($5, $1),
+ COERCE_SQL_SYNTAX,
@2);
}
/*
@@ -13040,6 +13041,7 @@ a_expr: c_expr { $$ = $1; }
{
FuncCall *n = makeFuncCall(SystemFuncName("like_escape"),
list_make2($3, $5),
+ COERCE_EXPLICIT_CALL,
@2);
$$ = (Node *) makeSimpleA_Expr(AEXPR_LIKE, "~~",
$1, (Node *) n, @2);
@@ -13053,6 +13055,7 @@ a_expr: c_expr { $$ = $1; }
{
FuncCall *n = makeFuncCall(SystemFuncName("like_escape"),
list_make2($4, $6),
+ COERCE_EXPLICIT_CALL,
@2);
$$ = (Node *) makeSimpleA_Expr(AEXPR_LIKE, "!~~",
$1, (Node *) n, @2);
@@ -13066,6 +13069,7 @@ a_expr: c_expr { $$ = $1; }
{
FuncCall *n = makeFuncCall(SystemFuncName("like_escape"),
list_make2($3, $5),
+ COERCE_EXPLICIT_CALL,
@2);
$$ = (Node *) makeSimpleA_Expr(AEXPR_ILIKE, "~~*",
$1, (Node *) n, @2);
@@ -13079,6 +13083,7 @@ a_expr: c_expr { $$ = $1; }
{
FuncCall *n = makeFuncCall(SystemFuncName("like_escape"),
list_make2($4, $6),
+ COERCE_EXPLICIT_CALL,
@2);
$$ = (Node *) makeSimpleA_Expr(AEXPR_ILIKE, "!~~*",
$1, (Node *) n, @2);
@@ -13088,6 +13093,7 @@ a_expr: c_expr { $$ = $1; }
{
FuncCall *n = makeFuncCall(SystemFuncName("similar_to_escape"),
list_make1($4),
+ COERCE_EXPLICIT_CALL,
@2);
$$ = (Node *) makeSimpleA_Expr(AEXPR_SIMILAR, "~",
$1, (Node *) n, @2);
@@ -13096,6 +13102,7 @@ a_expr: c_expr { $$ = $1; }
{
FuncCall *n = makeFuncCall(SystemFuncName("similar_to_escape"),
list_make2($4, $6),
+ COERCE_EXPLICIT_CALL,
@2);
$$ = (Node *) makeSimpleA_Expr(AEXPR_SIMILAR, "~",
$1, (Node *) n, @2);
@@ -13104,6 +13111,7 @@ a_expr: c_expr { $$ = $1; }
{
FuncCall *n = makeFuncCall(SystemFuncName("similar_to_escape"),
list_make1($5),
+ COERCE_EXPLICIT_CALL,
@2);
$$ = (Node *) makeSimpleA_Expr(AEXPR_SIMILAR, "!~",
$1, (Node *) n, @2);
@@ -13112,6 +13120,7 @@ a_expr: c_expr { $$ = $1; }
{
FuncCall *n = makeFuncCall(SystemFuncName("similar_to_escape"),
list_make2($5, $7),
+ COERCE_EXPLICIT_CALL,
@2);
$$ = (Node *) makeSimpleA_Expr(AEXPR_SIMILAR, "!~",
$1, (Node *) n, @2);
@@ -13172,6 +13181,7 @@ a_expr: c_expr { $$ = $1; }
parser_errposition(@3)));
$$ = (Node *) makeFuncCall(SystemFuncName("overlaps"),
list_concat($1, $3),
+ COERCE_SQL_SYNTAX,
@2);
}
| a_expr IS TRUE_P %prec IS
@@ -13359,19 +13369,33 @@ a_expr: c_expr { $$ = $1; }
}
| a_expr IS NORMALIZED %prec IS
{
- $$ = (Node *) makeFuncCall(SystemFuncName("is_normalized"), list_make1($1), @2);
+ $$ = (Node *) makeFuncCall(SystemFuncName("is_normalized"),
+ list_make1($1),
+ COERCE_SQL_SYNTAX,
+ @2);
}
| a_expr IS unicode_normal_form NORMALIZED %prec IS
{
- $$ = (Node *) makeFuncCall(SystemFuncName("is_normalized"), list_make2($1, makeStringConst($3, @3)), @2);
+ $$ = (Node *) makeFuncCall(SystemFuncName("is_normalized"),
+ list_make2($1, makeStringConst($3, @3)),
+ COERCE_SQL_SYNTAX,
+ @2);
}
| a_expr IS NOT NORMALIZED %prec IS
{
- $$ = makeNotExpr((Node *) makeFuncCall(SystemFuncName("is_normalized"), list_make1($1), @2), @2);
+ $$ = makeNotExpr((Node *) makeFuncCall(SystemFuncName("is_normalized"),
+ list_make1($1),
+ COERCE_SQL_SYNTAX,
+ @2),
+ @2);
}
| a_expr IS NOT unicode_normal_form NORMALIZED %prec IS
{
- $$ = makeNotExpr((Node *) makeFuncCall(SystemFuncName("is_normalized"), list_make2($1, makeStringConst($4, @4)), @2), @2);
+ $$ = makeNotExpr((Node *) makeFuncCall(SystemFuncName("is_normalized"),
+ list_make2($1, makeStringConst($4, @4)),
+ COERCE_SQL_SYNTAX,
+ @2),
+ @2);
}
| DEFAULT
{
@@ -13621,31 +13645,41 @@ c_expr: columnref { $$ = $1; }
func_application: func_name '(' ')'
{
- $$ = (Node *) makeFuncCall($1, NIL, @1);
+ $$ = (Node *) makeFuncCall($1, NIL,
+ COERCE_EXPLICIT_CALL,
+ @1);
}
| func_name '(' func_arg_list opt_sort_clause ')'
{
- FuncCall *n = makeFuncCall($1, $3, @1);
+ FuncCall *n = makeFuncCall($1, $3,
+ COERCE_EXPLICIT_CALL,
+ @1);
n->agg_order = $4;
$$ = (Node *)n;
}
| func_name '(' VARIADIC func_arg_expr opt_sort_clause ')'
{
- FuncCall *n = makeFuncCall($1, list_make1($4), @1);
+ FuncCall *n = makeFuncCall($1, list_make1($4),
+ COERCE_EXPLICIT_CALL,
+ @1);
n->func_variadic = true;
n->agg_order = $5;
$$ = (Node *)n;
}
| func_name '(' func_arg_list ',' VARIADIC func_arg_expr opt_sort_clause ')'
{
- FuncCall *n = makeFuncCall($1, lappend($3, $6), @1);
+ FuncCall *n = makeFuncCall($1, lappend($3, $6),
+ COERCE_EXPLICIT_CALL,
+ @1);
n->func_variadic = true;
n->agg_order = $7;
$$ = (Node *)n;
}
| func_name '(' ALL func_arg_list opt_sort_clause ')'
{
- FuncCall *n = makeFuncCall($1, $4, @1);
+ FuncCall *n = makeFuncCall($1, $4,
+ COERCE_EXPLICIT_CALL,
+ @1);
n->agg_order = $5;
/* Ideally we'd mark the FuncCall node to indicate
* "must be an aggregate", but there's no provision
@@ -13655,7 +13689,9 @@ func_application: func_name '(' ')'
}
| func_name '(' DISTINCT func_arg_list opt_sort_clause ')'
{
- FuncCall *n = makeFuncCall($1, $4, @1);
+ FuncCall *n = makeFuncCall($1, $4,
+ COERCE_EXPLICIT_CALL,
+ @1);
n->agg_order = $5;
n->agg_distinct = true;
$$ = (Node *)n;
@@ -13672,7 +13708,9 @@ func_application: func_name '(' ')'
* so that later processing can detect what the argument
* really was.
*/
- FuncCall *n = makeFuncCall($1, NIL, @1);
+ FuncCall *n = makeFuncCall($1, NIL,
+ COERCE_EXPLICIT_CALL,
+ @1);
n->agg_star = true;
$$ = (Node *)n;
}
@@ -13746,6 +13784,7 @@ func_expr_common_subexpr:
{
$$ = (Node *) makeFuncCall(SystemFuncName("pg_collation_for"),
list_make1($4),
+ COERCE_SQL_SYNTAX,
@1);
}
| CURRENT_DATE
@@ -13812,31 +13851,49 @@ func_expr_common_subexpr:
{ $$ = makeTypeCast($3, $5, @1); }
| EXTRACT '(' extract_list ')'
{
- $$ = (Node *) makeFuncCall(SystemFuncName("date_part"), $3, @1);
+ $$ = (Node *) makeFuncCall(SystemFuncName("date_part"),
+ $3,
+ COERCE_SQL_SYNTAX,
+ @1);
}
| NORMALIZE '(' a_expr ')'
{
- $$ = (Node *) makeFuncCall(SystemFuncName("normalize"), list_make1($3), @1);
+ $$ = (Node *) makeFuncCall(SystemFuncName("normalize"),
+ list_make1($3),
+ COERCE_SQL_SYNTAX,
+ @1);
}
| NORMALIZE '(' a_expr ',' unicode_normal_form ')'
{
- $$ = (Node *) makeFuncCall(SystemFuncName("normalize"), list_make2($3, makeStringConst($5, @5)), @1);
+ $$ = (Node *) makeFuncCall(SystemFuncName("normalize"),
+ list_make2($3, makeStringConst($5, @5)),
+ COERCE_SQL_SYNTAX,
+ @1);
}
| OVERLAY '(' overlay_list ')'
{
- $$ = (Node *) makeFuncCall(SystemFuncName("overlay"), $3, @1);
+ $$ = (Node *) makeFuncCall(SystemFuncName("overlay"),
+ $3,
+ COERCE_SQL_SYNTAX,
+ @1);
}
| POSITION '(' position_list ')'
{
/* position(A in B) is converted to position(B, A) */
- $$ = (Node *) makeFuncCall(SystemFuncName("position"), $3, @1);
+ $$ = (Node *) makeFuncCall(SystemFuncName("position"),
+ $3,
+ COERCE_SQL_SYNTAX,
+ @1);
}
| SUBSTRING '(' substr_list ')'
{
/* substring(A from B for C) is converted to
* substring(A, B, C) - thomas 2000-11-28
*/
- $$ = (Node *) makeFuncCall(SystemFuncName("substring"), $3, @1);
+ $$ = (Node *) makeFuncCall(SystemFuncName("substring"),
+ $3,
+ COERCE_SQL_SYNTAX,
+ @1);
}
| TREAT '(' a_expr AS Typename ')'
{
@@ -13849,28 +13906,41 @@ func_expr_common_subexpr:
* Convert SystemTypeName() to SystemFuncName() even though
* at the moment they result in the same thing.
*/
- $$ = (Node *) makeFuncCall(SystemFuncName(((Value *)llast($5->names))->val.str),
- list_make1($3),
- @1);
+ $$ = (Node *) makeFuncCall(SystemFuncName(((Value *) llast($5->names))->val.str),
+ list_make1($3),
+ COERCE_EXPLICIT_CALL,
+ @1);
}
| TRIM '(' BOTH trim_list ')'
{
/* various trim expressions are defined in SQL
* - thomas 1997-07-19
*/
- $$ = (Node *) makeFuncCall(SystemFuncName("btrim"), $4, @1);
+ $$ = (Node *) makeFuncCall(SystemFuncName("btrim"),
+ $4,
+ COERCE_SQL_SYNTAX,
+ @1);
}
| TRIM '(' LEADING trim_list ')'
{
- $$ = (Node *) makeFuncCall(SystemFuncName("ltrim"), $4, @1);
+ $$ = (Node *) makeFuncCall(SystemFuncName("ltrim"),
+ $4,
+ COERCE_SQL_SYNTAX,
+ @1);
}
| TRIM '(' TRAILING trim_list ')'
{
- $$ = (Node *) makeFuncCall(SystemFuncName("rtrim"), $4, @1);
+ $$ = (Node *) makeFuncCall(SystemFuncName("rtrim"),
+ $4,
+ COERCE_SQL_SYNTAX,
+ @1);
}
| TRIM '(' trim_list ')'
{
- $$ = (Node *) makeFuncCall(SystemFuncName("btrim"), $3, @1);
+ $$ = (Node *) makeFuncCall(SystemFuncName("btrim"),
+ $3,
+ COERCE_SQL_SYNTAX,
+ @1);
}
| NULLIF '(' a_expr ',' a_expr ')'
{
@@ -13923,7 +13993,10 @@ func_expr_common_subexpr:
{
/* xmlexists(A PASSING [BY REF] B [BY REF]) is
* converted to xmlexists(A, B)*/
- $$ = (Node *) makeFuncCall(SystemFuncName("xmlexists"), list_make2($3, $4), @1);
+ $$ = (Node *) makeFuncCall(SystemFuncName("xmlexists"),
+ list_make2($3, $4),
+ COERCE_SQL_SYNTAX,
+ @1);
}
| XMLFOREST '(' xml_attribute_list ')'
{
@@ -14453,10 +14526,10 @@ extract_arg:
;
unicode_normal_form:
- NFC { $$ = "nfc"; }
- | NFD { $$ = "nfd"; }
- | NFKC { $$ = "nfkc"; }
- | NFKD { $$ = "nfkd"; }
+ NFC { $$ = "NFC"; }
+ | NFD { $$ = "NFD"; }
+ | NFKC { $$ = "NFKC"; }
+ | NFKD { $$ = "NFKD"; }
;
/* OVERLAY() arguments */
diff --git a/src/backend/parser/parse_clause.c b/src/backend/parser/parse_clause.c
index 7460e61160..ea4a1f5aeb 100644
--- a/src/backend/parser/parse_clause.c
+++ b/src/backend/parser/parse_clause.c
@@ -541,10 +541,11 @@ transformRangeFunction(ParseState *pstate, RangeFunction *r)
list_length(fc->args) > 1 &&
fc->agg_order == NIL &&
fc->agg_filter == NULL &&
+ fc->over == NULL &&
!fc->agg_star &&
!fc->agg_distinct &&
!fc->func_variadic &&
- fc->over == NULL &&
+ fc->funcformat == COERCE_EXPLICIT_CALL &&
coldeflist == NIL)
{
ListCell *lc;
@@ -558,6 +559,7 @@ transformRangeFunction(ParseState *pstate, RangeFunction *r)
newfc = makeFuncCall(SystemFuncName("unnest"),
list_make1(arg),
+ COERCE_EXPLICIT_CALL,
fc->location);
newfexpr = transformExpr(pstate, (Node *) newfc,
diff --git a/src/backend/parser/parse_func.c b/src/backend/parser/parse_func.c
index a7a31704fb..8b4e3ca5e1 100644
--- a/src/backend/parser/parse_func.c
+++ b/src/backend/parser/parse_func.c
@@ -91,11 +91,12 @@ ParseFuncOrColumn(ParseState *pstate, List *funcname, List *fargs,
bool is_column = (fn == NULL);
List *agg_order = (fn ? fn->agg_order : NIL);
Expr *agg_filter = NULL;
+ WindowDef *over = (fn ? fn->over : NULL);
bool agg_within_group = (fn ? fn->agg_within_group : false);
bool agg_star = (fn ? fn->agg_star : false);
bool agg_distinct = (fn ? fn->agg_distinct : false);
bool func_variadic = (fn ? fn->func_variadic : false);
- WindowDef *over = (fn ? fn->over : NULL);
+ CoercionForm funcformat = (fn ? fn->funcformat : COERCE_EXPLICIT_CALL);
bool could_be_projection;
Oid rettype;
Oid funcid;
@@ -221,6 +222,7 @@ ParseFuncOrColumn(ParseState *pstate, List *funcname, List *fargs,
agg_order == NIL && agg_filter == NULL &&
!agg_star && !agg_distinct && over == NULL &&
!func_variadic && argnames == NIL &&
+ funcformat == COERCE_EXPLICIT_CALL &&
list_length(funcname) == 1 &&
(actual_arg_types[0] == RECORDOID ||
ISCOMPLEX(actual_arg_types[0])));
@@ -742,7 +744,7 @@ ParseFuncOrColumn(ParseState *pstate, List *funcname, List *fargs,
funcexpr->funcresulttype = rettype;
funcexpr->funcretset = retset;
funcexpr->funcvariadic = func_variadic;
- funcexpr->funcformat = COERCE_EXPLICIT_CALL;
+ funcexpr->funcformat = funcformat;
/* funccollid and inputcollid will be set by parse_collate.c */
funcexpr->args = fargs;
funcexpr->location = location;
diff --git a/src/backend/parser/parse_utilcmd.c b/src/backend/parser/parse_utilcmd.c
index 015b0538e3..254c0f65c2 100644
--- a/src/backend/parser/parse_utilcmd.c
+++ b/src/backend/parser/parse_utilcmd.c
@@ -604,6 +604,7 @@ transformColumnDefinition(CreateStmtContext *cxt, ColumnDef *column)
castnode->location = -1;
funccallnode = makeFuncCall(SystemFuncName("nextval"),
list_make1(castnode),
+ COERCE_EXPLICIT_CALL,
-1);
constraint = makeNode(Constraint);
constraint->contype = CONSTR_DEFAULT;
diff --git a/src/backend/utils/adt/ruleutils.c b/src/backend/utils/adt/ruleutils.c
index 6c656586e8..407719e494 100644
--- a/src/backend/utils/adt/ruleutils.c
+++ b/src/backend/utils/adt/ruleutils.c
@@ -443,6 +443,7 @@ static void get_agg_expr(Aggref *aggref, deparse_context *context,
static void get_agg_combine_expr(Node *node, deparse_context *context,
void *callback_arg);
static void get_windowfunc_expr(WindowFunc *wfunc, deparse_context *context);
+static bool get_func_sql_syntax(FuncExpr *expr, deparse_context *context);
static void get_coercion_expr(Node *arg, deparse_context *context,
Oid resulttype, int32 resulttypmod,
Node *parentNode);
@@ -9159,7 +9160,8 @@ looks_like_function(Node *node)
{
case T_FuncExpr:
/* OK, unless it's going to deparse as a cast */
- return (((FuncExpr *) node)->funcformat == COERCE_EXPLICIT_CALL);
+ return (((FuncExpr *) node)->funcformat == COERCE_EXPLICIT_CALL ||
+ ((FuncExpr *) node)->funcformat == COERCE_SQL_SYNTAX);
case T_NullIfExpr:
case T_CoalesceExpr:
case T_MinMaxExpr:
@@ -9261,6 +9263,17 @@ get_func_expr(FuncExpr *expr, deparse_context *context,
return;
}
+ /*
+ * If the function was called using one of the SQL spec's random special
+ * syntaxes, try to reproduce that. If we don't recognize the function,
+ * fall through.
+ */
+ if (expr->funcformat == COERCE_SQL_SYNTAX)
+ {
+ if (get_func_sql_syntax(expr, context))
+ return;
+ }
+
/*
* Normal function: display as proname(args). First we need to extract
* the argument datatypes.
@@ -9496,6 +9509,204 @@ get_windowfunc_expr(WindowFunc *wfunc, deparse_context *context)
}
}
+/*
+ * get_func_sql_syntax - Parse back a SQL-syntax function call
+ *
+ * Returns true if we successfully deparsed, false if we did not
+ * recognize the function.
+ */
+static bool
+get_func_sql_syntax(FuncExpr *expr, deparse_context *context)
+{
+ StringInfo buf = context->buf;
+ Oid funcoid = expr->funcid;
+
+ switch (funcoid)
+ {
+ case F_TIMESTAMP_IZONE:
+ case F_TIMESTAMP_ZONE:
+ case F_TIMESTAMPTZ_IZONE:
+ case F_TIMESTAMPTZ_ZONE:
+ case F_TIMETZ_IZONE:
+ case F_TIMETZ_ZONE:
+ /* AT TIME ZONE ... note reversed argument order */
+ appendStringInfoChar(buf, '(');
+ get_rule_expr((Node *) lsecond(expr->args), context, false);
+ appendStringInfoString(buf, " AT TIME ZONE ");
+ get_rule_expr((Node *) linitial(expr->args), context, false);
+ appendStringInfoChar(buf, ')');
+ return true;
+
+ /* XXX this fails to cover all built-in "overlaps" functions */
+ case F_OVERLAPS_TIME:
+ case F_OVERLAPS_TIMESTAMP:
+ case F_OVERLAPS_TIMETZ:
+ /* (x1, x2) OVERLAPS (y1, y2) */
+ appendStringInfoString(buf, "((");
+ get_rule_expr((Node *) linitial(expr->args), context, false);
+ appendStringInfoString(buf, ", ");
+ get_rule_expr((Node *) lsecond(expr->args), context, false);
+ appendStringInfoString(buf, ") OVERLAPS (");
+ get_rule_expr((Node *) lthird(expr->args), context, false);
+ appendStringInfoString(buf, ", ");
+ get_rule_expr((Node *) lfourth(expr->args), context, false);
+ appendStringInfoString(buf, "))");
+ return true;
+
+ case F_UNICODE_IS_NORMALIZED:
+ /* IS xxx NORMALIZED */
+ appendStringInfoString(buf, "((");
+ get_rule_expr((Node *) linitial(expr->args), context, false);
+ appendStringInfoString(buf, ") IS");
+ if (list_length(expr->args) == 2)
+ {
+ Const *con = (Const *) lsecond(expr->args);
+
+ Assert(IsA(con, Const) &&
+ con->consttype == TEXTOID &&
+ !con->constisnull);
+ appendStringInfo(buf, " %s",
+ TextDatumGetCString(con->constvalue));
+ }
+ appendStringInfoString(buf, " NORMALIZED)");
+ return true;
+
+ case F_PG_COLLATION_FOR:
+ /* COLLATION FOR */
+ appendStringInfoString(buf, "COLLATION FOR (");
+ get_rule_expr((Node *) linitial(expr->args), context, false);
+ appendStringInfoChar(buf, ')');
+ return true;
+
+ /*
+ * XXX EXTRACT, a/k/a date_part(), is intentionally not covered
+ * yet. Add it after we change the return type to numeric.
+ */
+
+ case F_UNICODE_NORMALIZE_FUNC:
+ /* NORMALIZE() */
+ appendStringInfoString(buf, "NORMALIZE(");
+ get_rule_expr((Node *) linitial(expr->args), context, false);
+ if (list_length(expr->args) == 2)
+ {
+ Const *con = (Const *) lsecond(expr->args);
+
+ Assert(IsA(con, Const) &&
+ con->consttype == TEXTOID &&
+ !con->constisnull);
+ appendStringInfo(buf, ", %s",
+ TextDatumGetCString(con->constvalue));
+ }
+ appendStringInfoChar(buf, ')');
+ return true;
+
+ case F_BITOVERLAY:
+ case F_BITOVERLAY_NO_LEN:
+ case F_BYTEAOVERLAY:
+ case F_BYTEAOVERLAY_NO_LEN:
+ case F_TEXTOVERLAY:
+ case F_TEXTOVERLAY_NO_LEN:
+ /* OVERLAY() */
+ appendStringInfoString(buf, "OVERLAY(");
+ get_rule_expr((Node *) linitial(expr->args), context, false);
+ appendStringInfoString(buf, " PLACING ");
+ get_rule_expr((Node *) lsecond(expr->args), context, false);
+ appendStringInfoString(buf, " FROM ");
+ get_rule_expr((Node *) lthird(expr->args), context, false);
+ if (list_length(expr->args) == 4)
+ {
+ appendStringInfoString(buf, " FOR ");
+ get_rule_expr((Node *) lfourth(expr->args), context, false);
+ }
+ appendStringInfoChar(buf, ')');
+ return true;
+
+ case F_BITPOSITION:
+ case F_BYTEAPOS:
+ case F_TEXTPOS:
+ /* POSITION() ... extra parens since args are b_expr not a_expr */
+ appendStringInfoString(buf, "POSITION((");
+ get_rule_expr((Node *) lsecond(expr->args), context, false);
+ appendStringInfoString(buf, ") IN (");
+ get_rule_expr((Node *) linitial(expr->args), context, false);
+ appendStringInfoString(buf, "))");
+ return true;
+
+ case F_BITSUBSTR:
+ case F_BITSUBSTR_NO_LEN:
+ case F_BYTEA_SUBSTR:
+ case F_BYTEA_SUBSTR_NO_LEN:
+ case F_TEXT_SUBSTR: /* XXX fails, because this is the wrong OID */
+ case F_TEXT_SUBSTR_NO_LEN: /* XXX fails likewise */
+ case F_TEXTREGEXSUBSTR:
+ /* SUBSTRING FROM/FOR */
+ appendStringInfoString(buf, "SUBSTRING(");
+ get_rule_expr((Node *) linitial(expr->args), context, false);
+ appendStringInfoString(buf, " FROM ");
+ get_rule_expr((Node *) lsecond(expr->args), context, false);
+ if (list_length(expr->args) == 3)
+ {
+ appendStringInfoString(buf, " FOR ");
+ get_rule_expr((Node *) lthird(expr->args), context, false);
+ }
+ appendStringInfoChar(buf, ')');
+ return true;
+
+ case F_BTRIM:
+ case F_BTRIM1:
+ case F_BYTEATRIM:
+ /* TRIM() */
+ appendStringInfoString(buf, "TRIM(BOTH");
+ if (list_length(expr->args) == 2)
+ {
+ appendStringInfoChar(buf, ' ');
+ get_rule_expr((Node *) lsecond(expr->args), context, false);
+ }
+ appendStringInfoString(buf, " FROM ");
+ get_rule_expr((Node *) linitial(expr->args), context, false);
+ appendStringInfoChar(buf, ')');
+ return true;
+
+ case F_LTRIM:
+ case F_LTRIM1:
+ /* TRIM() */
+ appendStringInfoString(buf, "TRIM(LEADING");
+ if (list_length(expr->args) == 2)
+ {
+ appendStringInfoChar(buf, ' ');
+ get_rule_expr((Node *) lsecond(expr->args), context, false);
+ }
+ appendStringInfoString(buf, " FROM ");
+ get_rule_expr((Node *) linitial(expr->args), context, false);
+ appendStringInfoChar(buf, ')');
+ return true;
+
+ case F_RTRIM:
+ case F_RTRIM1:
+ /* TRIM() */
+ appendStringInfoString(buf, "TRIM(TRAILING");
+ if (list_length(expr->args) == 2)
+ {
+ appendStringInfoChar(buf, ' ');
+ get_rule_expr((Node *) lsecond(expr->args), context, false);
+ }
+ appendStringInfoString(buf, " FROM ");
+ get_rule_expr((Node *) linitial(expr->args), context, false);
+ appendStringInfoChar(buf, ')');
+ return true;
+
+ case F_XMLEXISTS:
+ /* XMLEXISTS ... extra parens because args are c_expr */
+ appendStringInfoString(buf, "XMLEXISTS((");
+ get_rule_expr((Node *) linitial(expr->args), context, false);
+ appendStringInfoString(buf, ") PASSING (");
+ get_rule_expr((Node *) lsecond(expr->args), context, false);
+ appendStringInfoString(buf, "))");
+ return true;
+ }
+ return false;
+}
+
/* ----------
* get_coercion_expr
*
diff --git a/src/include/nodes/makefuncs.h b/src/include/nodes/makefuncs.h
index 31d9aedeeb..7ebd794713 100644
--- a/src/include/nodes/makefuncs.h
+++ b/src/include/nodes/makefuncs.h
@@ -79,7 +79,8 @@ extern ColumnDef *makeColumnDef(const char *colname,
extern FuncExpr *makeFuncExpr(Oid funcid, Oid rettype, List *args,
Oid funccollid, Oid inputcollid, CoercionForm fformat);
-extern FuncCall *makeFuncCall(List *name, List *args, int location);
+extern FuncCall *makeFuncCall(List *name, List *args,
+ CoercionForm funcformat, int location);
extern Expr *make_opclause(Oid opno, Oid opresulttype, bool opretset,
Expr *leftop, Expr *rightop,
diff --git a/src/include/nodes/parsenodes.h b/src/include/nodes/parsenodes.h
index ff584f2955..34090eddf9 100644
--- a/src/include/nodes/parsenodes.h
+++ b/src/include/nodes/parsenodes.h
@@ -353,11 +353,12 @@ typedef struct FuncCall
List *args; /* the arguments (list of exprs) */
List *agg_order; /* ORDER BY (list of SortBy) */
Node *agg_filter; /* FILTER clause, if any */
+ struct WindowDef *over; /* OVER clause, if any */
bool agg_within_group; /* ORDER BY appeared in WITHIN GROUP */
bool agg_star; /* argument was really '*' */
bool agg_distinct; /* arguments were labeled DISTINCT */
bool func_variadic; /* last argument was labeled VARIADIC */
- struct WindowDef *over; /* OVER clause, if any */
+ CoercionForm funcformat; /* how to display this node */
int location; /* token location, or -1 if unknown */
} FuncCall;
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index fd65ee8f9c..5b190bb99b 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -445,7 +445,10 @@ typedef enum CoercionContext
} CoercionContext;
/*
- * CoercionForm - how to display a node that could have come from a cast
+ * CoercionForm - how to display a FuncExpr or related node
+ *
+ * "Coercion" is a bit of a misnomer, since this value records other
+ * special syntaxes besides casts, but for now we'll keep this naming.
*
* NB: equal() ignores CoercionForm fields, therefore this *must* not carry
* any semantically significant information. We need that behavior so that
@@ -457,7 +460,8 @@ typedef enum CoercionForm
{
COERCE_EXPLICIT_CALL, /* display as a function call */
COERCE_EXPLICIT_CAST, /* display as an explicit cast */
- COERCE_IMPLICIT_CAST /* implicit cast, so hide it */
+ COERCE_IMPLICIT_CAST, /* implicit cast, so hide it */
+ COERCE_SQL_SYNTAX /* display with SQL-mandated special syntax */
} CoercionForm;
/*
diff --git a/src/test/modules/test_rls_hooks/test_rls_hooks.c b/src/test/modules/test_rls_hooks/test_rls_hooks.c
index 0bfa878a25..c0aaabdcdb 100644
--- a/src/test/modules/test_rls_hooks/test_rls_hooks.c
+++ b/src/test/modules/test_rls_hooks/test_rls_hooks.c
@@ -95,7 +95,10 @@ test_rls_hooks_permissive(CmdType cmdtype, Relation relation)
*/
n = makeFuncCall(list_make2(makeString("pg_catalog"),
- makeString("current_user")), NIL, 0);
+ makeString("current_user")),
+ NIL,
+ COERCE_EXPLICIT_CALL,
+ -1);
c = makeNode(ColumnRef);
c->fields = list_make1(makeString("username"));
@@ -155,7 +158,10 @@ test_rls_hooks_restrictive(CmdType cmdtype, Relation relation)
policy->roles = construct_array(&role, 1, OIDOID, sizeof(Oid), true, TYPALIGN_INT);
n = makeFuncCall(list_make2(makeString("pg_catalog"),
- makeString("current_user")), NIL, 0);
+ makeString("current_user")),
+ NIL,
+ COERCE_EXPLICIT_CALL,
+ -1);
c = makeNode(ColumnRef);
c->fields = list_make1(makeString("supervisor"));
diff --git a/src/test/regress/expected/create_view.out b/src/test/regress/expected/create_view.out
index f10a3a7a12..a3a3b12941 100644
--- a/src/test/regress/expected/create_view.out
+++ b/src/test/regress/expected/create_view.out
@@ -1710,7 +1710,7 @@ select pg_get_viewdef('tt20v', true);
i4.i4, +
i8.i8 +
FROM COALESCE(1, 2) c(c), +
- pg_collation_for('x'::text) col(col), +
+ COLLATION FOR ('x'::text) col(col), +
CURRENT_DATE d(d), +
LOCALTIMESTAMP(3) t(t), +
CAST(1 + 2 AS integer) i4(i4), +
diff --git a/src/test/regress/expected/timestamptz.out b/src/test/regress/expected/timestamptz.out
index 639b50308e..c300965554 100644
--- a/src/test/regress/expected/timestamptz.out
+++ b/src/test/regress/expected/timestamptz.out
@@ -2726,10 +2726,10 @@ create temp table tmptz (f1 timestamptz primary key);
insert into tmptz values ('2017-01-18 00:00+00');
explain (costs off)
select * from tmptz where f1 at time zone 'utc' = '2017-01-18 00:00';
- QUERY PLAN
--------------------------------------------------------------------------------------------------
+ QUERY PLAN
+-----------------------------------------------------------------------------------------------------
Seq Scan on tmptz
- Filter: (timezone('utc'::text, f1) = 'Wed Jan 18 00:00:00 2017'::timestamp without time zone)
+ Filter: ((f1 AT TIME ZONE 'utc'::text) = 'Wed Jan 18 00:00:00 2017'::timestamp without time zone)
(2 rows)
select * from tmptz where f1 at time zone 'utc' = '2017-01-18 00:00';