Skip to content

Refuse values and variables of the wrong kind, and truncate with a map of fields, as drivine4j does, at Cypher spec v0.10.0 - #302

Merged
jasperblues merged 2 commits into
mainfrom
tck-temporal-kinds
Oct 31, 2026
Merged

jasperblues merged 2 commits into
mainfrom
tck-temporal-kinds

Conversation

@jasperblues

@jasperblues jasperblues commented Oct 31, 2026 •

Copy link
Copy Markdown
Member

Ports drivine4j's TCK steps 1–3 (liberation-data/drivine4j#448, #451 with #464, #468), as liberation-data/drivine#482 does, and syncs the Cypher spec from v0.6.19 to v0.10.0.

Fixes #265
Fixes #301
Part of #263: the refusals a statement's written kinds allow. The run-time checks #263 also lists aren't here: a negative SKIP or LIMIT parameter (Oracle still runs it), and a deleted node's property read later in the statement.

Step 1: values of the wrong kind (part of #263). A value whose kind the statement fixes carries it (Json.known): a literal, a negated number, a list or a map written, or a variable a WITH binds to one, carried across a write, through a reduce() step's map and across a UNION in CALL { … } where every query gives it the same kind. Such a value is refused where an operator or a function takes another kind: AND/OR/XOR/NOT, a condition, a property (WITH 123 AS x RETURN x.num), an index, IN, range() and properties(). A step of 0 in range(), written or given, and a map, or a list of maps or lists, written in SET are refused too. Parameters, properties, null, CASE, coalesce() and unwound items have no known kind and compile as before.

Step 2: variable kinds (#301). drivine/cypher/sql/variable_kinds.py ports VariableKinds. It reads the statement first in compile_query and changes no SQL. It refuses a variable used as two kinds (MATCH (a)-[a]->()), a node, relationship or path a pattern makes where it is bound already (MATCH (a) CREATE (a), MATCH p = (p)-->()), and size() of a path. Inside a FOREACH, Neo4j takes a bound variable, so this does too. What a CALL's brackets import stays in scope after a WITH inside it (drivine4j#464).

Step 3: truncation (#265). truncate() now takes millennium, century, decade, weekYear and microsecond, plus a third argument, a map of fields laid over the value cut short. The spec's refusals apply. Not compiled yet: nanoseconds, the fields of a week or a quarter, and a zone by name.

Checks

  • Corpus: 3,657 statements from this repository's tests, samples and docs, the golden statements, the spec at v0.6.19 and v0.10.0, and drivine#482's corpus, compiled before and after on Postgres and Oracle. Every statement that compiled before gives byte-identical SQL, prelude and parameters. The only new refusals are the spec's own cases and drivine's refusal tests.
  • tests/golden/statements.json is unchanged.
  • Truncation matches Neo4j, Postgres and Oracle at century, decade and millennium ends, at weekYear across the year turn, and for microseconds with nanosecond input (cut, not rounded, including 23:59:59.999999999). The field maps match too.
  • Gate: ruff check, ruff format --check, pyright, unasync.py --check, and the full pytest with ORACLE_TESTS=true: 5638 passed, 298 skipped.

…p of fields, as drivine4j does, at Cypher spec v0.10.0

Ports drivine4j's TCK steps 1-3 (#448, #451 with #464, #468 there), as Drivine for TypeScript's #482 does:

- A value whose kind the statement fixes (a literal, a list or a map written, or a variable a WITH binds to one)
  carries it (Json.known), and is refused where an operator or a function takes another kind: AND, OR, XOR and NOT,
  a condition, a property, an index, IN, range() and properties(). A step of 0 in range(), written or given, and a
  map, or a list of maps or lists, written in SET are refused too. Parameters, properties and null keep no kind.
- check_variable_kinds reads the statement before it is compiled, and refuses a variable used as two kinds, or made
  by a pattern where it is bound already, as Neo4j does; what a CALL's brackets import stays in scope after a WITH in
  it (drivine4j#464).
- truncate() takes millennium, century, decade, weekYear and microsecond, and a map of fields laid over the value
  cut short, with the spec's refusals.

Every statement that compiled before gives the same SQL. The Cypher spec is synced to v0.10.0.
…ep of 0, and compile a negated float field as an expression
@jasperblues
jasperblues merged commit 5ac4eb9 into main Oct 31, 2026
7 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

1 participant