Uh oh!
There was an error while loading. Please reload this page.
Fix leading OPTIONAL MATCH dropping the row when the pattern has no match - #2483
Open
Yyunozor wants to merge 1 commit into
Open
Fix leading OPTIONAL MATCH dropping the row when the pattern has no match#2483Yyunozor wants to merge 1 commit into
Yyunozor wants to merge 1 commit into
Conversation
…atch A query that begins with OPTIONAL MATCH returned no rows when its pattern matched nothing: OPTIONAL MATCH (n:NoSuchLabel) RETURN n -- returned 0 rows Cypher gives every OPTIONAL MATCH left join semantics. With no clause to its left it joins against the implicit single row that begins a query, so the pattern above must still emit one row with n = NULL. That is what distinguishes OPTIONAL MATCH from MATCH. transform_cypher_match_pattern builds the null-preserving LATERAL LEFT JOIN out of the previous clause, and falls through to the plain MATCH path when there is none. A leading OPTIONAL MATCH has no previous clause, so it took the plain path. Any preceding clause, even a trivial one, already produced the correct result: WITH 1 AS x OPTIONAL MATCH (n:NoSuchLabel) RETURN n -- 1 row, n = NULL So the join itself was already right; only its left side was missing. This gives a leading OPTIONAL MATCH a previous clause that produces exactly one row, built the way transform_cypher_with already builds a synthesized cypher_return. Its lone column is named with the hidden variable prefix, so expand_pnsi_attrs keeps it out of RETURN * expansion. The clause is attached in analyze_cypher rather than in the transform because the transform temporarily clears prev to recurse into the join right side, and relies on prev being NULL there to take the plain MATCH path; synthesizing a prev inside the transform re-arms that recursion indefinitely. Tests cover the no-match case for node and relationship patterns, a WHERE that no binding survives, that a matching pattern gains no extra null row, that RETURN * does not expose the internal column, that a leading MATCH still returns no rows, and that MATCH following OPTIONAL MATCH still errors. Closesapache#2473
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for freeto 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.
A query that begins with
OPTIONAL MATCHreturns no rows when the pattern matches nothing:Cypher gives every
OPTIONAL MATCHleft join semantics. With no clause to its left it joins against the implicit single row that begins a query, so this has to return one row withn = NULL— that is what separatesOPTIONAL MATCHfromMATCH.transform_cypher_match_patternbuilds the null-preserving LATERAL LEFT JOIN out of the previous clause, and falls through to the plain MATCH path when there is none ("If there is no previous clause, transform to a general MATCH clause"). A leadingOPTIONAL MATCHhas no previous clause, so it took the plain path. Any preceding clause, however trivial, already produced the right answer:The join was already right; only its left side was missing. This gives a leading
OPTIONAL MATCHa previous clause that yields exactly one row, built the waytransform_cypher_withalready builds a synthesizedcypher_return. Its lone column is named with the hidden variable prefix, soexpand_pnsi_attrskeeps it out ofRETURN *expansion.The clause is attached in
analyze_cypherrather than in the transform. The transform temporarily clearsprevto recurse into the join's right side and relies onprevbeing NULL there to take the plain MATCH path, so synthesizing aprevinside it re-arms that recursion until the stack runs out.Tests cover the no-match case for node and relationship patterns, a
WHEREthat no binding survives, that a matching pattern gains no extra null row, thatRETURN *does not expose the internal column, that a leadingMATCHstill returns no rows, and thatMATCHfollowingOPTIONAL MATCHstill errors.make installcheckpasses 43/43 on this branch and on its parent; with only the two test files applied to the parent,cypher_matchfails.Known limitation: a
UNIONbranch beginning withOPTIONAL MATCHstill returns no rows. Branches are assembled throughcypher_parse_sub_analyze_union, notanalyze_cypher, so they never reach this path. Placing the same unit clause there would be a natural follow-up.Closes#2473