Repository navigation
Fixes in the ObjectScript classes, the Cypher translator, the parser and the schema migration. Callers who rely on
USE GRAPH to isolate tenants should upgrade (first entry below). Re-run initialize_schema() after upgrading to
deploy the changed classes.
Fixed
-
A
USE GRAPHread confined to a graph read other graphs, for these shapes. Every released version is affected;
callers who rely onUSE GRAPHto isolate tenants must upgrade.USE GRAPH 'g'scoped the tables of the main
MATCHand left the following unscoped, so the answer included rows of every graph in the namespace: aUNIONor
UNION ALL(no branch scoped); aWHERE NOT (a)-[:R]->(:Q)orWHERE EXISTS { MATCH ... }test; a pattern
comprehension[(a)-[:R]->(b) | b.name]; aCALL { }subquery (also refused withSQLCODE -23); the sort key behind
WITH a ORDER BY a.prop(which rows aLIMITkept); the null-row fallback of a leadingOPTIONAL MATCH(a label
held only by another graph suppressed the null row); and every hop after the first of a multi-hopOPTIONAL MATCH.
Two alias guesses in the translator also appended<alias>.graph_id = 'g'toWHEREfor aliases that are no table:
anUNWINDread failed (SQLCODE -29, which the driver turned into zero rows), and a LEFT-joined relationship in
OPTIONAL MATCH (a)-[r:R]->(b)turned the outer join inner and dropped every node without such an edge, with one
graph or many. All of it is scoped now: the pass reads the tables from the statement's ownFROM/JOINtext, a
joined table takes its predicate in itsONclause, and the guesses are removed. The native Cypher path declines
these shapes, so they run on SQL.tests/e2e/test_use_graph_scope_shapes_e2e.pyruns 19 shapes (three are controls) against two graphs
with different data on the native, SQL and shadow paths;tests/unit/test_use_graph_scope_every_alias.pychecks every
graph-owned alias of 53 read shapes. -
The Cypher parser never finished on a
UNIONbranch that held aWITHor began withUSE GRAPH, e.g.
MATCH (a) RETURN a.id AS i UNION MATCH (b) WITH b RETURN b.id AS i, and grew to about 8 GB in under two minutes: a
denial of service for any caller that parses untrusted Cypher (the Bolt server, the REST API)._parse_union_branch
appended an empty part for every tokenparse_query_partdoes not consume (WITH,),ORDER,LIMIT, any stray
token), forever. A branch now takesWITHstages as the main query does and stops at a token it cannot use, so
malformed input is aCypherParseError(a bareUNIONtoo, which was aValueError).USE GRAPHinside a later
branch is aCypherParseError: it prefixes the whole query. Queries that parsed before parse to the same tree. -
Graph.KG.Eraser.EraseNodeIds(and sodelete_node,bulk_delete_nodes,delete_nodesand
delete_nodes_by_prefix) no longer drops^NKGwhen it could not have made it stale. It dropped it whenever an
erase removed an edge, so a retention purge over nodes that were never interned (opsreview's, once per tenant every
four hours) threw away an index it had not touched.^NKGinterns node ids with no graph dimension, and every
writer of an^NKGedge interns both ends first, so an edge with an end that is not in^NKG("$NI")is not in
^NKG. The drop now happens only when an erased id, or both ends of a removed edge, is interned. An erase that
keeps^NKGstill bumps^KG("__version")and^NKG("$meta","version")and purges the cached algorithm answers,
and its effect on rows and^KGis unchanged. Redeploy the classes (initialize_schema()). -
erase_graphleft^NKGstanding, stale, for a graph whose edges are SQL rows.Graph.KG.Eraser.EraseGraphread
$Data(^KG("out", graph))after it had deleted therdf_edgesrows; since spec 252 the functional index clears that
subtree row by row, so the read was always 0 and^NKGwas never dropped. The adjacency entries are removed per row,
butedgeCountand the$aggHyperLogLog sketches cannot be, soCountDistinctKHop(andapprox_count_distinct
through it) went on counting the far ends of erased edges: with a node id shared with a surviving graph, a query
that should have said 1 said 3. The erase now decides before the rows go, from the adjacency and the rows
(GraphEdgesInNKG), and uses the rule of the entry above:^NKGis dropped when an edge of the graph has both ends
interned, and kept (not stale) when none has.EraseEdgesof an edge that has an adjacency entry but no row
counted nothing removed and never dropped^NKG; it does now. Redeploy the classes (initialize_schema()). -
initialize_schema()on a pre-4.3 install no longer runs a statement that fails withCREATE INDEX idx_props_key_valkey ... SQLCODE -400 ERROR #5017: Too many errors, and no longer can leave a wrong index under that
name. The base DDL ranCREATE INDEX idx_props_key_valkey ON Graph_KG.rdf_props (key, val_key)before the value-key
migration had addedval_key. IRIS does not reject an index over a column the table lacks: it built the index over
val(INFORMATION_SCHEMA.INDEXESlistskey,val). A value too long for a subscript is then a<SUBSCRIPT>error
at that row (one such value fails the statement), and from about a hundred of them IRIS reportsERROR #5017: Too many errors. IRIS rolls the statement back, the migration then built the right index, and the install ended up correct
with a warning in the log. When no value was long enough to fail, though, the index over(key, val)stayed under
the right name, the migration saw the name and skipped the real one, and the first long value written afterwards
failed with<SUBSCRIPT>. The base DDL no longer declares the index (the migration creates it after the column), the
bulk-load rebuild skips it where the column is missing, andensure_props_value_keydrops and rebuilds an index of
that name that is overval. No change toval_keyor to a correct index. -
A Cypher equality on a string with a character outside the BMP (an emoji) no longer returns nothing.
val_key
is$EXTRACT(val, 1, 128), which counts UTF-16 code units, and the translator wrote the first 128 code points; for
a value with such a character in front the two prefixes differed andval_key = '<prefix>'matched no row. The
translator now writes the stored prefix, and noval_keypredicate when the cut falls between the halves of a
surrogate pair (the exactvalcomparison alone is still right).
Documentation
- The README badge and
docs/BENCHMARKS.mdsaid IRIS 2024.1+;ADMIN_GUIDEsaid 2025.1. Both now say 2025.1, and
ADMIN_GUIDElists the releases IVG has been run on (2026.1 Community in CI, 2026.3 AI for the integration suites,
2025.1 Community last seen in May 2026) and says 2024.x was never run. The HNSW index needs 2025.1. docs/performance/ALT_VS_BASELINE.mdis regenerated with the spec 257 sections; README,ADMIN_GUIDEand the
walkthrough notebook link it rather thanREPORT_TEMPLATE.md, whose relative links only resolve from the report's
own location.specs/259-compiled-plan-levers/notes.md: where BI04, BI14, BI18 and BI11 spend their time after spec 257, the
levers measured, and the profiling scripts (scripts/bench/alt/prof259/).