Repository navigation
feat: integrate cypher, csr and engine work for 0.3.0 - #55
Merged
Merged
Conversation
Keep integer lists as Int64 values and convert numeric lists only at vector boundaries. Add singleton-row planning so standalone RETURN examples execute on configured engines. Issue #3. Implemented with assistance from Codex; coordinated runtime and benchmark validation pending.
Retry refresh when another replacement has been published since its starting snapshot. Add deterministic publication coverage and preserve failed-refresh and active-reader behavior. Resolves #12. Implemented with assistance from Codex; runtime and benchmark validation pending.
Migrate mapping readers while keeping public fields for source compatibility until a breaking release. Document migration and test normalized keys and original labels. Part of #23. Implemented with assistance from Codex; runtime and benchmark validation pending.
Use catalog schemas and scoped aliases to diagnose incompatible operands before planning. Preserve numeric and temporal coercions and defer unknown types to DataFusion. Add Rust semantic and Python execution coverage. Part of #23. Implemented with assistance from Codex; runtime tests queued and benchmark validation pending.
Reuse unoptimized plans in the engine, binding fresh scalar values before optimization. Bound the cache to 128 templates and retain ordinary planning for containers and value-dependent output names. Include result, schema, refresh and concurrency regressions and scoped timing harnesses. Part of #14. Isolated core Rust suite: 344 passed, 1 optional timing diagnostic ignored. Formatting, focused Ruff and strict docs passed. Python runtime, bridge test linking, benchmark and Clippy evidence remain pending. Submitted by David Hughes (@stugorf), with AI Collaboration.
Resolve literal-or-parameter row counts before logical planning, including chained WITH limits. Reject invalid counts without truncation or wraparound. Part of #20. Implemented with assistance from Codex; coordinated runtime and benchmark checks pending.
Keep the existing refresh test array downcast in scope after sharing its real snapshot fixture with cache tests. Submitted by David Hughes (@stugorf), with AI Collaboration.
Resolve all RETURN expressions before applying scalar aliases to the ORDER BY binding scope. Preserve whole-node projection behavior. Add focused planner and CSR/join Python regressions for issue #21. Document alias scope in the Cypher guide. Validation: Rust formatting, Python lint, strict docs build, spelling, and whitespace checks pass. Published 0.2.0 reproduction: 14 failures, 4 existing passes. Current-source runtime tests remain pending the coordinated build. Implemented by David Hughes with assistance from Codex.
Specialize size() of unmapped list comprehensions to filtered COUNT(1), preserving null elements, duplicates, padding and input-row multiplicity. Keep mapped and pattern comprehensions on their existing path. Document the non-nullable Arrow count field and add focused plan/result tests plus a reference benchmark. Part of #18. Isolated Rust suite: 349 passed, one optional timing diagnostic ignored. Formatting, focused Ruff and strict docs passed. Python, benchmark and SF1 measurements pending; no performance claim. Submitted by David Hughes (@stugorf), with AI Collaboration.
Implement the bounded issue #20 scalar-function group with strict argument kinds, Unicode string operations, list operations, and stepped ranges. Preserve Cypher null, invalid-length, and rounding behavior. Accept empty quoted string literals and document the supported signatures and limits. Validation: 343 Rust library tests, 176 focused Python tests, workspace all-target clippy, Python lint, strict docs, spelling, and executable docs pass. Full Python run: 581 pass, two prerequisite empty-map parameter failures; the prerequisite correction is being integrated separately. Submitted by David Hughes (@stugorf), with AI Collaboration.
Source and runtime checks confirm literals and arithmetic already use the stable expr column name. Remove the unnecessary fallback and correct its regression expectation and docs. Fresh-versus-cached Arrow schema/result tests pass for unaliased scalar parameters. Part of #14. Submitted by David Hughes (@stugorf), with AI Collaboration.
Use an explicitly unknown function in the rejection regression now that replace() is supported. Submitted by David Hughes (@stugorf), with AI Collaboration.
Address the read-only CALL portion of #20 with explicit imports and per-row streaming Apply execution. Preserve entity aliases, child aggregates and top-k semantics, and keep child variables scoped. Bypass snapshot templates for CALL and yield during long runs of empty children. Validation: workspace Rust tests, focused CALL regressions, 724 Python tests on the rebuilt extension, Clippy, executable docs, strict docs build, Ruff, rustfmt and typos 1.26.0. Submitted by David Hughes (@stugorf), with AI Collaboration.
Parse UNION and UNION ALL as independent branches, validate matching output columns and compatible Arrow types, and preserve distinct versus bag semantics. Include all branches in query metadata and document direct Rust AST migration. Part of #20. Submitted by David Hughes (@stugorf), with AI Collaboration. Full combined validation and SF1 measurements remain pending.
Seed branches from the same available imports while requiring each branch to declare its own WITH. Preserve entity outputs, scope catalog checks, and bypass cached shapes in every nested query. Use DataFusion UNION coercion before capturing child schemas and the final physical result schema when collecting batches. Validation: 10 focused Rust tests and 88 CALL/UNION Python cases pass, including streaming null/numeric coercion and repeated parameter changes. Combined feature validation follows integration. Submitted by David Hughes (@stugorf), with AI Collaboration.
Resolve #11 by resuming neighbor traversal between output batches and storing immutable deletion occurrence offsets. Add focused traversal/snapshot tests, a reusable hub memory scenario, and documentation. Assisted by Codex.
Resolve #17 with complete-layer bidirectional search for single-target CSR queries, preserving all shortest paths and duplicate edges. Keep shared forward search for multiple targets. Developed by David Hughes with assistance from Codex.
Implement keys for maps and exposed entity property columns, preserving null map fields while omitting absent entity properties. Resolve labels from canonical configured node bindings and preserve optional nulls and WITH aliases. Reject mixed-label bindings explicitly. Validation: 344 Rust library tests, 32 focused Python cases, all 615 Python tests, workspace all-target clippy, Python lint, executable documentation, strict docs build, spelling, formatting, and whitespace checks pass. Part of #20; mixed-label labels() remains an explicit engine boundary. Submitted by David Hughes (@stugorf), with AI Collaboration.
Part of #20. Keep fold variables local, preserve list order and null semantics, and correlate graph predicates with iterator bindings. Document supported forms and explicit body limits.
Add PEP 561 stubs matching the extension graph module, strict consumer checks, and runtime signature parity tests. Document the typing workflow and check wheel inclusion. Runtime and benchmark validation remain pending the coordinated build.
Add keyword-only timeout to the three Cypher execution entrypoints. Keep one deadline through streamed reads and release the owned stream while idle; preserve native Arrow reader errors and cooperative traversal cancellation. Issue #19. Submitted by David Hughes (@stugorf), with AI Collaboration. External benchmark runner adoption and performance measurements remain pending.
Keep one cooperative scheduling helper, bind and restore lambda-local types, and bypass cached templates for extension-owned path predicates and query-local inventories. Add CALL/UNION/path/fold, refresh isolation, parameter rebinding and deadline regressions. Combined runtime validation follows the inventory partition fix. Submitted by David Hughes (@stugorf), with AI Collaboration.
Preserve inventory source coalescing and refresh output partitioning after optimizer rewrites. Cover Arrow and Lance scans beyond the first source partition and the unmodified IC14 query over real relationship records.
The expression type check rejected comparisons such as p.age = '30', which DataFusion has always run by casting the string side. Let DataFusion's coercion decide scalar compatibility again, so only genuinely incompatible operands (string arithmetic, booleans against numbers, entity arithmetic) fail before planning. Update the tests that asserted the old rejection and document the behaviour.
Variable-length expansion started reading the target label from the logical plan, which defaults an unlabeled node to "Node". With relationships that record no endpoint labels (the deprecated builder), MATCH (a:Person)-[:KNOWS*1..2]->(b) then failed with "No mapping found for node label: Node". Fall back to the source variable's label in that case, as the planner did before, in both the analysis and label collection.
SQL export rejected every typed null outside the Default dialect, so ordinary queries such as coalesce(p.name, null) or CASE ... ELSE null stopped exporting for Spark, PostgreSQL, MySQL and SQLite. Those dialects have no arrow_cast; emit a bare NULL there, as exports did before, and keep the arrow_cast wrapper for the Default dialect.
openCypher allows one union mode per query; the planner folded a mix left to right instead. Report it during semantic analysis, update the tests that expected the folded result and document the rule.
DataFusion copies a predicate pushed past an extension node into every input. PathMatch's extra inputs (node and edge inventories) lack the start node's columns, so MATCH p = (a)-[...]->(b) WHERE a.id = 1 failed to plan with "No field named a__id". Only allow pushing predicates on columns every input carries.
target_partitions is now validated to 1..=1024 in the engine. Say so where the option is documented, and show the Rust CypherEngine::with_target_partitions constructor in the Rust guide.
Deploying lance-csr with
|
| Latest commit: |
bec6758
|
| Status: | ✅ Deploy successful! |
| Preview URL: | https://cf2d4dab.lance-csr.pages.dev |
| Branch Preview URL: | https://feat-integrate-cypher-and-en.lance-csr.pages.dev |
prrao87
marked this pull request as ready for review
October 26, 2026 17:33
with_execution_deadline was hidden plumbing for the Python bridge. Make it a documented API: nested-loop joins inside it check the deadline and fail with "Query timeout exceeded", and cross joins yield so an outer tokio::time::timeout_at can stop them. Add a test, a Timeouts section to the Rust guide with a compiled example, and a link from the Python deadline docs.
This was referenced Oct 26, 2026
Closed
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.
Why
A large set of Cypher, CSR and engine improvements was developed on a stack of branches and reviewed as PRs #30–#52. Most of those PRs merged into intermediate branches rather than
main, and they predate the crate rename and the move ofCypherEngineinto the Rust crate (#53). This PR brings the final integrated version of that work ontomainas one reviewed change, before the 0.3.0 release.The work arrives as a merge of
codex/issue-resolution-current-main, the final integration branch, so every original commit and its authorship is kept. Fixes for problems found in review follow as separate commits.What changed
Cypher
SKIP/LIMIT.abs,round,floor,ceil,sqrt) and string and list functions (left,right,substring,replace,split,trim,reverse,last,tail,size,rangewith a step).keys(),labels(),reduce, and theany/all/none/singlepredicates.UNION/UNION ALL, named path values withrelationships(),startNode()andendNode(), and read-onlyCALL { ... }subqueries.CSR and execution
size([x IN list WHERE ...]).timeout=onexecute,execute_batchesandCypherQuery.execute.target_partitionsonCypherEngine.Integration with the Rust engine. The Python engine changes from the original branches are ported into
lance_csr::CypherEngine(engine.rs):target_partitions, through a newCypherEngine::with_target_partitions.The Python class keeps argument validation, timeouts and the deadline-aware batch reader. Rust callers get the same cooperative deadlines through
lance_csr::query::with_execution_deadline, now a documented API with a "Timeouts" section in the Rust guide, a compiled example and a test. The engine unit tests moved intoengine.rs, where CI runs them; tests in the Python crate never run in CI.refresh()keeps the lock-based fix from #53 instead of the branch's retry loop; both fix #12.Fixes for problems found in review (each with a regression test):
p.age = '30'run again. The new type check had started rejecting them. DataFusion's coercion decides, as before.MATCH (a:Person)-[:KNOWS*1..2]->(b)failed with "No mapping found for node label: Node" when relationships have no endpoint labels.to_sqlworks again for Spark, PostgreSQL, MySQL and SQLite on queries with typed nulls (coalesce(x, null),CASE ... ELSE null). Those dialects get a bareNULL, as before.UNIONandUNION ALLin one query raisesValueError, as openCypher requires.MATCH p = (a)-[...]->(b) WHERE a.id = 1failed to plan, because DataFusion copied the filter into the path operator's inventory inputs.target_partitionsis capped at 1024. Huge values used to be accepted and could exhaust memory instead of raising.Known limitations (not changed here)
count(DISTINCT p)on a path, andcount(var)for variables without an id column, raise an error rather than counting.maintoo: a relationship variable reused across comma-separated pattern parts falls back to a cross join, and(a)-[r1]->(b)<-[r2]-(c)can bindr1andr2to the same relationship. AscendingORDER BYputs nulls first.CALLsubqueries plan and scan per outer row, so they get slow beyond a few thousand outer rows. The docs say so.WHEREon a path's start node is applied above the path operator rather than inside the start scan.Validation
cargo fmt --checkandcargo clippy --all-targets -- -D warningspass in both crates.cargo test --workspaceandcargo check -p lance-csr-benches --benchespass.cargo run -p lance-csr --example embedded_queryprints the documented output.python/:make install,make testandmake lintpass, with the full Python suite including the new feature suites.docs/check_snippets.pyreports no failures.zensical build --strictandtypospass.Benchmarks
I compared the published 0.2.0 wheel with a release wheel of this branch (same thin-LTO release profile) on both suites, using the published settings, on an Apple M5. To cancel drift, each suite and mode ran in ABBA order (0.2.0, branch, branch, 0.2.0), and the table compares averages. All 88 result assertions passed in every run. Values are the geometric mean of the per-query median ratio (branch / 0.2.0; below 1 is faster):
Faster: IC12 is about 7× faster (152 → 21 ms in memory, 190 → 33 ms lazy), IC13 about 4×, IC14 about 1.35×, and IC3 and IC11 about 1.2–1.3×.
Slower: two queries regress by more than run-to-run noise. A focused re-run with 15+ rounds, ABBA order and non-overlapping ranges confirmed both:
The cause isn't established. Per-batch overhead from bounded-chunk CSR expansion on high fan-out traversals is a candidate, but I haven't verified it. The remaining queries are within noise.
Engine lifecycle:
engine_cache.py(20 tables, 5 interleaved runs per side) shows no change. Construction, first query, warm query and unchanged refresh are all within ±2%.Published benchmark tables are unchanged. The crates.io publish and the release wheels follow after this merges.
🤖 Generated with Claude Code