10000 Track nesting depth correctly when drilling down into RECORD Vars. · postgres/postgres@a374f6c · GitHub
[go: up one dir, main page]

Skip to content

Commit a374f6c

Browse files
committed
Track nesting depth correctly when drilling down into RECORD Vars.
expandRecordVariable() failed to adjust the parse nesting structure correctly when recursing to inspect an outer-level Var. This could result in assertion failures or core dumps in corner cases. Likewise, get_name_for_var_field() failed to adjust the deparse namespace stack correctly when recursing to inspect an outer-level Var. In this case the likely result was a "bogus varno" error while deparsing a view. Per bug #18077 from Jingzhou Fu. Back-patch to all supported branches. Richard Guo, with some adjustments by me Discussion: https://postgr.es/m/18077-b9db97c6e0ab45d8@postgresql.org
1 parent 479b991 commit a374f6c

File tree

4 files changed

+123
-22
lines changed

4 files changed

+123
-22
lines changed

src/backend/parser/parse_target.c

Lines changed: 13 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1471,7 +1471,8 @@ ExpandRowReference(ParseState *pstate, Node *expr,
14711471
* drill down to find the ultimate defining expression and attempt to infer
14721472
* the tupdesc from it. We ereport if we can't determine the tupdesc.
14731473
*
1474-
* levelsup is an extra offset to interpret the Var's varlevelsup correctly.
1474+
* levelsup is an extra offset to interpret the Var's varlevelsup correctly
1475+
* when recursing. Outside callers should pass zero.
14751476
*/
14761477
TupleDesc
14771478
expandRecordVariable(ParseState *pstate, Var *var, int levelsup)
@@ -1552,11 +1553,17 @@ expandRecordVariable(ParseState *pstate, Var *var, int levelsup)
15521553
/*
15531554
* Recurse into the sub-select to see what its Var refers
15541555
* to. We have to build an additional level of ParseState
1555-
* to keep in step with varlevelsup in the subselect.
1556+
* to keep in step with varlevelsup in the subselect;
1557+
* furthermore, the subquery RTE might be from an outer
1558+
* query level, in which case the ParseState for the
1559+
* subselect must have that outer level as parent.
15561560
*/
1557-
ParseState mypstate;
1561+
ParseState mypstate = {0};
1562+
Index levelsup;
15581563

1559-
MemSet(&mypstate, 0, sizeof(mypstate));
1564+
/* this loop must work, since GetRTEByRangeTablePosn did */
1565+
for (levelsup = 0; levelsup < netlevelsup; levelsup++)
1566+
pstate = pstate->parentParseState;
15601567
mypstate.parentParseState = pstate;
15611568
mypstate.p_rtable = rte->subquery->rtable;
15621569
/* don't bother filling the rest of the fake pstate */
@@ -1607,12 +1614,11 @@ expandRecordVariable(ParseState *pstate, Var *var, int levelsup)
16071614
* Recurse into the CTE to see what its Var refers to. We
16081615
* have to build an additional level of ParseState to keep
16091616
* in step with varlevelsup in the CTE; furthermore it
1610-
* could be an outer CTE.
1617+
* could be an outer CTE (compare SUBQUERY case above).
16111618
*/
1612-
ParseState mypstate;
1619+
ParseState mypstate = {0};
16131620
Index levelsup;
16141621

1615-
MemSet(&mypstate, 0, sizeof(mypstate));
16161622
/* this loop must work, since GetCTEForRTE did */
16171623
for (levelsup = 0;
16181624
levelsup < rte->ctelevelsup + netlevelsup;

src/backend/utils/adt/ruleutils.c

Lines changed: 22 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -7114,22 +7114,28 @@ get_name_for_var_field(Var *var, int fieldno,
71147114
* Recurse into the sub-select to see what its Var
71157115
* refers to. We have to build an additional level of
71167116
* namespace to keep in step with varlevelsup in the
7117-
* subselect.
7117+
* subselect; furthermore, the subquery RTE might be
7118+
* from an outer query level, in which case the
7119 8000 +
* namespace for the subselect must have that outer
7120+
* level as parent namespace.
71187121
*/
7122+
List *save_nslist = context->namespaces;
7123+
List *parent_namespaces;
71197124
deparse_namespace mydpns;
71207125
const char *result;
71217126

7127+
parent_namespaces = list_copy_tail(context->namespaces,
7128+
netlevelsup);
7129+
71227130
set_deparse_for_query(&mydpns, rte->subquery,
7123-
context->namespaces);
7131+
parent_namespaces);
71247132

7125-
context->namespaces = lcons(&mydpns,
7126-
context->namespaces);
7133+
context->namespaces = lcons(&mydpns, parent_namespaces);
71277134

71287135
result = get_name_for_var_field((Var *) expr, fieldno,
71297136
0, context);
71307137

7131-
context->namespaces =
7132-
list_delete_first(context->namespaces);
7138+
context->namespaces = save_nslist;
71337139

71347140
return result;
71357141
}
@@ -7221,29 +7227,30 @@ get_name_for_var_field(Var *var, int fieldno,
72217227
attnum);
72227228

72237229
if (ste == NULL || ste->resjunk)
7224-
elog(ERROR, "subquery %s does not have attribute %d",
7230+
elog(ERROR, "CTE %s does not have attribute %d",
72257231
rte->eref->aliasname, attnum);
72267232
expr = (Node *) ste->expr;
72277233
if (IsA(expr, Var))
72287234
{
72297235
/*
72307236
* Recurse into the CTE to see what its Var refers to.
72317237
* We have to build an additional level of namespace
7232-
* to keep in step with varlevelsup in the CTE.
7233-
* Furthermore it could be an outer CTE, so we may
7234-
* have to delete some levels of namespace.
7238+
* to keep in step with varlevelsup in the CTE;
7239+
* furthermore it could be an outer CTE (compare
7240+
* SUBQUERY case above).
72357241
*/
72367242
List *save_nslist = context->namespaces;
7237-
List *new_nslist;
7243+
List *parent_namespaces;
72387244
deparse_namespace mydpns;
72397245
const char *result;
72407246

7247+
parent_namespaces = list_copy_tail(context->namespaces,
7248+
ctelevelsup);
7249+
72417250
set_deparse_for_query(&mydpns, ctequery,
7242-
context->namespaces);
7251+
parent_namespaces);
72437252

7244-
new_nslist = list_copy_tail(context->namespaces,
7245-
ctelevelsup);
7246-
context->namespaces = lcons(&mydpns, new_nslist);
7253+
context->namespaces = lcons(&mydpns, parent_namespaces);
72477254

72487255
result = get_name_for_var_field((Var *) expr, fieldno,
72497256
0, context);

src/test/regress/expected/rowtypes.out

Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1093,6 +1093,69 @@ select r, r is null as isnull, r is not null as isnotnull from r;
10931093
(,) | t | f
10941094
(6 rows)
10951095

1096+
--
1097+
-- Check parsing of indirect references to composite values (bug #18077)
1098+
--
1099+
explain (verbose, costs off)
1100+
with cte(c) as (select row(1, 2)),
1101+
cte2(c) as (select * from cte)
1102+
select * from cte2 as t
1103+
where (select * from (select c as c1) s
1104+
where (select (c1).f1 > 0)) is not null;
1105+
QUERY PLAN
1106+
------------------------------------------
1107+
CTE Scan on cte2 t
1108+
Output: t.c
1109+
Filter: ((SubPlan 4) IS NOT NULL)
1110+
CTE cte
1111+
-> Result
1112+
Output: '(1,2)'::record
1113+
CTE cte2
1114+
-> CTE Scan on cte
1115+
Output: cte.c
1116+
SubPlan 4
1117+
-> Result
1118+
Output: t.c
1119+
One-Time Filter: $3
1120+
InitPlan 3 (returns $3)
1121+
-> Result
1122+
Output: ((t.c).f1 > 0)
1123+
(16 rows)
1124+
1125+
with cte(c) as (select row(1, 2)),
1126+
cte2(c) as (select * from cte)
1127+
select * from cte2 as t
1128+
where (select * from (select c as c1) s
1129+
where (select (c1).f1 > 0)) is not null;
1130+
c
1131+
-------
1132+
(1,2)
1133+
(1 row)
1134+
1135+
-- Also check deparsing of such cases
1136+
create view composite_v as
1137+
with cte(c) as (select row(1, 2)),
1138+
cte2(c) as (select * from cte)
1139+
select 1 as one from cte2 as t
1140+
where (select * from (select c as c1) s
1141+
where (select (c1).f1 > 0)) is not null;
1142+
select pg_get_viewdef('composite_v', true);
1143+
pg_get_viewdef
1144+
--------------------------------------------------------
1145+
WITH cte(c) AS ( +
1146+
SELECT ROW(1, 2) AS "row" +
1147+
), cte2(c) AS ( +
1148+
SELECT cte.c +
1149+
FROM cte +
1150+
) +
1151+
SELECT 1 AS one +
1152+
FROM cte2 t +
1153+
WHERE (( SELECT s.c1 +
1154+
FROM ( SELECT t.c AS c1) s +
1155+
WHERE ( SELECT (s.c1).f1 > 0))) IS NOT NULL;
1156+
(1 row)
1157+
1158+
drop view composite_v;
10961159
--
10971160
-- Tests for component access / FieldSelect
10981161
--

src/test/regress/sql/rowtypes.sql

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -450,6 +450,31 @@ with r(a,b) as
450450
(null,row(1,2)), (null,row(null,null)), (null,null) )
451451
select r, r is null as isnull, r is not null as isnotnull from r;
452452

453+
--
454+
-- Check parsing of indirect references to composite values (bug #18077)
455+
--
456+
explain (verbose, costs off)
457+
with cte(c) as (select row(1, 2)),
458+
cte2(c) as (select * from cte)
459+
select * from cte2 as t
460+
where (select * from (select c as c1) s
461+
where (select (c1).f1 > 0)) is not null;
462+
463+
with cte(c) as (select row(1, 2)),
464+
cte2(c) as (select * from cte)
465+
select * from cte2 as t
466+
where (select * from (select c as c1) s
467+
where (select (c1).f1 > 0)) is not null;
468+
469+
-- Also check deparsing of such cases
470+
create view composite_v as
471+
with cte(c) as (select row(1, 2)),
472+
cte2(c) as (select * from cte)
473+
select 1 as one from cte2 as t
474+
where (select * from (select c as c1) s
475+
where (select (c1).f1 > 0)) is not null;
476+
select pg_get_viewdef('composite_v', true);
477+
drop view composite_v;
453478

454479
--
455480
-- Tests for component access / FieldSelect

0 commit comments

Comments
 (0)
0