[Bug]Assertion failure in LATERAL GRAPH_TABLE with multi-label pattern

Started by SATYANARAYANA NARLAPURAM27 days ago5 messageshackers
Jump to latest
#1SATYANARAYANA NARLAPURAM
satyanarlapuram@gmail.com

Hi hackers,

A LATERAL GRAPH_TABLE whose pattern matches more than one path query
fails with the assert

TRAP: failed Assert("!bms_is_member(rti, lateral_relids)"), File:
"initsplan.c", Line: 1428, PID: 3586144
postgres: postgres postgres [local]
SELECT(ExceptionalCondition+0x70)[0x63488e3cc070]
postgres: postgres postgres [local]
SELECT(create_lateral_join_info+0x468)[0x63488e14ac28]
postgres: postgres postgres [local]
SELECT(query_planner+0x13a)[0x63488e14dfca]

Repro:
SELECT v1.vname, gt.aname
FROM v1, LATERAL (SELECT * FROM GRAPH_TABLE (g1 MATCH (a IS vl1 | vl2
WHERE a.vprop1 = v1.vprop1) COLUMNS (a.vname AS aname)) g) gt
ORDER BY 1, 2;

Single-label GRAPH_TABLE with the same outer reference works fine.
rewriteGraphTable() turns the GRAPH_TABLE RTE into a subquery RTE and
bumps outer Vars by one sublevel. When the pattern produces multiple
path queries, generate_setop_from_pathqueries() wraps each one in
another subquery RTE for the UNION ALL but does not bump again, so the
lateral reference collapses onto GRAPH_TABLE's own RTE.

Tried fixing this by bumping each lquery's sublevels by 1 before the
addRangeTableEntry
ForSubquery() wrap. Single-pathquery queries skip this path entirely.

Attached a patch with the tests.

Thanks,
Satya

Attachments:

0001-Fix-assertion-failure-in-LATERAL-GRAPH_TABLE-with-mu.patchapplication/octet-stream; name=0001-Fix-assertion-failure-in-LATERAL-GRAPH_TABLE-with-mu.patchDownload+33-1
#2Ashutosh Bapat
ashutosh.bapat@enterprisedb.com
In reply to: SATYANARAYANA NARLAPURAM (#1)
Re: [Bug]Assertion failure in LATERAL GRAPH_TABLE with multi-label pattern

On Thu, May 7, 2026 at 9:43 AM SATYANARAYANA NARLAPURAM
<satyanarlapuram@gmail.com> wrote:

Hi hackers,

A LATERAL GRAPH_TABLE whose pattern matches more than one path query
fails with the assert

TRAP: failed Assert("!bms_is_member(rti, lateral_relids)"), File: "initsplan.c", Line: 1428, PID: 3586144
postgres: postgres postgres [local] SELECT(ExceptionalCondition+0x70)[0x63488e3cc070]
postgres: postgres postgres [local] SELECT(create_lateral_join_info+0x468)[0x63488e14ac28]
postgres: postgres postgres [local] SELECT(query_planner+0x13a)[0x63488e14dfca]

Repro:
SELECT v1.vname, gt.aname
FROM v1, LATERAL (SELECT * FROM GRAPH_TABLE (g1 MATCH (a IS vl1 | vl2 WHERE a.vprop1 = v1.vprop1) COLUMNS (a.vname AS aname)) g) gt
ORDER BY 1, 2;

Single-label GRAPH_TABLE with the same outer reference works fine.
rewriteGraphTable() turns the GRAPH_TABLE RTE into a subquery RTE and
bumps outer Vars by one sublevel. When the pattern produces multiple
path queries, generate_setop_from_pathqueries() wraps each one in
another subquery RTE for the UNION ALL but does not bump again, so the
lateral reference collapses onto GRAPH_TABLE's own RTE.

+ /* Wrapping lquery in a subquery RTE adds one query level, so bump
+ * outer-level Vars accordingly. */
+ IncrementVarSublevelsUp((Node *) lquery, 1, 1);
+

Multiline comments start with /* and end with */ on separate lines respectively.

From the comment it feels like we will add as many varlevels as the
depth of setop tree, since we will add a subquery RTE for each setop
level. In reality all the legs of the setop tree will be at the same
varlevel. A better comment would be "Vars in each path query are one
level away from the setop query combining them.".

The connection between varlevelsup, addition of subquery RTE and the
assertion failure isn't clear. Assertion failure indicates that the
relation being examined has lateral reference to itself which boils
down to range table entry index but the varlevelsup is about depth of
a given query. The connection between these two seemingly different
things needs to be explained in the commit message. Though the code
changes fix the problem, there may be a better place to increment
varlevels up. That will be clear once the connection is clear.

Tried fixing this by bumping each lquery's sublevels by 1 before the addRangeTableEntry
ForSubquery() wrap. Single-pathquery queries skip this path entirely.

Attached a patch with the tests.

Do we need both the tests? The second one has no label expression
which means it will try all the labels. So the test will reproduce the
bug only when there are multiple labels. The first test explicitly
adds the multi-label pattern. I think we should just leave the first
one and remove the second one. Also this test should be placed in the
section which tests other lateral references. myshop property graph
has two sets of elements that share a label - order and wishlist,
order_items and wishlist_items.

--
Best Wishes,
Ashutosh Bapat

#3SATYANARAYANA NARLAPURAM
satyanarlapuram@gmail.com
In reply to: Ashutosh Bapat (#2)
Re: [Bug]Assertion failure in LATERAL GRAPH_TABLE with multi-label pattern

HI,

On Tue, May 12, 2026 at 4:15 AM Ashutosh Bapat <ashutosh.bapat.oss@gmail.com>
wrote:

On Thu, May 7, 2026 at 9:43 AM SATYANARAYANA NARLAPURAM
<satyanarlapuram@gmail.com> wrote:

Hi hackers,

A LATERAL GRAPH_TABLE whose pattern matches more than one path query
fails with the assert

TRAP: failed Assert("!bms_is_member(rti, lateral_relids)"), File:

"initsplan.c", Line: 1428, PID: 3586144

postgres: postgres postgres [local]

SELECT(ExceptionalCondition+0x70)[0x63488e3cc070]

postgres: postgres postgres [local]

SELECT(create_lateral_join_info+0x468)[0x63488e14ac28]

postgres: postgres postgres [local]

SELECT(query_planner+0x13a)[0x63488e14dfca]

Repro:
SELECT v1.vname, gt.aname
FROM v1, LATERAL (SELECT * FROM GRAPH_TABLE (g1 MATCH (a IS vl1 | vl2

WHERE a.vprop1 = v1.vprop1) COLUMNS (a.vname AS aname)) g) gt

ORDER BY 1, 2;

Single-label GRAPH_TABLE with the same outer reference works fine.
rewriteGraphTable() turns the GRAPH_TABLE RTE into a subquery RTE and
bumps outer Vars by one sublevel. When the pattern produces multiple
path queries, generate_setop_from_pathqueries() wraps each one in
another subquery RTE for the UNION ALL but does not bump again, so the
lateral reference collapses onto GRAPH_TABLE's own RTE.

+ /* Wrapping lquery in a subquery RTE adds one query level, so bump
+ * outer-level Vars accordingly. */
+ IncrementVarSublevelsUp((Node *) lquery, 1, 1);
+

Multiline comments start with /* and end with */ on separate lines
respectively.

From the comment it feels like we will add as many varlevels as the
depth of setop tree, since we will add a subquery RTE for each setop
level. In reality all the legs of the setop tree will be at the same
varlevel. A better comment would be "Vars in each path query are one
level away from the setop query combining them.".

The connection between varlevelsup, addition of subquery RTE and the
assertion failure isn't clear. Assertion failure indicates that the
relation being examined has lateral reference to itself which boils
down to range table entry index but the varlevelsup is about depth of
a given query. The connection between these two seemingly different
things needs to be explained in the commit message. Though the code
changes fix the problem, there may be a better place to increment
varlevels up. That will be clear once the connection is clear.

Tried fixing this by bumping each lquery's sublevels by 1 before the

addRangeTableEntry

ForSubquery() wrap. Single-pathquery queries skip this path entirely.

Attached a patch with the tests.

Do we need both the tests? The second one has no label expression
which means it will try all the labels. So the test will reproduce the
bug only when there are multiple labels. The first test explicitly
adds the multi-label pattern. I think we should just leave the first
one and remove the second one. Also this test should be placed in the
section which tests other lateral references. myshop property graph
has two sets of elements that share a label - order and wishlist,
order_items and wishlist_items.

Thanks for reviewing, will address the comments shortly.

#4Ayush Tiwari
ayushtiwari.slg01@gmail.com
In reply to: SATYANARAYANA NARLAPURAM (#3)
Re: [Bug]Assertion failure in LATERAL GRAPH_TABLE with multi-label pattern

Hi,

On Tue, 12 May 2026 at 23:12, SATYANARAYANA NARLAPURAM <
satyanarlapuram@gmail.com> wrote:

HI,

On Tue, May 12, 2026 at 4:15 AM Ashutosh Bapat <
ashutosh.bapat.oss@gmail.com> wrote:

On Thu, May 7, 2026 at 9:43 AM SATYANARAYANA NARLAPURAM
<satyanarlapuram@gmail.com> wrote:

Hi hackers,

A LATERAL GRAPH_TABLE whose pattern matches more than one path query
fails with the assert

TRAP: failed Assert("!bms_is_member(rti, lateral_relids)"), File:

"initsplan.c", Line: 1428, PID: 3586144

postgres: postgres postgres [local]

SELECT(ExceptionalCondition+0x70)[0x63488e3cc070]

postgres: postgres postgres [local]

SELECT(create_lateral_join_info+0x468)[0x63488e14ac28]

postgres: postgres postgres [local]

SELECT(query_planner+0x13a)[0x63488e14dfca]

Repro:
SELECT v1.vname, gt.aname
FROM v1, LATERAL (SELECT * FROM GRAPH_TABLE (g1 MATCH (a IS vl1 | vl2

WHERE a.vprop1 = v1.vprop1) COLUMNS (a.vname AS aname)) g) gt

ORDER BY 1, 2;

Single-label GRAPH_TABLE with the same outer reference works fine.
rewriteGraphTable() turns the GRAPH_TABLE RTE into a subquery RTE and
bumps outer Vars by one sublevel. When the pattern produces multiple
path queries, generate_setop_from_pathqueries() wraps each one in
another subquery RTE for the UNION ALL but does not bump again, so the
lateral reference collapses onto GRAPH_TABLE's own RTE.

+ /* Wrapping lquery in a subquery RTE adds one query level, so bump
+ * outer-level Vars accordingly. */
+ IncrementVarSublevelsUp((Node *) lquery, 1, 1);
+

Multiline comments start with /* and end with */ on separate lines
respectively.

From the comment it feels like we will add as many varlevels as the
depth of setop tree, since we will add a subquery RTE for each setop
level. In reality all the legs of the setop tree will be at the same
varlevel. A better comment would be "Vars in each path query are one
level away from the setop query combining them.".

The connection between varlevelsup, addition of subquery RTE and the
assertion failure isn't clear. Assertion failure indicates that the
relation being examined has lateral reference to itself which boils
down to range table entry index but the varlevelsup is about depth of
a given query. The connection between these two seemingly different
things needs to be explained in the commit message. Though the code
changes fix the problem, there may be a better place to increment
varlevels up. That will be clear once the connection is clear.

Tried fixing this by bumping each lquery's sublevels by 1 before the

addRangeTableEntry

ForSubquery() wrap. Single-pathquery queries skip this path entirely.

Attached a patch with the tests.

Do we need both the tests? The second one has no label expression
which means it will try all the labels. So the test will reproduce the
bug only when there are multiple labels. The first test explicitly
adds the multi-label pattern. I think we should just leave the first
one and remove the second one. Also this test should be placed in the
section which tests other lateral references. myshop property graph
has two sets of elements that share a label - order and wishlist,
order_items and wishlist_items.

Thanks for reviewing, will address the comments shortly.

Attached is v2 of Satya's patch, addressing the review comments.

Changes in v2:

- Reworded the inline comment in PostgreSQL multi-line style. It now
explains
that each path query is inserted as a subquery RTE of the setop query, so
Vars that already refer outside the path query must be adjusted for the
additional query level. Local Vars belonging to the path query are
unchanged (the call uses min_sublevels_up = 1).

- Expanded the commit message to spell out the connection between the
missing
varlevelsup adjustment and the lateral self-reference assertion.

- Reduced the regression coverage to a single explicit multi-label case, and
moved it next to the existing lateral-reference tests rather than at the
end of the file.

The fix is kept at the same place (generate_setop_from_pathqueries), since
that is where the extra subquery RTE wrapping happens. Doing it any earlier
would over-adjust the single-pathquery path.

Regards,
Ayush

Attachments:

v2-0001-Fix-assertion-failure-in-LATERAL-GRAPH_TABLE-with-multi-label-pattern.patchapplication/octet-stream; name=v2-0001-Fix-assertion-failure-in-LATERAL-GRAPH_TABLE-with-multi-label-pattern.patchDownload+25-0
#5Ashutosh Bapat
ashutosh.bapat@enterprisedb.com
In reply to: Ayush Tiwari (#4)
Re: [Bug]Assertion failure in LATERAL GRAPH_TABLE with multi-label pattern

Hi Ayush,

On Tue, May 19, 2026 at 8:46 PM Ayush Tiwari
<ayushtiwari.slg01@gmail.com> wrote:

Hi,

On Tue, 12 May 2026 at 23:12, SATYANARAYANA NARLAPURAM <satyanarlapuram@gmail.com> wrote:

HI,

On Tue, May 12, 2026 at 4:15 AM Ashutosh Bapat <ashutosh.bapat.oss@gmail.com> wrote:

On Thu, May 7, 2026 at 9:43 AM SATYANARAYANA NARLAPURAM
<satyanarlapuram@gmail.com> wrote:

Hi hackers,

A LATERAL GRAPH_TABLE whose pattern matches more than one path query
fails with the assert

TRAP: failed Assert("!bms_is_member(rti, lateral_relids)"), File: "initsplan.c", Line: 1428, PID: 3586144
postgres: postgres postgres [local] SELECT(ExceptionalCondition+0x70)[0x63488e3cc070]
postgres: postgres postgres [local] SELECT(create_lateral_join_info+0x468)[0x63488e14ac28]
postgres: postgres postgres [local] SELECT(query_planner+0x13a)[0x63488e14dfca]

Repro:
SELECT v1.vname, gt.aname
FROM v1, LATERAL (SELECT * FROM GRAPH_TABLE (g1 MATCH (a IS vl1 | vl2 WHERE a.vprop1 = v1.vprop1) COLUMNS (a.vname AS aname)) g) gt
ORDER BY 1, 2;

Single-label GRAPH_TABLE with the same outer reference works fine.
rewriteGraphTable() turns the GRAPH_TABLE RTE into a subquery RTE and
bumps outer Vars by one sublevel. When the pattern produces multiple
path queries, generate_setop_from_pathqueries() wraps each one in
another subquery RTE for the UNION ALL but does not bump again, so the
lateral reference collapses onto GRAPH_TABLE's own RTE.

+ /* Wrapping lquery in a subquery RTE adds one query level, so bump
+ * outer-level Vars accordingly. */
+ IncrementVarSublevelsUp((Node *) lquery, 1, 1);
+

Multiline comments start with /* and end with */ on separate lines respectively.

From the comment it feels like we will add as many varlevels as the
depth of setop tree, since we will add a subquery RTE for each setop
level. In reality all the legs of the setop tree will be at the same
varlevel. A better comment would be "Vars in each path query are one
level away from the setop query combining them.".

The connection between varlevelsup, addition of subquery RTE and the
assertion failure isn't clear. Assertion failure indicates that the
relation being examined has lateral reference to itself which boils
down to range table entry index but the varlevelsup is about depth of
a given query. The connection between these two seemingly different
things needs to be explained in the commit message. Though the code
changes fix the problem, there may be a better place to increment
varlevels up. That will be clear once the connection is clear.

Tried fixing this by bumping each lquery's sublevels by 1 before the addRangeTableEntry
ForSubquery() wrap. Single-pathquery queries skip this path entirely.

Attached a patch with the tests.

Do we need both the tests? The second one has no label expression
which means it will try all the labels. So the test will reproduce the
bug only when there are multiple labels. The first test explicitly
adds the multi-label pattern. I think we should just leave the first
one and remove the second one. Also this test should be placed in the
section which tests other lateral references. myshop property graph
has two sets of elements that share a label - order and wishlist,
order_items and wishlist_items.

Thanks for reviewing, will address the comments shortly.

Attached is v2 of Satya's patch, addressing the review comments.

Thanks for the patch addressing comments.

Changes in v2:

- Reworded the inline comment in PostgreSQL multi-line style. It now explains
that each path query is inserted as a subquery RTE of the setop query, so
Vars that already refer outside the path query must be adjusted for the
additional query level. Local Vars belonging to the path query are
unchanged (the call uses min_sublevels_up = 1).

- Expanded the commit message to spell out the connection between the missing
varlevelsup adjustment and the lateral self-reference assertion.

- Reduced the regression coverage to a single explicit multi-label case, and
moved it next to the existing lateral-reference tests rather than at the
end of the file.

The fix is kept at the same place (generate_setop_from_pathqueries), since
that is where the extra subquery RTE wrapping happens. Doing it any earlier
would over-adjust the single-pathquery path.

Yes, fix is at the right place. We can not set the varlevelsup to the
correct value in replace_properties_references itself because when
that function is called, we do not know whether UNION is required or
not. The next place to adjust varlevelsup is when creating a UNION
statement.

Depending upon the varno of outer reference we will see different
symptoms. a crash or an error "plan should not reference subplan's
variable" or even data type mismatch. Also the levelsup is maintained
in nodes other than Var. I have modified the commit message to focus
on the problem instead of the symptom. Also some small edits to the
comments.

Attached patch has the number 0010 (vs 0001) since I am creating
patches for all the SQL/PGQ fixes from the same branch. It can be
applied independent of other patches.
--
Best Wishes,
Ashutosh Bapat

Attachments:

v20260602-0010-Fix-LATERAL-references-in-GRAPH_TABLE-with.patchtext/x-patch; charset=US-ASCII; name=v20260602-0010-Fix-LATERAL-references-in-GRAPH_TABLE-with.patchDownload+27-1