Bind entity-bound vars in pattern value position; widen Instant to int64 with cljs transit reps; fix multival 2-vector disambiguation - #17
Merged
Conversation
|
I'll fix CI failures and address comments from users with write access. I'll skip comments containing "(aside)".
|
|
Note on CI: the Locally on OCaml 5.5.1: |
…h cljs transit reps - query_value_term now maps a variable substituted to QEntity into the Ref value of the datom, so bound vars flowing into pattern value position (recursive rule calls, :in scalars holding entity results) actually constrain the v-probe instead of scanning the whole attribute. - Instant is int64: epoch-millisecond timestamps exceed int32 on melange. - melange, sqlite, and example storage codecs write Instant/Uuid through Transit's date/uuid reps (~t/~u wire-compatible with cljs transit) instead of ~#m/~#u tagged maps; legacy tagged blobs still decode.
datascript-ocaml-lg depends on lg (logseq/lg), which requires ocaml >= 5.5 — bump the CI compiler matrix and add the lg git pin-depends so opam can resolve it.
Restore/save ~/.opam keyed on runner, compiler, and the opam files hash, so dependency builds (including git-pinned deps) are reused across runs.
1013dcf refreshes the schema per schema-attr datom inside entity maps, so refresh_schema now sees partially-installed specs — e.g. db.type/tuple landing before db/tupleTypes — and validate_schema raised 'tuple value type requires tuple attrs or tuple types'. Upstream only validates the schema when it is set explicitly, not during a transaction; pass ~validate:false on the incremental path while the final strict recompute still validates.
devin-ai-integration
Bot
force-pushed
the
devin/query-engine-fixes-3
branch
from
September 22, 2026 08:16
650c46a to
ad6f88c
Compare
… unique attr Upstream maybe-wrap-multival treats a 2-element collection inside a multival context as a lookup ref only when its first element names a :db.unique/identity attribute; any other collection expands into individual values. The port treated any 2-element list/vector headed by an attr name as a lookup ref, so a many-ref value like [:logseq.class/Page :logseq.class/Page] (two ident refs) raised "Lookup ref attribute should be marked as :db/unique". Gate ref_lookup_collection_value on context.is_unique_identity of the head attr, matching upstream exactly. Vectors of ident keywords now expand into one ref datom per ident (idempotent adds collapse), while [:block/uuid u] still resolves as a lookup ref in both single- and multi-value attrs.
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
Four engine/storage bugs hit by the logseq db-worker port.
Bug A + B — bound vars dropped in pattern value position (one root cause). When a variable bound to an entity id lands in a pattern's value position —
(parent ?p ?c)inside a recursive rule body, or:in ?xwhere?xwas passed asArg_scalar (Result_entity e)— the constraint was silently discarded.query_value_termonly handledQValue; a var bound to an entity substitutes toQEntity, which fell through toNone, sopattern_datoms's v-probe scanned every datom of the attribute and the relation fast path returned children of every parent. Now:(parent 1 ?c)over two disjoint trees now returns{2,3}instead of{2,3,6,7};Arg_scalar (Result_entity 1)to:in ?xbinds identically toResult_value (Ref 1).Bug C — Instant/Uuid transit encoding incompatible with cljs.
melange/datascript_melange_storage.ml(and its near-identical copies insqlite/datascript_sqlite_codec.mlandexamples/logseq_sqlite_storage.ml) wroteTransit.Tagged ("m", Transit.Int v)/Tagged ("u", String v):emitEncodedcallsgetVerboseHandler()for tagged reps underpreferStrings→TypeErrorcrash on every Instant write (bootstrap transact stores:db/txInstant,file/created-at);~#m/~#utagged maps is not cljs's~t/~u, so kvs blobs aren't byte-compatible;Instant of inttruncates epoch ms on melange where int is 32-bit.Fix:
Ds.Instantwidened toint64(~30 use sites updated:parse_instant_millisnow computes in Int64,murmur3_hash_longtakes the int64 directly,Int64.to_stringfor display,`Intlitin the JS JSON binding), and all three codecs writeTransit.Date value/Transit.Uuid value— the transit lib's cljs-compatible reps emitting~t<iso>verbose /~m<ms>normal and~u. Legacy~#m/~#utagged blobs still decode via the kept read arms.Bug D — a 2-vector in a cardinality-many ref attr was always treated as a lookup ref.
ref_lookup_collection_valueclassified ANY 2-elementList/Vectorheaded by an attr name as a lookup ref, so{:block/tags [:logseq.class/Page :logseq.class/Page]}raisedLookup ref attribute should be marked as :db/unique. Upstreammaybe-wrap-multival(db.cljc) only keeps a 2-element collection as a lookup ref when(first vs)names a:db.unique/identityattr; otherwise it expands into individual values. Now:[:logseq.class/Page :logseq.class/Task]now expands into two ident-ref datoms (idempotent adds collapse);[:block/uuid u]still resolves as a lookup ref in both single- and multi-value attrs.Schema validation regression fix (main's
1013dcf): the per-attr mid-txrefresh_schemanow sees a partially-installed schema spec (db.type/tuplelands beforedb/tupleTypes), sovalidate_schemaraised insideschema_from_transaction_datoms.schema_from_transaction_datomsgained?(validate = true); the mid-txrefresh_schemacalls it with~validate:false(upstream never revalidates the whole schema mid-transaction), while the strict end-of-tx recompute still validates.CI: workflow moved to OCaml 5.5.1 (required by
lg/melange),datascript-ocaml-lg.opamgainedpin-dependsforlg(logseq/lg#main) soopam install --deps-onlycan resolve it, and the opam switch is cached keyed on OS/compiler/opam hashes.Tests
test_query_engine_fixes:recursive_rule_bound_head_arg— logseq's(parent ?p ?c)rule over disjoint trees, literal and:in-bound head arg (both returned children of every parent before);in_scalar_result_entity— entity vs ref scalar inputs bind identically;many_ref_vector_of_idents_expands/many_ref_vector_of_same_ident_idempotent— vector of ident keywords in a many-ref attr expands into one datom per ident (dup collapses);many_ref_vector_with_unique_head_is_lookup_ref/entity_map_lookup_ref_vector_form—[:block/uuid u]still resolves as a lookup ref inOneandManyattrs.All verified fail-before/pass-after (
Invalid_argument "Lookup ref attribute should be marked as :db/unique: [:logseq.class/Page :logseq.class/Task]"before).test_sqlite_storage:sqlite_codec_instant_uuid_transit_repsasserts Instant/Uuid encode asTransit.Date/Transit.Uuid(verbose wire bytes contain~t/~u), round-trip a >int32 millisecond value exactly, and still decode legacy~#m/~#ublobs.Full
dune runtestgreen, including upstream cljs parity (identical datom hash) and the js_of_ocaml cross-runtime check.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