diff --git a/src/backend/utils/adt/ruleutils.c b/src/backend/utils/adt/ruleutils.c index ad71d5fedbc..0f044dee217 100644 --- a/src/backend/utils/adt/ruleutils.c +++ b/src/backend/utils/adt/ruleutils.c @@ -228,6 +228,9 @@ static void get_rule_expr(Node *node, deparse_context *context, bool showimplicit); static void get_rule_expr_toplevel(Node *node, deparse_context *context, bool showimplicit); +static void get_rule_expr_funccall(Node *node, deparse_context *context, + bool showimplicit); +static bool looks_like_function(Node *node); static void get_oper_expr(OpExpr *expr, deparse_context *context); static void get_func_expr(FuncExpr *expr, deparse_context *context, bool showimplicit); @@ -6059,6 +6062,63 @@ get_rule_expr_toplevel(Node *node, deparse_context *context, get_rule_expr(node, context, showimplicit); } +/* + * get_rule_expr_funccall - Parse back a function-call expression + * + * Same as get_rule_expr(), except that we guarantee that the output will + * look like a function call, or like one of the things the grammar treats as + * equivalent to a function call (see the func_expr_windowless production). + * This is needed in places where the grammar uses func_expr_windowless and + * you can't substitute a parenthesized a_expr. If what we have isn't going + * to look like a function call, wrap it in a dummy CAST() expression, which + * will satisfy the grammar --- and, indeed, is likely what the user wrote to + * produce such a thing. + */ +static void +get_rule_expr_funccall(Node *node, deparse_context *context, + bool showimplicit) +{ + if (looks_like_function(node)) + get_rule_expr(node, context, showimplicit); + else + { + StringInfo buf = context->buf; + + appendStringInfoString(buf, "CAST("); + /* no point in showing any top-level implicit cast */ + get_rule_expr(node, context, false); + appendStringInfo(buf, " AS %s)", + format_type_with_typemod(exprType(node), + exprTypmod(node))); + } +} + +/* + * Helper function to identify node types that satisfy func_expr_windowless. + * If in doubt, "false" is always a safe answer. + */ +static bool +looks_like_function(Node *node) +{ + if (node == NULL) + return false; /* probably shouldn't happen */ + switch (nodeTag(node)) + { + case T_FuncExpr: + /* OK, unless it's going to deparse as a cast */ + return (((FuncExpr *) node)->funcformat == COERCE_EXPLICIT_CALL); + case T_NullIfExpr: + case T_CoalesceExpr: + case T_MinMaxExpr: + case T_XmlExpr: + /* these are all accepted by func_expr_common_subexpr */ + return true; + default: + break; + } + return false; +} + /* * get_oper_expr - Parse back an OpExpr node @@ -6792,7 +6852,7 @@ get_from_clause_item(Node *jtnode, Query *query, deparse_context *context) break; case RTE_FUNCTION: /* Function RTE */ - get_rule_expr(rte->funcexpr, context, true); + get_rule_expr_funccall(rte->funcexpr, context, true); break; case RTE_VALUES: /* Values list RTE */ diff --git a/src/test/regress/expected/create_view.out b/src/test/regress/expected/create_view.out index 708c6d6bfc4..0aaad2d63d5 100644 --- a/src/test/regress/expected/create_view.out +++ b/src/test/regress/expected/create_view.out @@ -409,6 +409,23 @@ select pg_get_viewdef('tt19v', true); 'foo'::text = ANY ((( SELECT ARRAY['abc'::text, 'def'::text, 'foo'::text] AS "array"))::text[]) AS c2; (1 row) +-- check display of assorted RTE_FUNCTION expressions +create view tt20v as +select * from + coalesce(1,2) as c, + collation for ('x'::text) col, + current_date as d, + cast(1+2 as int4) as i4, + cast(1+2 as int8) as i8; +select pg_get_viewdef('tt20v', true); + pg_get_viewdef +--------------------------------------------------------------------------- + SELECT c.c, col.col, d.d, i4.i4, i8.i8 + + FROM COALESCE(1, 2) c(c), pg_collation_for('x'::text) col(col), + + CAST('now'::text::date AS date) d(d), CAST(1 + 2 AS integer) i4(i4), + + CAST((1 + 2)::bigint AS bigint) i8(i8); +(1 row) + -- clean up all the random objects we made above set client_min_messages = warning; DROP SCHEMA temp_view_test CASCADE; diff --git a/src/test/regress/sql/create_view.sql b/src/test/regress/sql/create_view.sql index 9f7cdcd2fd5..6f6b442c688 100644 --- a/src/test/regress/sql/create_view.sql +++ b/src/test/regress/sql/create_view.sql @@ -261,6 +261,17 @@ select 'foo'::text = any(array['abc','def','foo']::text[]) c1, 'foo'::text = any((select array['abc','def','foo']::text[])::text[]) c2; select pg_get_viewdef('tt19v', true); +-- check display of assorted RTE_FUNCTION expressions + +create view tt20v as +select * from + coalesce(1,2) as c, + collation for ('x'::text) col, + current_date as d, + cast(1+2 as int4) as i4, + cast(1+2 as int8) as i8; +select pg_get_viewdef('tt20v', true); + -- clean up all the random objects we made above set client_min_messages = warning; DROP SCHEMA temp_view_test CASCADE;