From f9667899154e6a42e6e73d9f21393384a9ff1dc2 Mon Sep 17 00:00:00 2001 From: Ubuntu Date: Sat, 18 Apr 2026 07:34:58 +0000 Subject: [PATCH] Fix pg_get_viewdef() crash for GRAPH_TABLE views with lateral column refs get_from_clause_item() in ruleutils.c declares a local 'deparse_context context = {0}' inside the RTE_GRAPH_TABLE COLUMNS loop, which shadows the function's 'deparse_context *context' parameter. The zeroed struct has namespaces = NIL, so when get_rule_expr() encounters a Var node (from a lateral column reference in COLUMNS), get_variable() finds list_length(0) and errors with 'bogus varlevelsup: 0 offset 0'. Fix by removing the shadowing local variable and passing the outer context pointer through to get_rule_expr(), which is what every other RTE case in this function already does. This crash affects pg_get_viewdef(), pg_dump, and psql's \d+ for any view containing a GRAPH_TABLE with lateral column references in its COLUMNS clause. --- src/backend/utils/adt/ruleutils.c | 5 +---- src/test/regress/expected/graph_table.out | 19 +++++++++++++++++++ src/test/regress/sql/graph_table.sql | 11 +++++++++++ 3 files changed, 31 insertions(+), 4 deletions(-) diff --git a/src/backend/utils/adt/ruleutils.c b/src/backend/utils/adt/ruleutils.c index 090e8cc2..148eb5bb 100644 --- a/src/backend/utils/adt/ruleutils.c +++ b/src/backend/utils/adt/ruleutils.c @@ -13097,16 +13097,13 @@ get_from_clause_item(Node *jtnode, Query *query, deparse_context *context) foreach(lc, rte->graph_table_columns) { TargetEntry *te = lfirst_node(TargetEntry, lc); - deparse_context context = {0}; if (!first) appendStringInfoString(buf, ", "); else first = false; - context.buf = buf; - - get_rule_expr((Node *) te->expr, &context, false); + get_rule_expr((Node *) te->expr, context, false); appendStringInfoString(buf, " AS "); appendStringInfoString(buf, quote_identifier(te->resname)); } diff --git a/src/test/regress/expected/graph_table.out b/src/test/regress/expected/graph_table.out index b579e3df..d7b767be 100644 --- a/src/test/regress/expected/graph_table.out +++ b/src/test/regress/expected/graph_table.out @@ -932,6 +932,25 @@ SELECT pg_get_viewdef('customers_us'::regclass); ORDER BY customer_name, product_name; (1 row) +-- ruleutils reverse parsing with lateral column reference in COLUMNS +CREATE TABLE lateral_t (val int); +INSERT INTO lateral_t VALUES (1); +CREATE VIEW gt_lateral_view AS + SELECT * FROM lateral_t, + GRAPH_TABLE (myshop MATCH (c IS customers) + COLUMNS (c.name AS cname, lateral_t.val AS lval)); +SELECT pg_get_viewdef('gt_lateral_view'::regclass); + pg_get_viewdef +--------------------------------------------------------------------------------------------------- + SELECT lateral_t.val, + + "graph_table".cname, + + "graph_table".lval + + FROM lateral_t, + + GRAPH_TABLE (myshop MATCH (c IS customers) COLUMNS (c.name AS cname, lateral_t.val AS lval)); +(1 row) + +DROP VIEW gt_lateral_view; +DROP TABLE lateral_t; -- test view/graph nesting CREATE VIEW customers_view AS SELECT customer_id, 'redacted' || customer_id AS name_redacted, address FROM customers; SELECT * FROM customers; diff --git a/src/test/regress/sql/graph_table.sql b/src/test/regress/sql/graph_table.sql index 4ff98817..90e5ea2e 100644 --- a/src/test/regress/sql/graph_table.sql +++ b/src/test/regress/sql/graph_table.sql @@ -528,6 +528,17 @@ SELECT * FROM GRAPH_TABLE (g4 MATCH (s WHERE s.id = 3)-[e]-(d) COLUMNS (s.val, e CREATE VIEW customers_us AS SELECT * FROM GRAPH_TABLE (myshop MATCH (c IS customers WHERE c.address = 'US')-[IS customer_orders | customer_wishlists ]->(l IS orders | wishlists)-[ IS list_items]->(p IS products) COLUMNS (c.name AS customer_name, p.name AS product_name)) ORDER BY customer_name, product_name; SELECT pg_get_viewdef('customers_us'::regclass); +-- ruleutils reverse parsing with lateral column reference in COLUMNS +CREATE TABLE lateral_t (val int); +INSERT INTO lateral_t VALUES (1); +CREATE VIEW gt_lateral_view AS + SELECT * FROM lateral_t, + GRAPH_TABLE (myshop MATCH (c IS customers) + COLUMNS (c.name AS cname, lateral_t.val AS lval)); +SELECT pg_get_viewdef('gt_lateral_view'::regclass); +DROP VIEW gt_lateral_view; +DROP TABLE lateral_t; + -- test view/graph nesting CREATE VIEW customers_view AS SELECT customer_id, 'redacted' || customer_id AS name_redacted, address FROM customers; -- 2.43.0