@rocicorp/zero 1.11.0-canary.18 → 1.11.0-canary.19

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 (47) hide show
  1. package/out/zero/package.js +1 -1
  2. package/out/zero/package.js.map +1 -1
  3. package/out/zero-cache/src/server/inspector-delegate.d.ts +10 -0
  4. package/out/zero-cache/src/server/inspector-delegate.d.ts.map +1 -1
  5. package/out/zero-cache/src/server/inspector-delegate.js +17 -4
  6. package/out/zero-cache/src/server/inspector-delegate.js.map +1 -1
  7. package/out/zero-cache/src/server/replicator.d.ts +3 -0
  8. package/out/zero-cache/src/server/replicator.d.ts.map +1 -1
  9. package/out/zero-cache/src/server/replicator.js +45 -3
  10. package/out/zero-cache/src/server/replicator.js.map +1 -1
  11. package/out/zero-client/src/client/version.js +1 -1
  12. package/out/zero-protocol/src/down.d.ts +2 -2
  13. package/out/zero-protocol/src/inspect-down.d.ts +81 -81
  14. package/out/zql/src/builder/builder.d.ts.map +1 -1
  15. package/out/zql/src/builder/builder.js +37 -16
  16. package/out/zql/src/builder/builder.js.map +1 -1
  17. package/out/zql/src/ivm/flipped-join.d.ts +5 -3
  18. package/out/zql/src/ivm/flipped-join.d.ts.map +1 -1
  19. package/out/zql/src/ivm/flipped-join.js +26 -14
  20. package/out/zql/src/ivm/flipped-join.js.map +1 -1
  21. package/out/zql/src/ivm/join-index-tap.d.ts +33 -0
  22. package/out/zql/src/ivm/join-index-tap.d.ts.map +1 -0
  23. package/out/zql/src/ivm/join-index-tap.js +70 -0
  24. package/out/zql/src/ivm/join-index-tap.js.map +1 -0
  25. package/out/zql/src/ivm/join-index.d.ts +35 -0
  26. package/out/zql/src/ivm/join-index.d.ts.map +1 -0
  27. package/out/zql/src/ivm/join-index.js +141 -0
  28. package/out/zql/src/ivm/join-index.js.map +1 -0
  29. package/out/zql/src/ivm/join-utils.d.ts +0 -22
  30. package/out/zql/src/ivm/join-utils.d.ts.map +1 -1
  31. package/out/zql/src/ivm/join-utils.js +1 -85
  32. package/out/zql/src/ivm/join-utils.js.map +1 -1
  33. package/out/zql/src/ivm/join.d.ts +17 -3
  34. package/out/zql/src/ivm/join.d.ts.map +1 -1
  35. package/out/zql/src/ivm/join.js +35 -13
  36. package/out/zql/src/ivm/join.js.map +1 -1
  37. package/out/zqlite/src/db.d.ts.map +1 -1
  38. package/out/zqlite/src/db.js +23 -25
  39. package/out/zqlite/src/db.js.map +1 -1
  40. package/out/zqlite/src/query-builder.d.ts +17 -1
  41. package/out/zqlite/src/query-builder.d.ts.map +1 -1
  42. package/out/zqlite/src/query-builder.js +111 -20
  43. package/out/zqlite/src/query-builder.js.map +1 -1
  44. package/out/zqlite/src/table-source.d.ts.map +1 -1
  45. package/out/zqlite/src/table-source.js +39 -23
  46. package/out/zqlite/src/table-source.js.map +1 -1
  47. package/package.json +1 -1
@@ -100,6 +100,33 @@ export declare const inspectAnalyzeQueryDownSchema: v.ObjectType<Omit<{
100
100
  type: "attempt-start";
101
101
  attemptNumber: number;
102
102
  totalAttempts: number;
103
+ } | {
104
+ type: "connection-selected";
105
+ attemptNumber: number;
106
+ connection: string;
107
+ cost: number;
108
+ isRoot: boolean;
109
+ } | {
110
+ type: "plan-failed";
111
+ attemptNumber: number;
112
+ reason: string;
113
+ } | {
114
+ type: "best-plan-selected";
115
+ bestAttemptNumber: number;
116
+ totalCost: number;
117
+ flipPattern: number;
118
+ joinStates: {
119
+ join: string;
120
+ type: "flipped" | "semi" | "unflippable";
121
+ }[];
122
+ } | {
123
+ type: "node-constraint";
124
+ attemptNumber?: number | undefined;
125
+ nodeType: "connection" | "fan-in" | "fan-out" | "filter" | "join" | "terminus";
126
+ node: string;
127
+ branchPattern: number[];
128
+ constraint?: Record<string, unknown> | null | undefined;
129
+ from: string;
103
130
  } | {
104
131
  type: "connection-costs";
105
132
  attemptNumber: number;
@@ -125,12 +152,6 @@ export declare const inspectAnalyzeQueryDownSchema: v.ObjectType<Omit<{
125
152
  limit?: number | undefined;
126
153
  }>;
127
154
  }[];
128
- } | {
129
- type: "connection-selected";
130
- attemptNumber: number;
131
- connection: string;
132
- cost: number;
133
- isRoot: boolean;
134
155
  } | {
135
156
  type: "constraints-propagated";
136
157
  attemptNumber: number;
@@ -155,19 +176,6 @@ export declare const inspectAnalyzeQueryDownSchema: v.ObjectType<Omit<{
155
176
  join: string;
156
177
  type: "flipped" | "semi" | "unflippable";
157
178
  }[];
158
- } | {
159
- type: "plan-failed";
160
- attemptNumber: number;
161
- reason: string;
162
- } | {
163
- type: "best-plan-selected";
164
- bestAttemptNumber: number;
165
- totalCost: number;
166
- flipPattern: number;
167
- joinStates: {
168
- join: string;
169
- type: "flipped" | "semi" | "unflippable";
170
- }[];
171
179
  } | {
172
180
  type: "node-cost";
173
181
  attemptNumber?: number | undefined;
@@ -186,14 +194,6 @@ export declare const inspectAnalyzeQueryDownSchema: v.ObjectType<Omit<{
186
194
  filters?: import("./ast.ts").Condition | undefined;
187
195
  ordering?: readonly (readonly [string, "asc" | "desc"])[] | undefined;
188
196
  joinType?: "flipped" | "semi" | "unflippable" | undefined;
189
- } | {
190
- type: "node-constraint";
191
- attemptNumber?: number | undefined;
192
- nodeType: "connection" | "fan-in" | "fan-out" | "filter" | "join" | "terminus";
193
- node: string;
194
- branchPattern: number[];
195
- constraint?: Record<string, unknown> | null | undefined;
196
- from: string;
197
197
  })[]>;
198
198
  }, undefined>;
199
199
  }, undefined>;
@@ -266,6 +266,33 @@ export declare const inspectDownBodySchema: v.UnionType<[v.ObjectType<Omit<{
266
266
  type: "attempt-start";
267
267
  attemptNumber: number;
268
268
  totalAttempts: number;
269
+ } | {
270
+ type: "connection-selected";
271
+ attemptNumber: number;
272
+ connection: string;
273
+ cost: number;
274
+ isRoot: boolean;
275
+ } | {
276
+ type: "plan-failed";
277
+ attemptNumber: number;
278
+ reason: string;
279
+ } | {
280
+ type: "best-plan-selected";
281
+ bestAttemptNumber: number;
282
+ totalCost: number;
283
+ flipPattern: number;
284
+ joinStates: {
285
+ join: string;
286
+ type: "flipped" | "semi" | "unflippable";
287
+ }[];
288
+ } | {
289
+ type: "node-constraint";
290
+ attemptNumber?: number | undefined;
291
+ nodeType: "connection" | "fan-in" | "fan-out" | "filter" | "join" | "terminus";
292
+ node: string;
293
+ branchPattern: number[];
294
+ constraint?: Record<string, unknown> | null | undefined;
295
+ from: string;
269
296
  } | {
270
297
  type: "connection-costs";
271
298
  attemptNumber: number;
@@ -291,12 +318,6 @@ export declare const inspectDownBodySchema: v.UnionType<[v.ObjectType<Omit<{
291
318
  limit?: number | undefined;
292
319
  }>;
293
320
  }[];
294
- } | {
295
- type: "connection-selected";
296
- attemptNumber: number;
297
- connection: string;
298
- cost: number;
299
- isRoot: boolean;
300
321
  } | {
301
322
  type: "constraints-propagated";
302
323
  attemptNumber: number;
@@ -321,19 +342,6 @@ export declare const inspectDownBodySchema: v.UnionType<[v.ObjectType<Omit<{
321
342
  join: string;
322
343
  type: "flipped" | "semi" | "unflippable";
323
344
  }[];
324
- } | {
325
- type: "plan-failed";
326
- attemptNumber: number;
327
- reason: string;
328
- } | {
329
- type: "best-plan-selected";
330
- bestAttemptNumber: number;
331
- totalCost: number;
332
- flipPattern: number;
333
- joinStates: {
334
- join: string;
335
- type: "flipped" | "semi" | "unflippable";
336
- }[];
337
345
  } | {
338
346
  type: "node-cost";
339
347
  attemptNumber?: number | undefined;
@@ -352,14 +360,6 @@ export declare const inspectDownBodySchema: v.UnionType<[v.ObjectType<Omit<{
352
360
  filters?: import("./ast.ts").Condition | undefined;
353
361
  ordering?: readonly (readonly [string, "asc" | "desc"])[] | undefined;
354
362
  joinType?: "flipped" | "semi" | "unflippable" | undefined;
355
- } | {
356
- type: "node-constraint";
357
- attemptNumber?: number | undefined;
358
- nodeType: "connection" | "fan-in" | "fan-out" | "filter" | "join" | "terminus";
359
- node: string;
360
- branchPattern: number[];
361
- constraint?: Record<string, unknown> | null | undefined;
362
- from: string;
363
363
  })[]>;
364
364
  }, undefined>;
365
365
  }, undefined>, v.ObjectType<Omit<{
@@ -429,6 +429,33 @@ export declare const inspectDownMessageSchema: v.TupleType<[v.Type<"inspect">, v
429
429
  type: "attempt-start";
430
430
  attemptNumber: number;
431
431
  totalAttempts: number;
432
+ } | {
433
+ type: "connection-selected";
434
+ attemptNumber: number;
435
+ connection: string;
436
+ cost: number;
437
+ isRoot: boolean;
438
+ } | {
439
+ type: "plan-failed";
440
+ attemptNumber: number;
441
+ reason: string;
442
+ } | {
443
+ type: "best-plan-selected";
444
+ bestAttemptNumber: number;
445
+ totalCost: number;
446
+ flipPattern: number;
447
+ joinStates: {
448
+ join: string;
449
+ type: "flipped" | "semi" | "unflippable";
450
+ }[];
451
+ } | {
452
+ type: "node-constraint";
453
+ attemptNumber?: number | undefined;
454
+ nodeType: "connection" | "fan-in" | "fan-out" | "filter" | "join" | "terminus";
455
+ node: string;
456
+ branchPattern: number[];
457
+ constraint?: Record<string, unknown> | null | undefined;
458
+ from: string;
432
459
  } | {
433
460
  type: "connection-costs";
434
461
  attemptNumber: number;
@@ -454,12 +481,6 @@ export declare const inspectDownMessageSchema: v.TupleType<[v.Type<"inspect">, v
454
481
  limit?: number | undefined;
455
482
  }>;
456
483
  }[];
457
- } | {
458
- type: "connection-selected";
459
- attemptNumber: number;
460
- connection: string;
461
- cost: number;
462
- isRoot: boolean;
463
484
  } | {
464
485
  type: "constraints-propagated";
465
486
  attemptNumber: number;
@@ -484,19 +505,6 @@ export declare const inspectDownMessageSchema: v.TupleType<[v.Type<"inspect">, v
484
505
  join: string;
485
506
  type: "flipped" | "semi" | "unflippable";
486
507
  }[];
487
- } | {
488
- type: "plan-failed";
489
- attemptNumber: number;
490
- reason: string;
491
- } | {
492
- type: "best-plan-selected";
493
- bestAttemptNumber: number;
494
- totalCost: number;
495
- flipPattern: number;
496
- joinStates: {
497
- join: string;
498
- type: "flipped" | "semi" | "unflippable";
499
- }[];
500
508
  } | {
501
509
  type: "node-cost";
502
510
  attemptNumber?: number | undefined;
@@ -515,14 +523,6 @@ export declare const inspectDownMessageSchema: v.TupleType<[v.Type<"inspect">, v
515
523
  filters?: import("./ast.ts").Condition | undefined;
516
524
  ordering?: readonly (readonly [string, "asc" | "desc"])[] | undefined;
517
525
  joinType?: "flipped" | "semi" | "unflippable" | undefined;
518
- } | {
519
- type: "node-constraint";
520
- attemptNumber?: number | undefined;
521
- nodeType: "connection" | "fan-in" | "fan-out" | "filter" | "join" | "terminus";
522
- node: string;
523
- branchPattern: number[];
524
- constraint?: Record<string, unknown> | null | undefined;
525
- from: string;
526
526
  })[]>;
527
527
  }, undefined>;
528
528
  }, undefined>, v.ObjectType<Omit<{
@@ -1 +1 @@
1
- {"version":3,"file":"builder.d.ts","sourceRoot":"","sources":["../../../../../zql/src/builder/builder.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"builder.d.ts","sourceRoot":"","sources":["../../../../../zql/src/builder/builder.ts"],"names":[],"mappings":"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"}
@@ -8,9 +8,11 @@ import { Exists } from "../ivm/exists.js";
8
8
  import { FanIn } from "../ivm/fan-in.js";
9
9
  import { FanOut } from "../ivm/fan-out.js";
10
10
  import { Filter } from "../ivm/filter.js";
11
+ import { JoinIndex } from "../ivm/join-index.js";
11
12
  import { createPredicate, transformFilters } from "./filter.js";
12
13
  import { TakeGate } from "../ivm/take-gate.js";
13
14
  import { FlippedJoin } from "../ivm/flipped-join.js";
15
+ import { JoinIndexTap } from "../ivm/join-index-tap.js";
14
16
  import { Join } from "../ivm/join.js";
15
17
  import { Skip } from "../ivm/skip.js";
16
18
  import { UnionFanIn } from "../ivm/union-fan-in.js";
@@ -160,14 +162,22 @@ function buildPipelineInternal(ast, delegate, queryID, name, partitionKey, isNon
160
162
  delegate.addEdge(end, takeGate);
161
163
  end = delegate.decorateInput(takeGate, takeGateName);
162
164
  }
163
- for (const csqCondition of csqConditions) if (!csqCondition.flip) end = applyCorrelatedSubQuery({
164
- ...csqCondition.related,
165
- subquery: {
166
- ...csqCondition.related.subquery,
167
- limit: csqCondition.related.system === "permissions" ? PERMISSIONS_EXISTS_LIMIT : EXISTS_LIMIT
168
- }
169
- }, delegate, queryID, end, name, true, void 0, takeGate);
170
- if (ast.where && (!fullyAppliedFilters || delegate.applyFiltersAnyway)) end = applyWhere(end, ast.where, delegate, name, partitionKey, takeGate);
165
+ const existsParentIndexes = [];
166
+ for (const csqCondition of csqConditions) if (!csqCondition.flip) {
167
+ const parentIndex = new JoinIndex(csqCondition.related.correlation.parentField, end.getSchema().primaryKey);
168
+ existsParentIndexes.push(parentIndex);
169
+ end = applyCorrelatedSubQuery({
170
+ ...csqCondition.related,
171
+ subquery: {
172
+ ...csqCondition.related.subquery,
173
+ limit: csqCondition.related.system === "permissions" ? PERMISSIONS_EXISTS_LIMIT : EXISTS_LIMIT
174
+ }
175
+ }, delegate, queryID, end, name, true, void 0, takeGate, {
176
+ op: csqCondition.op,
177
+ parentIndex
178
+ });
179
+ }
180
+ if (ast.where && (!fullyAppliedFilters || delegate.applyFiltersAnyway)) end = applyWhere(end, ast.where, delegate, name, partitionKey, takeGate, existsParentIndexes);
171
181
  if (ast.limit !== void 0) if (useCap) {
172
182
  const capName = `${name}:cap`;
173
183
  const cap = new Cap(end, delegate.createStorage(capName), ast.limit, partitionKey);
@@ -181,6 +191,12 @@ function buildPipelineInternal(ast, delegate, queryID, name, partitionKey, isNon
181
191
  takeGate?.setBoundProvider(take);
182
192
  if (takeGate) take.setTakeGate(takeGate);
183
193
  }
194
+ if (existsParentIndexes.length > 0) {
195
+ const tapName = `${name}:join-index`;
196
+ const tap = new JoinIndexTap(end, existsParentIndexes);
197
+ delegate.addEdge(end, tap);
198
+ end = delegate.decorateInput(tap, tapName);
199
+ }
184
200
  if (ast.related) {
185
201
  const byAlias = /* @__PURE__ */ new Map();
186
202
  for (const csq of ast.related) byAlias.set(csq.subquery.alias ?? "", csq);
@@ -188,11 +204,11 @@ function buildPipelineInternal(ast, delegate, queryID, name, partitionKey, isNon
188
204
  }
189
205
  return end;
190
206
  }
191
- function applyWhere(input, condition, delegate, name, parentPartitionKey, boundProvider) {
207
+ function applyWhere(input, condition, delegate, name, parentPartitionKey, boundProvider, existsParentIndexes) {
192
208
  if (!conditionIncludesFlippedSubqueryAtAnyLevel(condition)) return buildFilterPipeline(input, delegate, (filterInput) => applyFilter(filterInput, condition, delegate, name), transformFilters(condition).filters);
193
- return applyFilterWithFlips(input, condition, delegate, name, parentPartitionKey, boundProvider);
209
+ return applyFilterWithFlips(input, condition, delegate, name, parentPartitionKey, boundProvider, existsParentIndexes);
194
210
  }
195
- function applyFilterWithFlips(input, condition, delegate, name, parentPartitionKey, boundProvider) {
211
+ function applyFilterWithFlips(input, condition, delegate, name, parentPartitionKey, boundProvider, existsParentIndexes) {
196
212
  let end = input;
197
213
  assert(condition.type !== "simple", "Simple conditions cannot have flips");
198
214
  switch (condition.type) {
@@ -206,7 +222,7 @@ function applyFilterWithFlips(input, condition, delegate, name, parentPartitionK
206
222
  end = buildFilterPipeline(input, delegate, (filterInput) => applyAnd(filterInput, andCondition, delegate, name), transformFilters(andCondition).filters);
207
223
  }
208
224
  assert(withFlipped.length > 0, "Impossible to have no flips here");
209
- for (const cond of withFlipped) end = applyFilterWithFlips(end, cond, delegate, name, parentPartitionKey, boundProvider);
225
+ for (const cond of withFlipped) end = applyFilterWithFlips(end, cond, delegate, name, parentPartitionKey, boundProvider, existsParentIndexes);
210
226
  break;
211
227
  }
212
228
  case "or": {
@@ -223,7 +239,7 @@ function applyFilterWithFlips(input, condition, delegate, name, parentPartitionK
223
239
  };
224
240
  branches.push(buildFilterPipeline(end, delegate, (filterInput) => applyOr(filterInput, orCondition, delegate, name), transformFilters(orCondition).filters));
225
241
  }
226
- for (const cond of withFlipped) branches.push(applyFilterWithFlips(end, cond, delegate, name, parentPartitionKey, boundProvider));
242
+ for (const cond of withFlipped) branches.push(applyFilterWithFlips(end, cond, delegate, name, parentPartitionKey, boundProvider, existsParentIndexes));
227
243
  const ufi = new UnionFanIn(ufo, branches);
228
244
  for (const branch of branches) delegate.addEdge(branch, ufi);
229
245
  end = delegate.decorateInput(ufi, `${name}:ufi`);
@@ -232,6 +248,11 @@ function applyFilterWithFlips(input, condition, delegate, name, parentPartitionK
232
248
  case "correlatedSubquery": {
233
249
  const sq = condition.related;
234
250
  const child = buildPipelineInternal(sq.subquery, delegate, "", `${name}.${sq.subquery.alias}`, sq.correlation.childField, false);
251
+ let parentIndex;
252
+ if (existsParentIndexes) {
253
+ parentIndex = new JoinIndex(sq.correlation.parentField, end.getSchema().primaryKey, parentPartitionKey);
254
+ existsParentIndexes.push(parentIndex);
255
+ }
235
256
  const flippedJoinName = `${name}:flipped-join(${sq.subquery.alias})`;
236
257
  const flippedJoin = new FlippedJoin({
237
258
  parent: end,
@@ -243,7 +264,7 @@ function applyFilterWithFlips(input, condition, delegate, name, parentPartitionK
243
264
  system: sq.system ?? "client",
244
265
  parentPartitionKey,
245
266
  boundProvider,
246
- storage: delegate.createStorage(flippedJoinName)
267
+ parentIndex
247
268
  });
248
269
  delegate.addEdge(end, flippedJoin);
249
270
  delegate.addEdge(child, flippedJoin);
@@ -315,7 +336,7 @@ function valuePosName(left) {
315
336
  case "column": return left.name;
316
337
  }
317
338
  }
318
- function applyCorrelatedSubQuery(sq, delegate, queryID, end, name, fromCondition, parentPartitionKey, boundProvider) {
339
+ function applyCorrelatedSubQuery(sq, delegate, queryID, end, name, fromCondition, parentPartitionKey, boundProvider, exists) {
319
340
  if (sq.subquery.limit === 0 && fromCondition) return end;
320
341
  assert(sq.subquery.alias, "Subquery must have an alias");
321
342
  const child = buildPipelineInternal(sq.subquery, delegate, queryID, `${name}.${sq.subquery.alias}`, sq.correlation.childField, fromCondition);
@@ -330,7 +351,7 @@ function applyCorrelatedSubQuery(sq, delegate, queryID, end, name, fromCondition
330
351
  system: sq.system ?? "client",
331
352
  parentPartitionKey: fromCondition ? void 0 : parentPartitionKey,
332
353
  boundProvider,
333
- storage: delegate.createStorage(joinName)
354
+ exists
334
355
  });
335
356
  delegate.addEdge(end, join);
336
357
  delegate.addEdge(child, join);
@@ -1 +1 @@
1
- {"version":3,"file":"builder.js","names":[],"sources":["../../../../../zql/src/builder/builder.ts"],"sourcesContent":["import type {LogContext} from '@rocicorp/logger';\nimport {assert, unreachable} from '../../../shared/src/asserts.ts';\nimport type {JSONValue} from '../../../shared/src/json.ts';\nimport {must} from '../../../shared/src/must.ts';\nimport type {\n AST,\n ColumnReference,\n CompoundKey,\n Condition,\n Conjunction,\n CorrelatedSubquery,\n CorrelatedSubqueryCondition,\n Disjunction,\n LiteralValue,\n Ordering,\n Parameter,\n SimpleCondition,\n ValuePosition,\n} from '../../../zero-protocol/src/ast.ts';\nimport type {Row} from '../../../zero-protocol/src/data.ts';\nimport type {PrimaryKey} from '../../../zero-protocol/src/primary-key.ts';\nimport {Cap} from '../ivm/cap.ts';\nimport {Exists} from '../ivm/exists.ts';\nimport {FanIn} from '../ivm/fan-in.ts';\nimport {FanOut} from '../ivm/fan-out.ts';\nimport {\n buildFilterPipeline,\n type FilterInput,\n} from '../ivm/filter-operators.ts';\nimport {Filter} from '../ivm/filter.ts';\nimport {FlippedJoin} from '../ivm/flipped-join.ts';\nimport {Join} from '../ivm/join.ts';\nimport type {Input, InputBase, Storage} from '../ivm/operator.ts';\nimport {Skip} from '../ivm/skip.ts';\nimport type {Source, SourceInput} from '../ivm/source.ts';\nimport {TakeGate, type TakeBoundProvider} from '../ivm/take-gate.ts';\nimport {Take} from '../ivm/take.ts';\nimport {UnionFanIn} from '../ivm/union-fan-in.ts';\nimport {UnionFanOut} from '../ivm/union-fan-out.ts';\nimport {planQuery, type PlanWarningSink} from '../planner/planner-builder.ts';\nimport type {ConnectionCostModel} from '../planner/planner-connection.ts';\nimport type {PlanDebugger} from '../planner/planner-debug.ts';\nimport {completeOrdering} from '../query/complete-ordering.ts';\nimport {pushDownCorrelatedPredicates} from './correlated-predicate-pushdown.ts';\nimport type {DebugDelegate} from './debug-delegate.ts';\nimport {\n createPredicate,\n transformFilters,\n type NoSubqueryCondition,\n} from './filter.ts';\n\nexport type StaticQueryParameters = {\n authData: Record<string, JSONValue>;\n preMutationRow?: Row | undefined;\n};\n\n/**\n * Interface required of caller to buildPipeline. Connects to constructed\n * pipeline to delegate environment to provide sources and storage.\n */\nexport interface BuilderDelegate {\n readonly applyFiltersAnyway?: boolean | undefined;\n debug?: DebugDelegate | undefined;\n\n /**\n * When true, allows NOT EXISTS conditions in queries.\n * Defaults to false.\n *\n * We only set this to true on the server.\n * The client-side query engine cannot support NOT EXISTS because:\n * 1. Zero only syncs a subset of data to the client\n * 2. On the client, we can't distinguish between a row not existing vs.\n * a row not being synced to the client\n * 3. NOT EXISTS requires complete knowledge of what doesn't exist\n */\n readonly enableNotExists?: boolean | undefined;\n\n /**\n * When true, does not copy a parent's conditions on correlation columns\n * into its subqueries (see {@link pushDownCorrelatedPredicates}).\n * Defaults to false.\n *\n * Only zero-cache sets this, as a kill switch.\n */\n readonly disableCorrelatedPredicatePushdown?: boolean | undefined;\n\n /**\n * When true, copies a parent's conditions into its subqueries before\n * planning instead of after, and tells the planner which conditions it\n * copied. The planner then sees when a copied condition makes a flipped\n * join cheap. Has no effect when `disableCorrelatedPredicatePushdown` is\n * true. Defaults to false.\n *\n * Only zero-cache sets this. This changes plans, and the pushdown alone\n * does not.\n */\n readonly enablePlannerAwarePushdown?: boolean | undefined;\n\n /**\n * Called once for each source needed by the AST.\n * Might be called multiple times with same tableName. It is OK to return\n * same storage instance in that case.\n */\n getSource(tableName: string): Source | undefined;\n\n /**\n * Called once for each operator that requires storage. Should return a new\n * unique storage object for each call.\n */\n createStorage(name: string): Storage;\n\n decorateInput(input: Input, name: string): Input;\n\n addEdge(source: InputBase, dest: InputBase): void;\n\n decorateFilterInput(input: FilterInput, name: string): FilterInput;\n\n decorateSourceInput(input: SourceInput, queryID: string): Input;\n\n /**\n * Receives warnings about the plan the planner chose for the query, when\n * there is a cost model to plan with. Only zero-cache sets this.\n */\n readonly planWarnings?: PlanWarningSink | undefined;\n\n /**\n * The AST is mapped on-the-wire between client and server names.\n *\n * There is no \"wire\" for zqlite tests so this function is provided\n * to allow tests to remap the AST.\n */\n mapAst?: ((ast: AST) => AST) | undefined;\n}\n\n/**\n * Builds a pipeline from an AST. Caller must provide a delegate to create source\n * and storage interfaces as necessary.\n *\n * Usage:\n *\n * ```ts\n * class MySink implements Output {\n * readonly #input: Input;\n *\n * constructor(input: Input) {\n * this.#input = input;\n * input.setOutput(this);\n * }\n *\n * push(change: Change, _: Operator) {\n * console.log(change);\n * }\n * }\n *\n * const input = buildPipeline(ast, myDelegate, hash(ast));\n * const sink = new MySink(input);\n * ```\n */\nexport function buildPipeline(\n ast: AST,\n delegate: BuilderDelegate,\n queryID: string,\n costModel?: ConnectionCostModel,\n lc?: LogContext,\n planDebugger?: PlanDebugger,\n): Input {\n ast = delegate.mapAst ? delegate.mapAst(ast) : ast;\n ast = completeOrdering(\n ast,\n tableName => must(delegate.getSource(tableName)).tableSchema.primaryKey,\n );\n\n const columnsOf = (tableName: string) =>\n must(delegate.getSource(tableName)).tableSchema.columns;\n const {planWarnings} = delegate;\n if (delegate.disableCorrelatedPredicatePushdown) {\n if (costModel) {\n ast = planQuery(\n ast,\n costModel,\n planDebugger,\n lc,\n undefined,\n planWarnings,\n );\n }\n } else if (delegate.enablePlannerAwarePushdown) {\n const pushed = new Set<SimpleCondition>();\n ast = pushDownCorrelatedPredicates(ast, columnsOf, pushed);\n if (costModel) {\n ast = planQuery(ast, costModel, planDebugger, lc, pushed, planWarnings);\n }\n } else {\n if (costModel) {\n ast = planQuery(\n ast,\n costModel,\n planDebugger,\n lc,\n undefined,\n planWarnings,\n );\n }\n // After planning, so that the planner does not read the pushed conditions\n // as selective filters.\n ast = pushDownCorrelatedPredicates(ast, columnsOf);\n }\n return buildPipelineInternal(ast, delegate, queryID, '');\n}\n\nexport function bindStaticParameters(\n ast: AST,\n staticQueryParameters: StaticQueryParameters | undefined,\n) {\n const visit = (node: AST): AST => ({\n ...node,\n where: node.where ? bindCondition(node.where) : undefined,\n related: node.related?.map(sq => ({\n ...sq,\n subquery: visit(sq.subquery),\n })),\n });\n\n function bindCondition(condition: Condition): Condition {\n if (condition.type === 'simple') {\n return {\n ...condition,\n left: bindValue(condition.left),\n right: bindValue(condition.right) as Exclude<\n ValuePosition,\n ColumnReference\n >,\n };\n }\n if (condition.type === 'correlatedSubquery') {\n return {\n ...condition,\n related: {\n ...condition.related,\n subquery: visit(condition.related.subquery),\n },\n };\n }\n\n return {\n ...condition,\n conditions: condition.conditions.map(bindCondition),\n };\n }\n\n const bindValue = (value: ValuePosition): ValuePosition => {\n if (isParameter(value)) {\n const anchor = must(\n staticQueryParameters,\n 'Static query params do not exist',\n )[value.anchor];\n const resolvedValue = resolveField(anchor, value.field);\n return {\n type: 'literal',\n value: resolvedValue as LiteralValue,\n };\n }\n return value;\n };\n\n return visit(ast);\n}\n\nfunction resolveField(\n anchor: Record<string, JSONValue> | Row | undefined,\n field: string | string[],\n): unknown {\n if (anchor === undefined) {\n return null;\n }\n\n if (Array.isArray(field)) {\n // oxlint-disable-next-line @typescript-eslint/no-explicit-any\n return field.reduce((acc, f) => (acc as any)?.[f], anchor) ?? null;\n }\n\n return anchor[field] ?? null;\n}\n\nfunction isParameter(value: ValuePosition): value is Parameter {\n return value.type === 'static';\n}\n\nconst EXISTS_LIMIT = 3;\nconst PERMISSIONS_EXISTS_LIMIT = 1;\n\n/**\n * Checks if a condition tree contains any NOT EXISTS operations.\n * Recursively checks AND/OR branches but does not recurse into nested subqueries\n * (those are checked when buildPipelineInternal processes them).\n */\nexport function assertNoNotExists(condition: Condition): void {\n switch (condition.type) {\n case 'simple':\n return;\n\n case 'correlatedSubquery':\n if (condition.op === 'NOT EXISTS') {\n throw new Error(\n 'not(exists()) is not supported on the client - see https://bugs.rocicorp.dev/issue/3438',\n );\n }\n return;\n\n case 'and':\n case 'or':\n for (const c of condition.conditions) {\n assertNoNotExists(c);\n }\n return;\n default:\n unreachable(condition);\n }\n}\n\nfunction buildPipelineInternal(\n ast: AST,\n delegate: BuilderDelegate,\n queryID: string,\n name: string,\n partitionKey?: CompoundKey,\n isNonFlippedExistsChild?: boolean,\n): Input {\n const source = delegate.getSource(ast.table);\n if (!source) {\n throw new Error(`Source not found: ${ast.table}`);\n }\n\n ast = uniquifyCorrelatedSubqueryConditionAliases(ast);\n\n if (!delegate.enableNotExists && ast.where) {\n assertNoNotExists(ast.where);\n }\n\n const csqConditions = gatherCorrelatedSubqueryQueryConditions(ast.where);\n const splitEditKeys: Set<string> = partitionKey\n ? new Set(partitionKey)\n : new Set();\n const aliases = new Set<string>();\n for (const csq of csqConditions) {\n aliases.add(csq.related.subquery.alias || '');\n for (const key of csq.related.correlation.parentField) {\n splitEditKeys.add(key);\n }\n }\n if (ast.related) {\n for (const csq of ast.related) {\n for (const key of csq.correlation.parentField) {\n splitEditKeys.add(key);\n }\n }\n }\n if (isNonFlippedExistsChild) {\n assert(ast.start === undefined, 'EXISTS subqueries must not have start');\n assert(\n ast.related === undefined,\n 'EXISTS subqueries must not have related',\n );\n }\n\n // The Cap optimization needs the source connect to be unordered, but\n // applyFilterWithFlips builds a UnionFanIn over the source whenever\n // ast.where contains a flipped subquery, and UnionFanIn requires a\n // sort on its inputs. In that case, fall back to the ordered + Take\n // path for this EXISTS child.\n const useCap =\n isNonFlippedExistsChild &&\n !(ast.where && conditionIncludesFlippedSubqueryAtAnyLevel(ast.where));\n\n const conn = source.connect(\n // exists pipelines are unordered — orderBy is ignored here.\n // Non-exists pipelines always have orderBy completed with PKs.\n useCap ? undefined : must(ast.orderBy),\n ast.where,\n splitEditKeys,\n delegate.debug,\n );\n\n let end: Input = delegate.decorateSourceInput(conn, queryID);\n end = delegate.decorateInput(end, `${name}:source(${ast.table})`);\n const {fullyAppliedFilters} = conn;\n\n if (ast.start) {\n const skip = new Skip(end, ast.start);\n delegate.addEdge(end, skip);\n end = delegate.decorateInput(skip, `${name}:skip)`);\n }\n\n let takeGate: TakeGate | undefined;\n if (ast.limit !== undefined && !useCap) {\n const takeGateName = `${name}:take-gate`;\n takeGate = new TakeGate(end);\n delegate.addEdge(end, takeGate);\n end = delegate.decorateInput(takeGate, takeGateName);\n }\n\n for (const csqCondition of csqConditions) {\n // flipped EXISTS are handled in applyWhere\n if (!csqCondition.flip) {\n end = applyCorrelatedSubQuery(\n {\n ...csqCondition.related,\n subquery: {\n ...csqCondition.related.subquery,\n limit:\n csqCondition.related.system === 'permissions'\n ? PERMISSIONS_EXISTS_LIMIT\n : EXISTS_LIMIT,\n },\n },\n delegate,\n queryID,\n end,\n name,\n true,\n undefined,\n takeGate,\n );\n }\n }\n\n if (ast.where && (!fullyAppliedFilters || delegate.applyFiltersAnyway)) {\n end = applyWhere(end, ast.where, delegate, name, partitionKey, takeGate);\n }\n\n if (ast.limit !== undefined) {\n // We end `exists` pipelines with `cap`\n // The reason is that `cap` does not care about the order of the pipeline.\n // This allows SQLite to chose the order and never end up creating temp b-trees.\n // The problem with SQLite creating a temp b-tree is it will incur a scan of the entire\n // result set where exists only needs the first row.\n if (useCap) {\n const capName = `${name}:cap`;\n const cap = new Cap(\n end,\n delegate.createStorage(capName),\n ast.limit,\n partitionKey,\n );\n delegate.addEdge(end, cap);\n end = delegate.decorateInput(cap, capName);\n } else {\n const takeName = `${name}:take`;\n const take = new Take(\n end,\n delegate.createStorage(takeName),\n ast.limit,\n partitionKey,\n );\n delegate.addEdge(end, take);\n end = delegate.decorateInput(take, takeName);\n takeGate?.setBoundProvider(take);\n if (takeGate) {\n take.setTakeGate(takeGate);\n }\n }\n }\n\n if (ast.related) {\n // Dedupe by alias - last one wins (LWW), like limit(5).limit(10)\n const byAlias = new Map<string, CorrelatedSubquery>();\n for (const csq of ast.related) {\n byAlias.set(csq.subquery.alias ?? '', csq);\n }\n for (const csq of byAlias.values()) {\n end = applyCorrelatedSubQuery(\n csq,\n delegate,\n queryID,\n end,\n name,\n false,\n partitionKey,\n );\n }\n }\n\n return end;\n}\n\nfunction applyWhere(\n input: Input,\n condition: Condition,\n delegate: BuilderDelegate,\n name: string,\n parentPartitionKey?: CompoundKey,\n boundProvider?: TakeBoundProvider,\n): Input {\n if (!conditionIncludesFlippedSubqueryAtAnyLevel(condition)) {\n return buildFilterPipeline(\n input,\n delegate,\n filterInput => applyFilter(filterInput, condition, delegate, name),\n transformFilters(condition).filters,\n );\n }\n\n return applyFilterWithFlips(\n input,\n condition,\n delegate,\n name,\n parentPartitionKey,\n boundProvider,\n );\n}\n\nfunction applyFilterWithFlips(\n input: Input,\n condition: Condition,\n delegate: BuilderDelegate,\n name: string,\n parentPartitionKey?: CompoundKey,\n boundProvider?: TakeBoundProvider,\n): Input {\n let end = input;\n assert(condition.type !== 'simple', 'Simple conditions cannot have flips');\n\n switch (condition.type) {\n case 'and': {\n const [withFlipped, withoutFlipped] = partitionBranches(\n condition.conditions,\n conditionIncludesFlippedSubqueryAtAnyLevel,\n );\n if (withoutFlipped.length > 0) {\n const andCondition: Conjunction = {\n type: 'and',\n conditions: withoutFlipped,\n };\n end = buildFilterPipeline(\n input,\n delegate,\n filterInput => applyAnd(filterInput, andCondition, delegate, name),\n transformFilters(andCondition).filters,\n );\n }\n assert(withFlipped.length > 0, 'Impossible to have no flips here');\n for (const cond of withFlipped) {\n end = applyFilterWithFlips(\n end,\n cond,\n delegate,\n name,\n parentPartitionKey,\n boundProvider,\n );\n }\n break;\n }\n case 'or': {\n const [withFlipped, withoutFlipped] = partitionBranches(\n condition.conditions,\n conditionIncludesFlippedSubqueryAtAnyLevel,\n );\n assert(withFlipped.length > 0, 'Impossible to have no flips here');\n\n const ufo = new UnionFanOut(end);\n delegate.addEdge(end, ufo);\n end = delegate.decorateInput(ufo, `${name}:ufo`);\n\n const branches: Input[] = [];\n if (withoutFlipped.length > 0) {\n const orCondition: Disjunction = {\n type: 'or',\n conditions: withoutFlipped,\n };\n branches.push(\n buildFilterPipeline(\n end,\n delegate,\n filterInput => applyOr(filterInput, orCondition, delegate, name),\n transformFilters(orCondition).filters,\n ),\n );\n }\n\n for (const cond of withFlipped) {\n branches.push(\n applyFilterWithFlips(\n end,\n cond,\n delegate,\n name,\n parentPartitionKey,\n boundProvider,\n ),\n );\n }\n\n const ufi = new UnionFanIn(ufo, branches);\n for (const branch of branches) {\n delegate.addEdge(branch, ufi);\n }\n end = delegate.decorateInput(ufi, `${name}:ufi`);\n\n break;\n }\n case 'correlatedSubquery': {\n const sq = condition.related;\n const child = buildPipelineInternal(\n sq.subquery,\n delegate,\n '',\n `${name}.${sq.subquery.alias}`,\n sq.correlation.childField,\n false,\n );\n const flippedJoinName = `${name}:flipped-join(${sq.subquery.alias})`;\n const flippedJoin = new FlippedJoin({\n parent: end,\n child,\n parentKey: sq.correlation.parentField,\n childKey: sq.correlation.childField,\n relationshipName: must(\n sq.subquery.alias,\n 'Subquery must have an alias',\n ),\n hidden: sq.hidden ?? false,\n system: sq.system ?? 'client',\n parentPartitionKey,\n boundProvider,\n storage: delegate.createStorage(flippedJoinName),\n });\n delegate.addEdge(end, flippedJoin);\n delegate.addEdge(child, flippedJoin);\n end = delegate.decorateInput(flippedJoin, flippedJoinName);\n break;\n }\n }\n\n return end;\n}\n\nfunction applyFilter(\n input: FilterInput,\n condition: Condition,\n delegate: BuilderDelegate,\n name: string,\n): FilterInput {\n switch (condition.type) {\n case 'and':\n return applyAnd(input, condition, delegate, name);\n case 'or':\n return applyOr(input, condition, delegate, name);\n case 'correlatedSubquery':\n return applyCorrelatedSubqueryCondition(input, condition, delegate, name);\n case 'simple':\n return applySimpleCondition(input, delegate, condition);\n }\n}\n\nfunction applyAnd(\n input: FilterInput,\n condition: Conjunction,\n delegate: BuilderDelegate,\n name: string,\n): FilterInput {\n for (const subCondition of condition.conditions) {\n input = applyFilter(input, subCondition, delegate, name);\n }\n return input;\n}\n\nexport function applyOr(\n input: FilterInput,\n condition: Disjunction,\n delegate: BuilderDelegate,\n name: string,\n): FilterInput {\n const [subqueryConditions, otherConditions] =\n groupSubqueryConditions(condition);\n // if there are no subquery conditions, no fan-in / fan-out is needed\n if (subqueryConditions.length === 0) {\n const filter = new Filter(\n input,\n createPredicate({\n type: 'or',\n conditions: otherConditions,\n }),\n );\n delegate.addEdge(input, filter);\n return filter;\n }\n\n const fanOut = new FanOut(input);\n delegate.addEdge(input, fanOut);\n const branches = subqueryConditions.map(subCondition =>\n applyFilter(fanOut, subCondition, delegate, name),\n );\n if (otherConditions.length > 0) {\n const filter = new Filter(\n fanOut,\n createPredicate({\n type: 'or',\n conditions: otherConditions,\n }),\n );\n delegate.addEdge(fanOut, filter);\n branches.push(filter);\n }\n const ret = new FanIn(fanOut, branches);\n for (const branch of branches) {\n delegate.addEdge(branch, ret);\n }\n fanOut.setFanIn(ret);\n return ret;\n}\n\nexport function groupSubqueryConditions(condition: Disjunction) {\n const partitioned: [\n subqueryConditions: Condition[],\n otherConditions: NoSubqueryCondition[],\n ] = [[], []];\n for (const subCondition of condition.conditions) {\n if (isNotAndDoesNotContainSubquery(subCondition)) {\n partitioned[1].push(subCondition);\n } else {\n partitioned[0].push(subCondition);\n }\n }\n return partitioned;\n}\n\nexport function isNotAndDoesNotContainSubquery(\n condition: Condition,\n): condition is NoSubqueryCondition {\n if (condition.type === 'correlatedSubquery') {\n return false;\n }\n if (condition.type === 'simple') {\n return true;\n }\n return condition.conditions.every(isNotAndDoesNotContainSubquery);\n}\n\nfunction applySimpleCondition(\n input: FilterInput,\n delegate: BuilderDelegate,\n condition: SimpleCondition,\n): FilterInput {\n const filter = new Filter(input, createPredicate(condition));\n delegate.decorateFilterInput(\n filter,\n `${valuePosName(condition.left)}:${condition.op}:${valuePosName(condition.right)}`,\n );\n delegate.addEdge(input, filter);\n return filter;\n}\n\nfunction valuePosName(left: ValuePosition) {\n switch (left.type) {\n case 'static':\n return left.field;\n case 'literal':\n return left.value;\n case 'column':\n return left.name;\n }\n}\n\nfunction applyCorrelatedSubQuery(\n sq: CorrelatedSubquery,\n delegate: BuilderDelegate,\n queryID: string,\n end: Input,\n name: string,\n fromCondition: boolean,\n parentPartitionKey?: CompoundKey,\n boundProvider?: TakeBoundProvider,\n) {\n // TODO: we only omit the join if the CSQ if from a condition since\n // we want to create an empty array for `related` fields that are `limit(0)`\n if (sq.subquery.limit === 0 && fromCondition) {\n return end;\n }\n\n assert(sq.subquery.alias, 'Subquery must have an alias');\n const child = buildPipelineInternal(\n sq.subquery,\n delegate,\n queryID,\n `${name}.${sq.subquery.alias}`,\n sq.correlation.childField,\n fromCondition,\n );\n\n const joinName = `${name}:join(${sq.subquery.alias})`;\n const join = new Join({\n parent: end,\n child,\n parentKey: sq.correlation.parentField,\n childKey: sq.correlation.childField,\n relationshipName: sq.subquery.alias,\n hidden: sq.hidden ?? false,\n system: sq.system ?? 'client',\n parentPartitionKey: fromCondition ? undefined : parentPartitionKey,\n boundProvider,\n storage: delegate.createStorage(joinName),\n });\n delegate.addEdge(end, join);\n delegate.addEdge(child, join);\n return delegate.decorateInput(join, joinName);\n}\n\nfunction applyCorrelatedSubqueryCondition(\n input: FilterInput,\n condition: CorrelatedSubqueryCondition,\n delegate: BuilderDelegate,\n name: string,\n): FilterInput {\n assert(\n condition.op === 'EXISTS' || condition.op === 'NOT EXISTS',\n 'Expected EXISTS or NOT EXISTS operator',\n );\n if (condition.related.subquery.limit === 0) {\n if (condition.op === 'EXISTS') {\n const filter = new Filter(input, () => false);\n delegate.addEdge(input, filter);\n return filter;\n }\n const filter = new Filter(input, () => true);\n delegate.addEdge(input, filter);\n return filter;\n }\n const existsName = `${name}:exists(${condition.related.subquery.alias})`;\n const exists = new Exists(\n input,\n must(condition.related.subquery.alias),\n condition.related.correlation.parentField,\n condition.op,\n );\n delegate.addEdge(input, exists);\n return delegate.decorateFilterInput(exists, existsName);\n}\n\nfunction gatherCorrelatedSubqueryQueryConditions(\n condition: Condition | undefined,\n) {\n const csqs: CorrelatedSubqueryCondition[] = [];\n const gather = (condition: Condition) => {\n if (condition.type === 'correlatedSubquery') {\n csqs.push(condition);\n return;\n }\n if (condition.type === 'and' || condition.type === 'or') {\n for (const c of condition.conditions) {\n gather(c);\n }\n return;\n }\n };\n if (condition) {\n gather(condition);\n }\n return csqs;\n}\n\nexport function assertOrderingIncludesPK(\n ordering: Ordering,\n pk: PrimaryKey,\n): void {\n // oxlint-disable-next-line unicorn/prefer-set-has -- Array is more appropriate here for small collections\n const orderingFields = ordering.map(([field]) => field);\n const missingFields = pk.filter(pkField => !orderingFields.includes(pkField));\n\n if (missingFields.length > 0) {\n throw new Error(\n `Ordering must include all primary key fields. Missing: ${missingFields.join(\n ', ',\n )}. ZQL automatically appends primary key fields to the ordering if they are missing \n so a common cause of this error is a casing mismatch between Postgres and ZQL.\n E.g., \"userid\" vs \"userID\".\n You may want to add double-quotes around your Postgres column names to prevent Postgres from lower-casing them:\n https://www.postgresql.org/docs/current/sql-syntax-lexical.htm`,\n );\n }\n}\n\nfunction uniquifyCorrelatedSubqueryConditionAliases(ast: AST): AST {\n if (!ast.where) {\n return ast;\n }\n const {where} = ast;\n if (where.type !== 'and' && where.type !== 'or') {\n return ast;\n }\n\n let count = 0;\n const uniquifyCorrelatedSubquery = (csqc: CorrelatedSubqueryCondition) => ({\n ...csqc,\n related: {\n ...csqc.related,\n subquery: {\n ...csqc.related.subquery,\n alias: (csqc.related.subquery.alias ?? '') + '_' + count++,\n },\n },\n });\n\n const uniquify = (cond: Condition): Condition => {\n if (cond.type === 'simple') {\n return cond;\n } else if (cond.type === 'correlatedSubquery') {\n return uniquifyCorrelatedSubquery(cond);\n }\n const conditions = [];\n for (const c of cond.conditions) {\n conditions.push(uniquify(c));\n }\n return {\n type: cond.type,\n conditions,\n };\n };\n\n const result = {\n ...ast,\n where: uniquify(where),\n };\n return result;\n}\n\nexport function conditionIncludesFlippedSubqueryAtAnyLevel(\n cond: Condition,\n): boolean {\n if (cond.type === 'correlatedSubquery') {\n return !!cond.flip;\n }\n if (cond.type === 'and' || cond.type === 'or') {\n return cond.conditions.some(c =>\n conditionIncludesFlippedSubqueryAtAnyLevel(c),\n );\n }\n // simple conditions don't have flips\n return false;\n}\n\nexport function partitionBranches(\n conditions: readonly Condition[],\n predicate: (c: Condition) => boolean,\n) {\n const matched: Condition[] = [];\n const notMatched: Condition[] = [];\n for (const c of conditions) {\n if (predicate(c)) {\n matched.push(c);\n } else {\n notMatched.push(c);\n }\n }\n return [matched, notMatched] as const;\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"builder.js","names":[],"sources":["../../../../../zql/src/builder/builder.ts"],"sourcesContent":["import type {LogContext} from '@rocicorp/logger';\nimport {assert, unreachable} from '../../../shared/src/asserts.ts';\nimport type {JSONValue} from '../../../shared/src/json.ts';\nimport {must} from '../../../shared/src/must.ts';\nimport type {\n AST,\n ColumnReference,\n CompoundKey,\n Condition,\n Conjunction,\n CorrelatedSubquery,\n CorrelatedSubqueryCondition,\n Disjunction,\n LiteralValue,\n Ordering,\n Parameter,\n SimpleCondition,\n ValuePosition,\n} from '../../../zero-protocol/src/ast.ts';\nimport type {Row} from '../../../zero-protocol/src/data.ts';\nimport type {PrimaryKey} from '../../../zero-protocol/src/primary-key.ts';\nimport {Cap} from '../ivm/cap.ts';\nimport {Exists} from '../ivm/exists.ts';\nimport {FanIn} from '../ivm/fan-in.ts';\nimport {FanOut} from '../ivm/fan-out.ts';\nimport {\n buildFilterPipeline,\n type FilterInput,\n} from '../ivm/filter-operators.ts';\nimport {Filter} from '../ivm/filter.ts';\nimport {FlippedJoin} from '../ivm/flipped-join.ts';\nimport {JoinIndexTap} from '../ivm/join-index-tap.ts';\nimport {JoinIndex} from '../ivm/join-index.ts';\nimport {Join, type ExistsJoin} from '../ivm/join.ts';\nimport type {Input, InputBase, Storage} from '../ivm/operator.ts';\nimport {Skip} from '../ivm/skip.ts';\nimport type {Source, SourceInput} from '../ivm/source.ts';\nimport {TakeGate, type TakeBoundProvider} from '../ivm/take-gate.ts';\nimport {Take} from '../ivm/take.ts';\nimport {UnionFanIn} from '../ivm/union-fan-in.ts';\nimport {UnionFanOut} from '../ivm/union-fan-out.ts';\nimport {planQuery, type PlanWarningSink} from '../planner/planner-builder.ts';\nimport type {ConnectionCostModel} from '../planner/planner-connection.ts';\nimport type {PlanDebugger} from '../planner/planner-debug.ts';\nimport {completeOrdering} from '../query/complete-ordering.ts';\nimport {pushDownCorrelatedPredicates} from './correlated-predicate-pushdown.ts';\nimport type {DebugDelegate} from './debug-delegate.ts';\nimport {\n createPredicate,\n transformFilters,\n type NoSubqueryCondition,\n} from './filter.ts';\n\nexport type StaticQueryParameters = {\n authData: Record<string, JSONValue>;\n preMutationRow?: Row | undefined;\n};\n\n/**\n * Interface required of caller to buildPipeline. Connects to constructed\n * pipeline to delegate environment to provide sources and storage.\n */\nexport interface BuilderDelegate {\n readonly applyFiltersAnyway?: boolean | undefined;\n debug?: DebugDelegate | undefined;\n\n /**\n * When true, allows NOT EXISTS conditions in queries.\n * Defaults to false.\n *\n * We only set this to true on the server.\n * The client-side query engine cannot support NOT EXISTS because:\n * 1. Zero only syncs a subset of data to the client\n * 2. On the client, we can't distinguish between a row not existing vs.\n * a row not being synced to the client\n * 3. NOT EXISTS requires complete knowledge of what doesn't exist\n */\n readonly enableNotExists?: boolean | undefined;\n\n /**\n * When true, does not copy a parent's conditions on correlation columns\n * into its subqueries (see {@link pushDownCorrelatedPredicates}).\n * Defaults to false.\n *\n * Only zero-cache sets this, as a kill switch.\n */\n readonly disableCorrelatedPredicatePushdown?: boolean | undefined;\n\n /**\n * When true, copies a parent's conditions into its subqueries before\n * planning instead of after, and tells the planner which conditions it\n * copied. The planner then sees when a copied condition makes a flipped\n * join cheap. Has no effect when `disableCorrelatedPredicatePushdown` is\n * true. Defaults to false.\n *\n * Only zero-cache sets this. This changes plans, and the pushdown alone\n * does not.\n */\n readonly enablePlannerAwarePushdown?: boolean | undefined;\n\n /**\n * Called once for each source needed by the AST.\n * Might be called multiple times with same tableName. It is OK to return\n * same storage instance in that case.\n */\n getSource(tableName: string): Source | undefined;\n\n /**\n * Called once for each operator that requires storage. Should return a new\n * unique storage object for each call.\n */\n createStorage(name: string): Storage;\n\n decorateInput(input: Input, name: string): Input;\n\n addEdge(source: InputBase, dest: InputBase): void;\n\n decorateFilterInput(input: FilterInput, name: string): FilterInput;\n\n decorateSourceInput(input: SourceInput, queryID: string): Input;\n\n /**\n * Receives warnings about the plan the planner chose for the query, when\n * there is a cost model to plan with. Only zero-cache sets this.\n */\n readonly planWarnings?: PlanWarningSink | undefined;\n\n /**\n * The AST is mapped on-the-wire between client and server names.\n *\n * There is no \"wire\" for zqlite tests so this function is provided\n * to allow tests to remap the AST.\n */\n mapAst?: ((ast: AST) => AST) | undefined;\n}\n\n/**\n * Builds a pipeline from an AST. Caller must provide a delegate to create source\n * and storage interfaces as necessary.\n *\n * Usage:\n *\n * ```ts\n * class MySink implements Output {\n * readonly #input: Input;\n *\n * constructor(input: Input) {\n * this.#input = input;\n * input.setOutput(this);\n * }\n *\n * push(change: Change, _: Operator) {\n * console.log(change);\n * }\n * }\n *\n * const input = buildPipeline(ast, myDelegate, hash(ast));\n * const sink = new MySink(input);\n * ```\n */\nexport function buildPipeline(\n ast: AST,\n delegate: BuilderDelegate,\n queryID: string,\n costModel?: ConnectionCostModel,\n lc?: LogContext,\n planDebugger?: PlanDebugger,\n): Input {\n ast = delegate.mapAst ? delegate.mapAst(ast) : ast;\n ast = completeOrdering(\n ast,\n tableName => must(delegate.getSource(tableName)).tableSchema.primaryKey,\n );\n\n const columnsOf = (tableName: string) =>\n must(delegate.getSource(tableName)).tableSchema.columns;\n const {planWarnings} = delegate;\n if (delegate.disableCorrelatedPredicatePushdown) {\n if (costModel) {\n ast = planQuery(\n ast,\n costModel,\n planDebugger,\n lc,\n undefined,\n planWarnings,\n );\n }\n } else if (delegate.enablePlannerAwarePushdown) {\n const pushed = new Set<SimpleCondition>();\n ast = pushDownCorrelatedPredicates(ast, columnsOf, pushed);\n if (costModel) {\n ast = planQuery(ast, costModel, planDebugger, lc, pushed, planWarnings);\n }\n } else {\n if (costModel) {\n ast = planQuery(\n ast,\n costModel,\n planDebugger,\n lc,\n undefined,\n planWarnings,\n );\n }\n // After planning, so that the planner does not read the pushed conditions\n // as selective filters.\n ast = pushDownCorrelatedPredicates(ast, columnsOf);\n }\n return buildPipelineInternal(ast, delegate, queryID, '');\n}\n\nexport function bindStaticParameters(\n ast: AST,\n staticQueryParameters: StaticQueryParameters | undefined,\n) {\n const visit = (node: AST): AST => ({\n ...node,\n where: node.where ? bindCondition(node.where) : undefined,\n related: node.related?.map(sq => ({\n ...sq,\n subquery: visit(sq.subquery),\n })),\n });\n\n function bindCondition(condition: Condition): Condition {\n if (condition.type === 'simple') {\n return {\n ...condition,\n left: bindValue(condition.left),\n right: bindValue(condition.right) as Exclude<\n ValuePosition,\n ColumnReference\n >,\n };\n }\n if (condition.type === 'correlatedSubquery') {\n return {\n ...condition,\n related: {\n ...condition.related,\n subquery: visit(condition.related.subquery),\n },\n };\n }\n\n return {\n ...condition,\n conditions: condition.conditions.map(bindCondition),\n };\n }\n\n const bindValue = (value: ValuePosition): ValuePosition => {\n if (isParameter(value)) {\n const anchor = must(\n staticQueryParameters,\n 'Static query params do not exist',\n )[value.anchor];\n const resolvedValue = resolveField(anchor, value.field);\n return {\n type: 'literal',\n value: resolvedValue as LiteralValue,\n };\n }\n return value;\n };\n\n return visit(ast);\n}\n\nfunction resolveField(\n anchor: Record<string, JSONValue> | Row | undefined,\n field: string | string[],\n): unknown {\n if (anchor === undefined) {\n return null;\n }\n\n if (Array.isArray(field)) {\n // oxlint-disable-next-line @typescript-eslint/no-explicit-any\n return field.reduce((acc, f) => (acc as any)?.[f], anchor) ?? null;\n }\n\n return anchor[field] ?? null;\n}\n\nfunction isParameter(value: ValuePosition): value is Parameter {\n return value.type === 'static';\n}\n\nconst EXISTS_LIMIT = 3;\nconst PERMISSIONS_EXISTS_LIMIT = 1;\n\n/**\n * Checks if a condition tree contains any NOT EXISTS operations.\n * Recursively checks AND/OR branches but does not recurse into nested subqueries\n * (those are checked when buildPipelineInternal processes them).\n */\nexport function assertNoNotExists(condition: Condition): void {\n switch (condition.type) {\n case 'simple':\n return;\n\n case 'correlatedSubquery':\n if (condition.op === 'NOT EXISTS') {\n throw new Error(\n 'not(exists()) is not supported on the client - see https://bugs.rocicorp.dev/issue/3438',\n );\n }\n return;\n\n case 'and':\n case 'or':\n for (const c of condition.conditions) {\n assertNoNotExists(c);\n }\n return;\n default:\n unreachable(condition);\n }\n}\n\nfunction buildPipelineInternal(\n ast: AST,\n delegate: BuilderDelegate,\n queryID: string,\n name: string,\n partitionKey?: CompoundKey,\n isNonFlippedExistsChild?: boolean,\n): Input {\n const source = delegate.getSource(ast.table);\n if (!source) {\n throw new Error(`Source not found: ${ast.table}`);\n }\n\n ast = uniquifyCorrelatedSubqueryConditionAliases(ast);\n\n if (!delegate.enableNotExists && ast.where) {\n assertNoNotExists(ast.where);\n }\n\n const csqConditions = gatherCorrelatedSubqueryQueryConditions(ast.where);\n const splitEditKeys: Set<string> = partitionKey\n ? new Set(partitionKey)\n : new Set();\n const aliases = new Set<string>();\n for (const csq of csqConditions) {\n aliases.add(csq.related.subquery.alias || '');\n for (const key of csq.related.correlation.parentField) {\n splitEditKeys.add(key);\n }\n }\n if (ast.related) {\n for (const csq of ast.related) {\n for (const key of csq.correlation.parentField) {\n splitEditKeys.add(key);\n }\n }\n }\n if (isNonFlippedExistsChild) {\n assert(ast.start === undefined, 'EXISTS subqueries must not have start');\n assert(\n ast.related === undefined,\n 'EXISTS subqueries must not have related',\n );\n }\n\n // The Cap optimization needs the source connect to be unordered, but\n // applyFilterWithFlips builds a UnionFanIn over the source whenever\n // ast.where contains a flipped subquery, and UnionFanIn requires a\n // sort on its inputs. In that case, fall back to the ordered + Take\n // path for this EXISTS child.\n const useCap =\n isNonFlippedExistsChild &&\n !(ast.where && conditionIncludesFlippedSubqueryAtAnyLevel(ast.where));\n\n const conn = source.connect(\n // exists pipelines are unordered — orderBy is ignored here.\n // Non-exists pipelines always have orderBy completed with PKs.\n useCap ? undefined : must(ast.orderBy),\n ast.where,\n splitEditKeys,\n delegate.debug,\n );\n\n let end: Input = delegate.decorateSourceInput(conn, queryID);\n end = delegate.decorateInput(end, `${name}:source(${ast.table})`);\n const {fullyAppliedFilters} = conn;\n\n if (ast.start) {\n const skip = new Skip(end, ast.start);\n delegate.addEdge(end, skip);\n end = delegate.decorateInput(skip, `${name}:skip)`);\n }\n\n let takeGate: TakeGate | undefined;\n if (ast.limit !== undefined && !useCap) {\n const takeGateName = `${name}:take-gate`;\n takeGate = new TakeGate(end);\n delegate.addEdge(end, takeGate);\n end = delegate.decorateInput(takeGate, takeGateName);\n }\n\n // The parent indexes of the EXISTS joins below. They hold the rows this\n // pipeline emits, which the JoinIndexTap after the limit maintains.\n const existsParentIndexes: JoinIndex[] = [];\n for (const csqCondition of csqConditions) {\n // flipped EXISTS are handled in applyWhere\n if (!csqCondition.flip) {\n const parentIndex = new JoinIndex(\n csqCondition.related.correlation.parentField,\n end.getSchema().primaryKey,\n );\n existsParentIndexes.push(parentIndex);\n end = applyCorrelatedSubQuery(\n {\n ...csqCondition.related,\n subquery: {\n ...csqCondition.related.subquery,\n limit:\n csqCondition.related.system === 'permissions'\n ? PERMISSIONS_EXISTS_LIMIT\n : EXISTS_LIMIT,\n },\n },\n delegate,\n queryID,\n end,\n name,\n true,\n undefined,\n takeGate,\n {op: csqCondition.op, parentIndex},\n );\n }\n }\n\n if (ast.where && (!fullyAppliedFilters || delegate.applyFiltersAnyway)) {\n end = applyWhere(\n end,\n ast.where,\n delegate,\n name,\n partitionKey,\n takeGate,\n existsParentIndexes,\n );\n }\n\n if (ast.limit !== undefined) {\n // We end `exists` pipelines with `cap`\n // The reason is that `cap` does not care about the order of the pipeline.\n // This allows SQLite to chose the order and never end up creating temp b-trees.\n // The problem with SQLite creating a temp b-tree is it will incur a scan of the entire\n // result set where exists only needs the first row.\n if (useCap) {\n const capName = `${name}:cap`;\n const cap = new Cap(\n end,\n delegate.createStorage(capName),\n ast.limit,\n partitionKey,\n );\n delegate.addEdge(end, cap);\n end = delegate.decorateInput(cap, capName);\n } else {\n const takeName = `${name}:take`;\n const take = new Take(\n end,\n delegate.createStorage(takeName),\n ast.limit,\n partitionKey,\n );\n delegate.addEdge(end, take);\n end = delegate.decorateInput(take, takeName);\n takeGate?.setBoundProvider(take);\n if (takeGate) {\n take.setTakeGate(takeGate);\n }\n }\n }\n\n if (existsParentIndexes.length > 0) {\n const tapName = `${name}:join-index`;\n const tap = new JoinIndexTap(end, existsParentIndexes);\n delegate.addEdge(end, tap);\n end = delegate.decorateInput(tap, tapName);\n }\n\n if (ast.related) {\n // Dedupe by alias - last one wins (LWW), like limit(5).limit(10)\n const byAlias = new Map<string, CorrelatedSubquery>();\n for (const csq of ast.related) {\n byAlias.set(csq.subquery.alias ?? '', csq);\n }\n for (const csq of byAlias.values()) {\n end = applyCorrelatedSubQuery(\n csq,\n delegate,\n queryID,\n end,\n name,\n false,\n partitionKey,\n );\n }\n }\n\n return end;\n}\n\nfunction applyWhere(\n input: Input,\n condition: Condition,\n delegate: BuilderDelegate,\n name: string,\n parentPartitionKey?: CompoundKey,\n boundProvider?: TakeBoundProvider,\n existsParentIndexes?: JoinIndex[],\n): Input {\n if (!conditionIncludesFlippedSubqueryAtAnyLevel(condition)) {\n return buildFilterPipeline(\n input,\n delegate,\n filterInput => applyFilter(filterInput, condition, delegate, name),\n transformFilters(condition).filters,\n );\n }\n\n return applyFilterWithFlips(\n input,\n condition,\n delegate,\n name,\n parentPartitionKey,\n boundProvider,\n existsParentIndexes,\n );\n}\n\nfunction applyFilterWithFlips(\n input: Input,\n condition: Condition,\n delegate: BuilderDelegate,\n name: string,\n parentPartitionKey?: CompoundKey,\n boundProvider?: TakeBoundProvider,\n existsParentIndexes?: JoinIndex[],\n): Input {\n let end = input;\n assert(condition.type !== 'simple', 'Simple conditions cannot have flips');\n\n switch (condition.type) {\n case 'and': {\n const [withFlipped, withoutFlipped] = partitionBranches(\n condition.conditions,\n conditionIncludesFlippedSubqueryAtAnyLevel,\n );\n if (withoutFlipped.length > 0) {\n const andCondition: Conjunction = {\n type: 'and',\n conditions: withoutFlipped,\n };\n end = buildFilterPipeline(\n input,\n delegate,\n filterInput => applyAnd(filterInput, andCondition, delegate, name),\n transformFilters(andCondition).filters,\n );\n }\n assert(withFlipped.length > 0, 'Impossible to have no flips here');\n for (const cond of withFlipped) {\n end = applyFilterWithFlips(\n end,\n cond,\n delegate,\n name,\n parentPartitionKey,\n boundProvider,\n existsParentIndexes,\n );\n }\n break;\n }\n case 'or': {\n const [withFlipped, withoutFlipped] = partitionBranches(\n condition.conditions,\n conditionIncludesFlippedSubqueryAtAnyLevel,\n );\n assert(withFlipped.length > 0, 'Impossible to have no flips here');\n\n const ufo = new UnionFanOut(end);\n delegate.addEdge(end, ufo);\n end = delegate.decorateInput(ufo, `${name}:ufo`);\n\n const branches: Input[] = [];\n if (withoutFlipped.length > 0) {\n const orCondition: Disjunction = {\n type: 'or',\n conditions: withoutFlipped,\n };\n branches.push(\n buildFilterPipeline(\n end,\n delegate,\n filterInput => applyOr(filterInput, orCondition, delegate, name),\n transformFilters(orCondition).filters,\n ),\n );\n }\n\n for (const cond of withFlipped) {\n branches.push(\n applyFilterWithFlips(\n end,\n cond,\n delegate,\n name,\n parentPartitionKey,\n boundProvider,\n existsParentIndexes,\n ),\n );\n }\n\n const ufi = new UnionFanIn(ufo, branches);\n for (const branch of branches) {\n delegate.addEdge(branch, ufi);\n }\n end = delegate.decorateInput(ufi, `${name}:ufi`);\n\n break;\n }\n case 'correlatedSubquery': {\n const sq = condition.related;\n const child = buildPipelineInternal(\n sq.subquery,\n delegate,\n '',\n `${name}.${sq.subquery.alias}`,\n sq.correlation.childField,\n false,\n );\n let parentIndex: JoinIndex | undefined;\n if (existsParentIndexes) {\n parentIndex = new JoinIndex(\n sq.correlation.parentField,\n end.getSchema().primaryKey,\n parentPartitionKey,\n );\n existsParentIndexes.push(parentIndex);\n }\n const flippedJoinName = `${name}:flipped-join(${sq.subquery.alias})`;\n const flippedJoin = new FlippedJoin({\n parent: end,\n child,\n parentKey: sq.correlation.parentField,\n childKey: sq.correlation.childField,\n relationshipName: must(\n sq.subquery.alias,\n 'Subquery must have an alias',\n ),\n hidden: sq.hidden ?? false,\n system: sq.system ?? 'client',\n parentPartitionKey,\n boundProvider,\n parentIndex,\n });\n delegate.addEdge(end, flippedJoin);\n delegate.addEdge(child, flippedJoin);\n end = delegate.decorateInput(flippedJoin, flippedJoinName);\n break;\n }\n }\n\n return end;\n}\n\nfunction applyFilter(\n input: FilterInput,\n condition: Condition,\n delegate: BuilderDelegate,\n name: string,\n): FilterInput {\n switch (condition.type) {\n case 'and':\n return applyAnd(input, condition, delegate, name);\n case 'or':\n return applyOr(input, condition, delegate, name);\n case 'correlatedSubquery':\n return applyCorrelatedSubqueryCondition(input, condition, delegate, name);\n case 'simple':\n return applySimpleCondition(input, delegate, condition);\n }\n}\n\nfunction applyAnd(\n input: FilterInput,\n condition: Conjunction,\n delegate: BuilderDelegate,\n name: string,\n): FilterInput {\n for (const subCondition of condition.conditions) {\n input = applyFilter(input, subCondition, delegate, name);\n }\n return input;\n}\n\nexport function applyOr(\n input: FilterInput,\n condition: Disjunction,\n delegate: BuilderDelegate,\n name: string,\n): FilterInput {\n const [subqueryConditions, otherConditions] =\n groupSubqueryConditions(condition);\n // if there are no subquery conditions, no fan-in / fan-out is needed\n if (subqueryConditions.length === 0) {\n const filter = new Filter(\n input,\n createPredicate({\n type: 'or',\n conditions: otherConditions,\n }),\n );\n delegate.addEdge(input, filter);\n return filter;\n }\n\n const fanOut = new FanOut(input);\n delegate.addEdge(input, fanOut);\n const branches = subqueryConditions.map(subCondition =>\n applyFilter(fanOut, subCondition, delegate, name),\n );\n if (otherConditions.length > 0) {\n const filter = new Filter(\n fanOut,\n createPredicate({\n type: 'or',\n conditions: otherConditions,\n }),\n );\n delegate.addEdge(fanOut, filter);\n branches.push(filter);\n }\n const ret = new FanIn(fanOut, branches);\n for (const branch of branches) {\n delegate.addEdge(branch, ret);\n }\n fanOut.setFanIn(ret);\n return ret;\n}\n\nexport function groupSubqueryConditions(condition: Disjunction) {\n const partitioned: [\n subqueryConditions: Condition[],\n otherConditions: NoSubqueryCondition[],\n ] = [[], []];\n for (const subCondition of condition.conditions) {\n if (isNotAndDoesNotContainSubquery(subCondition)) {\n partitioned[1].push(subCondition);\n } else {\n partitioned[0].push(subCondition);\n }\n }\n return partitioned;\n}\n\nexport function isNotAndDoesNotContainSubquery(\n condition: Condition,\n): condition is NoSubqueryCondition {\n if (condition.type === 'correlatedSubquery') {\n return false;\n }\n if (condition.type === 'simple') {\n return true;\n }\n return condition.conditions.every(isNotAndDoesNotContainSubquery);\n}\n\nfunction applySimpleCondition(\n input: FilterInput,\n delegate: BuilderDelegate,\n condition: SimpleCondition,\n): FilterInput {\n const filter = new Filter(input, createPredicate(condition));\n delegate.decorateFilterInput(\n filter,\n `${valuePosName(condition.left)}:${condition.op}:${valuePosName(condition.right)}`,\n );\n delegate.addEdge(input, filter);\n return filter;\n}\n\nfunction valuePosName(left: ValuePosition) {\n switch (left.type) {\n case 'static':\n return left.field;\n case 'literal':\n return left.value;\n case 'column':\n return left.name;\n }\n}\n\nfunction applyCorrelatedSubQuery(\n sq: CorrelatedSubquery,\n delegate: BuilderDelegate,\n queryID: string,\n end: Input,\n name: string,\n fromCondition: boolean,\n parentPartitionKey?: CompoundKey,\n boundProvider?: TakeBoundProvider,\n exists?: ExistsJoin,\n) {\n // TODO: we only omit the join if the CSQ if from a condition since\n // we want to create an empty array for `related` fields that are `limit(0)`\n if (sq.subquery.limit === 0 && fromCondition) {\n return end;\n }\n\n assert(sq.subquery.alias, 'Subquery must have an alias');\n const child = buildPipelineInternal(\n sq.subquery,\n delegate,\n queryID,\n `${name}.${sq.subquery.alias}`,\n sq.correlation.childField,\n fromCondition,\n );\n\n const joinName = `${name}:join(${sq.subquery.alias})`;\n const join = new Join({\n parent: end,\n child,\n parentKey: sq.correlation.parentField,\n childKey: sq.correlation.childField,\n relationshipName: sq.subquery.alias,\n hidden: sq.hidden ?? false,\n system: sq.system ?? 'client',\n parentPartitionKey: fromCondition ? undefined : parentPartitionKey,\n boundProvider,\n exists,\n });\n delegate.addEdge(end, join);\n delegate.addEdge(child, join);\n return delegate.decorateInput(join, joinName);\n}\n\nfunction applyCorrelatedSubqueryCondition(\n input: FilterInput,\n condition: CorrelatedSubqueryCondition,\n delegate: BuilderDelegate,\n name: string,\n): FilterInput {\n assert(\n condition.op === 'EXISTS' || condition.op === 'NOT EXISTS',\n 'Expected EXISTS or NOT EXISTS operator',\n );\n if (condition.related.subquery.limit === 0) {\n if (condition.op === 'EXISTS') {\n const filter = new Filter(input, () => false);\n delegate.addEdge(input, filter);\n return filter;\n }\n const filter = new Filter(input, () => true);\n delegate.addEdge(input, filter);\n return filter;\n }\n const existsName = `${name}:exists(${condition.related.subquery.alias})`;\n const exists = new Exists(\n input,\n must(condition.related.subquery.alias),\n condition.related.correlation.parentField,\n condition.op,\n );\n delegate.addEdge(input, exists);\n return delegate.decorateFilterInput(exists, existsName);\n}\n\nfunction gatherCorrelatedSubqueryQueryConditions(\n condition: Condition | undefined,\n) {\n const csqs: CorrelatedSubqueryCondition[] = [];\n const gather = (condition: Condition) => {\n if (condition.type === 'correlatedSubquery') {\n csqs.push(condition);\n return;\n }\n if (condition.type === 'and' || condition.type === 'or') {\n for (const c of condition.conditions) {\n gather(c);\n }\n return;\n }\n };\n if (condition) {\n gather(condition);\n }\n return csqs;\n}\n\nexport function assertOrderingIncludesPK(\n ordering: Ordering,\n pk: PrimaryKey,\n): void {\n // oxlint-disable-next-line unicorn/prefer-set-has -- Array is more appropriate here for small collections\n const orderingFields = ordering.map(([field]) => field);\n const missingFields = pk.filter(pkField => !orderingFields.includes(pkField));\n\n if (missingFields.length > 0) {\n throw new Error(\n `Ordering must include all primary key fields. Missing: ${missingFields.join(\n ', ',\n )}. ZQL automatically appends primary key fields to the ordering if they are missing \n so a common cause of this error is a casing mismatch between Postgres and ZQL.\n E.g., \"userid\" vs \"userID\".\n You may want to add double-quotes around your Postgres column names to prevent Postgres from lower-casing them:\n https://www.postgresql.org/docs/current/sql-syntax-lexical.htm`,\n );\n }\n}\n\nfunction uniquifyCorrelatedSubqueryConditionAliases(ast: AST): AST {\n if (!ast.where) {\n return ast;\n }\n const {where} = ast;\n if (where.type !== 'and' && where.type !== 'or') {\n return ast;\n }\n\n let count = 0;\n const uniquifyCorrelatedSubquery = (csqc: CorrelatedSubqueryCondition) => ({\n ...csqc,\n related: {\n ...csqc.related,\n subquery: {\n ...csqc.related.subquery,\n alias: (csqc.related.subquery.alias ?? '') + '_' + count++,\n },\n },\n });\n\n const uniquify = (cond: Condition): Condition => {\n if (cond.type === 'simple') {\n return cond;\n } else if (cond.type === 'correlatedSubquery') {\n return uniquifyCorrelatedSubquery(cond);\n }\n const conditions = [];\n for (const c of cond.conditions) {\n conditions.push(uniquify(c));\n }\n return {\n type: cond.type,\n conditions,\n };\n };\n\n const result = {\n ...ast,\n where: uniquify(where),\n };\n return result;\n}\n\nexport function conditionIncludesFlippedSubqueryAtAnyLevel(\n cond: Condition,\n): boolean {\n if (cond.type === 'correlatedSubquery') {\n return !!cond.flip;\n }\n if (cond.type === 'and' || cond.type === 'or') {\n return cond.conditions.some(c =>\n conditionIncludesFlippedSubqueryAtAnyLevel(c),\n );\n }\n // simple conditions don't have flips\n return false;\n}\n\nexport function partitionBranches(\n conditions: readonly Condition[],\n predicate: (c: Condition) => boolean,\n) {\n const matched: Condition[] = [];\n const notMatched: Condition[] = [];\n for (const c of conditions) {\n if (predicate(c)) {\n matched.push(c);\n } else {\n notMatched.push(c);\n }\n }\n return [matched, notMatched] as const;\n}\n"],"mappings":"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"}
@@ -1,7 +1,8 @@
1
1
  import type { CompoundKey, System } from '../../../zero-protocol/src/ast.ts';
2
2
  import type { Node } from './data.ts';
3
+ import { JoinIndex } from './join-index.ts';
3
4
  import { canonicalKeyForTest } from './join-utils.ts';
4
- import { type FetchRequest, type Input, type Output, type Storage } from './operator.ts';
5
+ import { type FetchRequest, type Input, type Output } from './operator.ts';
5
6
  import type { SourceSchema } from './schema.ts';
6
7
  import { type Stream } from './stream.ts';
7
8
  import { type TakeBoundProvider } from './take-gate.ts';
@@ -18,7 +19,7 @@ type Args = {
18
19
  system: System;
19
20
  parentPartitionKey?: CompoundKey | undefined;
20
21
  boundProvider?: TakeBoundProvider | undefined;
21
- storage: Storage;
22
+ parentIndex?: JoinIndex | undefined;
22
23
  };
23
24
  /**
24
25
  * An *inner* join which fetches nodes from its child input first and then
@@ -30,8 +31,9 @@ type Args = {
30
31
  */
31
32
  export declare class FlippedJoin implements Input {
32
33
  #private;
33
- constructor({ parent, child, parentKey, childKey, relationshipName, hidden, system, parentPartitionKey, boundProvider, storage, }: Args);
34
+ constructor({ parent, child, parentKey, childKey, relationshipName, hidden, system, parentPartitionKey, boundProvider, parentIndex, }: Args);
34
35
  destroy(): void;
36
+ get parentIndexForTest(): JoinIndex;
35
37
  setOutput(output: Output): void;
36
38
  getSchema(): SourceSchema;
37
39
  fetch(req: FetchRequest): Stream<Node | 'yield'>;
@@ -1 +1 @@
1
- {"version":3,"file":"flipped-join.d.ts","sourceRoot":"","sources":["../../../../../zql/src/ivm/flipped-join.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAC,WAAW,EAAE,MAAM,EAAC,MAAM,mCAAmC,CAAC;AAY3E,OAAO,KAAK,EAAC,IAAI,EAAC,MAAM,WAAW,CAAC;AACpC,OAAO,EAGL,mBAAmB,EAQpB,MAAM,iBAAiB,CAAC;AAEzB,OAAO,EAEL,KAAK,YAAY,EACjB,KAAK,KAAK,EAEV,KAAK,MAAM,EACX,KAAK,OAAO,EACb,MAAM,eAAe,CAAC;AACvB,OAAO,KAAK,EAAC,YAAY,EAAC,MAAM,aAAa,CAAC;AAC9C,OAAO,EAAC,KAAK,MAAM,EAAC,MAAM,aAAa,CAAC;AACxC,OAAO,EAIL,KAAK,iBAAiB,EACvB,MAAM,gBAAgB,CAAC;AA6BxB,wBAAgB,2BAA2B,IAAI,MAAM,CAEpD;AAED,6CAA6C;AAC7C,wBAAgB,kCAAkC,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,IAAI,CAM3E;AAED,KAAK,IAAI,GAAG;IACV,MAAM,EAAE,KAAK,CAAC;IACd,KAAK,EAAE,KAAK,CAAC;IAEb,SAAS,EAAE,WAAW,CAAC;IACvB,QAAQ,EAAE,WAAW,CAAC;IAEtB,gBAAgB,EAAE,MAAM,CAAC;IACzB,MAAM,EAAE,OAAO,CAAC;IAChB,MAAM,EAAE,MAAM,CAAC;IACf,kBAAkB,CAAC,EAAE,WAAW,GAAG,SAAS,CAAC;IAC7C,aAAa,CAAC,EAAE,iBAAiB,GAAG,SAAS,CAAC;IAC9C,OAAO,EAAE,OAAO,CAAC;CAClB,CAAC;AAEF;;;;;;;GAOG;AACH,qBAAa,WAAY,YAAW,KAAK;;IAiBvC,YAAY,EACV,MAAM,EACN,KAAK,EACL,SAAS,EACT,QAAQ,EACR,gBAAgB,EAChB,MAAM,EACN,MAAM,EACN,kBAAkB,EAClB,aAAa,EACb,OAAO,GACR,EAAE,IAAI,EAqCN;IAQD,OAAO,IAAI,IAAI,CAId;IAED,SAAS,CAAC,MAAM,EAAE,MAAM,GAAG,IAAI,CAE9B;IAED,SAAS,IAAI,YAAY,CAExB;IAEA,KAAK,CAAC,GAAG,EAAE,YAAY,GAAG,MAAM,CAAC,IAAI,GAAG,OAAO,CAAC,CA4ChD;CAscF;AAED,OAAO,EAAC,mBAAmB,EAAC,CAAC"}
1
+ {"version":3,"file":"flipped-join.d.ts","sourceRoot":"","sources":["../../../../../zql/src/ivm/flipped-join.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAC,WAAW,EAAE,MAAM,EAAC,MAAM,mCAAmC,CAAC;AAY3E,OAAO,KAAK,EAAC,IAAI,EAAC,MAAM,WAAW,CAAC;AACpC,OAAO,EAAC,SAAS,EAAC,MAAM,iBAAiB,CAAC;AAC1C,OAAO,EAGL,mBAAmB,EAIpB,MAAM,iBAAiB,CAAC;AAEzB,OAAO,EAEL,KAAK,YAAY,EACjB,KAAK,KAAK,EAEV,KAAK,MAAM,EACZ,MAAM,eAAe,CAAC;AACvB,OAAO,KAAK,EAAC,YAAY,EAAC,MAAM,aAAa,CAAC;AAC9C,OAAO,EAAC,KAAK,MAAM,EAAC,MAAM,aAAa,CAAC;AACxC,OAAO,EAIL,KAAK,iBAAiB,EACvB,MAAM,gBAAgB,CAAC;AA6BxB,wBAAgB,2BAA2B,IAAI,MAAM,CAEpD;AAED,6CAA6C;AAC7C,wBAAgB,kCAAkC,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,IAAI,CAM3E;AAED,KAAK,IAAI,GAAG;IACV,MAAM,EAAE,KAAK,CAAC;IACd,KAAK,EAAE,KAAK,CAAC;IAEb,SAAS,EAAE,WAAW,CAAC;IACvB,QAAQ,EAAE,WAAW,CAAC;IAEtB,gBAAgB,EAAE,MAAM,CAAC;IACzB,MAAM,EAAE,OAAO,CAAC;IAChB,MAAM,EAAE,MAAM,CAAC;IACf,kBAAkB,CAAC,EAAE,WAAW,GAAG,SAAS,CAAC;IAC7C,aAAa,CAAC,EAAE,iBAAiB,GAAG,SAAS,CAAC;IAC9C,WAAW,CAAC,EAAE,SAAS,GAAG,SAAS,CAAC;CACrC,CAAC;AAEF;;;;;;;GAOG;AACH,qBAAa,WAAY,YAAW,KAAK;;IAqBvC,YAAY,EACV,MAAM,EACN,KAAK,EACL,SAAS,EACT,QAAQ,EACR,gBAAgB,EAChB,MAAM,EACN,MAAM,EACN,kBAAkB,EAClB,aAAa,EACb,WAAW,GACZ,EAAE,IAAI,EA8CN;IAQD,OAAO,IAAI,IAAI,CAGd;IAED,IAAI,kBAAkB,IAAI,SAAS,CAElC;IAED,SAAS,CAAC,MAAM,EAAE,MAAM,GAAG,IAAI,CAE9B;IAED,SAAS,IAAI,YAAY,CAExB;IAEA,KAAK,CAAC,GAAG,EAAE,YAAY,GAAG,MAAM,CAAC,IAAI,GAAG,OAAO,CAAC,CA4ChD;CAybF;AAED,OAAO,EAAC,mBAAmB,EAAC,CAAC"}