0001-Limit-GRAPH_TABLE-path-combinations-to-prevent-memor.patch
application/octet-stream
Filename: 0001-Limit-GRAPH_TABLE-path-combinations-to-prevent-memor.patch
Type: application/octet-stream
Part: 0
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: Limit GRAPH_TABLE path combinations to prevent memory exhaustion
| File | + | − |
|---|---|---|
| src/backend/rewrite/rewriteGraphTable.c | 25 | 0 |
| src/test/regress/expected/graph_table.out | 70 | 0 |
| src/test/regress/sql/graph_table.sql | 64 | 0 |
From bc1cbe54442757e973cf7d6ee9592a9e5babcb54 Mon Sep 17 00:00:00 2001
From: Satya Narlapuram <satyanarlapuram@gmail.com>
Date: Wed, 29 Apr 2026 16:22:42 +0000
Subject: [PATCH] Limit GRAPH_TABLE path combinations to prevent memory
exhaustion
generate_queries_for_path_pattern_recurse() enumerates all path
combinations by recursing over the Cartesian product of matching elements
per pattern position. Without IS label filters, each position matches
ALL tables of that kind, leading to N^K combinations (N tables, K
pattern positions). Each combination allocates a Query node via palloc
into process memory with no limit, causing rapid memory exhaustion and
potential OOM kills.
Add a pre-computation check that calculates the total number of path
combinations before calling
generate_queries_for_path_pattern_recurse(). If the product exceeds
MAX_GRAPH_TABLE_PATH_COMBINATIONS (10000), report an error with a hint
suggesting IS label filters to reduce the search space.
This provides protection against memory exhaustion while
preserving correct behavior for reasonable graph patterns.
---
src/backend/rewrite/rewriteGraphTable.c | 25 ++++++++
src/test/regress/expected/graph_table.out | 70 +++++++++++++++++++++++
src/test/regress/sql/graph_table.sql | 64 +++++++++++++++++++++
3 files changed, 159 insertions(+)
diff --git a/src/backend/rewrite/rewriteGraphTable.c b/src/backend/rewrite/rewriteGraphTable.c
index 6867de6d..8a866e0c 100644
--- a/src/backend/rewrite/rewriteGraphTable.c
+++ b/src/backend/rewrite/rewriteGraphTable.c
@@ -45,6 +45,9 @@
#include "utils/ruleutils.h"
#include "utils/syscache.h"
+/* Limit on total path combinations to prevent combinatorial explosion. */
+#define MAX_GRAPH_TABLE_PATH_COMBINATIONS 10000
+
/*
* Represents one path factor in a path.
@@ -343,6 +346,28 @@ generate_queries_for_path_pattern(RangeTblEntry *rte, List *path_pattern)
path_elem_lists = lappend(path_elem_lists,
get_path_elements_for_path_factor(rte->relid, pf));
+ /*
+ * Check that the total number of path combinations doesn't exceed a
+ * reasonable limit. Without explicit IS label expressions, each element
+ * pattern matches all graph elements of the same kind, producing a
+ * Cartesian product that can easily exhaust process memory.
+ */
+ {
+ int64 total_paths = 1;
+
+ foreach_ptr(List, elems, path_elem_lists)
+ {
+ if (list_length(elems) > 0)
+ total_paths *= list_length(elems);
+ if (total_paths > MAX_GRAPH_TABLE_PATH_COMBINATIONS)
+ ereport(ERROR,
+ (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+ errmsg("too many path combinations (%lld) in GRAPH_TABLE pattern",
+ (long long) total_paths),
+ errhint("Use explicit IS label expressions to reduce the number of matching elements.")));
+ }
+ }
+
pathqueries = generate_queries_for_path_pattern_recurse(rte, pathqueries,
NIL, path_elem_lists, 0);
if (!pathqueries)
diff --git a/src/test/regress/expected/graph_table.out b/src/test/regress/expected/graph_table.out
index 12b8706b..56fa1b5f 100644
--- a/src/test/regress/expected/graph_table.out
+++ b/src/test/regress/expected/graph_table.out
@@ -1032,4 +1032,74 @@ SELECT sname, dname FROM GRAPH_TABLE (g1 MATCH (src)->(dest) WHERE src.vprop1 >
ERROR: subqueries within GRAPH_TABLE reference are not supported
SELECT sname, dname FROM GRAPH_TABLE (g1 MATCH (src)->(dest) WHERE out_degree(src.vname) > (SELECT max(out_degree(nname)) FROM GRAPH_TABLE (g1 MATCH (node) COLUMNS (node.vname AS nname))) COLUMNS(src.vname AS sname, dest.vname AS dname));
ERROR: subqueries within GRAPH_TABLE reference are not supported
+-- Test: path combination limit prevents combinatorial explosion in the
+-- rewriter when MATCH patterns lack explicit IS label expressions.
+CREATE TABLE v4 (id int PRIMARY KEY, val int);
+CREATE TABLE v5 (id int PRIMARY KEY, val int);
+CREATE TABLE v6 (id int PRIMARY KEY, val int);
+CREATE TABLE v7 (id int PRIMARY KEY, val int);
+CREATE TABLE v8 (id int PRIMARY KEY, val int);
+CREATE TABLE v9 (id int PRIMARY KEY, val int);
+CREATE TABLE v10 (id int PRIMARY KEY, val int);
+CREATE TABLE v11 (id int PRIMARY KEY, val int);
+CREATE TABLE e1 (id int PRIMARY KEY, src int, dest int);
+CREATE TABLE e2 (id int PRIMARY KEY, src int, dest int);
+CREATE TABLE e3 (id int PRIMARY KEY, src int, dest int);
+CREATE TABLE e4 (id int PRIMARY KEY, src int, dest int);
+CREATE TABLE e5 (id int PRIMARY KEY, src int, dest int);
+CREATE TABLE e6 (id int PRIMARY KEY, src int, dest int);
+CREATE TABLE e7 (id int PRIMARY KEY, src int, dest int);
+CREATE TABLE e8 (id int PRIMARY KEY, src int, dest int);
+INSERT INTO v4 VALUES (1, 10);
+INSERT INTO e1 VALUES (1, 1, 1);
+CREATE PROPERTY GRAPH g5
+ VERTEX TABLES (
+ v4 LABEL vl PROPERTIES (id, val) LABEL vl2 PROPERTIES (id, val),
+ v5 LABEL vl PROPERTIES (id, val),
+ v6 LABEL vl PROPERTIES (id, val),
+ v7 LABEL vl PROPERTIES (id, val),
+ v8 LABEL vl PROPERTIES (id, val),
+ v9 LABEL vl PROPERTIES (id, val),
+ v10 LABEL vl PROPERTIES (id, val),
+ v11 LABEL vl PROPERTIES (id, val)
+ )
+ EDGE TABLES (
+ e1 SOURCE KEY (src) REFERENCES v4 (id) DESTINATION KEY (dest) REFERENCES v5 (id)
+ LABEL el PROPERTIES (id),
+ e2 SOURCE KEY (src) REFERENCES v5 (id) DESTINATION KEY (dest) REFERENCES v6 (id)
+ LABEL el PROPERTIES (id),
+ e3 SOURCE KEY (src) REFERENCES v6 (id) DESTINATION KEY (dest) REFERENCES v7 (id)
+ LABEL el PROPERTIES (id),
+ e4 SOURCE KEY (src) REFERENCES v7 (id) DESTINATION KEY (dest) REFERENCES v8 (id)
+ LABEL el PROPERTIES (id),
+ e5 SOURCE KEY (src) REFERENCES v8 (id) DESTINATION KEY (dest) REFERENCES v9 (id)
+ LABEL el PROPERTIES (id),
+ e6 SOURCE KEY (src) REFERENCES v9 (id) DESTINATION KEY (dest) REFERENCES v10 (id)
+ LABEL el PROPERTIES (id),
+ e7 SOURCE KEY (src) REFERENCES v10 (id) DESTINATION KEY (dest) REFERENCES v11 (id)
+ LABEL el PROPERTIES (id),
+ e8 SOURCE KEY (src) REFERENCES v11 (id) DESTINATION KEY (dest) REFERENCES v4 (id)
+ LABEL el PROPERTIES (id)
+ );
+-- 3-element unlabeled: 8 * 8 * 8 = 512 combinations (under limit, succeeds)
+SELECT count(*) FROM GRAPH_TABLE (g5 MATCH (a)-[e]->(b) COLUMNS (a.id AS aid));
+ count
+-------
+ 0
+(1 row)
+
+-- 5-element unlabeled: 8^5 = 32,768 combinations (over limit, rejected)
+SELECT count(*) FROM GRAPH_TABLE (g5 MATCH (a)-[e1]->(b)-[e2]->(c) COLUMNS (a.id AS aid));
+ERROR: too many path combinations (32768) in GRAPH_TABLE pattern
+HINT: Use explicit IS label expressions to reduce the number of matching elements.
+-- IS vl2 pins first vertex to 1 table: 1*8*8*8*8 = 4,096 (under limit)
+SELECT count(*) FROM GRAPH_TABLE (g5 MATCH (a IS vl2)-[e1]->(b)-[e2]->(c) COLUMNS (a.id AS aid));
+ count
+-------
+ 0
+(1 row)
+
+DROP PROPERTY GRAPH g5;
+DROP TABLE e8, e7, e6, e5, e4, e3, e2, e1;
+DROP TABLE v11, v10, v9, v8, v7, v6, v5, v4;
-- leave the objects behind for pg_upgrade/pg_dump tests
diff --git a/src/test/regress/sql/graph_table.sql b/src/test/regress/sql/graph_table.sql
index a5df4647..69313544 100644
--- a/src/test/regress/sql/graph_table.sql
+++ b/src/test/regress/sql/graph_table.sql
@@ -590,4 +590,68 @@ SELECT * FROM customers co WHERE co.customer_id = (SELECT customer_id FROM GRAPH
SELECT sname, dname FROM GRAPH_TABLE (g1 MATCH (src)->(dest) WHERE src.vprop1 > (SELECT max(v1.vprop1) FROM v1) COLUMNS(src.vname AS sname, dest.vname AS dname));
SELECT sname, dname FROM GRAPH_TABLE (g1 MATCH (src)->(dest) WHERE out_degree(src.vname) > (SELECT max(out_degree(nname)) FROM GRAPH_TABLE (g1 MATCH (node) COLUMNS (node.vname AS nname))) COLUMNS(src.vname AS sname, dest.vname AS dname));
+-- Test: path combination limit prevents combinatorial explosion in the
+-- rewriter when MATCH patterns lack explicit IS label expressions.
+CREATE TABLE v4 (id int PRIMARY KEY, val int);
+CREATE TABLE v5 (id int PRIMARY KEY, val int);
+CREATE TABLE v6 (id int PRIMARY KEY, val int);
+CREATE TABLE v7 (id int PRIMARY KEY, val int);
+CREATE TABLE v8 (id int PRIMARY KEY, val int);
+CREATE TABLE v9 (id int PRIMARY KEY, val int);
+CREATE TABLE v10 (id int PRIMARY KEY, val int);
+CREATE TABLE v11 (id int PRIMARY KEY, val int);
+CREATE TABLE e1 (id int PRIMARY KEY, src int, dest int);
+CREATE TABLE e2 (id int PRIMARY KEY, src int, dest int);
+CREATE TABLE e3 (id int PRIMARY KEY, src int, dest int);
+CREATE TABLE e4 (id int PRIMARY KEY, src int, dest int);
+CREATE TABLE e5 (id int PRIMARY KEY, src int, dest int);
+CREATE TABLE e6 (id int PRIMARY KEY, src int, dest int);
+CREATE TABLE e7 (id int PRIMARY KEY, src int, dest int);
+CREATE TABLE e8 (id int PRIMARY KEY, src int, dest int);
+INSERT INTO v4 VALUES (1, 10);
+INSERT INTO e1 VALUES (1, 1, 1);
+
+CREATE PROPERTY GRAPH g5
+ VERTEX TABLES (
+ v4 LABEL vl PROPERTIES (id, val) LABEL vl2 PROPERTIES (id, val),
+ v5 LABEL vl PROPERTIES (id, val),
+ v6 LABEL vl PROPERTIES (id, val),
+ v7 LABEL vl PROPERTIES (id, val),
+ v8 LABEL vl PROPERTIES (id, val),
+ v9 LABEL vl PROPERTIES (id, val),
+ v10 LABEL vl PROPERTIES (id, val),
+ v11 LABEL vl PROPERTIES (id, val)
+ )
+ EDGE TABLES (
+ e1 SOURCE KEY (src) REFERENCES v4 (id) DESTINATION KEY (dest) REFERENCES v5 (id)
+ LABEL el PROPERTIES (id),
+ e2 SOURCE KEY (src) REFERENCES v5 (id) DESTINATION KEY (dest) REFERENCES v6 (id)
+ LABEL el PROPERTIES (id),
+ e3 SOURCE KEY (src) REFERENCES v6 (id) DESTINATION KEY (dest) REFERENCES v7 (id)
+ LABEL el PROPERTIES (id),
+ e4 SOURCE KEY (src) REFERENCES v7 (id) DESTINATION KEY (dest) REFERENCES v8 (id)
+ LABEL el PROPERTIES (id),
+ e5 SOURCE KEY (src) REFERENCES v8 (id) DESTINATION KEY (dest) REFERENCES v9 (id)
+ LABEL el PROPERTIES (id),
+ e6 SOURCE KEY (src) REFERENCES v9 (id) DESTINATION KEY (dest) REFERENCES v10 (id)
+ LABEL el PROPERTIES (id),
+ e7 SOURCE KEY (src) REFERENCES v10 (id) DESTINATION KEY (dest) REFERENCES v11 (id)
+ LABEL el PROPERTIES (id),
+ e8 SOURCE KEY (src) REFERENCES v11 (id) DESTINATION KEY (dest) REFERENCES v4 (id)
+ LABEL el PROPERTIES (id)
+ );
+
+-- 3-element unlabeled: 8 * 8 * 8 = 512 combinations (under limit, succeeds)
+SELECT count(*) FROM GRAPH_TABLE (g5 MATCH (a)-[e]->(b) COLUMNS (a.id AS aid));
+
+-- 5-element unlabeled: 8^5 = 32,768 combinations (over limit, rejected)
+SELECT count(*) FROM GRAPH_TABLE (g5 MATCH (a)-[e1]->(b)-[e2]->(c) COLUMNS (a.id AS aid));
+
+-- IS vl2 pins first vertex to 1 table: 1*8*8*8*8 = 4,096 (under limit)
+SELECT count(*) FROM GRAPH_TABLE (g5 MATCH (a IS vl2)-[e1]->(b)-[e2]->(c) COLUMNS (a.id AS aid));
+
+DROP PROPERTY GRAPH g5;
+DROP TABLE e8, e7, e6, e5, e4, e3, e2, e1;
+DROP TABLE v11, v10, v9, v8, v7, v6, v5, v4;
+
-- leave the objects behind for pg_upgrade/pg_dump tests
--
2.43.0