Skip to content

Releases: colliery-io/graphqlite

v0.4.4

19 Apr 01:54

Choose a tag to compare

What's Changed

  • Release 0.4.4: fix all 7 sub-bugs of issue #61 + dispatcher hardening by @dylanbstorey in #62

Full Changelog: v0.4.3...v0.4.4

v0.4.3

31 Mar 13:05

Choose a tag to compare

Bug Fixes (#59)

  • COUNT + OPTIONAL MATCH null handling: COUNT(r) where r is null from OPTIONAL MATCH now correctly returns 0 instead of 1
  • Edge variables through WITH: Relationship variables now pass through WITH for type(r) and r.property access
  • Functions in CREATE property maps: CREATE (n {ts: timestamp(), upper: toUpper('hello')}) now evaluates function calls
  • CALL subquery inner RETURN export: CALL { WITH a RETURN a.id AS x } RETURN x now propagates inner columns to outer scope
  • CALL subquery multi-row inner MATCH: Inner MATCH now processes all matching rows, not just the first

Test Coverage

  • 937 CUnit tests, 47 functional SQL files, 351 Python tests, 250 Rust tests

Closed Issues

v0.4.2

31 Mar 02:22

Choose a tag to compare

Bug Fixes

  • UNWIND $param write paths (#49): UNWIND $param AS item CREATE/MERGE/SET now works with parameterized lists, literal lists + SET, and per-item MERGE iteration
  • startNode(r)/endNode(r) alias collision (#50): RETURN startNode(r).name, endNode(r).name now produces distinct column names
  • CALL subquery MERGE scoping (#51): CALL { WITH c MATCH (d) MERGE (c)-[:REL]->(d) } correctly resolves inner MATCH variables
  • OPTIONAL MATCH WHERE null preservation (#34b): OPTIONAL MATCH ... WHERE r.prop = "x" preserves null rows when filter doesn't match
  • WITH+MATCH+MERGE dispatch: MATCH (a) WITH a MATCH (b) MERGE (a)-[:REL]->(b) now routes correctly
  • Functions in SET with property refs: SET n.trimmed = trim(n.name) resolves node properties in function arguments
  • SQL alias sanitization (#55): Backtick-quoted Cypher identifiers no longer produce invalid SQL aliases

Test Coverage

  • 937 CUnit tests, 46 functional SQL files, 346 Python tests, 245 Rust tests
  • New cross-clause hardening suite (38_cross_clause_hardening.sql)
  • All 12 historical expected-failure issue repros promoted to passing regression suite
  • Issue regression tests added to Python and Rust binding suites

Closed Issues

v0.4.1

29 Mar 19:53

Choose a tag to compare

What's New

CALL {} Subquery Support (openCypher)

Full implementation of CALL {} subqueries — a core openCypher feature for batch operations, correlated subqueries, and conditional writes.

-- Write-only: SET properties via outer variable binding
MATCH (n:Person) CALL { WITH n SET n.processed = true }

-- UNION branches inside CALL
MATCH (c:Company) CALL {
  WITH c MERGE (c)-[:HAS_DEPT]->(:Dept {name: "Eng"})
  UNION
  WITH c MERGE (c)-[:HAS_DEPT]->(:Dept {name: "Sales"})
}

-- Standalone with RETURN
CALL { MATCH (n:Person) RETURN n.name ORDER BY n.name LIMIT 5 }

-- Nested CALL
CALL { CALL { RETURN 42 AS answer } }

Supported patterns:

  • Standalone CALL { ... } with inner MATCH, RETURN, CREATE
  • MATCH + CALL { WITH ... SET/MERGE/CREATE } — outer variable binding per row
  • CALL { ... UNION ... } — execute multiple branches with shared WITH imports
  • WITH a AS x aliasing inside CALL scope
  • Nested CALL { CALL { ... } }
  • Scope isolation: variables not imported via WITH are inaccessible

CREATE...RETURN Support

CREATE (n:Label {props}) RETURN n.prop now works — previously returned "not yet implemented". This also unblocked the functional test suite which was silently stopping at test 26.

Structured JSON Error Codes

All error responses from the C extension now return structured JSON with error codes:

{"error": "Line 1, Col 6: syntax error", "code": "PARSE_ERROR"}

Architecture Hardening

From the architecture review (Wave 3):

  • REC-02: Fixed integer overflow, unchecked realloc, and O(n²) strcat in result serialization
  • REC-03/04/05: Eliminated strict-aliasing violations and fixed-size truncation buffers
  • REC-07: Added scale guards for O(N²) graph algorithms
  • REC-08: Migrated Graph API to parameterized queries (SQL injection prevention)
  • REC-09: Deleted unused bundled_init.c (593 lines of dead code)
  • REC-10: Single-pass result enumeration in build_query_results
  • REC-11: Structured JSON error codes from C extension
  • REC-12: Schema versioning via PRAGMA user_version
  • REC-13: Hardened Rust extension extraction with atomic write and fallback
  • REC-14: Migrated all static transform globals into per-query context (thread safety)

Test Coverage

  • 926 CUnit tests (up from 770)
  • 44 functional SQL test files (all running — previously stopped at test 26)
  • 332 Python binding tests
  • 233 Rust binding tests

Breaking Changes

None. All existing APIs are backward compatible.

Full Changelog

v0.4.0...v0.4.1

v0.4.0

28 Mar 03:17

Choose a tag to compare

What's New in v0.4.0

Bug Fixes (8)

  • Integer storage widened to 64-bit (#43) — Integer properties were silently truncated to 32-bit, affecting timestamp(), large IDs, and any value > 2.1 billion. Now uses int64_t throughout the pipeline.
  • size(labels(n)) returns label count (#42) — Was returning the string length of the JSON representation instead of the number of labels.
  • DELETE + RETURN COUNT(n) reports actual count (#39) — Was always returning 0 because the RETURN re-queried the already-empty graph.
  • OPTIONAL MATCH respects label filters (#34) — OPTIONAL MATCH (a)-->(r:Label) was ignoring the label constraint and returning unrelated nodes.
  • UNWIND $param accepts parameters (#37) — Parameter references and map property access (item.id) now work in UNWIND expressions, enabling batch ingestion patterns.
  • startNode(r).name resolves properties (#41) — startNode() and endNode() now support property access and function composition.
  • MERGE variables persist through WITH (#36) — MERGE ... SET ... WITH i MATCH ... MERGE ... now works via a new clause pipeline handler.
  • SET n.prop = function() evaluates correctly (#35, #38) — Function calls and parameterized bulk SET (SET n += $param) now work in SET clauses.

Documentation

  • Comprehensive Diátaxis documentation rewrite covering tutorials, how-to guides, reference, and explanation sections.

Testing

  • 13 new reproduction tests covering all fixed bugs (Python + functional SQL)
  • Rust test parallelism fix across CI and release workflows

Upgrading

This release changes integer storage from 32-bit to 64-bit. Existing databases are unaffected (SQLite INTEGER columns already support 64-bit). New integer values will now be stored at full precision.

Full Changelog: v0.3.10...v0.4.0

v0.3.10

22 Mar 13:05
a205847

Choose a tag to compare

What's New

Pattern predicates in WHERE clause (openCypher 9 compliance)

Bare relationship patterns now work as boolean expressions in WHERE clauses:

-- These are now equivalent:
MATCH (n) WHERE NOT (n)-[:BELONGS_TO]->() RETURN n
MATCH (n) WHERE NOT EXISTS((n)-[:BELONGS_TO]->()) RETURN n

Supports all directions (->, <-, -), typed and untyped relationships, and combination with NOT/AND/OR/XOR.

Bug fixes

  • EXISTS direction fix: EXISTS(pattern) now correctly handles incoming (<-) and undirected (-) relationship patterns. Previously always assumed outgoing direction.

Testing

  • Added Clotho bug reproduction tests to Rust integration suite, confirming count(), OPTIONAL MATCH, property-match {key: value}, and undirected match all work correctly through the Rust binding path.

Full Changelog: v0.3.9...v0.3.10

v0.3.9

18 Mar 01:49

Choose a tag to compare

What's Changed

  • feat: Cypher spec compliance push — 70% to 88% coverage by @dylanbstorey in #31

Full Changelog: v0.3.8...v0.3.9

GraphQLite 0.3.8

17 Mar 13:55

Choose a tag to compare

Bare relationship syntax is here. (a)--(b), -->, and <-- just work now. Undirected matches also got a correctness fix — they now properly query both edge directions instead of silently going forward-only.

Leading-zero strings stay strings. Zip codes like "02134" no longer get silently coerced to 2134 when returned via property access. Fixed at the C layer.

Rust bindings got a lot more ergonomic:

  • PropertyValue enum — pass typed values (42_i64, 0.87_f64, true) directly to upsert, not just strings
  • Value::get("key") and Index<&str> — no more pattern matching to read node properties
  • get_edges_from(), get_edges_to(), get_edges_by_type() — edge queries without raw Cypher
  • Graph::query_params() — one-liner parameterized queries
  • GraphStats fields renamed to node_count/edge_count (breaking)

Python binding bug fixes:

  • BFS/DFS and APSP now return actual results (were silently returning [])
  • Same node_count/edge_count rename for consistency

Algorithm parameters work correctly — integer and double params passed to graph algorithms no longer cause segfaults (#29).

Big shout-out to @kynx for their first contribution to GraphQLite! Welcome aboard.

Full Changelog: v0.3.7...v0.3.8

v0.3.7

03 Mar 02:50

Choose a tag to compare

What's Changed

Full Changelog: v0.3.6...v0.3.7

v0.3.5

18 Feb 19:36

Choose a tag to compare

What's Changed

  • feat: make upsert_edge type-aware with property update support by @anonenity in #25

New Contributors

Full Changelog: v0.3.4...v0.3.5