Re: Authorization semantics and candidate pruning in GRAPH_TABLE

From: Ayoub Kazar <ayoub(dot)kazar(at)data-bene(dot)io>
To: Wataru Naotsuka <naotsukawataru(at)gmail(dot)com>, pgsql-hackers(at)lists(dot)postgresql(dot)org
Cc: ashutosh(dot)bapat(dot)oss(at)gmail(dot)com, wataru(dot)naotsuka(dot)bp(at)nttdata(dot)com
Subject: Re: Authorization semantics and candidate pruning in GRAPH_TABLE
Date: 2026-09-08 15:48:15
Message-ID: a525907b-bb7c-45e2-be69-3a7e65025675@data-bene.io
Views: Whole Thread | Raw Message | Download mbox | Resend email
Thread:
Lists: pgsql-hackers

Hello,

On 08/09/2026 06:49, Wataru Naotsuka wrote:
>
> Hello pgsql-hackers,
>
> I would like to ask about authorization behavior for unlabeled
> patterns in Property Graph / GRAPH_TABLE, in particular whether the
> current permission error behavior is an intentional authorization
> semantic or mainly a consequence of the current rewrite implementation.
>
> As I understand it, a user executing GRAPH_TABLE needs SELECT
> privileges on the property graph itself, as well as on the underlying
> base relations that are actually referenced. I also understand the
> motivation for using security-invoker semantics, in order to avoid
> unintended privilege escalation through the graph owner's privileges.
>
> However, unlabeled patterns seem to have an interesting consequence.
>
> My understanding of the current rewrite flow is roughly:
>
> |candidate ↓ does it form a valid path? ├─ no → does not remain as a
> Query/RTE └─ yes → generate RTE → permission check|
>
> For example, suppose the possible vertex candidates are A, B, and C.
> The current user has SELECT privilege on A and C, but not on B.
>
> If a path containing B is structurally valid and therefore rewritten
> into an RTE, the entire query appears to fail with|permission denied|,
> even if that path would never actually return any rows.
>
> The first thing I would like to clarify is whether the following
> behavior is intentional:
>
> If any valid path candidate internally generated by unlabeled
> expansion contains an element for which the current user lacks the
> required privilege, the entire query is rejected.
>
> Is this an intended authorization semantic, or is it primarily a
> consequence of applying the normal RTE permission checks to the
> current rewrite result?
>
> I would also be interested to know whether SQL/PGQ or the SQL standard
> provides any requirement or guidance on this point.
>
>
> Relation to future candidate pruning
>
> One reason I am interested in clarifying this now is that I think
> there is room for more static candidate pruning before DFS or during
> path generation, in order to reduce the combinatorial explosion of
> possible path candidates.
>
In addition to what Ashutosh said, there's a proposed patch for early
pruning for path generation [1] (in case you missed it), delayed until
later advancements in SQL/PGQ state.
>
> Even today, candidates that cannot form a structurally valid path are
> not retained as final Query/RTEs. In the future, it may also be
> possible to eliminate candidates earlier based on information such as
> available properties or WHERE predicates.
>
> For example, suppose a future optimization reduces:
>
> |[A, B(!permitted), C] ↓ static pruning [C] ↓ DFS → Query / RTE|
>
> Before such an optimization, B may be materialized as an RTE and
> cause|permission denied|. After the optimization, B may disappear
> before RTE generation, and the same query may succeed.
>
> What I would especially like to avoid is a situation where, for
> semantically equivalent queries, whether|permission denied|is raised
> depends on implementation details of the rewrite, such as which
> candidates happen to be eliminated before RTE generation.
>
> For that reason, I wonder whether it would be better to establish the
> authorization boundary explicitly before introducing more
> candidate-pruning optimizations.
>
> I see two broad possibilities.
>
> *1. Make all-or-error an explicit semantic*
>
> |[A, B(!permitted), C] ↓ authorization permission denied|
>
> Privileges would be checked against the semantic candidate set before
> optimization. If any required element is not accessible, the query
> would fail.
>
> This would preserve the current behavior while making the
> authorization result independent of later candidate pruning.
>
> *2. Filter candidates by the current user's privileges first*
>
> |[A, B(!permitted), C] ↓ authorization-based filtering [A, C] ↓ static
> pruning [C] ↓ DFS → Query / RTE|
>
> Personally, I am interested in exploring this direction.
>
> Under this model, GRAPH_TABLE could be viewed as querying the part of
> the graph visible to the current user. If authorization determines the
> candidate set first, then later pruning based on connectivity,
> properties, WHERE predicates, or other static information would be
> less likely to affect authorization behavior.
>
> This filtering step would also reduce the number of candidates passed
> to DFS. As a possible future step, I would like to explore
> optimizations such as eliminating an element candidate early when a
> property referenced by a restrictive predicate is known not to exist
> on that element. I am not proposing that optimization in this
> discussion; my main goal here is to clarify the authorization boundary
> first.
>
I think a case like GRAPH_TABLE(g MATCH (a)-[e]->(b) COLUMNS (1))
retuning no rows because b's only path element was pruned is counter
intuitive (well this introduces again the "visible subgraph" part),
compared to failing and saying we didn't have access to the element table.
>
> Of course, visible-subgraph semantics also has trade-offs.
> Reachability, shortest paths, aggregates, and similar results may
> differ depending on which part of the graph is visible to the current
> user, so such semantics would need to be clearly defined and documented.
>
> There are also related questions that would need further discussion,
> for example:
>
> *
>
> whether the same visible-subgraph semantics should apply when
> labels are explicitly specified, or only for unlabeled expansion;
>
> *
>
> how column-level SELECT privileges should be handled;
>
> *
>
> and where exactly such authorization filtering should occur in the
> rewrite pipeline.
>
> I am not proposing security-definer semantics or using the graph
> owner's privileges to access underlying tables. I agree with the
> motivation for security-invoker behavior. The question I am interested
> in is instead at what stage the set of elements visible to the current
> user should be determined.
>
> If the current all-or-error behavior is intentional, I would like to
> understand the reasoning and would then consider making authorization
> explicit before candidate pruning.
>
> If, on the other hand, the current behavior is mainly a consequence of
> the rewrite implementation and permission-based candidate filtering
> would be acceptable, I would be interested in exploring that as a
> first step toward more general static candidate pruning.
>
> I would appreciate any thoughts on the intended authorization
> semantics here, especially regarding SQL/PGQ and what authorization
> boundary would be preferable in view of future rewrite optimizations.
>
I interpreted Subclause 9.5 (Converting a tabular property graph to a
pure property graph) of the standard as: The pure property graph is
generated from all vertex tables and all edge tables defined in the
tabular property graph descriptor, across all rows. So i don't think we
can think of something like "visible subgraph" by definition, maybe.

[1] :
https://www.postgresql.org/message-id/flat/CAEG8a3%2B4tA%3De2M_f%3DdfaMgur%2BHJQ4R11v0torj2nc_k8XX3U6A%40mail.gmail.com

Regards,
Ayoub

In response to

Browse pgsql-hackers by date

  From Date Subject
Next Message Bharath Rupireddy 2026-09-08 15:58:05 Re: REPACK (CONCURRENTLY) rewrites tables marked with user_catalog_table
Previous Message Sami Imseih 2026-09-08 15:41:35 Re: Fix GRAPH TABLE label and property error reporting