Skip to content

Test the labels of a node a MATCH finds after a grouping WITH by their text on Oracle, which crashed the session building its cursor (#418) - #424

Merged
jasperblues merged 2 commits into
mainfrom
fix-418-grouped-labels
Nov 5, 2026
Merged

jasperblues merged 2 commits into
mainfrom
fix-418-grouped-labels

Conversation

@jasperblues

Copy link
Copy Markdown
Member

Closes #418. Port of liberation-data/drivine4j#606 (drivine4j#601). In TS this is drivine#623 and drivine#627.

Cause

On Oracle 23.26, a labelled node that a MATCH finds after a WITH that groups its rows is tested against the multivalue JSON index over labels. A grouping WITH is one that aggregates or is DISTINCT, or any WITH after one. Oracle can crash the session building that statement's cursor:

  • ORA-03113, with ORA-00600 [QJSNMV_GET_MVI_MD:2] in the alert log;
  • or ORA-07445 [qjsnmvFindOpt()+16] after a rolled-back delete.

The Cypher spec case "Maps and lists holding relationships compare by identity" hits it, on its :Movie node.

The fix

  • _Select.after_grouping is set in _with_having for a grouping WITH, and for any WITH after one. OPTIONAL MATCH and shortestPath laterals inherit it from the select around them, as in drivine4j's review.
  • grouped_labels is a new store hook. Its base writes has_labels. Oracle's writes each label in holds_label's INSTR text form, which the index can't answer.
  • A pattern node read after a grouping tests its labels through the hook. So does a WHERE n:Label on a node newly found after it, which is tracked in _grouped.
  • Unchanged: a node the WITH passed on, the nodes before the WITH, and every other read keep the index.

The compiled SQL

1,684 steps of the Cypher spec (queries, writes, temporal and refusals), main against this branch:

  • Postgres: byte-identical.
  • SQLite: byte-identical, apart from the compile-time clock.
  • Oracle: 2 change. They are steps 15 and 16 of "Maps and lists holding relationships compare by identity", where the :Movie test moves from JSON_EXISTS to the INSTR form. With that form mapped back, both are byte-identical to main's SQL.

tests/golden/statements.json is unchanged.

Tests

tests/unit/test_cypher_grouped_labels.py ports drivine4j's GroupedLabelsTest, which covers drivine#623's cases, and adds a SQLite case. It has 20 cases:

  • 12 failed on main:
    • the spec case;
    • each kind of grouping WITH, and each label of a node with two labels;
    • OPTIONAL MATCH (a pattern label, and WHERE m:Movie) and shortestPath laterals;
    • WHERE m:Movie on a node found after the grouping;
    • a UNION, where only the grouped branch changes.
  • 8 pin what stays the same, and passed on main:
    • no grouping;
    • a plain WITH, and WITH … ORDER BY … LIMIT;
    • a node the WITH passed on, and the nodes before it;
    • Postgres and SQLite.

docs/04-statements.md gains the Oracle note that drivine4j adds to its docs/09-supported-engines.md.

Gate

  • ruff check, ruff format, pyright and unasync --check: clean.
  • ORACLE_TESTS=true … pytest -k oracle: 2,397 passed.
  • Full pytest: 7,469 passed, 1 failed. The failure was the Postgres CALL…RETURN timing case, which this change doesn't touch: its Postgres SQL is unchanged. It passed when run again, and is tracked under A compiled MERGE of many relationships is quadratic on Postgres once the edge table is vacuumed empty #420.
  • After review: the new tests, the goldens, and 64 Oracle spec cases covering grouping, aggregation, DISTINCT, collect and identity, on a real store: green.
  • Three review passes, the last clean.

…r text on Oracle, which crashed the session building its cursor (#418)

Port of liberation-data/drivine4j#606. On Oracle 23.26 a labelled node a
MATCH finds after a WITH that aggregates or is DISTINCT (or any WITH
after one) could crash the session when tested against the multivalue
label index (ORA-00600 [QJSNMV_GET_MVI_MD:2]). A new store hook,
grouped_labels, writes such a test: has_labels on every store but
Oracle, where it is holds_label's INSTR form. OPTIONAL MATCH and
shortestPath laterals inherit the flag, and a WHERE n:Label on a node
found after the grouping uses it too.

Closes #418
@jasperblues
jasperblues merged commit 6bfe6a6 into main Nov 5, 2026
7 checks passed
@jasperblues
jasperblues deleted the fix-418-grouped-labels branch November 5, 2026 17:28
@jasperblues jasperblues mentioned this pull request Nov 6, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Oracle: a labelled node a MATCH finds after a grouping WITH crashes the session in the label index (ORA-00600 QJSNMV_GET_MVI_MD:2)

1 participant