0001-Fix-pg_get_viewdef-crash-for-GRAPH_TABLE-views-with-.patch

application/octet-stream

Filename: 0001-Fix-pg_get_viewdef-crash-for-GRAPH_TABLE-views-with-.patch
Type: application/octet-stream
Part: 0
Message: Bug: pg_get_viewdef() fails on GRAPH_TABLE views with lateral column references

Patch

Same data as JSON: GET /api/v1/attachments/:id/patch the parsed metadata as JSON — format, series position, per-file stats; never the diff bytes. API reference →
Format: format-patch
Series: patch 0001
Subject: Fix pg_get_viewdef() crash for GRAPH_TABLE views with lateral column refs
File+
src/backend/utils/adt/ruleutils.c 1 4
src/test/regress/expected/graph_table.out 19 0
src/test/regress/sql/graph_table.sql 11 0
From f9667899154e6a42e6e73d9f21393384a9ff1dc2 Mon Sep 17 00:00:00 2001
From: Ubuntu
 <azureuser@pgclient.5sdvw5dac2hu3o3frfdg3sxydd.zx.internal.cloudapp.net>
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