Location via proxy:   [ UP ]  
[Report a bug]   [Manage cookies]                
Skip to content

Commit 49076fd

Browse files
committed
Fix list-manipulation bug in WITH RECURSIVE processing.
makeDependencyGraphWalker and checkWellFormedRecursionWalker thought they could hold onto a pointer to a list's first cons cell while the list was modified by recursive calls. That was okay when the cons cell was actually separately palloc'd ... but since commit 1cff1b9, it's quite unsafe, leading to core dumps or incorrect complaints of faulty WITH nesting. In the field this'd require at least a seven-deep WITH nest to cause an issue, but enabling DEBUG_LIST_MEMORY_USAGE allows the bug to be seen with lesser nesting depths. Per bug #16801 from Alexander Lakhin. Back-patch to v13. Michael Paquier and Tom Lane Discussion: https://postgr.es/m/16801-393c7922143eaa4d@postgresql.org
1 parent 1f56ae3 commit 49076fd

File tree

3 files changed

+109
-6
lines changed

3 files changed

+109
-6
lines changed

src/backend/parser/parse_cte.c

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -537,15 +537,15 @@ makeDependencyGraphWalker(Node *node, CteState *cstate)
537537
* In the non-RECURSIVE case, query names are visible to the
538538
* WITH items after them and to the main query.
539539
*/
540-
ListCell *cell1;
541-
542540
cstate->innerwiths = lcons(NIL, cstate->innerwiths);
543-
cell1 = list_head(cstate->innerwiths);
544541
foreach(lc, stmt->withClause->ctes)
545542
{
546543
CommonTableExpr *cte = (CommonTableExpr *) lfirst(lc);
544+
ListCell *cell1;
547545

548546
(void) makeDependencyGraphWalker(cte->ctequery, cstate);
547+
/* note that recursion could mutate innerwiths list */
548+
cell1 = list_head(cstate->innerwiths);
549549
lfirst(cell1) = lappend((List *) lfirst(cell1), cte);
550550
}
551551
(void) raw_expression_tree_walker(node,
@@ -813,15 +813,15 @@ checkWellFormedRecursionWalker(Node *node, CteState *cstate)
813813
* In the non-RECURSIVE case, query names are visible to the
814814
* WITH items after them and to the main query.
815815
*/
816-
ListCell *cell1;
817-
818816
cstate->innerwiths = lcons(NIL, cstate->innerwiths);
819-
cell1 = list_head(cstate->innerwiths);
820817
foreach(lc, stmt->withClause->ctes)
821818
{
822819
CommonTableExpr *cte = (CommonTableExpr *) lfirst(lc);
820+
ListCell *cell1;
823821

824822
(void) checkWellFormedRecursionWalker(cte->ctequery, cstate);
823+
/* note that recursion could mutate innerwiths list */
824+
cell1 = list_head(cstate->innerwiths);
825825
lfirst(cell1) = lappend((List *) lfirst(cell1), cte);
826826
}
827827
checkWellFormedSelectStmt(stmt, cstate);

src/test/regress/expected/with.out

Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -167,6 +167,65 @@ ERROR: operator does not exist: text + integer
167167
LINE 4: SELECT n+1 FROM t WHERE n < 10
168168
^
169169
HINT: No operator matches the given name and argument types. You might need to add explicit type casts.
170+
-- Deeply nested WITH caused a list-munging problem in v13
171+
-- Detection of cross-references and self-references
172+
WITH RECURSIVE w1(c1) AS
173+
(WITH w2(c2) AS
174+
(WITH w3(c3) AS
175+
(WITH w4(c4) AS
176+
(WITH w5(c5) AS
177+
(WITH RECURSIVE w6(c6) AS
178+
(WITH w6(c6) AS
179+
(WITH w8(c8) AS
180+
(SELECT 1)
181+
SELECT * FROM w8)
182+
SELECT * FROM w6)
183+
SELECT * FROM w6)
184+
SELECT * FROM w5)
185+
SELECT * FROM w4)
186+
SELECT * FROM w3)
187+
SELECT * FROM w2)
188+
SELECT * FROM w1;
189+
c1
190+
----
191+
1
192+
(1 row)
193+
194+
-- Detection of invalid self-references
195+
WITH RECURSIVE outermost(x) AS (
196+
SELECT 1
197+
UNION (WITH innermost1 AS (
198+
SELECT 2
199+
UNION (WITH innermost2 AS (
200+
SELECT 3
201+
UNION (WITH innermost3 AS (
202+
SELECT 4
203+
UNION (WITH innermost4 AS (
204+
SELECT 5
205+
UNION (WITH innermost5 AS (
206+
SELECT 6
207+
UNION (WITH innermost6 AS
208+
(SELECT 7)
209+
SELECT * FROM innermost6))
210+
SELECT * FROM innermost5))
211+
SELECT * FROM innermost4))
212+
SELECT * FROM innermost3))
213+
SELECT * FROM innermost2))
214+
SELECT * FROM outermost
215+
UNION SELECT * FROM innermost1)
216+
)
217+
SELECT * FROM outermost ORDER BY 1;
218+
x
219+
---
220+
1
221+
2
222+
3
223+
4
224+
5
225+
6
226+
7
227+
(7 rows)
228+
170229
--
171230
-- Some examples with a tree
172231
--

src/test/regress/sql/with.sql

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -87,6 +87,50 @@ UNION ALL
8787
)
8888
SELECT n, n IS OF (int) AS is_int FROM t;
8989

90+
-- Deeply nested WITH caused a list-munging problem in v13
91+
-- Detection of cross-references and self-references
92+
WITH RECURSIVE w1(c1) AS
93+
(WITH w2(c2) AS
94+
(WITH w3(c3) AS
95+
(WITH w4(c4) AS
96+
(WITH w5(c5) AS
97+
(WITH RECURSIVE w6(c6) AS
98+
(WITH w6(c6) AS
99+
(WITH w8(c8) AS
100+
(SELECT 1)
101+
SELECT * FROM w8)
102+
SELECT * FROM w6)
103+
SELECT * FROM w6)
104+
SELECT * FROM w5)
105+
SELECT * FROM w4)
106+
SELECT * FROM w3)
107+
SELECT * FROM w2)
108+
SELECT * FROM w1;
109+
-- Detection of invalid self-references
110+
WITH RECURSIVE outermost(x) AS (
111+
SELECT 1
112+
UNION (WITH innermost1 AS (
113+
SELECT 2
114+
UNION (WITH innermost2 AS (
115+
SELECT 3
116+
UNION (WITH innermost3 AS (
117+
SELECT 4
118+
UNION (WITH innermost4 AS (
119+
SELECT 5
120+
UNION (WITH innermost5 AS (
121+
SELECT 6
122+
UNION (WITH innermost6 AS
123+
(SELECT 7)
124+
SELECT * FROM innermost6))
125+
SELECT * FROM innermost5))
126+
SELECT * FROM innermost4))
127+
SELECT * FROM innermost3))
128+
SELECT * FROM innermost2))
129+
SELECT * FROM outermost
130+
UNION SELECT * FROM innermost1)
131+
)
132+
SELECT * FROM outermost ORDER BY 1;
133+
90134
--
91135
-- Some examples with a tree
92136
--

0 commit comments

Comments
 (0)