From 0fb556bbe3e1437dfb744b9671e25ce4a9657357 Mon Sep 17 00:00:00 2001
From: Ewan Young <kdbase.hack@gmail.com>
Date: Wed, 17 Jun 2026 00:40:40 +0800
Subject: [PATCH] Disallow aggregates, window functions, and SRFs in
 GRAPH_TABLE COLUMNS

The COLUMNS list of a GRAPH_TABLE query is parsed as an ordinary select
target list, which permits aggregate functions, window functions, and
set-returning functions.  GRAPH_TABLE has no machinery to evaluate them,
though: the rewriter copies the COLUMNS target list verbatim into a
freshly built subquery whose hasAggs/hasWindowFuncs/hasTargetSRFs flags
are never set, so the planner builds no Agg/WindowAgg node (and no SRF
expansion) and the Aggref/WindowFunc/SRF reaches the executor.  This
triggers an assertion failure ("ecxt_aggvalues != NULL"), or "Aggref
found in non-Agg plan node" on a non-assert build, for otherwise
parser-accepted SQL such as

    SELECT max(c) FROM GRAPH_TABLE
        (g MATCH (x IS v) COLUMNS (count(*) AS c));

Reject these constructs in transformRangeGraphTable() the same way
subqueries are already handled: save and clear pstate->p_hasAggs,
p_hasWindowFuncs, and p_hasTargetSRFs around the transformation of the
COLUMNS list, and raise a "not supported" error if any of them got set.

This is deliberately a blanket prohibition for now.  Once quantified
element patterns such as (a)->{1,5} are supported, aggregates over
property references of higher degree (e.g. count(a) or sum(a.val)) can
be allowed; at that point the check will need to inspect the aggregate
arguments rather than reject all aggregates outright.
---
 src/backend/parser/parse_clause.c         | 30 +++++++++++++++++++++++
 src/test/regress/expected/graph_table.out |  7 ++++++
 src/test/regress/sql/graph_table.sql      |  4 +++
 3 files changed, 41 insertions(+)

diff --git a/src/backend/parser/parse_clause.c b/src/backend/parser/parse_clause.c
index 5fe5257b019..df5d25161c1 100644
--- a/src/backend/parser/parse_clause.c
+++ b/src/backend/parser/parse_clause.c
@@ -946,6 +946,9 @@ transformRangeGraphTable(ParseState *pstate, RangeGraphTable *rgt)
 	ListCell   *lc;
 	int			resno = 0;
 	bool		saved_hasSublinks;
+	bool		saved_hasAggs;
+	bool		saved_hasWindowFuncs;
+	bool		saved_hasTargetSRFs;
 
 	rel = parserOpenPropGraph(pstate, rgt->graph_name, AccessShareLock);
 
@@ -967,6 +970,13 @@ transformRangeGraphTable(ParseState *pstate, RangeGraphTable *rgt)
 	saved_hasSublinks = pstate->p_hasSubLinks;
 	pstate->p_hasSubLinks = false;
 
+	saved_hasAggs = pstate->p_hasAggs;
+	pstate->p_hasAggs = false;
+	saved_hasWindowFuncs = pstate->p_hasWindowFuncs;
+	pstate->p_hasWindowFuncs = false;
+	saved_hasTargetSRFs = pstate->p_hasTargetSRFs;
+	pstate->p_hasTargetSRFs = false;
+
 	gp = transformGraphPattern(pstate, rgt->graph_pattern);
 
 	/*
@@ -1027,6 +1037,26 @@ transformRangeGraphTable(ParseState *pstate, RangeGraphTable *rgt)
 				 errmsg("subqueries within GRAPH_TABLE reference are not supported")));
 	pstate->p_hasSubLinks = saved_hasSublinks;
 
+	/*
+	 * Likewise, GRAPH_TABLE cannot yet evaluate aggregate, window, or
+	 * set-returning functions in its COLUMNS list, so prohibit them for now.
+	 */
+	if (pstate->p_hasAggs)
+		ereport(ERROR,
+				(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+				 errmsg("aggregate functions within GRAPH_TABLE COLUMNS are not supported")));
+	if (pstate->p_hasWindowFuncs)
+		ereport(ERROR,
+				(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+				 errmsg("window functions within GRAPH_TABLE COLUMNS are not supported")));
+	if (pstate->p_hasTargetSRFs)
+		ereport(ERROR,
+				(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+				 errmsg("set-returning functions within GRAPH_TABLE COLUMNS are not supported")));
+	pstate->p_hasAggs = saved_hasAggs;
+	pstate->p_hasWindowFuncs = saved_hasWindowFuncs;
+	pstate->p_hasTargetSRFs = saved_hasTargetSRFs;
+
 	return addRangeTableEntryForGraphTable(pstate, graphid, castNode(GraphPattern, gp), columns, colnames, rgt->alias, false, true);
 }
 
diff --git a/src/test/regress/expected/graph_table.out b/src/test/regress/expected/graph_table.out
index 70d986e8ab0..0c7448bfd38 100644
--- a/src/test/regress/expected/graph_table.out
+++ b/src/test/regress/expected/graph_table.out
@@ -455,6 +455,13 @@ SELECT * FROM GRAPH_TABLE (myshop MATCH (c IS customers WHERE c.* IS NOT NULL)-[
 ERROR:  "*" not allowed here
 LINE 1: ...M GRAPH_TABLE (myshop MATCH (c IS customers WHERE c.* IS NOT...
                                                              ^
+-- aggregate, window, and set-returning functions are not supported in COLUMNS
+SELECT * FROM GRAPH_TABLE (myshop MATCH (c IS customers) COLUMNS (count(*) AS num));
+ERROR:  aggregate functions within GRAPH_TABLE COLUMNS are not supported
+SELECT * FROM GRAPH_TABLE (myshop MATCH (c IS customers) COLUMNS (row_number() OVER () AS rn));
+ERROR:  window functions within GRAPH_TABLE COLUMNS are not supported
+SELECT * FROM GRAPH_TABLE (myshop MATCH (c IS customers) COLUMNS (generate_series(1, 2) AS gs));
+ERROR:  set-returning functions within GRAPH_TABLE COLUMNS are not supported
 -- consecutive element patterns with same kind
 SELECT * FROM GRAPH_TABLE (g1 MATCH ()() COLUMNS (1 as one));
 ERROR:  adjacent vertex patterns are not supported
diff --git a/src/test/regress/sql/graph_table.sql b/src/test/regress/sql/graph_table.sql
index 0b44f70d7e7..30335d7ef6d 100644
--- a/src/test/regress/sql/graph_table.sql
+++ b/src/test/regress/sql/graph_table.sql
@@ -302,6 +302,10 @@ SELECT * FROM GRAPH_TABLE (g1 MATCH (src IS el1 | vl1)-[conn]->(dest) COLUMNS (c
 SELECT * FROM GRAPH_TABLE (myshop MATCH (c IS customers WHERE c.address = 'US')-[IS customer_orders]->(o IS orders) COLUMNS (c.*));
 -- star anywhere else is not allowed as a property reference
 SELECT * FROM GRAPH_TABLE (myshop MATCH (c IS customers WHERE c.* IS NOT NULL)-[IS customer_orders]->(o IS orders) COLUMNS (c.name));
+-- aggregate, window, and set-returning functions are not supported in COLUMNS
+SELECT * FROM GRAPH_TABLE (myshop MATCH (c IS customers) COLUMNS (count(*) AS num));
+SELECT * FROM GRAPH_TABLE (myshop MATCH (c IS customers) COLUMNS (row_number() OVER () AS rn));
+SELECT * FROM GRAPH_TABLE (myshop MATCH (c IS customers) COLUMNS (generate_series(1, 2) AS gs));
 -- consecutive element patterns with same kind
 SELECT * FROM GRAPH_TABLE (g1 MATCH ()() COLUMNS (1 as one));
 SELECT * FROM GRAPH_TABLE (g1 MATCH -> COLUMNS (1 AS one));
-- 
2.47.3

