Skip to content

Commit 7d0e872

Browse files
committed
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.
1 parent a37ab81 commit 7d0e872

File tree

3 files changed

+139
-5
lines changed

3 files changed

+139
-5
lines changed

src/backend/utils/adt/ruleutils.c

Lines changed: 30 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -378,6 +378,8 @@ static void appendContextKeyword(deparse_context *context, const char *str,
378378
static void removeStringInfoSpaces(StringInfo str);
379379
static void get_rule_expr(Node *node, deparse_context *context,
380380
bool showimplicit);
381+
static void get_rule_expr_toplevel(Node *node, deparse_context *context,
382+
bool showimplicit);
381383
static void get_oper_expr(OpExpr *expr, deparse_context *context);
382384
static void get_func_expr(FuncExpr *expr, deparse_context *context,
383385
bool showimplicit);
@@ -4162,10 +4164,10 @@ get_values_def(List *values_lists, deparse_context *context)
41624164

41634165
/*
41644166
* Strip any top-level nodes representing indirection assignments,
4165-
* then print the result.
4167+
* then print the result. Whole-row Vars need special treatment.
41664168
*/
4167-
get_rule_expr(processIndirection(col, context, false),
4168-
context, false);
4169+
get_rule_expr_toplevel(processIndirection(col, context, false),
4170+
context, false);
41694171
}
41704172
appendStringInfoChar(buf, ')');
41714173
}
@@ -4556,7 +4558,8 @@ get_target_list(List *targetList, deparse_context *context,
45564558
* the top level of a SELECT list it's not right (the parser will
45574559
* expand that notation into multiple columns, yielding behavior
45584560
* different from a whole-row Var). We need to call get_variable
4559-
* directly so that we can tell it to do the right thing.
4561+
* directly so that we can tell it to do the right thing, and so that
4562+
* we can get the attribute name which is the default AS label.
45604563
*/
45614564
if (tle->expr && IsA(tle->expr, Var))
45624565
{
@@ -7045,7 +7048,8 @@ get_rule_expr(Node *node, deparse_context *context,
70457048
!tupdesc->attrs[i]->attisdropped)
70467049
{
70477050
appendStringInfoString(buf, sep);
7048-
get_rule_expr(e, context, true);
7051+
/* Whole-row Vars need special treatment here */
7052+
get_rule_expr_toplevel(e, context, true);
70497053
sep = ", ";
70507054
}
70517055
i++;
@@ -7425,6 +7429,27 @@ get_rule_expr(Node *node, deparse_context *context,
74257429
}
74267430
}
74277431

7432+
/*
7433+
* get_rule_expr_toplevel - Parse back a toplevel expression
7434+
*
7435+
* Same as get_rule_expr(), except that if the expr is just a Var, we pass
7436+
* istoplevel = true not false to get_variable(). This causes whole-row Vars
7437+
* to get printed with decoration that will prevent expansion of "*".
7438+
* We need to use this in contexts such as ROW() and VALUES(), where the
7439+
* parser would expand "foo.*" appearing at top level. (In principle we'd
7440+
* use this in get_target_list() too, but that has additional worries about
7441+
* whether to print AS, so it needs to invoke get_variable() directly anyway.)
7442+
*/
7443+
static void
7444+
get_rule_expr_toplevel(Node *node, deparse_context *context,
7445+
bool showimplicit)
7446+
{
7447+
if (node && IsA(node, Var))
7448+
(void) get_variable((Var *) node, 0, true, context);
7449+
else
7450+
get_rule_expr(node, context, showimplicit);
7451+
}
7452+
74287453

74297454
/*
74307455
* get_oper_expr - Parse back an OpExpr node

src/test/regress/expected/create_view.out

Lines changed: 91 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1384,6 +1384,97 @@ select * from tt14v;
13841384
foo | | quux
13851385
(1 row)
13861386

1387+
-- check display of whole-row variables in some corner cases
1388+
create type nestedcomposite as (x int8_tbl);
1389+
create view tt15v as select row(i)::nestedcomposite from int8_tbl i;
1390+
select * from tt15v;
1391+
row
1392+
------------------------------------------
1393+
("(123,456)")
1394+
("(123,4567890123456789)")
1395+
("(4567890123456789,123)")
1396+
("(4567890123456789,4567890123456789)")
1397+
("(4567890123456789,-4567890123456789)")
1398+
(5 rows)
1399+
1400+
select pg_get_viewdef('tt15v', true);
1401+
pg_get_viewdef
1402+
------------------------------------------------------
1403+
SELECT ROW(i.*::int8_tbl)::nestedcomposite AS "row"+
1404+
FROM int8_tbl i;
1405+
(1 row)
1406+
1407+
select row(i.*::int8_tbl)::nestedcomposite from int8_tbl i;
1408+
row
1409+
------------------------------------------
1410+
("(123,456)")
1411+
("(123,4567890123456789)")
1412+
("(4567890123456789,123)")
1413+
("(4567890123456789,4567890123456789)")
1414+
("(4567890123456789,-4567890123456789)")
1415+
(5 rows)
1416+
1417+
create view tt16v as select * from int8_tbl i, lateral(values(i)) ss;
1418+
select * from tt16v;
1419+
q1 | q2 | column1
1420+
------------------+-------------------+--------------------------------------
1421+
123 | 456 | (123,456)
1422+
123 | 4567890123456789 | (123,4567890123456789)
1423+
4567890123456789 | 123 | (4567890123456789,123)
1424+
4567890123456789 | 4567890123456789 | (4567890123456789,4567890123456789)
1425+
4567890123456789 | -4567890123456789 | (4567890123456789,-4567890123456789)
1426+
(5 rows)
1427+
1428+
select pg_get_viewdef('tt16v', true);
1429+
pg_get_viewdef
1430+
-------------------------------------------
1431+
SELECT i.q1, +
1432+
i.q2, +
1433+
ss.column1 +
1434+
FROM int8_tbl i, +
1435+
LATERAL ( VALUES (i.*::int8_tbl)) ss;
1436+
(1 row)
1437+
1438+
select * from int8_tbl i, lateral(values(i.*::int8_tbl)) ss;
1439+
q1 | q2 | column1
1440+
------------------+-------------------+--------------------------------------
1441+
123 | 456 | (123,456)
1442+
123 | 4567890123456789 | (123,4567890123456789)
1443+
4567890123456789 | 123 | (4567890123456789,123)
1444+
4567890123456789 | 4567890123456789 | (4567890123456789,4567890123456789)
1445+
4567890123456789 | -4567890123456789 | (4567890123456789,-4567890123456789)
1446+
(5 rows)
1447+
1448+
create view tt17v as select * from int8_tbl i where i in (values(i));
1449+
select * from tt17v;
1450+
q1 | q2
1451+
------------------+-------------------
1452+
123 | 456
1453+
123 | 4567890123456789
1454+
4567890123456789 | 123
1455+
4567890123456789 | 4567890123456789
1456+
4567890123456789 | -4567890123456789
1457+
(5 rows)
1458+
1459+
select pg_get_viewdef('tt17v', true);
1460+
pg_get_viewdef
1461+
---------------------------------------------
1462+
SELECT i.q1, +
1463+
i.q2 +
1464+
FROM int8_tbl i +
1465+
WHERE (i.* IN ( VALUES (i.*::int8_tbl)));
1466+
(1 row)
1467+
1468+
select * from int8_tbl i where i.* in (values(i.*::int8_tbl));
1469+
q1 | q2
1470+
------------------+-------------------
1471+
123 | 456
1472+
123 | 4567890123456789
1473+
4567890123456789 | 123
1474+
4567890123456789 | 4567890123456789
1475+
4567890123456789 | -4567890123456789
1476+
(5 rows)
1477+
13871478
-- clean up all the random objects we made above
13881479
set client_min_messages = warning;
13891480
DROP SCHEMA temp_view_test CASCADE;

src/test/regress/sql/create_view.sql

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -469,6 +469,24 @@ alter table tt14t drop column f3;
469469
select pg_get_viewdef('tt14v', true);
470470
select * from tt14v;
471471

472+
-- check display of whole-row variables in some corner cases
473+
474+
create type nestedcomposite as (x int8_tbl);
475+
create view tt15v as select row(i)::nestedcomposite from int8_tbl i;
476+
select * from tt15v;
477+
select pg_get_viewdef('tt15v', true);
478+
select row(i.*::int8_tbl)::nestedcomposite from int8_tbl i;
479+
480+
create view tt16v as select * from int8_tbl i, lateral(values(i)) ss;
481+
select * from tt16v;
482+
select pg_get_viewdef('tt16v', true);
483+
select * from int8_tbl i, lateral(values(i.*::int8_tbl)) ss;
484+
485+
create view tt17v as select * from int8_tbl i where i in (values(i));
486+
select * from tt17v;
487+
select pg_get_viewdef('tt17v', true);
488+
select * from int8_tbl i where i.* in (values(i.*::int8_tbl));
489+
472490
-- clean up all the random objects we made above
473491
set client_min_messages = warning;
474492
DROP SCHEMA temp_view_test CASCADE;

0 commit comments

Comments
 (0)