@rocicorp/zero 1.5.0-canary.4 → 1.6.0-canary.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (172) hide show
  1. package/out/analyze-query/src/analyze-cli.js +2 -2
  2. package/out/analyze-query/src/analyze-cli.js.map +1 -1
  3. package/out/replicache/src/btree/node.d.ts +3 -0
  4. package/out/replicache/src/btree/node.d.ts.map +1 -1
  5. package/out/replicache/src/btree/node.js +114 -1
  6. package/out/replicache/src/btree/node.js.map +1 -1
  7. package/out/replicache/src/btree/write.d.ts +7 -0
  8. package/out/replicache/src/btree/write.d.ts.map +1 -1
  9. package/out/replicache/src/btree/write.js +50 -0
  10. package/out/replicache/src/btree/write.js.map +1 -1
  11. package/out/replicache/src/db/write.d.ts +8 -0
  12. package/out/replicache/src/db/write.d.ts.map +1 -1
  13. package/out/replicache/src/db/write.js +15 -0
  14. package/out/replicache/src/db/write.js.map +1 -1
  15. package/out/replicache/src/kv/sqlite-store.d.ts +2 -5
  16. package/out/replicache/src/kv/sqlite-store.d.ts.map +1 -1
  17. package/out/replicache/src/kv/sqlite-store.js +21 -24
  18. package/out/replicache/src/kv/sqlite-store.js.map +1 -1
  19. package/out/replicache/src/replicache-impl.d.ts.map +1 -1
  20. package/out/replicache/src/replicache-impl.js.map +1 -1
  21. package/out/replicache/src/sync/patch.d.ts +15 -0
  22. package/out/replicache/src/sync/patch.d.ts.map +1 -1
  23. package/out/replicache/src/sync/patch.js +85 -26
  24. package/out/replicache/src/sync/patch.js.map +1 -1
  25. package/out/shared/src/testing.d.ts +3 -0
  26. package/out/shared/src/testing.d.ts.map +1 -0
  27. package/out/zero/package.js +5 -6
  28. package/out/zero/package.js.map +1 -1
  29. package/out/zero-cache/src/auth/write-authorizer.js +1 -1
  30. package/out/zero-cache/src/config/zero-config.d.ts +4 -0
  31. package/out/zero-cache/src/config/zero-config.d.ts.map +1 -1
  32. package/out/zero-cache/src/config/zero-config.js +8 -0
  33. package/out/zero-cache/src/config/zero-config.js.map +1 -1
  34. package/out/zero-cache/src/server/inspector-delegate.d.ts +3 -2
  35. package/out/zero-cache/src/server/inspector-delegate.d.ts.map +1 -1
  36. package/out/zero-cache/src/server/inspector-delegate.js +19 -9
  37. package/out/zero-cache/src/server/inspector-delegate.js.map +1 -1
  38. package/out/zero-cache/src/server/runner/run-worker.js +1 -1
  39. package/out/zero-cache/src/services/change-source/custom/change-source.js +2 -2
  40. package/out/zero-cache/src/services/change-source/custom/change-source.js.map +1 -1
  41. package/out/zero-cache/src/services/change-source/pg/backfill-stream.js +7 -6
  42. package/out/zero-cache/src/services/change-source/pg/backfill-stream.js.map +1 -1
  43. package/out/zero-cache/src/services/change-source/pg/change-source.d.ts.map +1 -1
  44. package/out/zero-cache/src/services/change-source/pg/change-source.js +49 -66
  45. package/out/zero-cache/src/services/change-source/pg/change-source.js.map +1 -1
  46. package/out/zero-cache/src/services/change-source/pg/initial-sync.d.ts +0 -8
  47. package/out/zero-cache/src/services/change-source/pg/initial-sync.d.ts.map +1 -1
  48. package/out/zero-cache/src/services/change-source/pg/initial-sync.js +22 -52
  49. package/out/zero-cache/src/services/change-source/pg/initial-sync.js.map +1 -1
  50. package/out/zero-cache/src/services/change-source/pg/replication-slots.d.ts +57 -0
  51. package/out/zero-cache/src/services/change-source/pg/replication-slots.d.ts.map +1 -0
  52. package/out/zero-cache/src/services/change-source/pg/replication-slots.js +162 -0
  53. package/out/zero-cache/src/services/change-source/pg/replication-slots.js.map +1 -0
  54. package/out/zero-cache/src/services/change-source/pg/schema/init.d.ts.map +1 -1
  55. package/out/zero-cache/src/services/change-source/pg/schema/init.js +18 -0
  56. package/out/zero-cache/src/services/change-source/pg/schema/init.js.map +1 -1
  57. package/out/zero-cache/src/services/change-source/pg/schema/shard.d.ts +17 -3
  58. package/out/zero-cache/src/services/change-source/pg/schema/shard.d.ts.map +1 -1
  59. package/out/zero-cache/src/services/change-source/pg/schema/shard.js +43 -16
  60. package/out/zero-cache/src/services/change-source/pg/schema/shard.js.map +1 -1
  61. package/out/zero-cache/src/services/change-streamer/change-streamer-http.d.ts +2 -3
  62. package/out/zero-cache/src/services/change-streamer/change-streamer-http.d.ts.map +1 -1
  63. package/out/zero-cache/src/services/change-streamer/change-streamer-http.js +5 -5
  64. package/out/zero-cache/src/services/change-streamer/change-streamer-http.js.map +1 -1
  65. package/out/zero-cache/src/services/change-streamer/change-streamer-service.d.ts +10 -1
  66. package/out/zero-cache/src/services/change-streamer/change-streamer-service.d.ts.map +1 -1
  67. package/out/zero-cache/src/services/change-streamer/change-streamer-service.js +13 -3
  68. package/out/zero-cache/src/services/change-streamer/change-streamer-service.js.map +1 -1
  69. package/out/zero-cache/src/services/change-streamer/change-streamer.d.ts +6 -11
  70. package/out/zero-cache/src/services/change-streamer/change-streamer.d.ts.map +1 -1
  71. package/out/zero-cache/src/services/change-streamer/change-streamer.js +0 -1
  72. package/out/zero-cache/src/services/change-streamer/change-streamer.js.map +1 -1
  73. package/out/zero-cache/src/services/change-streamer/forwarder.d.ts.map +1 -1
  74. package/out/zero-cache/src/services/change-streamer/forwarder.js +2 -2
  75. package/out/zero-cache/src/services/change-streamer/forwarder.js.map +1 -1
  76. package/out/zero-cache/src/services/change-streamer/storer.d.ts +12 -5
  77. package/out/zero-cache/src/services/change-streamer/storer.d.ts.map +1 -1
  78. package/out/zero-cache/src/services/change-streamer/storer.js +43 -21
  79. package/out/zero-cache/src/services/change-streamer/storer.js.map +1 -1
  80. package/out/zero-cache/src/services/change-streamer/subscriber.d.ts +4 -5
  81. package/out/zero-cache/src/services/change-streamer/subscriber.d.ts.map +1 -1
  82. package/out/zero-cache/src/services/change-streamer/subscriber.js +18 -16
  83. package/out/zero-cache/src/services/change-streamer/subscriber.js.map +1 -1
  84. package/out/zero-cache/src/services/litestream/commands.d.ts.map +1 -1
  85. package/out/zero-cache/src/services/litestream/commands.js +3 -2
  86. package/out/zero-cache/src/services/litestream/commands.js.map +1 -1
  87. package/out/zero-cache/src/services/litestream/config.yml +1 -0
  88. package/out/zero-cache/src/services/mutagen/pusher.d.ts +2 -2
  89. package/out/zero-cache/src/services/view-syncer/cvr-store.js +2 -2
  90. package/out/zero-cache/src/services/view-syncer/cvr-store.js.map +1 -1
  91. package/out/zero-cache/src/services/view-syncer/pipeline-driver.js +1 -1
  92. package/out/zero-cache/src/services/view-syncer/pipeline-driver.js.map +1 -1
  93. package/out/zero-cache/src/services/view-syncer/view-syncer.d.ts.map +1 -1
  94. package/out/zero-cache/src/services/view-syncer/view-syncer.js +5 -6
  95. package/out/zero-cache/src/services/view-syncer/view-syncer.js.map +1 -1
  96. package/out/zero-cache/src/types/streams.d.ts +4 -0
  97. package/out/zero-cache/src/types/streams.d.ts.map +1 -1
  98. package/out/zero-cache/src/types/streams.js +13 -10
  99. package/out/zero-cache/src/types/streams.js.map +1 -1
  100. package/out/zero-cache/src/workers/connection.js +5 -5
  101. package/out/zero-cache/src/workers/connection.js.map +1 -1
  102. package/out/zero-client/src/client/inspector/inspector.d.ts.map +1 -1
  103. package/out/zero-client/src/client/inspector/inspector.js +15 -2
  104. package/out/zero-client/src/client/inspector/inspector.js.map +1 -1
  105. package/out/zero-client/src/client/inspector/lazy-inspector.d.ts +9 -3
  106. package/out/zero-client/src/client/inspector/lazy-inspector.d.ts.map +1 -1
  107. package/out/zero-client/src/client/inspector/lazy-inspector.js +27 -6
  108. package/out/zero-client/src/client/inspector/lazy-inspector.js.map +1 -1
  109. package/out/zero-client/src/client/inspector/query.d.ts.map +1 -1
  110. package/out/zero-client/src/client/inspector/query.js +3 -3
  111. package/out/zero-client/src/client/inspector/query.js.map +1 -1
  112. package/out/zero-client/src/client/ivm-branch.d.ts.map +1 -1
  113. package/out/zero-client/src/client/ivm-branch.js +16 -2
  114. package/out/zero-client/src/client/ivm-branch.js.map +1 -1
  115. package/out/zero-client/src/client/options.d.ts +12 -4
  116. package/out/zero-client/src/client/options.d.ts.map +1 -1
  117. package/out/zero-client/src/client/options.js.map +1 -1
  118. package/out/zero-client/src/client/query-manager.d.ts +8 -1
  119. package/out/zero-client/src/client/query-manager.d.ts.map +1 -1
  120. package/out/zero-client/src/client/query-manager.js +28 -3
  121. package/out/zero-client/src/client/query-manager.js.map +1 -1
  122. package/out/zero-client/src/client/version.js +1 -1
  123. package/out/zero-client/src/client/zero.d.ts.map +1 -1
  124. package/out/zero-client/src/client/zero.js +12 -11
  125. package/out/zero-client/src/client/zero.js.map +1 -1
  126. package/out/zero-protocol/src/down.d.ts +1 -1
  127. package/out/zero-protocol/src/inspect-down.d.ts +15 -4
  128. package/out/zero-protocol/src/inspect-down.d.ts.map +1 -1
  129. package/out/zero-protocol/src/inspect-down.js +11 -1
  130. package/out/zero-protocol/src/inspect-down.js.map +1 -1
  131. package/out/zero-protocol/src/protocol-version.d.ts +1 -1
  132. package/out/zero-protocol/src/protocol-version.d.ts.map +1 -1
  133. package/out/zero-protocol/src/protocol-version.js.map +1 -1
  134. package/out/zero-react/src/use-query.d.ts.map +1 -1
  135. package/out/zero-react/src/use-query.js.map +1 -1
  136. package/out/zero-react/src/zero-provider.d.ts +6 -0
  137. package/out/zero-react/src/zero-provider.d.ts.map +1 -1
  138. package/out/zero-react/src/zero-provider.js +21 -1
  139. package/out/zero-react/src/zero-provider.js.map +1 -1
  140. package/out/zero-solid/src/use-zero.d.ts +6 -0
  141. package/out/zero-solid/src/use-zero.d.ts.map +1 -1
  142. package/out/zero-solid/src/use-zero.js +24 -4
  143. package/out/zero-solid/src/use-zero.js.map +1 -1
  144. package/out/zql/src/builder/builder.d.ts.map +1 -1
  145. package/out/zql/src/builder/builder.js +18 -8
  146. package/out/zql/src/builder/builder.js.map +1 -1
  147. package/out/zql/src/ivm/cap.d.ts +32 -0
  148. package/out/zql/src/ivm/cap.d.ts.map +1 -0
  149. package/out/zql/src/ivm/cap.js +205 -0
  150. package/out/zql/src/ivm/cap.js.map +1 -0
  151. package/out/zql/src/ivm/constraint.d.ts.map +1 -1
  152. package/out/zql/src/ivm/constraint.js.map +1 -1
  153. package/out/zql/src/ivm/flipped-join.d.ts +9 -0
  154. package/out/zql/src/ivm/flipped-join.d.ts.map +1 -1
  155. package/out/zql/src/ivm/flipped-join.js +56 -69
  156. package/out/zql/src/ivm/flipped-join.js.map +1 -1
  157. package/out/zql/src/ivm/memory-source.d.ts +24 -3
  158. package/out/zql/src/ivm/memory-source.d.ts.map +1 -1
  159. package/out/zql/src/ivm/memory-source.js +162 -7
  160. package/out/zql/src/ivm/memory-source.js.map +1 -1
  161. package/out/zql/src/ivm/operator.d.ts +26 -0
  162. package/out/zql/src/ivm/operator.d.ts.map +1 -1
  163. package/out/zql/src/ivm/operator.js.map +1 -1
  164. package/out/zql/src/ivm/take.js +2 -2
  165. package/out/zqlite/src/query-builder.d.ts +14 -2
  166. package/out/zqlite/src/query-builder.d.ts.map +1 -1
  167. package/out/zqlite/src/query-builder.js +32 -1
  168. package/out/zqlite/src/query-builder.js.map +1 -1
  169. package/out/zqlite/src/table-source.d.ts.map +1 -1
  170. package/out/zqlite/src/table-source.js +4 -4
  171. package/out/zqlite/src/table-source.js.map +1 -1
  172. package/package.json +5 -6
@@ -1,10 +1,11 @@
1
1
  import { assert, unreachable } from "../../../shared/src/asserts.js";
2
2
  import { binarySearch } from "../../../shared/src/binary-search.js";
3
- import { emptyArray } from "../../../shared/src/sentinels.js";
3
+ import { must } from "../../../shared/src/must.js";
4
4
  import { throwOutput } from "./operator.js";
5
5
  import { makeAddChange, makeChildChange, makeEditChange, makeRemoveChange } from "./change.js";
6
6
  import { constraintsAreCompatible, keyMatchesPrimaryKey } from "./constraint.js";
7
7
  import { buildJoinConstraint, generateWithOverlayNoYield, isJoinMatch, rowEqualsForCompoundKey } from "./join-utils.js";
8
+ import { mergeSortedStreams } from "./memory-source.js";
8
9
  //#region ../zql/src/ivm/flipped-join.ts
9
10
  /**
10
11
  * An *inner* join which fetches nodes from its child input first and then
@@ -125,75 +126,36 @@ var FlippedJoin = class {
125
126
  }
126
127
  }
127
128
  *#fetchMergeSort(req, childNodes) {
128
- const parentIterators = [];
129
- let threw = false;
130
- try {
131
- for (const childNode of childNodes) {
132
- const constraintFromChild = buildJoinConstraint(childNode.row, this.#childKey, this.#parentKey);
133
- if (!constraintFromChild || req.constraint && !constraintsAreCompatible(constraintFromChild, req.constraint)) parentIterators.push(emptyArray[Symbol.iterator]());
134
- else {
135
- const iterator = this.#parent.fetch({
136
- ...req,
137
- constraint: {
138
- ...req.constraint,
139
- ...constraintFromChild
140
- }
141
- })[Symbol.iterator]();
142
- parentIterators.push(iterator);
143
- }
144
- }
145
- const nextParentNodes = [];
146
- for (let i = 0; i < parentIterators.length; i++) {
147
- const iter = parentIterators[i];
148
- let result = iter.next();
149
- while (!result.done && result.value === "yield") {
150
- yield result.value;
151
- result = iter.next();
152
- }
153
- nextParentNodes[i] = result.done ? null : result.value;
154
- }
155
- while (true) {
156
- let minParentNode = null;
157
- let minParentNodeChildIndexes = [];
158
- for (let i = 0; i < nextParentNodes.length; i++) {
159
- const parentNode = nextParentNodes[i];
160
- if (parentNode === null) continue;
161
- if (minParentNode === null) {
162
- minParentNode = parentNode;
163
- minParentNodeChildIndexes.push(i);
164
- } else {
165
- const compareResult = this.#schema.compareRows(parentNode.row, minParentNode.row) * (req.reverse ? -1 : 1);
166
- if (compareResult === 0) minParentNodeChildIndexes.push(i);
167
- else if (compareResult < 0) {
168
- minParentNode = parentNode;
169
- minParentNodeChildIndexes = [i];
170
- }
171
- }
172
- }
173
- if (minParentNode === null) return;
174
- const relatedChildNodes = [];
175
- for (const minParentNodeChildIndex of minParentNodeChildIndexes) {
176
- relatedChildNodes.push(childNodes[minParentNodeChildIndex]);
177
- const iter = parentIterators[minParentNodeChildIndex];
178
- let result = iter.next();
179
- while (!result.done && result.value === "yield") {
180
- yield result.value;
181
- result = iter.next();
182
- }
183
- nextParentNodes[minParentNodeChildIndex] = result.done ? null : result.value;
184
- }
185
- yield* this.#yieldParentWithOverlay(minParentNode, relatedChildNodes);
129
+ const parentKey = this.#parentKey;
130
+ const computedKeys = [];
131
+ const childIndexesByKey = /* @__PURE__ */ new Map();
132
+ for (let i = 0; i < childNodes.length; i++) {
133
+ const constraintFromChild = buildJoinConstraint(childNodes[i].row, this.#childKey, parentKey);
134
+ if (!constraintFromChild || req.constraint && !constraintsAreCompatible(constraintFromChild, req.constraint)) continue;
135
+ const key = canonicalKey(constraintFromChild, parentKey);
136
+ const existing = childIndexesByKey.get(key);
137
+ if (existing === void 0) {
138
+ childIndexesByKey.set(key, [i]);
139
+ computedKeys.push(constraintFromChild);
140
+ } else existing.push(i);
141
+ }
142
+ if (computedKeys.length === 0) return;
143
+ const compareRows = this.#schema.compareRows;
144
+ const compare = req.reverse ? (a, b) => compareRows(b.row, a.row) : (a, b) => compareRows(a.row, b.row);
145
+ const streams = computedKeys.map((c) => this.#parent.fetch({
146
+ ...req,
147
+ constraint: req.constraint ? {
148
+ ...req.constraint,
149
+ ...c
150
+ } : c
151
+ }));
152
+ for (const node of mergeSortedStreams(streams, compare)) {
153
+ if (node === "yield") {
154
+ yield "yield";
155
+ continue;
186
156
  }
187
- } catch (e) {
188
- threw = true;
189
- for (const iter of parentIterators) try {
190
- iter.throw?.(e);
191
- } catch (_cleanupError) {}
192
- throw e;
193
- } finally {
194
- if (!threw) for (const iter of parentIterators) try {
195
- iter.return?.();
196
- } catch (_cleanupError) {}
157
+ const relatedChildNodes = must(childIndexesByKey.get(canonicalKey(node.row, parentKey))).map((i) => childNodes[i]);
158
+ yield* this.#yieldParentWithOverlay(node, relatedChildNodes);
197
159
  }
198
160
  }
199
161
  *#yieldParentWithOverlay(minParentNode, relatedChildNodes) {
@@ -318,6 +280,31 @@ var FlippedJoin = class {
318
280
  }
319
281
  }
320
282
  };
283
+ /**
284
+ * Canonical string key over `keys` of `record`, used by `#fetchMergeSort`
285
+ * both to dedupe per-child fetches and to map each returned parent row
286
+ * back to the children that referenced its parent-key tuple.
287
+ *
288
+ * Exported for testing.
289
+ */
290
+ function canonicalKey(record, keys) {
291
+ if (keys.length === 1) return canonicalValue(record[keys[0]]);
292
+ let s = "";
293
+ for (let i = 0; i < keys.length; i++) {
294
+ if (i > 0) s += "\0";
295
+ s += canonicalValue(record[keys[i]]);
296
+ }
297
+ return s;
298
+ }
299
+ function canonicalValue(v) {
300
+ if (v === null || v === void 0) return "n";
301
+ const t = typeof v;
302
+ if (t === "string") return "s" + v;
303
+ if (t === "number") return "d" + v;
304
+ if (t === "bigint") return "b" + v.toString();
305
+ if (t === "boolean") return v ? "t" : "f";
306
+ return "j" + JSON.stringify(v);
307
+ }
321
308
  //#endregion
322
309
  export { FlippedJoin };
323
310
 
@@ -1 +1 @@
1
- {"version":3,"file":"flipped-join.js","names":["#parent","#child","#parentKey","#childKey","#relationshipName","#schema","#parentKeyIsUnique","#pushParent","#pushChild","#output","#inprogressChildChange","#fetchQuicksort","#fetchMergeSort","#yieldParentWithOverlay","#inprogressChildChangePosition","#pushChildChange"],"sources":["../../../../../zql/src/ivm/flipped-join.ts"],"sourcesContent":["import {assert, unreachable} from '../../../shared/src/asserts.ts';\nimport {binarySearch} from '../../../shared/src/binary-search.ts';\nimport {emptyArray} from '../../../shared/src/sentinels.ts';\nimport type {CompoundKey, System} from '../../../zero-protocol/src/ast.ts';\nimport type {Row, Value} from '../../../zero-protocol/src/data.ts';\nimport {ChangeIndex} from './change-index.ts';\nimport {ChangeType} from './change-type.ts';\nimport {\n makeAddChange,\n makeChildChange,\n makeEditChange,\n makeRemoveChange,\n type Change,\n} from './change.ts';\nimport {constraintsAreCompatible, keyMatchesPrimaryKey} from './constraint.ts';\nimport type {Node} from './data.ts';\nimport {\n buildJoinConstraint,\n generateWithOverlayNoYield,\n isJoinMatch,\n rowEqualsForCompoundKey,\n} from './join-utils.ts';\nimport {\n throwOutput,\n type FetchRequest,\n type Input,\n type Output,\n} from './operator.ts';\nimport type {SourceSchema} from './schema.ts';\nimport {type Stream} from './stream.ts';\n\ntype Args = {\n parent: Input;\n child: Input;\n // The nth key in childKey corresponds to the nth key in parentKey.\n parentKey: CompoundKey;\n childKey: CompoundKey;\n\n relationshipName: string;\n hidden: boolean;\n system: System;\n};\n\n/**\n * An *inner* join which fetches nodes from its child input first and then\n * fetches their related nodes from its parent input. Output nodes are the\n * nodes from parent input (in parent input order), which have at least one\n * related child. These output nodes have a new relationship added to them,\n * which has the name `relationshipName`. The value of the relationship is a\n * stream of related nodes from the child input (in child input order).\n */\nexport class FlippedJoin implements Input {\n readonly #parent: Input;\n readonly #child: Input;\n readonly #parentKey: CompoundKey;\n readonly #childKey: CompoundKey;\n readonly #relationshipName: string;\n readonly #schema: SourceSchema;\n readonly #parentKeyIsUnique: boolean;\n\n #output: Output = throwOutput;\n\n #inprogressChildChange: Change | undefined;\n #inprogressChildChangePosition: Row | undefined;\n\n constructor({\n parent,\n child,\n parentKey,\n childKey,\n relationshipName,\n hidden,\n system,\n }: Args) {\n assert(parent !== child, 'Parent and child must be different operators');\n assert(\n parentKey.length === childKey.length,\n 'The parentKey and childKey keys must have same length',\n );\n this.#parent = parent;\n this.#child = child;\n this.#parentKey = parentKey;\n this.#childKey = childKey;\n this.#relationshipName = relationshipName;\n\n const parentSchema = parent.getSchema();\n const childSchema = child.getSchema();\n this.#parentKeyIsUnique =\n keyMatchesPrimaryKey(parentKey, parentSchema.primaryKey) ||\n (parentSchema.uniqueIndexes?.some(idx =>\n keyMatchesPrimaryKey(parentKey, idx),\n ) ??\n false);\n this.#schema = {\n ...parentSchema,\n relationships: {\n ...parentSchema.relationships,\n [relationshipName]: {\n ...childSchema,\n isHidden: hidden,\n system,\n },\n },\n };\n\n parent.setOutput({\n push: (change: Change) => this.#pushParent(change),\n });\n child.setOutput({\n push: (change: Change) => this.#pushChild(change),\n });\n }\n\n destroy(): void {\n this.#child.destroy();\n this.#parent.destroy();\n }\n\n setOutput(output: Output): void {\n this.#output = output;\n }\n\n getSchema(): SourceSchema {\n return this.#schema;\n }\n\n *fetch(req: FetchRequest): Stream<Node | 'yield'> {\n // Translate constraints for the parent on parts of the join key to\n // constraints for the child.\n const childConstraint: Record<string, Value> = {};\n let hasChildConstraint = false;\n if (req.constraint) {\n for (const [key, value] of Object.entries(req.constraint)) {\n const index = this.#parentKey.indexOf(key);\n if (index !== -1) {\n hasChildConstraint = true;\n childConstraint[this.#childKey[index]] = value;\n }\n }\n }\n\n const childNodes: Node[] = [];\n for (const node of this.#child.fetch(\n hasChildConstraint ? {constraint: childConstraint} : {},\n )) {\n if (node === 'yield') {\n yield node;\n continue;\n }\n childNodes.push(node);\n }\n\n // FlippedJoin's split-push change overlay logic is largely\n // the same as Join's with the exception of remove. For remove,\n // the change is undone here, and then re-applied to parents with order\n // less than or equal to change.position below. This is necessary\n // because if the removed node was the last related child, the\n // related parents with position greater than change.position\n // (which should not yet have the node removed), would not even\n // be fetched here, and would be absent from the output all together.\n if (this.#inprogressChildChange?.[ChangeIndex.TYPE] === ChangeType.REMOVE) {\n const removedNode = this.#inprogressChildChange[ChangeIndex.NODE];\n const compare = this.#child.getSchema().compareRows;\n const insertPos = binarySearch(childNodes.length, i =>\n compare(removedNode.row, childNodes[i].row),\n );\n childNodes.splice(insertPos, 0, removedNode);\n }\n\n if (this.#parentKeyIsUnique) {\n yield* this.#fetchQuicksort(req, childNodes);\n } else {\n yield* this.#fetchMergeSort(req, childNodes);\n }\n }\n\n // When parentKey matches a unique index on the parent (primary or\n // otherwise) each child -> parent fetch returns at most one row, so the\n // merge-sort degenerates to N simultaneous prepared-statement iterators\n // each holding a single-row cursor. Instead, fetch sequentially (letting\n // the statement cache reuse a single prepared statement) and sort the\n // resulting parents into order.\n *#fetchQuicksort(\n req: FetchRequest,\n childNodes: Node[],\n ): Stream<Node | 'yield'> {\n const pairs: {childNode: Node; parentNode: Node}[] = [];\n for (const childNode of childNodes) {\n const constraintFromChild = buildJoinConstraint(\n childNode.row,\n this.#childKey,\n this.#parentKey,\n );\n if (\n !constraintFromChild ||\n (req.constraint &&\n !constraintsAreCompatible(constraintFromChild, req.constraint))\n ) {\n continue;\n }\n const stream = this.#parent.fetch({\n ...req,\n constraint: {\n ...req.constraint,\n ...constraintFromChild,\n },\n });\n // parentKey matches a unique index, so this fetch returns at most\n // one row under the Source contract. Iterate to completion rather\n // than breaking to preserve yield propagation and to avoid silently\n // changing behavior if a source ever returns more.\n for (const node of stream) {\n if (node === 'yield') {\n yield 'yield';\n continue;\n }\n pairs.push({childNode, parentNode: node});\n }\n }\n\n const compareRows = this.#schema.compareRows;\n const dir = req.reverse ? -1 : 1;\n pairs.sort((a, b) => compareRows(a.parentNode.row, b.parentNode.row) * dir);\n\n // Group consecutive pairs with equal parent rows. Array.sort is stable,\n // and childNodes was already in child order, so children within each\n // group retain child order.\n let i = 0;\n while (i < pairs.length) {\n const minParentNode = pairs[i].parentNode;\n const relatedChildNodes: Node[] = [];\n while (\n i < pairs.length &&\n compareRows(pairs[i].parentNode.row, minParentNode.row) === 0\n ) {\n relatedChildNodes.push(pairs[i].childNode);\n i++;\n }\n yield* this.#yieldParentWithOverlay(minParentNode, relatedChildNodes);\n }\n }\n\n *#fetchMergeSort(\n req: FetchRequest,\n childNodes: Node[],\n ): Stream<Node | 'yield'> {\n const parentIterators: Iterator<Node | 'yield'>[] = [];\n let threw = false;\n try {\n for (const childNode of childNodes) {\n // TODO: consider adding the ability to pass a set of\n // ids to fetch, and have them applied to sqlite using IN.\n const constraintFromChild = buildJoinConstraint(\n childNode.row,\n this.#childKey,\n this.#parentKey,\n );\n if (\n !constraintFromChild ||\n (req.constraint &&\n !constraintsAreCompatible(constraintFromChild, req.constraint))\n ) {\n parentIterators.push(emptyArray[Symbol.iterator]());\n } else {\n const stream = this.#parent.fetch({\n ...req,\n constraint: {\n ...req.constraint,\n ...constraintFromChild,\n },\n });\n const iterator = stream[Symbol.iterator]();\n parentIterators.push(iterator);\n }\n }\n const nextParentNodes: (Node | null)[] = [];\n for (let i = 0; i < parentIterators.length; i++) {\n const iter = parentIterators[i];\n let result = iter.next();\n // yield yields when initializing\n while (!result.done && result.value === 'yield') {\n yield result.value;\n result = iter.next();\n }\n nextParentNodes[i] = result.done ? null : (result.value as Node);\n }\n\n while (true) {\n let minParentNode = null;\n let minParentNodeChildIndexes: number[] = [];\n for (let i = 0; i < nextParentNodes.length; i++) {\n const parentNode = nextParentNodes[i];\n if (parentNode === null) {\n continue;\n }\n if (minParentNode === null) {\n minParentNode = parentNode;\n minParentNodeChildIndexes.push(i);\n } else {\n const compareResult =\n this.#schema.compareRows(parentNode.row, minParentNode.row) *\n (req.reverse ? -1 : 1);\n if (compareResult === 0) {\n minParentNodeChildIndexes.push(i);\n } else if (compareResult < 0) {\n minParentNode = parentNode;\n minParentNodeChildIndexes = [i];\n }\n }\n }\n if (minParentNode === null) {\n return;\n }\n const relatedChildNodes: Node[] = [];\n for (const minParentNodeChildIndex of minParentNodeChildIndexes) {\n relatedChildNodes.push(childNodes[minParentNodeChildIndex]);\n const iter = parentIterators[minParentNodeChildIndex];\n let result = iter.next();\n // yield yields when advancing\n while (!result.done && result.value === 'yield') {\n yield result.value;\n result = iter.next();\n }\n nextParentNodes[minParentNodeChildIndex] = result.done\n ? null\n : (result.value as Node);\n }\n yield* this.#yieldParentWithOverlay(minParentNode, relatedChildNodes);\n }\n } catch (e) {\n threw = true;\n for (const iter of parentIterators) {\n try {\n iter.throw?.(e);\n } catch (_cleanupError) {\n // error in the iter.throw cleanup,\n // catch so other iterators are cleaned up\n }\n }\n throw e;\n } finally {\n if (!threw) {\n for (const iter of parentIterators) {\n try {\n iter.return?.();\n } catch (_cleanupError) {\n // error in the iter.return cleanup,\n // catch so other iterators are cleaned up\n }\n }\n }\n }\n }\n\n *#yieldParentWithOverlay(\n minParentNode: Node,\n relatedChildNodes: Node[],\n ): Stream<Node> {\n let overlaidRelatedChildNodes = relatedChildNodes;\n if (\n this.#inprogressChildChange &&\n this.#inprogressChildChangePosition &&\n isJoinMatch(\n this.#inprogressChildChange[ChangeIndex.NODE].row,\n this.#childKey,\n minParentNode.row,\n this.#parentKey,\n )\n ) {\n const hasInprogressChildChangeBeenPushedForMinParentNode =\n this.#parent\n .getSchema()\n .compareRows(\n minParentNode.row,\n this.#inprogressChildChangePosition,\n ) <= 0;\n if (this.#inprogressChildChange[ChangeIndex.TYPE] === ChangeType.REMOVE) {\n if (hasInprogressChildChangeBeenPushedForMinParentNode) {\n // Remove from relatedChildNodes since the removed child\n // was inserted into childNodes above.\n overlaidRelatedChildNodes = relatedChildNodes.filter(\n n => n !== this.#inprogressChildChange?.[ChangeIndex.NODE],\n );\n }\n } else if (!hasInprogressChildChangeBeenPushedForMinParentNode) {\n overlaidRelatedChildNodes = [\n ...generateWithOverlayNoYield(\n relatedChildNodes,\n this.#inprogressChildChange,\n this.#child.getSchema(),\n ),\n ];\n }\n }\n\n // yield node if after the overlay it still has relationship nodes\n if (overlaidRelatedChildNodes.length > 0) {\n yield {\n ...minParentNode,\n relationships: {\n ...minParentNode.relationships,\n [this.#relationshipName]: () => overlaidRelatedChildNodes,\n },\n };\n }\n }\n\n *#pushChild(change: Change): Stream<'yield'> {\n switch (change[ChangeIndex.TYPE]) {\n case ChangeType.ADD:\n case ChangeType.REMOVE:\n yield* this.#pushChildChange(change);\n break;\n case ChangeType.EDIT: {\n assert(\n rowEqualsForCompoundKey(\n change[ChangeIndex.OLD_NODE].row,\n change[ChangeIndex.NODE].row,\n this.#childKey,\n ),\n `Child edit must not change relationship.`,\n );\n yield* this.#pushChildChange(change, true);\n break;\n }\n case ChangeType.CHILD:\n yield* this.#pushChildChange(change, true);\n break;\n }\n }\n\n *#pushChildChange(change: Change, exists?: boolean): Stream<'yield'> {\n this.#inprogressChildChange = change;\n this.#inprogressChildChangePosition = undefined;\n try {\n const constraint = buildJoinConstraint(\n change[ChangeIndex.NODE].row,\n this.#childKey,\n this.#parentKey,\n );\n const parentNodeStream = constraint\n ? this.#parent.fetch({constraint})\n : [];\n for (const parentNode of parentNodeStream) {\n if (parentNode === 'yield') {\n yield 'yield';\n continue;\n }\n this.#inprogressChildChange = change;\n this.#inprogressChildChangePosition = parentNode.row;\n const childNodeStream = () => {\n const constraint = buildJoinConstraint(\n parentNode.row,\n this.#parentKey,\n this.#childKey,\n );\n return constraint ? this.#child.fetch({constraint}) : [];\n };\n if (!exists) {\n for (const childNode of childNodeStream()) {\n if (childNode === 'yield') {\n yield 'yield';\n continue;\n }\n if (\n this.#child\n .getSchema()\n .compareRows(childNode.row, change[ChangeIndex.NODE].row) !== 0\n ) {\n exists = true;\n break;\n }\n }\n }\n if (exists) {\n yield* this.#output.push(\n makeChildChange(\n {\n ...parentNode,\n relationships: {\n ...parentNode.relationships,\n [this.#relationshipName]: childNodeStream,\n },\n },\n {\n relationshipName: this.#relationshipName,\n change,\n },\n ),\n this,\n );\n } else {\n const newNode = {\n ...parentNode,\n relationships: {\n ...parentNode.relationships,\n [this.#relationshipName]: () => [change[ChangeIndex.NODE]],\n },\n };\n yield* this.#output.push(\n change[ChangeIndex.TYPE] === ChangeType.ADD\n ? makeAddChange(newNode)\n : makeRemoveChange(newNode),\n this,\n );\n }\n }\n } finally {\n this.#inprogressChildChange = undefined;\n }\n }\n\n *#pushParent(change: Change): Stream<'yield'> {\n const childNodeStream = (node: Node) => () => {\n const constraint = buildJoinConstraint(\n node.row,\n this.#parentKey,\n this.#childKey,\n );\n return constraint ? this.#child.fetch({constraint}) : [];\n };\n\n const flip = (node: Node) => ({\n ...node,\n relationships: {\n ...node.relationships,\n [this.#relationshipName]: childNodeStream(node),\n },\n });\n\n // If no related child don't push as this is an inner join.\n let hasRelatedChild = false;\n for (const node of childNodeStream(change[ChangeIndex.NODE])()) {\n if (node === 'yield') {\n yield 'yield';\n continue;\n } else {\n hasRelatedChild = true;\n break;\n }\n }\n if (!hasRelatedChild) {\n return;\n }\n\n switch (change[ChangeIndex.TYPE]) {\n case ChangeType.ADD:\n yield* this.#output.push(\n makeAddChange(flip(change[ChangeIndex.NODE])),\n this,\n );\n break;\n case ChangeType.REMOVE:\n yield* this.#output.push(\n makeRemoveChange(flip(change[ChangeIndex.NODE])),\n this,\n );\n break;\n case ChangeType.CHILD: {\n yield* this.#output.push(\n makeChildChange(\n flip(change[ChangeIndex.NODE]),\n change[ChangeIndex.CHILD_DATA],\n ),\n this,\n );\n break;\n }\n case ChangeType.EDIT: {\n assert(\n rowEqualsForCompoundKey(\n change[ChangeIndex.OLD_NODE].row,\n change[ChangeIndex.NODE].row,\n this.#parentKey,\n ),\n `Parent edit must not change relationship.`,\n );\n yield* this.#output.push(\n makeEditChange(\n flip(change[ChangeIndex.NODE]),\n flip(change[ChangeIndex.OLD_NODE]),\n ),\n this,\n );\n break;\n }\n default:\n unreachable(change);\n }\n }\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"flipped-join.js","names":["#parent","#child","#parentKey","#childKey","#relationshipName","#schema","#parentKeyIsUnique","#pushParent","#pushChild","#output","#inprogressChildChange","#fetchQuicksort","#fetchMergeSort","#yieldParentWithOverlay","#inprogressChildChangePosition","#pushChildChange"],"sources":["../../../../../zql/src/ivm/flipped-join.ts"],"sourcesContent":["import {assert, unreachable} from '../../../shared/src/asserts.ts';\nimport {binarySearch} from '../../../shared/src/binary-search.ts';\nimport {must} from '../../../shared/src/must.ts';\nimport type {CompoundKey, System} from '../../../zero-protocol/src/ast.ts';\nimport type {Row, Value} from '../../../zero-protocol/src/data.ts';\nimport {ChangeIndex} from './change-index.ts';\nimport {ChangeType} from './change-type.ts';\nimport {\n makeAddChange,\n makeChildChange,\n makeEditChange,\n makeRemoveChange,\n type Change,\n} from './change.ts';\nimport {\n constraintsAreCompatible,\n keyMatchesPrimaryKey,\n type Constraint,\n} from './constraint.ts';\nimport type {Node} from './data.ts';\nimport {\n buildJoinConstraint,\n generateWithOverlayNoYield,\n isJoinMatch,\n rowEqualsForCompoundKey,\n} from './join-utils.ts';\nimport {mergeSortedStreams} from './memory-source.ts';\nimport {\n throwOutput,\n type FetchRequest,\n type Input,\n type Output,\n} from './operator.ts';\nimport type {SourceSchema} from './schema.ts';\nimport {type Stream} from './stream.ts';\n\ntype Args = {\n parent: Input;\n child: Input;\n // The nth key in childKey corresponds to the nth key in parentKey.\n parentKey: CompoundKey;\n childKey: CompoundKey;\n\n relationshipName: string;\n hidden: boolean;\n system: System;\n};\n\n/**\n * An *inner* join which fetches nodes from its child input first and then\n * fetches their related nodes from its parent input. Output nodes are the\n * nodes from parent input (in parent input order), which have at least one\n * related child. These output nodes have a new relationship added to them,\n * which has the name `relationshipName`. The value of the relationship is a\n * stream of related nodes from the child input (in child input order).\n */\nexport class FlippedJoin implements Input {\n readonly #parent: Input;\n readonly #child: Input;\n readonly #parentKey: CompoundKey;\n readonly #childKey: CompoundKey;\n readonly #relationshipName: string;\n readonly #schema: SourceSchema;\n readonly #parentKeyIsUnique: boolean;\n\n #output: Output = throwOutput;\n\n #inprogressChildChange: Change | undefined;\n #inprogressChildChangePosition: Row | undefined;\n\n constructor({\n parent,\n child,\n parentKey,\n childKey,\n relationshipName,\n hidden,\n system,\n }: Args) {\n assert(parent !== child, 'Parent and child must be different operators');\n assert(\n parentKey.length === childKey.length,\n 'The parentKey and childKey keys must have same length',\n );\n this.#parent = parent;\n this.#child = child;\n this.#parentKey = parentKey;\n this.#childKey = childKey;\n this.#relationshipName = relationshipName;\n\n const parentSchema = parent.getSchema();\n const childSchema = child.getSchema();\n this.#parentKeyIsUnique =\n keyMatchesPrimaryKey(parentKey, parentSchema.primaryKey) ||\n (parentSchema.uniqueIndexes?.some(idx =>\n keyMatchesPrimaryKey(parentKey, idx),\n ) ??\n false);\n this.#schema = {\n ...parentSchema,\n relationships: {\n ...parentSchema.relationships,\n [relationshipName]: {\n ...childSchema,\n isHidden: hidden,\n system,\n },\n },\n };\n\n parent.setOutput({\n push: (change: Change) => this.#pushParent(change),\n });\n child.setOutput({\n push: (change: Change) => this.#pushChild(change),\n });\n }\n\n destroy(): void {\n this.#child.destroy();\n this.#parent.destroy();\n }\n\n setOutput(output: Output): void {\n this.#output = output;\n }\n\n getSchema(): SourceSchema {\n return this.#schema;\n }\n\n *fetch(req: FetchRequest): Stream<Node | 'yield'> {\n // Translate constraints for the parent on parts of the join key to\n // constraints for the child.\n const childConstraint: Record<string, Value> = {};\n let hasChildConstraint = false;\n if (req.constraint) {\n for (const [key, value] of Object.entries(req.constraint)) {\n const index = this.#parentKey.indexOf(key);\n if (index !== -1) {\n hasChildConstraint = true;\n childConstraint[this.#childKey[index]] = value;\n }\n }\n }\n\n const childNodes: Node[] = [];\n for (const node of this.#child.fetch(\n hasChildConstraint ? {constraint: childConstraint} : {},\n )) {\n if (node === 'yield') {\n yield node;\n continue;\n }\n childNodes.push(node);\n }\n\n // FlippedJoin's split-push change overlay logic is largely\n // the same as Join's with the exception of remove. For remove,\n // the change is undone here, and then re-applied to parents with order\n // less than or equal to change.position below. This is necessary\n // because if the removed node was the last related child, the\n // related parents with position greater than change.position\n // (which should not yet have the node removed), would not even\n // be fetched here, and would be absent from the output all together.\n if (this.#inprogressChildChange?.[ChangeIndex.TYPE] === ChangeType.REMOVE) {\n const removedNode = this.#inprogressChildChange[ChangeIndex.NODE];\n const compare = this.#child.getSchema().compareRows;\n const insertPos = binarySearch(childNodes.length, i =>\n compare(removedNode.row, childNodes[i].row),\n );\n childNodes.splice(insertPos, 0, removedNode);\n }\n\n if (this.#parentKeyIsUnique) {\n yield* this.#fetchQuicksort(req, childNodes);\n } else {\n yield* this.#fetchMergeSort(req, childNodes);\n }\n }\n\n // When parentKey matches a unique index on the parent (primary or\n // otherwise) each child -> parent fetch returns at most one row, so the\n // merge-sort degenerates to N simultaneous prepared-statement iterators\n // each holding a single-row cursor. Instead, fetch sequentially (letting\n // the statement cache reuse a single prepared statement) and sort the\n // resulting parents into order.\n *#fetchQuicksort(\n req: FetchRequest,\n childNodes: Node[],\n ): Stream<Node | 'yield'> {\n const pairs: {childNode: Node; parentNode: Node}[] = [];\n for (const childNode of childNodes) {\n const constraintFromChild = buildJoinConstraint(\n childNode.row,\n this.#childKey,\n this.#parentKey,\n );\n if (\n !constraintFromChild ||\n (req.constraint &&\n !constraintsAreCompatible(constraintFromChild, req.constraint))\n ) {\n continue;\n }\n const stream = this.#parent.fetch({\n ...req,\n constraint: {\n ...req.constraint,\n ...constraintFromChild,\n },\n });\n // parentKey matches a unique index, so this fetch returns at most\n // one row under the Source contract. Iterate to completion rather\n // than breaking to preserve yield propagation and to avoid silently\n // changing behavior if a source ever returns more.\n for (const node of stream) {\n if (node === 'yield') {\n yield 'yield';\n continue;\n }\n pairs.push({childNode, parentNode: node});\n }\n }\n\n const compareRows = this.#schema.compareRows;\n const dir = req.reverse ? -1 : 1;\n pairs.sort((a, b) => compareRows(a.parentNode.row, b.parentNode.row) * dir);\n\n // Group consecutive pairs with equal parent rows. Array.sort is stable,\n // and childNodes was already in child order, so children within each\n // group retain child order.\n let i = 0;\n while (i < pairs.length) {\n const minParentNode = pairs[i].parentNode;\n const relatedChildNodes: Node[] = [];\n while (\n i < pairs.length &&\n compareRows(pairs[i].parentNode.row, minParentNode.row) === 0\n ) {\n relatedChildNodes.push(pairs[i].childNode);\n i++;\n }\n yield* this.#yieldParentWithOverlay(minParentNode, relatedChildNodes);\n }\n }\n\n *#fetchMergeSort(\n req: FetchRequest,\n childNodes: Node[],\n ): Stream<Node | 'yield'> {\n // Group children by parent-key value so children sharing a value\n // share one fetch (and one cursor). Without this, two children with\n // the same parent-key value would each open their own iterator that\n // re-fetches the same parent rows — wasted IO.\n const parentKey = this.#parentKey;\n const computedKeys: Constraint[] = [];\n const childIndexesByKey = new Map<string, number[]>();\n for (let i = 0; i < childNodes.length; i++) {\n const constraintFromChild = buildJoinConstraint(\n childNodes[i].row,\n this.#childKey,\n parentKey,\n );\n if (\n !constraintFromChild ||\n (req.constraint &&\n !constraintsAreCompatible(constraintFromChild, req.constraint))\n ) {\n continue;\n }\n const key = canonicalKey(constraintFromChild, parentKey);\n const existing = childIndexesByKey.get(key);\n if (existing === undefined) {\n childIndexesByKey.set(key, [i]);\n computedKeys.push(constraintFromChild);\n } else {\n existing.push(i);\n }\n }\n\n if (computedKeys.length === 0) {\n return;\n }\n\n const compareRows = this.#schema.compareRows;\n const compare: (a: Node, b: Node) => number = req.reverse\n ? (a, b) => compareRows(b.row, a.row)\n : (a, b) => compareRows(a.row, b.row);\n\n // One stream per unique parent-key value. Each stream returns its\n // matching parent rows in compareRows order; the heap merges them\n // into a globally ordered stream. Distinct rows can't compare equal\n // (compareRows includes the primary key), so no tie handling needed\n // — every emit maps back to exactly one entry in childIndexesByKey.\n const streams: Stream<Node | 'yield'>[] = computedKeys.map(c =>\n this.#parent.fetch({\n ...req,\n constraint: req.constraint ? {...req.constraint, ...c} : c,\n }),\n );\n\n for (const node of mergeSortedStreams(streams, compare)) {\n if (node === 'yield') {\n yield 'yield';\n continue;\n }\n // Every fetched parent row matches the constraint for one entry\n // in `computedKeys`, whose canonical key was inserted into the\n // map — so the lookup is guaranteed to hit. Children retain\n // their original input order within the group because we\n // appended to the indexes array in iteration order.\n const idxs = must(\n childIndexesByKey.get(canonicalKey(node.row, parentKey)),\n );\n const relatedChildNodes: Node[] = idxs.map(i => childNodes[i]);\n yield* this.#yieldParentWithOverlay(node, relatedChildNodes);\n }\n }\n\n *#yieldParentWithOverlay(\n minParentNode: Node,\n relatedChildNodes: Node[],\n ): Stream<Node> {\n let overlaidRelatedChildNodes = relatedChildNodes;\n if (\n this.#inprogressChildChange &&\n this.#inprogressChildChangePosition &&\n isJoinMatch(\n this.#inprogressChildChange[ChangeIndex.NODE].row,\n this.#childKey,\n minParentNode.row,\n this.#parentKey,\n )\n ) {\n const hasInprogressChildChangeBeenPushedForMinParentNode =\n this.#parent\n .getSchema()\n .compareRows(\n minParentNode.row,\n this.#inprogressChildChangePosition,\n ) <= 0;\n if (this.#inprogressChildChange[ChangeIndex.TYPE] === ChangeType.REMOVE) {\n if (hasInprogressChildChangeBeenPushedForMinParentNode) {\n // Remove from relatedChildNodes since the removed child\n // was inserted into childNodes above.\n overlaidRelatedChildNodes = relatedChildNodes.filter(\n n => n !== this.#inprogressChildChange?.[ChangeIndex.NODE],\n );\n }\n } else if (!hasInprogressChildChangeBeenPushedForMinParentNode) {\n overlaidRelatedChildNodes = [\n ...generateWithOverlayNoYield(\n relatedChildNodes,\n this.#inprogressChildChange,\n this.#child.getSchema(),\n ),\n ];\n }\n }\n\n // yield node if after the overlay it still has relationship nodes\n if (overlaidRelatedChildNodes.length > 0) {\n yield {\n ...minParentNode,\n relationships: {\n ...minParentNode.relationships,\n [this.#relationshipName]: () => overlaidRelatedChildNodes,\n },\n };\n }\n }\n\n *#pushChild(change: Change): Stream<'yield'> {\n switch (change[ChangeIndex.TYPE]) {\n case ChangeType.ADD:\n case ChangeType.REMOVE:\n yield* this.#pushChildChange(change);\n break;\n case ChangeType.EDIT: {\n assert(\n rowEqualsForCompoundKey(\n change[ChangeIndex.OLD_NODE].row,\n change[ChangeIndex.NODE].row,\n this.#childKey,\n ),\n `Child edit must not change relationship.`,\n );\n yield* this.#pushChildChange(change, true);\n break;\n }\n case ChangeType.CHILD:\n yield* this.#pushChildChange(change, true);\n break;\n }\n }\n\n *#pushChildChange(change: Change, exists?: boolean): Stream<'yield'> {\n this.#inprogressChildChange = change;\n this.#inprogressChildChangePosition = undefined;\n try {\n const constraint = buildJoinConstraint(\n change[ChangeIndex.NODE].row,\n this.#childKey,\n this.#parentKey,\n );\n const parentNodeStream = constraint\n ? this.#parent.fetch({constraint})\n : [];\n for (const parentNode of parentNodeStream) {\n if (parentNode === 'yield') {\n yield 'yield';\n continue;\n }\n this.#inprogressChildChange = change;\n this.#inprogressChildChangePosition = parentNode.row;\n const childNodeStream = () => {\n const constraint = buildJoinConstraint(\n parentNode.row,\n this.#parentKey,\n this.#childKey,\n );\n return constraint ? this.#child.fetch({constraint}) : [];\n };\n if (!exists) {\n for (const childNode of childNodeStream()) {\n if (childNode === 'yield') {\n yield 'yield';\n continue;\n }\n if (\n this.#child\n .getSchema()\n .compareRows(childNode.row, change[ChangeIndex.NODE].row) !== 0\n ) {\n exists = true;\n break;\n }\n }\n }\n if (exists) {\n yield* this.#output.push(\n makeChildChange(\n {\n ...parentNode,\n relationships: {\n ...parentNode.relationships,\n [this.#relationshipName]: childNodeStream,\n },\n },\n {\n relationshipName: this.#relationshipName,\n change,\n },\n ),\n this,\n );\n } else {\n const newNode = {\n ...parentNode,\n relationships: {\n ...parentNode.relationships,\n [this.#relationshipName]: () => [change[ChangeIndex.NODE]],\n },\n };\n yield* this.#output.push(\n change[ChangeIndex.TYPE] === ChangeType.ADD\n ? makeAddChange(newNode)\n : makeRemoveChange(newNode),\n this,\n );\n }\n }\n } finally {\n this.#inprogressChildChange = undefined;\n }\n }\n\n *#pushParent(change: Change): Stream<'yield'> {\n const childNodeStream = (node: Node) => () => {\n const constraint = buildJoinConstraint(\n node.row,\n this.#parentKey,\n this.#childKey,\n );\n return constraint ? this.#child.fetch({constraint}) : [];\n };\n\n const flip = (node: Node) => ({\n ...node,\n relationships: {\n ...node.relationships,\n [this.#relationshipName]: childNodeStream(node),\n },\n });\n\n // If no related child don't push as this is an inner join.\n let hasRelatedChild = false;\n for (const node of childNodeStream(change[ChangeIndex.NODE])()) {\n if (node === 'yield') {\n yield 'yield';\n continue;\n } else {\n hasRelatedChild = true;\n break;\n }\n }\n if (!hasRelatedChild) {\n return;\n }\n\n switch (change[ChangeIndex.TYPE]) {\n case ChangeType.ADD:\n yield* this.#output.push(\n makeAddChange(flip(change[ChangeIndex.NODE])),\n this,\n );\n break;\n case ChangeType.REMOVE:\n yield* this.#output.push(\n makeRemoveChange(flip(change[ChangeIndex.NODE])),\n this,\n );\n break;\n case ChangeType.CHILD: {\n yield* this.#output.push(\n makeChildChange(\n flip(change[ChangeIndex.NODE]),\n change[ChangeIndex.CHILD_DATA],\n ),\n this,\n );\n break;\n }\n case ChangeType.EDIT: {\n assert(\n rowEqualsForCompoundKey(\n change[ChangeIndex.OLD_NODE].row,\n change[ChangeIndex.NODE].row,\n this.#parentKey,\n ),\n `Parent edit must not change relationship.`,\n );\n yield* this.#output.push(\n makeEditChange(\n flip(change[ChangeIndex.NODE]),\n flip(change[ChangeIndex.OLD_NODE]),\n ),\n this,\n );\n break;\n }\n default:\n unreachable(change);\n }\n }\n}\n\n/**\n * Canonical string key over `keys` of `record`, used by `#fetchMergeSort`\n * both to dedupe per-child fetches and to map each returned parent row\n * back to the children that referenced its parent-key tuple.\n *\n * Exported for testing.\n */\nexport function canonicalKey(\n record: Record<string, Value | undefined>,\n keys: CompoundKey,\n): string {\n if (keys.length === 1) {\n return canonicalValue(record[keys[0]]);\n }\n let s = '';\n for (let i = 0; i < keys.length; i++) {\n if (i > 0) s += '\\x00';\n s += canonicalValue(record[keys[i]]);\n }\n return s;\n}\n\nfunction canonicalValue(v: Value | bigint | undefined): string {\n // Tag by type so we don't conflate e.g. `1` (number) with `\"1\"` (string).\n // Bigint shows up at runtime when zqlite's safeIntegers is on, even\n // though the static `Value` type doesn't list it.\n if (v === null || v === undefined) return 'n';\n const t = typeof v;\n if (t === 'string') return 's' + (v as string);\n if (t === 'number') return 'd' + (v as number);\n if (t === 'bigint') return 'b' + (v as bigint).toString();\n if (t === 'boolean') return v ? 't' : 'f';\n return 'j' + JSON.stringify(v);\n}\n"],"mappings":"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"}
@@ -7,7 +7,7 @@ import type { DebugDelegate } from '../builder/debug-delegate.ts';
7
7
  import { type NoSubqueryCondition } from '../builder/filter.ts';
8
8
  import { type Constraint } from './constraint.ts';
9
9
  import { type Comparator, type Node } from './data.ts';
10
- import { type Input, type Output, type Start } from './operator.ts';
10
+ import { type Input, type MultiConstraint, type Output, type Start } from './operator.ts';
11
11
  import type { Source, SourceChange, SourceInput } from './source.ts';
12
12
  import type { Stream } from './stream.ts';
13
13
  export type Overlay = {
@@ -67,10 +67,12 @@ export declare function generateWithStart(nodes: Iterable<Node | 'yield'>, start
67
67
  * @param overlay - the overlay values to splice in
68
68
  * @param compare - the comparator to use to find the position for the overlay
69
69
  */
70
- export declare function generateWithOverlay(startAt: Row | undefined, rows: Iterable<Row>, constraint: Constraint | undefined, overlay: Overlay | undefined, lastPushedEpoch: number, compare: Comparator, filterPredicate?: (row: Row) => boolean | undefined): Generator<{
70
+ export declare function generateWithOverlay(startAt: Row | undefined, rows: Iterable<Row>, constraint: Constraint | undefined, overlay: Overlay | undefined, lastPushedEpoch: number, compare: Comparator, filterPredicate?: (row: Row) => boolean | undefined, multiConstraints?: readonly MultiConstraint[] | undefined): Generator<{
71
71
  row: Readonly<Record<string, import("../../../shared/src/json.ts").ReadonlyJSONValue | undefined>>;
72
72
  relationships: {};
73
73
  }, void, unknown>;
74
+ export { overlaysForMultiConstraint as overlaysForMultiConstraintForTest };
75
+ declare function overlaysForMultiConstraint({ add, remove }: Overlays, multiConstraint: MultiConstraint): Overlays;
74
76
  export { overlaysForStartAt as overlaysForStartAtForTest };
75
77
  declare function overlaysForStartAt({ add, remove }: Overlays, startAt: Row, compare: Comparator): Overlays;
76
78
  export { overlaysForConstraint as overlaysForConstraintForTest };
@@ -84,7 +86,7 @@ export declare function generateWithOverlayInner(rowIterator: Iterable<Row>, ove
84
86
  * No `startAt` or comparator needed. Injects remove/old-edit rows eagerly
85
87
  * at the start, and suppresses add/new-edit rows inline by PK match.
86
88
  */
87
- export declare function generateWithOverlayUnordered(rows: Iterable<Row>, constraint: Constraint | undefined, overlay: Overlay | undefined, lastPushedEpoch: number, primaryKey: PrimaryKey, filterPredicate?: (row: Row) => boolean): Generator<{
89
+ export declare function generateWithOverlayUnordered(rows: Iterable<Row>, constraint: Constraint | undefined, overlay: Overlay | undefined, lastPushedEpoch: number, primaryKey: PrimaryKey, filterPredicate?: (row: Row) => boolean, multiConstraints?: readonly MultiConstraint[] | undefined): Generator<{
88
90
  row: Readonly<Record<string, import("../../../shared/src/json.ts").ReadonlyJSONValue | undefined>>;
89
91
  relationships: {};
90
92
  }, void, unknown>;
@@ -93,4 +95,23 @@ export declare function generateWithOverlayInnerUnordered(rowIterator: Iterable<
93
95
  relationships: {};
94
96
  }, void, unknown>;
95
97
  export declare function stringify(change: SourceChange): string;
98
+ /**
99
+ * N-way merge of pre-sorted Node streams. Each input stream must yield
100
+ * Nodes in `compare` order (and may interleave 'yield' values, which are
101
+ * forwarded as-is). The merged stream yields Nodes in `compare` order.
102
+ *
103
+ * Implemented as a binary min-heap keyed by `compare(entry.row, ...)`, so
104
+ * each emit costs O(log K) (K = number of streams) rather than the O(K)
105
+ * a linear-scan-of-heads merge would. Matters when FlippedJoin chunks a
106
+ * large `multiConstraints` IN-list into hundreds of sub-fetches.
107
+ *
108
+ * If the merged stream is closed early (e.g. downstream `Take` `break`s
109
+ * after hitting its limit, prompting JS to call `.return()` on this
110
+ * generator), the `finally` block propagates `.return()` to each
111
+ * non-exhausted sub-iterator so the underlying sources (SQLite cursors,
112
+ * etc.) can run their cleanup. Without this, mid-merge early-termination
113
+ * leaves cursors open, causing later writes on the same connection to
114
+ * fail with "database connection is busy executing a query".
115
+ */
116
+ export declare function mergeSortedStreams(streams: readonly Stream<Node | 'yield'>[], compare: (a: Node, b: Node) => number): Stream<Node | 'yield'>;
96
117
  //# sourceMappingURL=memory-source.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"memory-source.d.ts","sourceRoot":"","sources":["../../../../../zql/src/ivm/memory-source.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"memory-source.d.ts","sourceRoot":"","sources":["../../../../../zql/src/ivm/memory-source.ts"],"names":[],"mappings":"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"}
@@ -4,12 +4,12 @@ import { once, toSorted } from "../../../shared/src/iterables.js";
4
4
  import { must } from "../../../shared/src/must.js";
5
5
  import { skipYields } from "./skip-yields.js";
6
6
  import { makeAddChange, makeEditChange, makeRemoveChange } from "./change.js";
7
+ import { assertOrderingIncludesPK } from "../query/complete-ordering.js";
7
8
  import { compareValues, makeComparator, valuesEqual } from "./data.js";
8
9
  import { filterPush } from "./filter-push.js";
9
10
  import { constraintMatchesPrimaryKey, constraintMatchesRow, primaryKeyConstraintFromFilters } from "./constraint.js";
10
- import { assertOrderingIncludesPK } from "../query/complete-ordering.js";
11
- import { createPredicate, transformFilters } from "../builder/filter.js";
12
11
  import { BTreeSet } from "../../../shared/src/btree-set.js";
12
+ import { createPredicate, transformFilters } from "../builder/filter.js";
13
13
  import { makeSourceChangeAdd, makeSourceChangeRemove } from "./source.js";
14
14
  //#region ../zql/src/ivm/memory-source.ts
15
15
  /**
@@ -129,6 +129,10 @@ var MemorySource = class MemorySource {
129
129
  return [...this.#indexes.keys()];
130
130
  }
131
131
  *#fetch(req, conn) {
132
+ if (req.multiConstraints && req.multiConstraints.some((mc) => mc.length > 0)) {
133
+ yield* this.#fetchMulti(req, conn);
134
+ return;
135
+ }
132
136
  const requestedSort = must(conn.sort);
133
137
  const { compareRows } = conn;
134
138
  const connectionComparator = req.reverse ? (r1, r2) => compareRows(r2, r1) : compareRows;
@@ -151,6 +155,46 @@ var MemorySource = class MemorySource {
151
155
  const withConstraint = generateWithConstraint(skipYields(generateWithStart(generateWithOverlay(startAt, pkConstraint ? once(rowsIterable) : rowsIterable, req.constraint, this.#overlay, conn.lastPushedEpoch, indexComparator, conn.filters?.predicate), req.start, connectionComparator)), req.constraint);
152
156
  yield* conn.filters ? generateWithFilter(withConstraint, conn.filters.predicate) : withConstraint;
153
157
  }
158
+ *#fetchMulti(req, conn) {
159
+ const multis = must(req.multiConstraints).filter((mc) => mc.length > 0);
160
+ const primary = multis[0];
161
+ const rest = multis.slice(1);
162
+ const baseConstraint = req.constraint;
163
+ const merged = mergeSortedStreams(primary.map((c) => {
164
+ const merged = baseConstraint ? {
165
+ ...baseConstraint,
166
+ ...c
167
+ } : c;
168
+ return this.#fetch({
169
+ ...req,
170
+ constraint: merged,
171
+ multiConstraints: void 0
172
+ }, conn);
173
+ }), req.reverse ? (a, b) => conn.compareRows(b.row, a.row) : (a, b) => conn.compareRows(a.row, b.row));
174
+ if (rest.length === 0) {
175
+ yield* merged;
176
+ return;
177
+ }
178
+ for (const node of merged) {
179
+ if (node === "yield") {
180
+ yield "yield";
181
+ continue;
182
+ }
183
+ let matchesAll = true;
184
+ for (const mc of rest) {
185
+ let any = false;
186
+ for (const c of mc) if (constraintMatchesRow(c, node.row)) {
187
+ any = true;
188
+ break;
189
+ }
190
+ if (!any) {
191
+ matchesAll = false;
192
+ break;
193
+ }
194
+ }
195
+ if (matchesAll) yield node;
196
+ }
197
+ }
154
198
  *push(change) {
155
199
  for (const result of this.genPush(change)) if (result === "yield") yield result;
156
200
  }
@@ -277,12 +321,12 @@ function* generateWithStart(nodes, start, compare) {
277
321
  * @param overlay - the overlay values to splice in
278
322
  * @param compare - the comparator to use to find the position for the overlay
279
323
  */
280
- function* generateWithOverlay(startAt, rows, constraint, overlay, lastPushedEpoch, compare, filterPredicate) {
324
+ function* generateWithOverlay(startAt, rows, constraint, overlay, lastPushedEpoch, compare, filterPredicate, multiConstraints) {
281
325
  let overlayToApply = void 0;
282
326
  if (overlay && lastPushedEpoch >= overlay.epoch) overlayToApply = overlay;
283
- yield* generateWithOverlayInner(rows, computeOverlays(startAt, constraint, overlayToApply, compare, filterPredicate), compare);
327
+ yield* generateWithOverlayInner(rows, computeOverlays(startAt, constraint, overlayToApply, compare, filterPredicate, multiConstraints), compare);
284
328
  }
285
- function computeOverlays(startAt, constraint, overlay, compare, filterPredicate) {
329
+ function computeOverlays(startAt, constraint, overlay, compare, filterPredicate, multiConstraints) {
286
330
  let overlays = {
287
331
  add: void 0,
288
332
  remove: void 0
@@ -309,9 +353,26 @@ function computeOverlays(startAt, constraint, overlay, compare, filterPredicate)
309
353
  }
310
354
  if (startAt) overlays = overlaysForStartAt(overlays, startAt, compare);
311
355
  if (constraint) overlays = overlaysForConstraint(overlays, constraint);
356
+ overlays = applyMultiConstraintsToOverlays(overlays, multiConstraints);
312
357
  if (filterPredicate) overlays = overlaysForFilterPredicate(overlays, filterPredicate);
313
358
  return overlays;
314
359
  }
360
+ function applyMultiConstraintsToOverlays(overlays, multiConstraints) {
361
+ if (!multiConstraints) return overlays;
362
+ for (const mc of multiConstraints) if (mc.length > 0) overlays = overlaysForMultiConstraint(overlays, mc);
363
+ return overlays;
364
+ }
365
+ function overlaysForMultiConstraint({ add, remove }, multiConstraint) {
366
+ const matchesAny = (row) => {
367
+ if (row === void 0) return false;
368
+ for (const c of multiConstraint) if (constraintMatchesRow(c, row)) return true;
369
+ return false;
370
+ };
371
+ return {
372
+ add: matchesAny(add) ? add : void 0,
373
+ remove: matchesAny(remove) ? remove : void 0
374
+ };
375
+ }
315
376
  function overlaysForStartAt({ add, remove }, startAt, compare) {
316
377
  const undefinedIfBeforeStartAt = (row) => row === void 0 || compare(row, startAt) < 0 ? void 0 : row;
317
378
  return {
@@ -367,7 +428,7 @@ function* generateWithOverlayInner(rowIterator, overlays, compare) {
367
428
  * No `startAt` or comparator needed. Injects remove/old-edit rows eagerly
368
429
  * at the start, and suppresses add/new-edit rows inline by PK match.
369
430
  */
370
- function* generateWithOverlayUnordered(rows, constraint, overlay, lastPushedEpoch, primaryKey, filterPredicate) {
431
+ function* generateWithOverlayUnordered(rows, constraint, overlay, lastPushedEpoch, primaryKey, filterPredicate, multiConstraints) {
371
432
  let overlayToApply = void 0;
372
433
  if (overlay && lastPushedEpoch >= overlay.epoch) overlayToApply = overlay;
373
434
  let overlays = {
@@ -395,6 +456,7 @@ function* generateWithOverlayUnordered(rows, constraint, overlay, lastPushedEpoc
395
456
  break;
396
457
  }
397
458
  if (constraint) overlays = overlaysForConstraint(overlays, constraint);
459
+ overlays = applyMultiConstraintsToOverlays(overlays, multiConstraints);
398
460
  if (filterPredicate) overlays = overlaysForFilterPredicate(overlays, filterPredicate);
399
461
  yield* generateWithOverlayInnerUnordered(rows, overlays, primaryKey);
400
462
  }
@@ -451,7 +513,100 @@ function* generateRows(data, scanStart, reverse) {
451
513
  function stringify(change) {
452
514
  return JSON.stringify(change, (_, v) => typeof v === "bigint" ? v.toString() : v);
453
515
  }
516
+ /**
517
+ * N-way merge of pre-sorted Node streams. Each input stream must yield
518
+ * Nodes in `compare` order (and may interleave 'yield' values, which are
519
+ * forwarded as-is). The merged stream yields Nodes in `compare` order.
520
+ *
521
+ * Implemented as a binary min-heap keyed by `compare(entry.row, ...)`, so
522
+ * each emit costs O(log K) (K = number of streams) rather than the O(K)
523
+ * a linear-scan-of-heads merge would. Matters when FlippedJoin chunks a
524
+ * large `multiConstraints` IN-list into hundreds of sub-fetches.
525
+ *
526
+ * If the merged stream is closed early (e.g. downstream `Take` `break`s
527
+ * after hitting its limit, prompting JS to call `.return()` on this
528
+ * generator), the `finally` block propagates `.return()` to each
529
+ * non-exhausted sub-iterator so the underlying sources (SQLite cursors,
530
+ * etc.) can run their cleanup. Without this, mid-merge early-termination
531
+ * leaves cursors open, causing later writes on the same connection to
532
+ * fail with "database connection is busy executing a query".
533
+ */
534
+ function* mergeSortedStreams(streams, compare) {
535
+ const iterators = streams.map((s) => s[Symbol.iterator]());
536
+ const active = new Array(iterators.length).fill(true);
537
+ const heap = [];
538
+ const siftUp = (start) => {
539
+ let i = start;
540
+ while (i > 0) {
541
+ const p = i - 1 >> 1;
542
+ if (compare(heap[i].row, heap[p].row) >= 0) return;
543
+ const t = heap[i];
544
+ heap[i] = heap[p];
545
+ heap[p] = t;
546
+ i = p;
547
+ }
548
+ };
549
+ const siftDown = (start) => {
550
+ let i = start;
551
+ const n = heap.length;
552
+ while (true) {
553
+ const l = (i << 1) + 1;
554
+ const r = l + 1;
555
+ let smallest = i;
556
+ if (l < n && compare(heap[l].row, heap[smallest].row) < 0) smallest = l;
557
+ if (r < n && compare(heap[r].row, heap[smallest].row) < 0) smallest = r;
558
+ if (smallest === i) return;
559
+ const t = heap[i];
560
+ heap[i] = heap[smallest];
561
+ heap[smallest] = t;
562
+ i = smallest;
563
+ }
564
+ };
565
+ const pullNext = function* (idx) {
566
+ while (true) {
567
+ const r = iterators[idx].next();
568
+ if (r.done) {
569
+ active[idx] = false;
570
+ return;
571
+ }
572
+ if (r.value === "yield") {
573
+ yield "yield";
574
+ continue;
575
+ }
576
+ return r.value;
577
+ }
578
+ };
579
+ try {
580
+ for (let i = 0; i < iterators.length; i++) {
581
+ const row = yield* pullNext(i);
582
+ if (row !== void 0) {
583
+ heap.push({
584
+ row,
585
+ idx: i
586
+ });
587
+ siftUp(heap.length - 1);
588
+ }
589
+ }
590
+ while (heap.length > 0) {
591
+ const top = heap[0];
592
+ yield top.row;
593
+ const next = yield* pullNext(top.idx);
594
+ if (next !== void 0) {
595
+ top.row = next;
596
+ siftDown(0);
597
+ } else {
598
+ const last = must(heap.pop());
599
+ if (heap.length > 0) {
600
+ heap[0] = last;
601
+ siftDown(0);
602
+ }
603
+ }
604
+ }
605
+ } finally {
606
+ for (let i = 0; i < iterators.length; i++) if (active[i]) iterators[i].return?.();
607
+ }
608
+ }
454
609
  //#endregion
455
- export { MemorySource, genPushAndWriteWithSplitEdit, generateWithOverlay, generateWithOverlayUnordered, generateWithStart };
610
+ export { MemorySource, genPushAndWriteWithSplitEdit, generateWithOverlay, generateWithOverlayUnordered, generateWithStart, mergeSortedStreams };
456
611
 
457
612
  //# sourceMappingURL=memory-source.js.map