From 7b21d1bcafef3bb039cef3725c01894070700f99 Mon Sep 17 00:00:00 2001 From: Tom Lane Date: Sun, 15 Nov 2015 14:41:09 -0500 Subject: [PATCH] Fix ruleutils.c's dumping of whole-row Vars in ROW() and VALUES() contexts. Normally ruleutils prints a whole-row Var as "foo.*". We already knew that that doesn't work at top level of a SELECT list, because the parser would treat the "*" as a directive to expand the reference into separate columns, not a whole-row Var. However, Joshua Yanovski points out in bug #13776 that the same thing happens at top level of a ROW() construct; and some nosing around in the parser shows that the same is true in VALUES(). Hence, apply the same workaround already devised for the SELECT-list case, namely to add a forced cast to the appropriate rowtype in these cases. (The alternative of just printing "foo" was rejected because it is difficult to avoid ambiguity against plain columns named "foo".) Back-patch to all supported branches. --- src/backend/utils/adt/ruleutils.c | 35 ++++++-- src/test/regress/expected/create_view.out | 101 +++++++++++++--------- src/test/regress/sql/create_view.sql | 15 ++++ 3 files changed, 106 insertions(+), 45 deletions(-) diff --git a/src/backend/utils/adt/ruleutils.c b/src/backend/utils/adt/ruleutils.c index 09c95600f7..bf98620460 100644 --- a/src/backend/utils/adt/ruleutils.c +++ b/src/backend/utils/adt/ruleutils.c @@ -217,6 +217,8 @@ static void appendContextKeyword(deparse_context *context, const char *str, int indentBefore, int indentAfter, int indentPlus); 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_oper_expr(OpExpr *expr, deparse_context *context); static void get_func_expr(FuncExpr *expr, deparse_context *context, bool showimplicit); @@ -2706,10 +2708,10 @@ get_values_def(List *values_lists, deparse_context *context) /* * Strip any top-level nodes representing indirection assignments, - * then print the result. + * then print the result. Whole-row Vars need special treatment. */ - get_rule_expr(processIndirection(col, context, false), - context, false); + get_rule_expr_toplevel(processIndirection(col, context, false), + context, false); } appendStringInfoChar(buf, ')'); } @@ -3073,7 +3075,8 @@ get_target_list(List *targetList, deparse_context *context, * the top level of a SELECT list it's not right (the parser will * expand that notation into multiple columns, yielding behavior * different from a whole-row Var). We need to call get_variable - * directly so that we can tell it to do the right thing. + * directly so that we can tell it to do the right thing, and so that + * we can get the attribute name which is the default AS label. */ if (tle->expr && IsA(tle->expr, Var)) { @@ -5456,7 +5459,8 @@ get_rule_expr(Node *node, deparse_context *context, !tupdesc->attrs[i]->attisdropped) { appendStringInfoString(buf, sep); - get_rule_expr(e, context, true); + /* Whole-row Vars need special treatment here */ + get_rule_expr_toplevel(e, context, true); sep = ", "; } i++; @@ -5836,6 +5840,27 @@ get_rule_expr(Node *node, deparse_context *context, } } +/* + * get_rule_expr_toplevel - Parse back a toplevel expression + * + * Same as get_rule_expr(), except that if the expr is just a Var, we pass + * istoplevel = true not false to get_variable(). This causes whole-row Vars + * to get printed with decoration that will prevent expansion of "*". + * We need to use this in contexts such as ROW() and VALUES(), where the + * parser would expand "foo.*" appearing at top level. (In principle we'd + * use this in get_target_list() too, but that has additional worries about + * whether to print AS, so it needs to invoke get_variable() directly anyway.) + */ +static void +get_rule_expr_toplevel(Node *node, deparse_context *context, + bool showimplicit) +{ + if (node && IsA(node, Var)) + (void) get_variable((Var *) node, 0, true, context); + else + get_rule_expr(node, context, showimplicit); +} + /* * get_oper_expr - Parse back an OpExpr node diff --git a/src/test/regress/expected/create_view.out b/src/test/regress/expected/create_view.out index f9490a3a55..56a1566aee 100644 --- a/src/test/regress/expected/create_view.out +++ b/src/test/regress/expected/create_view.out @@ -239,46 +239,67 @@ And relnamespace IN (SELECT OID FROM pg_namespace WHERE nspname LIKE 'pg_temp%') 1 (1 row) +-- check display of whole-row variables in some corner cases +create type nestedcomposite as (x int8_tbl); +create view tt15v as select row(i)::nestedcomposite from int8_tbl i; +select * from tt15v; + row +------------------------------------------ + ("(123,456)") + ("(123,4567890123456789)") + ("(4567890123456789,123)") + ("(4567890123456789,4567890123456789)") + ("(4567890123456789,-4567890123456789)") +(5 rows) + +select pg_get_viewdef('tt15v', true); + pg_get_viewdef +------------------------------------------------------ + SELECT ROW(i.*::int8_tbl)::nestedcomposite AS "row"+ + FROM int8_tbl i; +(1 row) + +select row(i.*::int8_tbl)::nestedcomposite from int8_tbl i; + row +------------------------------------------ + ("(123,456)") + ("(123,4567890123456789)") + ("(4567890123456789,123)") + ("(4567890123456789,4567890123456789)") + ("(4567890123456789,-4567890123456789)") +(5 rows) + +create view tt17v as select * from int8_tbl i where i in (values(i)); +select * from tt17v; + q1 | q2 +------------------+------------------- + 123 | 456 + 123 | 4567890123456789 + 4567890123456789 | 123 + 4567890123456789 | 4567890123456789 + 4567890123456789 | -4567890123456789 +(5 rows) + +select pg_get_viewdef('tt17v', true); + pg_get_viewdef +--------------------------------------------- + SELECT i.q1, i.q2 + + FROM int8_tbl i + + WHERE (i.* IN ( VALUES (i.*::int8_tbl))); +(1 row) + +select * from int8_tbl i where i.* in (values(i.*::int8_tbl)); + q1 | q2 +------------------+------------------- + 123 | 456 + 123 | 4567890123456789 + 4567890123456789 | 123 + 4567890123456789 | 4567890123456789 + 4567890123456789 | -4567890123456789 +(5 rows) + +-- clean up all the random objects we made above +set client_min_messages = warning; DROP SCHEMA temp_view_test CASCADE; -NOTICE: drop cascades to 22 other objects -DETAIL: drop cascades to table temp_view_test.base_table -drop cascades to view v7_temp -drop cascades to view v10_temp -drop cascades to view v11_temp -drop cascades to view v12_temp -drop cascades to view v2_temp -drop cascades to view v4_temp -drop cascades to view v6_temp -drop cascades to view v8_temp -drop cascades to view v9_temp -drop cascades to table temp_view_test.base_table2 -drop cascades to view v5_temp -drop cascades to view temp_view_test.v1 -drop cascades to view temp_view_test.v2 -drop cascades to view temp_view_test.v3 -drop cascades to view temp_view_test.v4 -drop cascades to view temp_view_test.v5 -drop cascades to view temp_view_test.v6 -drop cascades to view temp_view_test.v7 -drop cascades to view temp_view_test.v8 -drop cascades to sequence temp_view_test.seq1 -drop cascades to view temp_view_test.v9 DROP SCHEMA testviewschm2 CASCADE; -NOTICE: drop cascades to 16 other objects -DETAIL: drop cascades to table t1 -drop cascades to view temporal1 -drop cascades to view temporal2 -drop cascades to view temporal3 -drop cascades to view temporal4 -drop cascades to table t2 -drop cascades to view nontemp1 -drop cascades to view nontemp2 -drop cascades to view nontemp3 -drop cascades to view nontemp4 -drop cascades to table tbl1 -drop cascades to table tbl2 -drop cascades to table tbl3 -drop cascades to table tbl4 -drop cascades to view mytempview -drop cascades to view pubview SET search_path to public; diff --git a/src/test/regress/sql/create_view.sql b/src/test/regress/sql/create_view.sql index 86cfc5162c..6ea6b98708 100644 --- a/src/test/regress/sql/create_view.sql +++ b/src/test/regress/sql/create_view.sql @@ -191,6 +191,21 @@ AND NOT EXISTS (SELECT g FROM tbl4 LEFT JOIN tmptbl ON tbl4.h = tmptbl.j); SELECT count(*) FROM pg_class where relname LIKE 'mytempview' And relnamespace IN (SELECT OID FROM pg_namespace WHERE nspname LIKE 'pg_temp%'); +-- check display of whole-row variables in some corner cases + +create type nestedcomposite as (x int8_tbl); +create view tt15v as select row(i)::nestedcomposite from int8_tbl i; +select * from tt15v; +select pg_get_viewdef('tt15v', true); +select row(i.*::int8_tbl)::nestedcomposite from int8_tbl i; + +create view tt17v as select * from int8_tbl i where i in (values(i)); +select * from tt17v; +select pg_get_viewdef('tt17v', true); +select * from int8_tbl i where i.* in (values(i.*::int8_tbl)); + +-- clean up all the random objects we made above +set client_min_messages = warning; DROP SCHEMA temp_view_test CASCADE; DROP SCHEMA testviewschm2 CASCADE; -- 2.40.0