Repository navigation
Group, de-duplicate and sort a map's null and a missing key as one null, and merge a CALL's UNION ALL marks from every part, as drivine4j does - #322
Merged
Conversation
…ll, and merge a CALL's UNION ALL marks from every part, as drivine4j does, at Cypher spec v0.20.0 On Postgres and Oracle a null read out of a map or a list is JSON's null and a missing key SQL's, so the two grouped, de-duplicated and sorted apart where Neo4j has one null (#314, port of liberation-data/drivine4j#500). A JSON value now carries `json_null` where it may be JSON's null: a property or an item of a map or a list not known free of nulls, a parameter's entry, an unwound item, head() and last() of such a list, a null written, a reduce(), and a CASE that may give one. A projection that groups or keeps its rows distinct compares such an item with the two nulls as one (`grouped_null`, on Oracle `NVL(x, JSON('null'))`), an order term that is the item sorts by the same column, a part of a UNION without ALL gives it as SQL's null (`compared_null`), and Oracle's order ties the two nulls so the next term decides (`json_null_sort_key`). A property read from the store is never flagged, so its SQL is unchanged. A CALL's UNION ALL merged its parts' marks from the first part only (#321, port of liberation-data/drivine4j#512): the merged column now may be JSON's null where any part's may be, and is a list, a list of lists or flat only where every part's is. The Cypher spec moves to v0.20.0, which adds two steps to "A null in a map and a missing key are one null"; the #314 holds are taken out. Fixes #314 Fixes #321
…le's null test reuse compared_null
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 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.
Fixes #314
Fixes #321
Port of liberation-data/drivine4j#500 and liberation-data/drivine4j#512 (drivine4j PRs #503 and #514, review-pass commits included). Drivine for TypeScript has the first in liberation-data/drivine#501.
What changes
A map's null and a missing key are one null (#314). On Postgres and Oracle, a null read out of a map or a list is JSON's null, and a missing key is SQL's null. The two grouped, de-duplicated and sorted apart, where Neo4j has one null. A
Jsonvalue now carriesjson_nullwhere it may be JSON's null. That is:head()andlast()of such a list;reduce();CASEthat may give any of these.Where it is set:
grouped_null:NULLIF(x, 'null'::jsonb)on Postgres, andNVL(x, JSON('null'))on Oracle, as in drivine4j's review. The GROUP BY key and the DISTINCT column both read it that way. An order term that is such an item sorts by the same column._held_firstchecks the compared form for a subquery.compared_null). This applies to a top-level UNION, a subquery's UNION, and a CALL's UNION. Under a CALL's UNION parts (_distinct_parts, set and restored by_comparing_parts), grouping also compares by SQL's null, soCALL { RETURN null AS v UNION RETURN {}.a AS v }doesn't split.json_null_sort_keysorts by[rank, grouped_null(x)], so the next term decides between the two nulls.A CALL's UNION ALL marks (#321).
_called_unionused to take its marks from the first part only. Now:text/textsalready were.Cypher spec
Synced to v0.20.0 with
scripts/sync_cypher_spec.py v0.20.0. It adds two steps to "A null in a map and a missing key are one null" (liberation-data/drivine-cypher-spec#45). All of #314's holds are removed: the Postgres and Oracle holds on that case inqueries.yaml, and the SQL hold on "A null in a written row and a missing key are one null" inwrites.yaml. #321's two new steps pass with no hold.Tests
These are in
tests/integration/test_cypher_compiled.py, and in its sync copy generated byscripts/unasync.py:CypherQueryContractandCypherWriteContractcases of #500 that the spec doesn't hold, plus Drivine for TypeScript's own cases from #501'sone-null.yamlbeside them: a subquery's UNION,head()/last(),$p['a'], and aCASEgiving a null parameter.SQL comparison
I compiled every statement of spec v0.20.0 (1,496) and of the openCypher TCK corpus (4,119) on
origin/main(79f5d87, with #320) and on this branch, for Postgres and Oracle.queries.yamlandwrites.yaml.UNWIND ['b', null, 'B'],UNWIND [n.name, 'B'],UNWIND [i, i, 3], a pattern comprehension's list); an item taken by index ([...][0],values[i]);head(collect(...))(TCK Return4 [11], With4 [6]); and an entry of a map that may hold a null (m.a,who.name,p {.name}thenp.name).UNWIND [2, 'b', null] … ORDER BY t DESC. It now checks that a list without a null sorts as before, and that one holding a null sorts byNVL(…, JSON('null')).Differences from drivine4j and Drivine for TypeScript
_distinct_partsis on the compiler._beginresets it, and_comparing_partssets and restores it, including for a top-level UNION, as Drivine for TypeScript does. drivine4j sets the writer's flag once instead. The effect is the same.compared(value)is_row_comparedhere, as it isrowComparedin Drivine for TypeScript, because_comparedalready names the sides of a binary comparison.one-null.yaml, because it ran an older spec. Here the spec runs at v0.20.0, so only the cases beyond the spec are added, as Python tests.