fix: honor $parameters in rel inline filters (#96), RETURN created rel vars (#95), caller-assigned edge ids for upsert_edge (#97) - #98
Merged
Conversation
…lters (#96) MATCH ()-[r:TYPE {prop: $param}]->() silently skipped the parameter and matched every edge of the type — dangerous for SET/DELETE scoped by such a filter. Mirror the node-pattern parameter handling: OR of EXISTS subqueries across the four edge property type tables bound to the named parameter. Literal filters and WHERE-clause parameters were already correct. Co-Authored-By: Claude <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01QuzbsuTddFm245sZ9egekS
MATCH (x),(y) CREATE (x)-[r:T]->(y) RETURN r raised 'Unknown variable: r' after the CREATE had already committed: the handler re-executed MATCH+RETURN, which has no binding for variables introduced by CREATE. execute_multi_match_create_query can now hand back one variable_map per processed MATCH row (matched + CREATE-introduced bindings). The MATCH+CREATE+RETURN handler detects RETURN items referencing CREATE-only variables and projects them from those row maps (bare var, var.prop, and aggregates, with SKIP/LIMIT), one result row per MATCH row. Queries whose RETURN only references MATCH-bound variables keep the legacy path unchanged. Co-Authored-By: Claude <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01QuzbsuTddFm245sZ9egekS
MERGE previously ignored parameter-valued inline properties: the match
phase skipped them (matching any node of the label / any edge on the
triple) and node creation dropped them. Fix find_node_by_pattern,
find_edge_by_pattern, and the node-create property phase to resolve
$parameters, so MERGE (a)-[r:T {id: $eid}]->(b) matches/creates by that
property like a literal would.
On top of that, expose caller-assigned edge identities in the bindings:
- Python: upsert_edge(..., edge_id=None). With edge_id, the edge is
merged on that id (stored as an 'id' relationship property) instead of
the (source, target, rel_type) triple, so parallel edges on the same
triple are individually addressable and upsertable in place. Default
behavior is unchanged.
- Rust: new upsert_edge_with_id(source, target, props, rel_type, edge_id)
with the same semantics; upsert_edge is untouched.
Co-Authored-By: Claude <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01QuzbsuTddFm245sZ9egekS
Requested in #95: neither the README nor the docs mentioned concurrent writers, multi-process access, or WAL mode, leaving it an open question for anyone evaluating embedded concurrent-write use. Co-Authored-By: Claude <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01QuzbsuTddFm245sZ9egekS
Adds tests/functional/39_issue_regression_tests.sql (the file the semantic-coverage-matrix process points at) with hard assertions — a CHECK-constrained temp table aborts the run under sqlite3 -bail on any mismatch — covering all three fixes, and links the cells from docs/testing/semantic-coverage-matrix.md. Co-Authored-By: Claude <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01QuzbsuTddFm245sZ9egekS
9 tasks
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.
Summary
MATCH ()-[r:T {k: $param}]->()silently skipped parameter-valued inline relationship property filters and matched every edge of the type (dangerous for scopedSET/DELETE).transform_match.cnow emits the same OR-of-EXISTS parameter condition the node path already used.MATCH (x),(y) CREATE (x)-[r:T]->(y) RETURN rraisedUnknown variable: rafter the CREATE had committed (the handler re-ran MATCH+RETURN, which can't see CREATE-introduced variables).execute_multi_match_create_querycan now hand back one variable map per processed MATCH row, and the MATCH+CREATE+RETURN handler projects CREATE-only variables from those maps (barer,r.k, aggregates, SKIP/LIMIT; one result row per MATCH row). Queries returning only MATCH-bound variables keep the legacy path unchanged. The concurrency/WAL docs note requested in the same issue is included.upsert_edge. Prerequisite fix: the MERGE match phase ignored$paraminline properties for both nodes and edges (matching any node of the label / any edge on the triple), and node creation dropped them — fixed inexecutor_merge.c(find_node_by_pattern,find_edge_by_pattern, node-create property phase). On top: Pythonupsert_edge(..., edge_id=None)and Rustupsert_edge_with_id(...)merge on anidrelationship property, so parallel edges on the same(source, target, type)triple are individually addressable and upsertable in place. Default (no-id) behavior is unchanged; the existing Rustupsert_edgesignature is untouched.Test plan
make test-unit— 947/947 pass (3 new CUnit regression tests: rel$paramfilter, MATCH+CREATE+RETURN created var, MERGE$paramproperties)make test-functional— clean, including newtests/functional/39_issue_regression_tests.sql(17 hard assertions; CHECK-constrained temp table aborts undersqlite3 -bailon any mismatch)python -m tests.tck --backend extension— 3778 pass; per-scenario diff against a pre-change run of the same tree: zero regressionsmake test-python— 357 pass (3 newedge_idtests)make test-rust— 244 integration tests pass (1 newupsert_edge_with_idtest)Semantic coverage matrix
docs/testing/semantic-coverage-matrix.md:MATCH (a) CREATE (a)-[:R]->(b)literal cell now coversRETURN r/r.kread-back on the created rel var (39:GH-95)39:GH-96), MERGE node inline prop match phase (39:GH-97 3.1), MERGE rel inline prop match phase (39:GH-97 3.2)tests/functional/:tests/functional/39_issue_regression_tests.sql(new — the file the matrix process references)skip-coverage-matrixlabel (pure refactors only).Checklist
angreal test functionalgreen locally (run viamake test-functionalequivalent —sqlite3 -bailovertests/functional/*.sql)angreal test unitgreen locally (run viamake test-unit)fix:,feat:,chore:…)Fixes #95, fixes #96, closes #97.
🤖 Generated with Claude Code
https://claude.ai/code/session_01QuzbsuTddFm245sZ9egekS
Generated by Claude Code