postgres.git / summary / log / commit / refs

commit    8ce749f8f65c8991d6d2f5010116530e0d7fb2ad
Author:   Peter Eisentraut <peter@eisentraut.org>
Date:     Wed Aug 05 08:44:10 2026 +0000

    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.
    
    Author: Ewan Young <kdbase.hack@gmail.com>
    Reviewed-by: Ashutosh Bapat <ashutosh.bapat.oss@gmail.com>
    Discussion: https://www.postgresql.org/message-id/flat/CAON2xHOYAmYLkB2jGi6g77d6Fqv8YgOrfV-riQVf0K_7AdxD3w@mail.gmail.com


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 485e33b9e5a..68b525ffdcc 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); /* @@ -1031,6 +1041,26 @@ transformRangeGraphTable(ParseState *pstate, RangeGraphTable *rgt) errmsg("subqueries within GRAPH_TABLE reference are not supported"))); pstate->p_hasSubLinks = saved_hasSublinks; + /* + * 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 in GRAPH_TABLE COLUMNS are not supported")); + if (pstate->p_hasWindowFuncs) + ereport(ERROR, + errcode(ERRCODE_FEATURE_NOT_SUPPORTED), + errmsg("window functions in GRAPH_TABLE COLUMNS are not supported")); + if (pstate->p_hasTargetSRFs) + ereport(ERROR, + errcode(ERRCODE_FEATURE_NOT_SUPPORTED), + errmsg("set-returning functions in 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 b7a6182457d..cde3114ebf4 100644 --- a/src/test/regress/expected/graph_table.out +++ b/src/test/regress/expected/graph_table.out @@ -471,6 +471,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 in GRAPH_TABLE COLUMNS are not supported +SELECT * FROM GRAPH_TABLE (myshop MATCH (c IS customers) COLUMNS (row_number() OVER () AS rn)); +ERROR: window functions in 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 in 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 85298f93964..7a4189833d8 100644 --- a/src/test/regress/sql/graph_table.sql +++ b/src/test/regress/sql/graph_table.sql @@ -306,6 +306,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)); [parent: 0e2fb2c8c307]