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