Skip to content

Resolve forward lookup refs in transactions, apply value predicates to entity ids - #16

Merged
tiensonqin merged 1 commit into
mainfrom
devin/query-engine-fixes-2
Sep 22, 2026
Merged

tiensonqin merged 1 commit into
mainfrom
devin/query-engine-fixes-2

Conversation

@tiensonqin

Copy link
Copy Markdown
Contributor

Summary

Two engine fixes for issues hit by the logseq db-worker port.

1. Forward lookup refs inside a transaction are deferred, not fatal

An entity map (or [:db/add] op) whose ref-attr value is a lookup ref pointing at an entity defined by a later op in the same tx previously raised Invalid_argument "Nothing found for entity id [:block/uuid <b>]" from Transact.resolve_entity_ref. Example:

[{:block/uuid <a> :block/refs [[:block/uuid <b>]]}   ;; refs B, defined later
 {:block/uuid <b> :block/title "Leaf Page"}]
  • resolve_entity_ref's Lookup_ref arm now raises a dedicated Unresolved_lookup_ref of attr * value exception instead of Invalid_argument when the ref doesn't resolve against the datoms accumulated so far.
  • apply_ops catches it, rolls back the op atomically (tx state is immutable), and queues the op on deferred_ops; drain_deferred_ops retries pending ops in original order as the tx accumulates datoms.
  • A pass with no progress re-raises the identical "Nothing found for entity id" Invalid_argument, so genuinely missing targets still error exactly as before (upstream parity for real misses).
  • Covers scalar One_value, Many_values collections, entity-map attrs, Add e- and v-positions, CompareAndSet, and nested/tx-fn-produced ops. The bulk fast path (try_apply_bulk_explicit_entities) falls back to the sequential path when an eager lookup fails.

Note on upstream semantics: upstream DataScript itself raises on forward lookup refs (logseq's cljs handles this case in outliner preprocessing via stable-entity-ref-with-tx-data). This is a documented, requested divergence so the engine can run logseq's real transactions directly.

2. Value/numeric predicates now see entity-id bindings

[(even? ?x)], number?, integer?, etc. over a variable bound to an entity id returned no rows. Entity ids are plain numbers upstream; the port wraps them as Ref/Result_entity and two places dropped them:

  • eval_value_predicate_clause (and the same pattern in parse_complement_predicate_clause) only matched Result_value; it now converts via value_of_query_result (Result_entity e → Ref e).
  • Built_ins.matches_numeric_predicate / matches_value_predicate / numeric_value now treat Ref e as Int e — consistent with compare_value, which already compares Ref numerically.

This is what makes upstream's "Joining regular clauses with rule" test ([_ _ ?x] (rule ?x ?y) [(even? ?x)]) pass — it returned empty before.

Verified-not-broken (kept as regression tests)

  • Recursive rules direction: logseq's own :parent/:class-extends rule shape and upstream's mutually-recursive-rules case both pass.
  • avet after restore: sqlite-restored dbs index ref-attr values and answer _attr reverse patterns; covered by test_sqlite_storage_restored_reverse_ref_lookup (and memory_storage restore empirically).
  • [?x ...] collection :in: already covered by PR Fix query-engine bugs: :in predicate bindings, intra-tx lookup refs, EDN symbol chars #15's collection_in_binding.

New tests in test_query_engine_fixes: entity_map_lookup_ref_later_tx_entity, _many_values, add_op_lookup_ref_later_tx_entity, entity_map_lookup_ref_never_resolves_raises, logseq_parent_rule, mutually_recursive_rules, rule_joined_with_clauses — all verified fail-before/pass-after where a repro existed. Full dune runtest green including upstream parity.

Link to Devin session: https://app.devin.ai/sessions/c82925b6976a4a6da2688c410e65011a
Open in Devin Desktop: https://app.devin.ai/desktop/session/c82925b6976a4a6da2688c410e65011a?variant=devin
Requested by: @tiensonqin

Copilot AI lite review requested due to automatic review settings September 22, 2026 06:11
@devin-ai-integration

Copy link
Copy Markdown

I'll fix CI failures and address comments from users with write access. I'll skip comments containing "(aside)".

  • Disable automatic comment, CI, and merge conflict monitoring

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot was unable to review this pull request because the user who requested the review has reached their quota limit.

@tiensonqin
tiensonqin merged commit 8db9e3c into main Sep 22, 2026
1 of 2 checks passed
@tiensonqin
tiensonqin deleted the devin/query-engine-fixes-2 branch September 22, 2026 06:12
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.

2 participants