Support pattern expressions as boolean expressions - #2360
Merged
Conversation
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Adds support for openCypher pattern expressions as general boolean expressions. Pattern expressions are now accepted anywhere a regular expression is valid — in
WHERE,RETURN,WITH,SET,CASE, and as operands of boolean combinators — not just inWHERE.Implementation
Grammar (
cypher_gram.y)%glr-parser) to handle the inherent ambiguity between parenthesized expressions and graph patterns. Both start with(, so a single-token lookahead cannot decide which production applies until several tokens later. GLR forks at the conflict point and discards the failing alternative.anonymous_pathtoexpr_atomwith a%dprec 1annotation. A bare(a)prefers the expression-variable interpretation (%dprec 2on'(' expr ')'), so single-node pattern expressions still resolve as plain variable references.make_exists_pattern_sublink()to wrap the pattern in anEXISTSsubquery — a bare pattern(a)-[:KNOWS]->(b)in expression context is semantically equivalent toEXISTS((a)-[:KNOWS]->(b)).make_explain_stmt()and placed it immediately above its docstring comment so the grammar file stays readable.(rhs)[1]withYYRHSLOC(rhs, 1)in theYYLLOC_DEFAULTmacro — a required GLR-correctness fix, not stylistic. GLR's rhs stack has a different layout than LALR(1)'s; the raw index lands on the wrong slot under GLR and produces incorrect source locations.%expect/%expect-rr. GLR produces 7 shift/reduce and 3 reduce/reduce conflicts (all arising from the(ambiguity between path_node and parenthesized expr, plus expr_var/var_name_opt overlap on)/}/=). These are handled correctly at runtime by GLR +%dprec. The grammar declares%expect 7and%expect-rr 3so any deviation up or down fails the build and forces an audit of the new conflicts. The Makefile keeps-Werrorand scopes the relaxation to-Wno-error=conflicts-sr -Wno-error=conflicts-rr, so other Bison warning categories (unused rules, undeclared types, etc.) still fail the build. Earlier rounds of this PR tried%expect-less variants for "Bison version portability," but Bison's%expectis exact-match — so the right answer is to pin the totals and update them deliberately if a future Bison reports different counts, not to remove the alarm.Parser (
cypher_analyze.c,cypher_clause.c)Files changed
src/backend/parser/cypher_gram.y%glr-parser,%dprecannotations,anonymous_pathinexpr_atom,make_exists_pattern_sublink(), GLR comment block explaining why conflicts are expectedMakefileBISONFLAGS += -Werror -Wno-error=conflicts-sr -Wno-error=conflicts-rr— keep-Werrorfor other warning categories, scope relaxation to the two known-noise conflict categories; registerpattern_expressionregression testregress/sql/pattern_expression.sqlregress/expected/pattern_expression.outTest plan
WHERE (a)-[:KNOWS]->(b)WHERE NOT (a)-[:KNOWS]->(:Person)WHERE (a)-[:KNOWS]->(:Person)ASalias → boolean columnCASE WHEN ... THEN ... ELSE ... ENDANDandORin RETURNSET a.flag = (a)-[:R]->(:L)WITHprojection + subsequentWHEREfilterRETURN (1 + 2),RETURN (n.name)— parenthesized expressions still work-Werrorwith%expect 7 %expect-rr 3pinned (Bison 3.8.2); any conflict-count drift now fails the build deliberatelyImplementation notes (added during review)
YYRHSLOCmacro change is a required GLR correctness fix, not cosmetic. GLR uses a different RHS stack layout than LALR(1); the previous(rhs)[1]indexing reads the wrong slot under GLR. The macro update is load-bearing for correct error locations under%glr-parser.RETURN/WITH/SETprojection (e.g.RETURN a.name, (a)-[:KNOWS]->(:Person)) desugars to a correlatedEXISTSsublink evaluated once per outer row — O(rows × subquery cost). This is the correct openCypher semantics, but distinct from a pattern inWHERE, where the planner may pull the sublink up into a semi-join. Regression tests use tiny datasets so this is invisible; on large data, projection-context pattern expressions cost more than WHERE-context ones.Known limitation (pre-existing, tracked separately)
(a:Label)do not filter by label. Because this PR lets a bare pattern appear as an expression anywhere,(a:Label)is now syntactically accepted as a boolean (e.g.WHERE (a:Person)orRETURN (a:Person)). It does not behave as an openCypher label predicate: the single-vertex pattern desugars to anEXISTSsubquery with no label constraint and so is trivially true. Root cause is pre-existing in the transform layer —make_path_join_quals()(src/backend/parser/cypher_clause.c:5454) early-returns for vertex-only patterns (list_length(entities) < 3), skipping the label-filter quals that are only emitted for relationship patterns. This is not introduced by this PR (the code is byte-identical onmaster, and this PR touches no transform code); the grammar change merely makes the limitation reachable via a new surface. Relationship patterns — the feature this PR targets — correlate and filter correctly. Tracked as a follow-up; use a relationship pattern orlabel(a) = 'Label'for label checks in the meantime.