Re: Disallow outer-level and WHERE-clause aggregates in GRAPH_TABLE

From: Sami Imseih <samimseih(dot)pg(at)gmail(dot)com>
To: Ashutosh Bapat <ashutosh(dot)bapat(dot)oss(at)gmail(dot)com>
Cc: Sami Imseih <samimseih(at)gmail(dot)com>, Tom Lane <tgl(at)sss(dot)pgh(dot)pa(dot)us>, pgsql-hackers <pgsql-hackers(at)postgresql(dot)org>, Ewan Young <kdbase(dot)hack(at)gmail(dot)com>
Subject: Re: Disallow outer-level and WHERE-clause aggregates in GRAPH_TABLE
Date: 2026-09-01 00:14:50
Message-ID: CAN12+Y+vCoEcJ3Ncpg2M9sgSbkM29THt9KFboX9O1KnsJN0wGg@mail.gmail.com
Views: Whole Thread | Raw Message | Download mbox | Resend email
Thread:
Lists: pgsql-hackers

Hi Ashutosh,

Thanks for the review.

> > > So what I'm thinking right now is
> > > that this is a fundamental design error, and that we should nuke
> > > GraphPropertyRef altogether in favor of using a Var that references
> > > the appropriate column of the RTE_GRAPH_TABLE relation.
> >
> > I spent time today on this and I think the direction is right. In what
> > I have running
> > locally, a property reference is emitted as a plain Var over the
> > RTE_GRAPH_TABLE relation,
> > so the Var handles leveling and the rest for free. GraphPropertyRef
> > does not go away
> > entirely, though. It moves onto a side list on the RTE that the
> > rewriter uses to resolve
> > each property Var back to its graph property.
> >
>
> I share the concern raised by Tom about bugs due to missing
> GraphPropertyRef handling. However, I don't think we can use Var
> instead of GraphPropertyRef. It represents a reference to a property
> of all the graph elements that are bound to an element pattern in a
> GRAPH_TABLE. The element pattern is not represented by a range table
> and Var node does not have a field to represent an element pattern
> variable. Further the same GraphPropertyRef may be rewritten as
> different expressions depending on the element table it gets
> associated with when generating a subquery for a given path.

You are right. A Var does not seem correct here after looking at it a
bit more. GraphPropertyRef is not a column of an existing RTE. Also,
the same reference can resolve differently in different generated path
subqueries depending on which element table that path uses. So keeping a
separate placeholder seems more natural.

> this with Var will be quite complex, if not impossible. The question
> is whether we can find all the places where we need to treat
> GraphPropertyRef as a Var. Given that the GraphPropertyRefs are
> generated during transformation and vanish after rewrite, the places
> where we need to handle them are limited, as noted by Tom already.
> Many of those are already covered by the patch. Going through all the
> places, rather painfully, where we use levelsup for Var-like nodes,
> looking at var.c, I see that most of that code is applicable to
> planning, optimization and execution. The additional places which I
> think we need to handle are:
> a. locate_var_of_level_walker() to handle GraphPropertyRef so that the
> correct error location can be reported. We have separate
> locate_aggref_of_level_walker(). So first I thought that it would be
> appropriate to add a separate
> locate_graphpropertyref_of_level_walker() function. But we want to
> report an error from a place where the distinction between Var and
> GraphPropertyRef is lost. So a separate function doesn't make sense.
> b. contain_vars_of_level() should not see GraphPropertyRef since the
> latter lives within GRAPH_TABLE only. But the function is used during
> query transformation, so it will be good to add a GraphPropertyRef
> case and Assert()/throw error if it is seen. We should add a comment
> there to expect GraphPropertyRef there once we start supporting nested
> subqueries in GRAPH_TABLE.
> c. IncrementVarSublevelsUp() will need to handle GraphPropertyRef once
> we support nested subqueries in GRAPH_TABLE. For now we may want to
> add a GraphPropertyRef case and Assert()/throw error if it is seen. We
> should add a comment there to expect GraphPropertyRef there once we
> start supporting nested subqueries in GRAPH_TABLE. Please note, I
> don't expect ChangeVarNodes to handle GraphPropertyRef since there is
> no rtindex in GraphPropertyRef.

Right. The places where GraphPropertyRef needs Var-like treatment seem
fairly limited. In v7, I handled the walkers that need query level
information, such as locate_var_of_level_walker() and
check_agg_arguments_walker(). I also added a GraphPropertyRef case to
contain_vars_of_level(), since that can be reached during query
transformation before rewrite removes GraphPropertyRef.

IncrementVarSublevelsUp() is different. There I added a
GraphPropertyRef case that leaves it alone and asserts
gprlevelsup == 0, since we do not yet support subqueries inside
GRAPH_TABLE. Once we do, we will need to adjust the levels there as
well.

> Here are some comments on v6 patches.

> @@ -769,9 +775,17 @@ check_agg_arguments_walker(Node *node,
> {
> if (node == NULL)
> return false;
> - if (IsA(node, Var))
> + if (IsA(node, Var) || IsA(node, GraphPropertyRef))
> {

> I would rather add a separate GraphPropertyRef case instead of
> combining it with Var. When we start supporting nested subqueries in
> GRAPH_TABLE, we will need to add levelsup to GraphPropertyRef and
> handle it appropriately. I am leaning towards doing that now and
> setting it to 0. Then use here instead of hardcoded 0.

Done in v7. I added gprlevelsup to carry the level.

> Also pass it on
> to the Vars in the expression that replaces the GraphPropertyRef by
> calling ChangeVarNodes() with sublevelsup = GraphPropertyRef::levelsup
> instead of 0. Once subquery support is added, we will set
> GraphPropertyRef::levelsup to the appropriate value and the
> ChangeVarNodes() will propagate it to the Vars in the replacement
> expression. If we go about it this way, we should add an Assert in
> replace_property_refs() to make sure that GraphPropertyRef::levelsup
> == 0. But I am also fine with implicitly treating GraphPropertyRef as
> level 0 with a comment that we should pass on the levelsup to the Vars
> in the replacement expression once we support nested subqueries in
> GRAPH_TABLE.

... replace_property_refs() now passes gprlevelsup through to
ChangeVarNodes() instead of hardcoding 0. I also added
gprlevelsup == 0 asserts in both IncrementVarSublevelsUp_walker() and
replace_property_refs_mutator(), since subqueries inside GRAPH_TABLE
are not supported yet.

> @@ -1845,6 +1847,10 @@ transformSubLink(ParseState *pstate, SubLink
*sublink)
> case EXPR_KIND_CYCLE_MARK:
> /* okay */
> break;
> + case EXPR_KIND_GRAPH_TABLE_COLUMNS:
> + case EXPR_KIND_GRAPH_TABLE_WHERE:
> + err = _("subqueries within GRAPH_TABLE reference are not supported");
>
>
> I would use "cannot use subquery in GRAPH_TABLE reference" to be
> consistent with the other error messages in this function.

Done in v7.

> I think we could make this function lean by letting
> IncrementVarSublevelsUp() do the levelsup adjustment before the
> GraphPropertyRef is replaced. It will require traversing the
> expression tree twice, but it will eliminate the possibility of
> replace_property_refs_mutator() missing a node type that has levelsup.

Done in v7. I removed the special Var-levelsup handling from
replace_property_refs_mutator() and let IncrementVarSublevelsUp()
adjust the copied expression in generate_query_for_graph_path() before
GraphPropertyRef is replaced.

> @@ -84,6 +84,8 @@ typedef enum ParseExprKind
> EXPR_KIND_GENERATED_COLUMN, /* generation expression for a column */
> EXPR_KIND_CYCLE_MARK, /* cycle mark value */
> EXPR_KIND_PROPGRAPH_PROPERTY, /* derived property expression */
> + EXPR_KIND_GRAPH_TABLE_COLUMNS, /* GRAPH_TABLE COLUMNS list item */
> + EXPR_KIND_GRAPH_TABLE_WHERE, /* WHERE in a GRAPH_TABLE pattern */
>
>
> Nit. I would just write WHERE in a GRAPH_TABLE since GRAPH_TABLE is
> not a pattern, but a table function.

Done in v7.

Also, with these changes, I did see what it would take to support
subqueries and
I have something that I can show in a different thread. I think the
aub-query support
will be a v20 item, while v7- should be targeted for v19.

--
Sami Imseih
Amazon Web Services (AWS)

Attachment Content-Type Size
v7-0001-Rework-GRAPH_TABLE-aggregate-window-SRF-rejection.patch application/octet-stream 28.6 KB

In response to

Responses

Browse pgsql-hackers by date

  From Date Subject
Next Message Jeff Davis 2026-09-01 00:33:03 Re: Commit Sequence Numbers and Visibility
Previous Message shihao zhong 2026-09-01 00:13:32 Re: [PATCH] pageinspect: validate line pointers before using them