public inbox for [email protected]  
help / color / mirror / Atom feed
Bug: Missing check_stack_depth() in GRAPH_TABLE rewriter
4+ messages / 3 participants
[nested] [flat]

* Bug: Missing check_stack_depth() in GRAPH_TABLE rewriter
@ 2026-04-11 03:57 SATYANARAYANA NARLAPURAM <[email protected]>
  2026-04-15 15:07 ` Re: Bug: Missing check_stack_depth() in GRAPH_TABLE rewriter Ashutosh Bapat <[email protected]>
  0 siblings, 1 reply; 4+ messages in thread

From: SATYANARAYANA NARLAPURAM @ 2026-04-11 03:57 UTC (permalink / raw)
  To: PostgreSQL Hackers <[email protected]>; Ashutosh Bapat <[email protected]>; Peter Eisentraut <[email protected]>

Hi Hackers,

Two recursive functions, generate_setop_from_pathqueries() and
generate_queries_for_path_pattern_recurse() in GRAPH_TABLE rewriter are
missing check_stack_depth() calls. Consider a property graph with  400 edge
tables with a 2-hop pattern produce 160,000 path queries and therefore
160,000 recursion frames in
generate_setop_from_pathqueries(). This can easily exceed the OS stack
limit for some systems and crashes the backend.

I am proposing a stop gap fix of checking stack depth by calling
check_stack_depth for now but in future we may want to reduce the recursion
depth (for example use a balanced tree to reduce depth from O(N) to O(log
N).

Attached a patch for adding the check_stack_depth() check.

Repro:

CREATE TABLE sv (id int PRIMARY KEY);
  INSERT INTO sv VALUES (1);

  DO $$
  BEGIN
    FOR i IN 1..400 LOOP
      EXECUTE format(
        'CREATE TABLE se_%s (id int PRIMARY KEY, src int, dst int)', i);
    END LOOP;
  END$$;

  DO $$
  DECLARE
    sql text;
    edges text := '';
  BEGIN
    FOR i IN 1..400 LOOP
      IF i > 1 THEN edges := edges || ', '; END IF;
      edges := edges || format(
        'se_%s KEY (id) SOURCE KEY (src) REFERENCES sv (id) '
        'DESTINATION KEY (dst) REFERENCES sv (id)', i);
    END LOOP;
    EXECUTE 'CREATE PROPERTY GRAPH g VERTEX TABLES (sv KEY (id)) '
         || 'EDGE TABLES (' || edges || ')';
  END$$;

  SELECT * FROM GRAPH_TABLE(g
    MATCH (a)-[e1]->(b)-[e2]->(c)
    COLUMNS(a.id AS a_id))
  LIMIT 1;

Thanks,
Satya


Attachments:

  [application/octet-stream] 0001-graph-table-check-stack-depth.patch (1.9K, 3-0001-graph-table-check-stack-depth.patch)
  download | inline diff:
From 7920501aea7877bcda762bf0cdd14b20b817eadb Mon Sep 17 00:00:00 2001
From: Satya Narlapuram <[email protected]>
Date: Sat, 11 Apr 2026 03:35:06 +0000
Subject: [PATCH] Add check_stack_depth() to GRAPH_TABLE rewriter recursive
 functions

generate_setop_from_pathqueries() builds a right-recursive UNION ALL tree
with recursion depth equal to the number of valid path queries. Similarly,
generate_queries_for_path_pattern_recurse() recurses once per path factor
position to enumerate path combinations. Neither function called
check_stack_depth().

Add check_stack_depth() to both functions so that excessive recursion is
caught cleanly with an ERROR rather than a server crash.
---
 src/backend/rewrite/rewriteGraphTable.c | 7 +++++++
 1 file changed, 7 insertions(+)

diff --git a/src/backend/rewrite/rewriteGraphTable.c b/src/backend/rewrite/rewriteGraphTable.c
index 2c3199d3..429ef1cf 100644
--- a/src/backend/rewrite/rewriteGraphTable.c
+++ b/src/backend/rewrite/rewriteGraphTable.c
@@ -17,6 +17,7 @@
 #include "access/sysattr.h"
 #include "access/table.h"
 #include "access/htup_details.h"
+#include "miscadmin.h"
 #include "catalog/pg_operator.h"
 #include "catalog/pg_propgraph_element.h"
 #include "catalog/pg_propgraph_element_label.h"
@@ -361,6 +362,9 @@ generate_queries_for_path_pattern_recurse(RangeTblEntry *rte, List *pathqueries,
 {
 	List	   *path_elems = list_nth_node(List, path_elem_lists, elempos);
 
+	/* Guard against stack overflow due to complex path patterns. */
+	check_stack_depth();
+
 	foreach_ptr(struct path_element, pe, path_elems)
 	{
 		/* Update current path being built with current element. */
@@ -698,6 +702,9 @@ generate_setop_from_pathqueries(List *pathqueries, List **rtable, List **targetl
 	List	   *rtargetlist;
 	ParseNamespaceItem *pni;
 
+	/* Guard against stack overflow due to many path queries. */
+	check_stack_depth();
+
 	/* Recursion termination condition. */
 	if (list_length(pathqueries) == 0)
 	{
-- 
2.43.0



^ permalink  raw  reply  [nested|flat] 4+ messages in thread

* Re: Bug: Missing check_stack_depth() in GRAPH_TABLE rewriter
  2026-04-11 03:57 Bug: Missing check_stack_depth() in GRAPH_TABLE rewriter SATYANARAYANA NARLAPURAM <[email protected]>
@ 2026-04-15 15:07 ` Ashutosh Bapat <[email protected]>
  2026-04-24 06:24   ` Re: Bug: Missing check_stack_depth() in GRAPH_TABLE rewriter Peter Eisentraut <[email protected]>
  0 siblings, 1 reply; 4+ messages in thread

From: Ashutosh Bapat @ 2026-04-15 15:07 UTC (permalink / raw)
  To: SATYANARAYANA NARLAPURAM <[email protected]>; +Cc: PostgreSQL Hackers <[email protected]>; Peter Eisentraut <[email protected]>

On Sat, Apr 11, 2026 at 9:28 AM SATYANARAYANA NARLAPURAM
<[email protected]> wrote:
>
> Hi Hackers,
>
> Two recursive functions, generate_setop_from_pathqueries() and
> generate_queries_for_path_pattern_recurse() in GRAPH_TABLE rewriter are missing check_stack_depth() calls. Consider a property graph with  400 edge tables with a 2-hop pattern produce 160,000 path queries and therefore 160,000 recursion frames in
> generate_setop_from_pathqueries(). This can easily exceed the OS stack limit for some systems and crashes the backend.
>
> I am proposing a stop gap fix of checking stack depth by calling check_stack_depth for now but in future we may want to reduce the recursion depth (for example use a balanced tree to reduce depth from O(N) to O(log N).
>
> Attached a patch for adding the check_stack_depth() check.
>
> Repro:
>
> CREATE TABLE sv (id int PRIMARY KEY);
>   INSERT INTO sv VALUES (1);
>
>   DO $$
>   BEGIN
>     FOR i IN 1..400 LOOP
>       EXECUTE format(
>         'CREATE TABLE se_%s (id int PRIMARY KEY, src int, dst int)', i);
>     END LOOP;
>   END$$;
>
>   DO $$
>   DECLARE
>     sql text;
>     edges text := '';
>   BEGIN
>     FOR i IN 1..400 LOOP
>       IF i > 1 THEN edges := edges || ', '; END IF;
>       edges := edges || format(
>         'se_%s KEY (id) SOURCE KEY (src) REFERENCES sv (id) '
>         'DESTINATION KEY (dst) REFERENCES sv (id)', i);
>     END LOOP;
>     EXECUTE 'CREATE PROPERTY GRAPH g VERTEX TABLES (sv KEY (id)) '
>          || 'EDGE TABLES (' || edges || ')';
>   END$$;
>
>   SELECT * FROM GRAPH_TABLE(g
>     MATCH (a)-[e1]->(b)-[e2]->(c)
>     COLUMNS(a.id AS a_id))
>   LIMIT 1;
>

Thanks for the report. I could reproduce the segfault on my laptop.
The attached patch fixes it and gives ERROR:  stack depth limit
exceeded.

generate_queries_for_path_pattern_recurse() - has to work in a linear
fashion since the elements need to be processed in an order. Each
permutation of elements produces one query. These queries can be
arranged in a balanced tree as you  suggest OR when constructing the
setop tree we could generate it in divide-and-conquer manner. However,
the tree will be flattened in the planner anyway (See
flatten_simple_union_all() and pull_up_simple_union_all()). Thus the
final planning will require a deeper stack anyway. The code complexity
doesn't seem to be worth it.

I also looked at a few commits that add check_stack_depth() to see if
we add tests for these scenarios. But I didn't find any. So no tests
added with this commit.



--
Best Wishes,
Ashutosh Bapat


Attachments:

  [text/x-patch] v20260415-0001-Check-for-stack-overflow-when-rewriting-gr.patch (2.1K, 2-v20260415-0001-Check-for-stack-overflow-when-rewriting-gr.patch)
  download | inline diff:
From 71a851e9d74e1c3d74428700eca1f11437600bcd Mon Sep 17 00:00:00 2001
From: Ashutosh Bapat <[email protected]>
Date: Wed, 15 Apr 2026 17:38:25 +0530
Subject: [PATCH v20260415 2/3] Check for stack overflow when rewriting graph
 queries.

generate_queries_for_path_pattern_recurse() and
generate_setop_from_pathqueries() are recursive functions. For a property graph
with hundreds of tables, a graph pattern with a handful element patterns can
cause stack overflow. Fix it by calling check_stack_depth() at the beginning of
these functions.

Reported-by: Satyanarayana Narlapuram <[email protected]>
Author: Satyanarayana Narlapuram <[email protected]>
Reviewed-by: Ashutosh Bapat <[email protected]>
Discussion: https://www.postgresql.org/message-id/CAHg+QDfgK0xddH8f3eAb+UVn7sBDOnv8RvM6OkP4HtHAt6aD7w@mail.gmail.com
---
 src/backend/rewrite/rewriteGraphTable.c | 7 +++++++
 1 file changed, 7 insertions(+)

diff --git a/src/backend/rewrite/rewriteGraphTable.c b/src/backend/rewrite/rewriteGraphTable.c
index 2c3199d3230..429ef1cf1f4 100644
--- a/src/backend/rewrite/rewriteGraphTable.c
+++ b/src/backend/rewrite/rewriteGraphTable.c
@@ -17,6 +17,7 @@
 #include "access/sysattr.h"
 #include "access/table.h"
 #include "access/htup_details.h"
+#include "miscadmin.h"
 #include "catalog/pg_operator.h"
 #include "catalog/pg_propgraph_element.h"
 #include "catalog/pg_propgraph_element_label.h"
@@ -361,6 +362,9 @@ generate_queries_for_path_pattern_recurse(RangeTblEntry *rte, List *pathqueries,
 {
 	List	   *path_elems = list_nth_node(List, path_elem_lists, elempos);
 
+	/* Guard against stack overflow due to complex path patterns. */
+	check_stack_depth();
+
 	foreach_ptr(struct path_element, pe, path_elems)
 	{
 		/* Update current path being built with current element. */
@@ -698,6 +702,9 @@ generate_setop_from_pathqueries(List *pathqueries, List **rtable, List **targetl
 	List	   *rtargetlist;
 	ParseNamespaceItem *pni;
 
+	/* Guard against stack overflow due to many path queries. */
+	check_stack_depth();
+
 	/* Recursion termination condition. */
 	if (list_length(pathqueries) == 0)
 	{
-- 
2.34.1



^ permalink  raw  reply  [nested|flat] 4+ messages in thread

* Re: Bug: Missing check_stack_depth() in GRAPH_TABLE rewriter
  2026-04-11 03:57 Bug: Missing check_stack_depth() in GRAPH_TABLE rewriter SATYANARAYANA NARLAPURAM <[email protected]>
  2026-04-15 15:07 ` Re: Bug: Missing check_stack_depth() in GRAPH_TABLE rewriter Ashutosh Bapat <[email protected]>
@ 2026-04-24 06:24   ` Peter Eisentraut <[email protected]>
  2026-04-24 09:29     ` Re: Bug: Missing check_stack_depth() in GRAPH_TABLE rewriter Ashutosh Bapat <[email protected]>
  0 siblings, 1 reply; 4+ messages in thread

From: Peter Eisentraut @ 2026-04-24 06:24 UTC (permalink / raw)
  To: Ashutosh Bapat <[email protected]>; SATYANARAYANA NARLAPURAM <[email protected]>; +Cc: PostgreSQL Hackers <[email protected]>

On 15.04.26 17:07, Ashutosh Bapat wrote:
> Thanks for the report. I could reproduce the segfault on my laptop.
> The attached patch fixes it and gives ERROR:  stack depth limit
> exceeded.
> 
> generate_queries_for_path_pattern_recurse() - has to work in a linear
> fashion since the elements need to be processed in an order. Each
> permutation of elements produces one query. These queries can be
> arranged in a balanced tree as you  suggest OR when constructing the
> setop tree we could generate it in divide-and-conquer manner. However,
> the tree will be flattened in the planner anyway (See
> flatten_simple_union_all() and pull_up_simple_union_all()). Thus the
> final planning will require a deeper stack anyway. The code complexity
> doesn't seem to be worth it.
> 
> I also looked at a few commits that add check_stack_depth() to see if
> we add tests for these scenarios. But I didn't find any. So no tests
> added with this commit.

committed

(I moved the #include "miscadmin.h" to a more alphabetical position.)






^ permalink  raw  reply  [nested|flat] 4+ messages in thread

* Re: Bug: Missing check_stack_depth() in GRAPH_TABLE rewriter
  2026-04-11 03:57 Bug: Missing check_stack_depth() in GRAPH_TABLE rewriter SATYANARAYANA NARLAPURAM <[email protected]>
  2026-04-15 15:07 ` Re: Bug: Missing check_stack_depth() in GRAPH_TABLE rewriter Ashutosh Bapat <[email protected]>
  2026-04-24 06:24   ` Re: Bug: Missing check_stack_depth() in GRAPH_TABLE rewriter Peter Eisentraut <[email protected]>
@ 2026-04-24 09:29     ` Ashutosh Bapat <[email protected]>
  0 siblings, 0 replies; 4+ messages in thread

From: Ashutosh Bapat @ 2026-04-24 09:29 UTC (permalink / raw)
  To: Peter Eisentraut <[email protected]>; +Cc: SATYANARAYANA NARLAPURAM <[email protected]>; PostgreSQL Hackers <[email protected]>

On Fri, Apr 24, 2026 at 11:54 AM Peter Eisentraut <[email protected]> wrote:
>
> On 15.04.26 17:07, Ashutosh Bapat wrote:
> > Thanks for the report. I could reproduce the segfault on my laptop.
> > The attached patch fixes it and gives ERROR:  stack depth limit
> > exceeded.
> >
> > generate_queries_for_path_pattern_recurse() - has to work in a linear
> > fashion since the elements need to be processed in an order. Each
> > permutation of elements produces one query. These queries can be
> > arranged in a balanced tree as you  suggest OR when constructing the
> > setop tree we could generate it in divide-and-conquer manner. However,
> > the tree will be flattened in the planner anyway (See
> > flatten_simple_union_all() and pull_up_simple_union_all()). Thus the
> > final planning will require a deeper stack anyway. The code complexity
> > doesn't seem to be worth it.
> >
> > I also looked at a few commits that add check_stack_depth() to see if
> > we add tests for these scenarios. But I didn't find any. So no tests
> > added with this commit.
>
> committed
>

Thanks a lot for committing this and other fixes.

> (I moved the #include "miscadmin.h" to a more alphabetical position.)
>

Didn't notice this. Sorry.

-- 
Best Wishes,
Ashutosh Bapat





^ permalink  raw  reply  [nested|flat] 4+ messages in thread


end of thread, other threads:[~2026-04-24 09:29 UTC | newest]

Thread overview: 4+ messages (download: mbox mbox.gz follow: Atom feed)
-- links below jump to the message on this page --
2026-04-11 03:57 Bug: Missing check_stack_depth() in GRAPH_TABLE rewriter SATYANARAYANA NARLAPURAM <[email protected]>
2026-04-15 15:07 ` Ashutosh Bapat <[email protected]>
2026-04-24 06:24   ` Peter Eisentraut <[email protected]>
2026-04-24 09:29     ` Ashutosh Bapat <[email protected]>

This inbox is served by agora; see mirroring instructions
for how to clone and mirror all data and code used for this inbox