@omgbase/oqx 0.14.0 → 0.14.1

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 (52) hide show
  1. package/CHANGELOG.md +64 -0
  2. package/README.md +139 -6
  3. package/dist/adapters/indexed.d.ts +13 -1
  4. package/dist/adapters/indexed.d.ts.map +1 -1
  5. package/dist/adapters/indexed.js +55 -24
  6. package/dist/adapters/indexed.js.map +1 -1
  7. package/dist/context.d.ts +24 -0
  8. package/dist/context.d.ts.map +1 -1
  9. package/dist/context.js.map +1 -1
  10. package/dist/engine.d.ts +55 -2
  11. package/dist/engine.d.ts.map +1 -1
  12. package/dist/engine.js +268 -35
  13. package/dist/engine.js.map +1 -1
  14. package/dist/index.d.ts +3 -1
  15. package/dist/index.d.ts.map +1 -1
  16. package/dist/index.js +1 -0
  17. package/dist/index.js.map +1 -1
  18. package/dist/optimize/analysis.d.ts +45 -0
  19. package/dist/optimize/analysis.d.ts.map +1 -0
  20. package/dist/optimize/analysis.js +194 -0
  21. package/dist/optimize/analysis.js.map +1 -0
  22. package/dist/optimize/driver.d.ts +8 -0
  23. package/dist/optimize/driver.d.ts.map +1 -0
  24. package/dist/optimize/driver.js +35 -0
  25. package/dist/optimize/driver.js.map +1 -0
  26. package/dist/optimize/hash-index.d.ts +34 -0
  27. package/dist/optimize/hash-index.d.ts.map +1 -0
  28. package/dist/optimize/hash-index.js +79 -0
  29. package/dist/optimize/hash-index.js.map +1 -0
  30. package/dist/optimize/index.d.ts +8 -0
  31. package/dist/optimize/index.d.ts.map +1 -0
  32. package/dist/optimize/index.js +11 -0
  33. package/dist/optimize/index.js.map +1 -0
  34. package/dist/optimize/ir.d.ts +84 -0
  35. package/dist/optimize/ir.d.ts.map +1 -0
  36. package/dist/optimize/ir.js +60 -0
  37. package/dist/optimize/ir.js.map +1 -0
  38. package/dist/optimize/rules.d.ts +8 -0
  39. package/dist/optimize/rules.d.ts.map +1 -0
  40. package/dist/optimize/rules.js +136 -0
  41. package/dist/optimize/rules.js.map +1 -0
  42. package/package.json +1 -1
  43. package/src/adapters/indexed.ts +60 -24
  44. package/src/context.ts +24 -0
  45. package/src/engine.ts +303 -33
  46. package/src/index.ts +6 -1
  47. package/src/optimize/analysis.ts +183 -0
  48. package/src/optimize/driver.ts +37 -0
  49. package/src/optimize/hash-index.ts +91 -0
  50. package/src/optimize/index.ts +16 -0
  51. package/src/optimize/ir.ts +128 -0
  52. package/src/optimize/rules.ts +136 -0
@@ -0,0 +1,45 @@
1
+ import type { Expr, OpNode, Where } from "../ast.js";
2
+ import type { RuleContext } from "./ir.js";
3
+ /** Does `e`, evaluated in a scope at depth `at` (root = 0), read any scope whose
4
+ * depth lies in `[1, upTo]`? Reads of the root (named roots, depth 0) and of
5
+ * scopes deeper than `upTo` (the fragment's own rows) do not count. */
6
+ export declare function exprReadsScopeIn(e: Expr, at: number, upTo: number): boolean;
7
+ export declare function whereReadsScopeIn(w: Where, at: number, upTo: number): boolean;
8
+ /** A directive evaluated in a scope at depth `at`: its receiver is read there;
9
+ * its rows are scopes at `at + 1`. */
10
+ export declare function opReadsScopeIn(op: OpNode, at: number, upTo: number): boolean;
11
+ /** Does `e` read the scope it is evaluated in (a bare identifier anywhere)? */
12
+ export declare function exprReadsCurrentScope(e: Expr): boolean;
13
+ /** Can evaluating `e` raise? Calls can (unknown function/method, regex errors,
14
+ * a context's own failures); a binding at or past the run's count does; nothing
15
+ * else in a scalar expression can (§23). */
16
+ export declare function exprRaiseFree(e: Expr, ctx: RuleContext): boolean;
17
+ /** Can evaluating `w` raise, or bind a lift? A where tree is raise-free when
18
+ * its scalars are and its directives are raise-free (`opRaiseFree`). */
19
+ export declare function whereRaiseFree(w: Where, ctx: RuleContext): boolean;
20
+ /** A where-position directive that can neither raise nor bind: `exists`,
21
+ * `none` or `count` (never `single`, which raises on several rows; `collect`
22
+ * binds lifts; `first`/`single` project), without `follow` (raises in where
23
+ * position), without a bound (an invalid value raises), without `distinct`
24
+ * (dedup projects, and a projected range raises), whose receiver, `from`,
25
+ * `where` and `order by` (evaluated by `count`) are raise-free and which lifts
26
+ * nothing anywhere. */
27
+ export declare function opRaiseFree(op: OpNode, ctx: RuleContext): boolean;
28
+ /** Does any `^name:` item occur anywhere inside the directive (its own select,
29
+ * or any nested block's)? */
30
+ export declare function opHasLifts(op: OpNode): boolean;
31
+ /** Does any `^name:` item occur inside a directive of this where tree? */
32
+ export declare function whereHasLifts(w: Where): boolean;
33
+ /** The scope-metadata intrinsics: `$key` (an entry's key) and the `follow`
34
+ * occurrence fields. In a block's row scope these read scope metadata when it is
35
+ * present and fall through to the context otherwise; every other `$`-name
36
+ * (`$id`, `$path`, a context's own intrinsics) is always a plain `get` on the
37
+ * row. */
38
+ export declare const SCOPE_INTRINSICS: ReadonlySet<string>;
39
+ /** The property path a bare identifier or member chain reads off the current
40
+ * row — `customer_id` → `["customer_id"]`, `meta.id` → `["meta", "id"]`,
41
+ * `$value` → `[]`, `$value.x` → `["x"]`, `$path` → `["$path"]` — or `null` when
42
+ * `e` is not such a chain or starts from a scope intrinsic (`$key`, `$depth`,
43
+ * …, whose value is scope metadata rather than a row property). */
44
+ export declare function localPath(e: Expr): readonly string[] | null;
45
+ //# sourceMappingURL=analysis.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"analysis.d.ts","sourceRoot":"","sources":["../../src/optimize/analysis.ts"],"names":[],"mappings":"AAiBA,OAAO,KAAK,EAAE,IAAI,EAAU,MAAM,EAAwB,KAAK,EAAE,MAAM,WAAW,CAAC;AACnF,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,SAAS,CAAC;AAI3C;;uEAEuE;AACvE,wBAAgB,gBAAgB,CAAC,CAAC,EAAE,IAAI,EAAE,EAAE,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,OAAO,CAY3E;AAED,wBAAgB,iBAAiB,CAAC,CAAC,EAAE,KAAK,EAAE,EAAE,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,OAAO,CAO7E;AAED;sCACsC;AACtC,wBAAgB,cAAc,CAAC,EAAE,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,OAAO,CAE5E;AA6BD,+EAA+E;AAC/E,wBAAgB,qBAAqB,CAAC,CAAC,EAAE,IAAI,GAAG,OAAO,CAEtD;AAID;;4CAE4C;AAC5C,wBAAgB,aAAa,CAAC,CAAC,EAAE,IAAI,EAAE,GAAG,EAAE,WAAW,GAAG,OAAO,CAWhE;AAED;wEACwE;AACxE,wBAAgB,cAAc,CAAC,CAAC,EAAE,KAAK,EAAE,GAAG,EAAE,WAAW,GAAG,OAAO,CAOlE;AAED;;;;;;uBAMuB;AACvB,wBAAgB,WAAW,CAAC,EAAE,EAAE,MAAM,EAAE,GAAG,EAAE,WAAW,GAAG,OAAO,CASjE;AAID;6BAC6B;AAC7B,wBAAgB,UAAU,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,CAE9C;AASD,0EAA0E;AAC1E,wBAAgB,aAAa,CAAC,CAAC,EAAE,KAAK,GAAG,OAAO,CAO/C;AAID;;;;UAIU;AACV,eAAO,MAAM,gBAAgB,EAAE,WAAW,CAAC,MAAM,CAA0E,CAAC;AAE5H;;;;mEAImE;AACnE,wBAAgB,SAAS,CAAC,CAAC,EAAE,IAAI,GAAG,SAAS,MAAM,EAAE,GAAG,IAAI,CAU3D"}
@@ -0,0 +1,194 @@
1
+ // Static (syntactic) analyses the rules rest on. Each answers one question
2
+ // about an AST fragment without evaluating anything:
3
+ //
4
+ // • which scopes does it READ (`readsScopeIn`) — by depth, since a bare name
5
+ // reads the current scope and `^name` reads exactly `levels` scopes out
6
+ // (SEMANTICS §2), so an expression evaluated at a known depth reads a
7
+ // known set of depths;
8
+ // • can evaluating it RAISE (`raiseFree`) — the eval errors SEMANTICS §23
9
+ // enumerates: a call (unknown name, bad regex, wrong receiver), `single`
10
+ // over several rows, an invalid `limit`/`offset`, `follow` in a where op, a
11
+ // range in a result, a binding out of range. Property reads are total
12
+ // (navigating from absent is absent); a `DataContext.get` that throws is
13
+ // outside this model and is caught at the points the engine relies on it
14
+ // (index construction, probe-value evaluation), where it degrades to a scan;
15
+ // • does it have SIDE EFFECTS on scopes (`hasLifts`) — a `^name:` item binds a
16
+ // value into an enclosing scope (§19), observable by later conjuncts.
17
+ // ---- scope reads --------------------------------------------------------------
18
+ /** Does `e`, evaluated in a scope at depth `at` (root = 0), read any scope whose
19
+ * depth lies in `[1, upTo]`? Reads of the root (named roots, depth 0) and of
20
+ * scopes deeper than `upTo` (the fragment's own rows) do not count. */
21
+ export function exprReadsScopeIn(e, at, upTo) {
22
+ switch (e.kind) {
23
+ case "lit":
24
+ case "binding": return false;
25
+ case "ident": return at >= 1 && at <= upTo;
26
+ case "outer": {
27
+ const d = at - e.levels;
28
+ return d >= 1 && d <= upTo;
29
+ }
30
+ case "member": return exprReadsScopeIn(e.recv, at, upTo);
31
+ case "index": return exprReadsScopeIn(e.recv, at, upTo) || exprReadsScopeIn(e.index, at, upTo);
32
+ case "call": return (e.recv !== null && exprReadsScopeIn(e.recv, at, upTo)) || e.args.some((a) => exprReadsScopeIn(a, at, upTo));
33
+ case "unary": return exprReadsScopeIn(e.expr, at, upTo);
34
+ case "binary":
35
+ case "logical":
36
+ case "in": return exprReadsScopeIn(e.left, at, upTo) || exprReadsScopeIn(e.right, at, upTo);
37
+ case "range": return (e.lo !== null && exprReadsScopeIn(e.lo, at, upTo)) || (e.hi !== null && exprReadsScopeIn(e.hi, at, upTo));
38
+ }
39
+ }
40
+ export function whereReadsScopeIn(w, at, upTo) {
41
+ switch (w.kind) {
42
+ case "and":
43
+ case "or": return w.parts.some((p) => whereReadsScopeIn(p, at, upTo));
44
+ case "not": return whereReadsScopeIn(w.expr, at, upTo);
45
+ case "scalar": return exprReadsScopeIn(w.expr, at, upTo);
46
+ case "op": return opReadsScopeIn(w, at, upTo);
47
+ }
48
+ }
49
+ /** A directive evaluated in a scope at depth `at`: its receiver is read there;
50
+ * its rows are scopes at `at + 1`. */
51
+ export function opReadsScopeIn(op, at, upTo) {
52
+ return exprReadsScopeIn(op.receiver, at, upTo) || subReadsScopeIn(op.sub, at + 1, upTo);
53
+ }
54
+ /** A block body whose rows are scopes at depth `at`. `from` re-projections,
55
+ * `where`, `select`, `order by` and the bound are read in the row scope (the
56
+ * bound in a row-less scope at the same depth); `follow` walks occurrences at
57
+ * `at`, with its successor `where` one deeper (§20). */
58
+ function subReadsScopeIn(sub, at, upTo) {
59
+ if (sub.from.some((e) => exprReadsScopeIn(e, at, upTo)))
60
+ return true;
61
+ if (sub.where && whereReadsScopeIn(sub.where, at, upTo))
62
+ return true;
63
+ if (sub.select.some((item) => itemReadsScopeIn(item, at, upTo)))
64
+ return true;
65
+ if (sub.orderBy?.some((o) => exprReadsScopeIn(o.expr, at, upTo)))
66
+ return true;
67
+ if (sub.limit && exprReadsScopeIn(sub.limit, at, upTo))
68
+ return true;
69
+ if (sub.offset && exprReadsScopeIn(sub.offset, at, upTo))
70
+ return true;
71
+ return sub.follow !== null && followReadsScopeIn(sub.follow, at, upTo);
72
+ }
73
+ function itemReadsScopeIn(item, at, upTo) {
74
+ return item.kind === "field" ? exprReadsScopeIn(item.expr, at, upTo) : opReadsScopeIn(item.op, at, upTo);
75
+ }
76
+ function followReadsScopeIn(f, at, upTo) {
77
+ for (const d of f.destinations) {
78
+ if (d.kind === "op" ? opReadsScopeIn(d, at, upTo) : exprReadsScopeIn(d, at, upTo))
79
+ return true;
80
+ }
81
+ if (f.where && exprReadsScopeIn(f.where, at + 1, upTo))
82
+ return true;
83
+ if (f.frontier && exprReadsScopeIn(f.frontier, at, upTo))
84
+ return true;
85
+ return f.by !== null && exprReadsScopeIn(f.by, at, upTo);
86
+ }
87
+ /** Does `e` read the scope it is evaluated in (a bare identifier anywhere)? */
88
+ export function exprReadsCurrentScope(e) {
89
+ return exprReadsScopeIn(e, 1, 1);
90
+ }
91
+ // ---- raise freedom ------------------------------------------------------------
92
+ /** Can evaluating `e` raise? Calls can (unknown function/method, regex errors,
93
+ * a context's own failures); a binding at or past the run's count does; nothing
94
+ * else in a scalar expression can (§23). */
95
+ export function exprRaiseFree(e, ctx) {
96
+ switch (e.kind) {
97
+ case "lit":
98
+ case "ident":
99
+ case "outer": return true;
100
+ case "binding": return e.index < ctx.bindingCount;
101
+ case "call": return false;
102
+ case "member": return exprRaiseFree(e.recv, ctx);
103
+ case "index": return exprRaiseFree(e.recv, ctx) && exprRaiseFree(e.index, ctx);
104
+ case "unary": return exprRaiseFree(e.expr, ctx);
105
+ case "binary":
106
+ case "logical":
107
+ case "in": return exprRaiseFree(e.left, ctx) && exprRaiseFree(e.right, ctx);
108
+ case "range": return (e.lo === null || exprRaiseFree(e.lo, ctx)) && (e.hi === null || exprRaiseFree(e.hi, ctx));
109
+ }
110
+ }
111
+ /** Can evaluating `w` raise, or bind a lift? A where tree is raise-free when
112
+ * its scalars are and its directives are raise-free (`opRaiseFree`). */
113
+ export function whereRaiseFree(w, ctx) {
114
+ switch (w.kind) {
115
+ case "and":
116
+ case "or": return w.parts.every((p) => whereRaiseFree(p, ctx));
117
+ case "not": return whereRaiseFree(w.expr, ctx);
118
+ case "scalar": return exprRaiseFree(w.expr, ctx);
119
+ case "op": return opRaiseFree(w, ctx);
120
+ }
121
+ }
122
+ /** A where-position directive that can neither raise nor bind: `exists`,
123
+ * `none` or `count` (never `single`, which raises on several rows; `collect`
124
+ * binds lifts; `first`/`single` project), without `follow` (raises in where
125
+ * position), without a bound (an invalid value raises), without `distinct`
126
+ * (dedup projects, and a projected range raises), whose receiver, `from`,
127
+ * `where` and `order by` (evaluated by `count`) are raise-free and which lifts
128
+ * nothing anywhere. */
129
+ export function opRaiseFree(op, ctx) {
130
+ if (op.op !== "exists" && op.op !== "none" && op.op !== "count")
131
+ return false;
132
+ const sub = op.sub;
133
+ if (sub.follow || sub.limit || sub.offset || op.distinct)
134
+ return false;
135
+ if (!exprRaiseFree(op.receiver, ctx))
136
+ return false;
137
+ if (!sub.from.every((e) => exprRaiseFree(e, ctx)))
138
+ return false;
139
+ if (sub.where && !whereRaiseFree(sub.where, ctx))
140
+ return false;
141
+ if (sub.orderBy && !sub.orderBy.every((o) => exprRaiseFree(o.expr, ctx)))
142
+ return false;
143
+ return !opHasLifts(op);
144
+ }
145
+ // ---- lifts --------------------------------------------------------------------
146
+ /** Does any `^name:` item occur anywhere inside the directive (its own select,
147
+ * or any nested block's)? */
148
+ export function opHasLifts(op) {
149
+ return subHasLifts(op.sub);
150
+ }
151
+ function subHasLifts(sub) {
152
+ if (sub.select.some((item) => (item.kind === "field" ? item.lift > 0 : opHasLifts(item.op))))
153
+ return true;
154
+ if (sub.where && whereHasLifts(sub.where))
155
+ return true;
156
+ if (sub.follow?.destinations.some((d) => d.kind === "op" && opHasLifts(d)))
157
+ return true;
158
+ return false;
159
+ }
160
+ /** Does any `^name:` item occur inside a directive of this where tree? */
161
+ export function whereHasLifts(w) {
162
+ switch (w.kind) {
163
+ case "and":
164
+ case "or": return w.parts.some(whereHasLifts);
165
+ case "not": return whereHasLifts(w.expr);
166
+ case "scalar": return false;
167
+ case "op": return opHasLifts(w);
168
+ }
169
+ }
170
+ // ---- local paths --------------------------------------------------------------
171
+ /** The scope-metadata intrinsics: `$key` (an entry's key) and the `follow`
172
+ * occurrence fields. In a block's row scope these read scope metadata when it is
173
+ * present and fall through to the context otherwise; every other `$`-name
174
+ * (`$id`, `$path`, a context's own intrinsics) is always a plain `get` on the
175
+ * row. */
176
+ export const SCOPE_INTRINSICS = new Set(["$key", "$depth", "$stop", "$leaf", "$frontier", "$ordinal"]);
177
+ /** The property path a bare identifier or member chain reads off the current
178
+ * row — `customer_id` → `["customer_id"]`, `meta.id` → `["meta", "id"]`,
179
+ * `$value` → `[]`, `$value.x` → `["x"]`, `$path` → `["$path"]` — or `null` when
180
+ * `e` is not such a chain or starts from a scope intrinsic (`$key`, `$depth`,
181
+ * …, whose value is scope metadata rather than a row property). */
182
+ export function localPath(e) {
183
+ if (e.kind === "ident") {
184
+ if (e.name === "$value")
185
+ return [];
186
+ return SCOPE_INTRINSICS.has(e.name) ? null : [e.name];
187
+ }
188
+ if (e.kind === "member") {
189
+ const head = localPath(e.recv);
190
+ return head === null ? null : [...head, e.name];
191
+ }
192
+ return null;
193
+ }
194
+ //# sourceMappingURL=analysis.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"analysis.js","sourceRoot":"","sources":["../../src/optimize/analysis.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,8 @@
1
+ import type { OpNode } from "../ast.js";
2
+ import type { BlockPlan, Rule, RuleContext } from "./ir.js";
3
+ /** Rewrite `logicalBlock(node, depth)` to a fixpoint of `rules`. */
4
+ export declare function optimizeBlock(node: OpNode, depth: number, ctx: RuleContext, rules: readonly Rule[]): BlockPlan;
5
+ /** `optimizeBlock`, memoized on the node (re-planned when depth, binding count
6
+ * or rule set differ from the cached entry). */
7
+ export declare function planFor(node: OpNode, depth: number, ctx: RuleContext, rules: readonly Rule[]): BlockPlan;
8
+ //# sourceMappingURL=driver.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"driver.d.ts","sourceRoot":"","sources":["../../src/optimize/driver.ts"],"names":[],"mappings":"AAKA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,WAAW,CAAC;AACxC,OAAO,KAAK,EAAE,SAAS,EAAE,IAAI,EAAE,WAAW,EAAE,MAAM,SAAS,CAAC;AAK5D,oEAAoE;AACpE,wBAAgB,aAAa,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,GAAG,EAAE,WAAW,EAAE,KAAK,EAAE,SAAS,IAAI,EAAE,GAAG,SAAS,CAW9G;AAKD;gDACgD;AAChD,wBAAgB,OAAO,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,GAAG,EAAE,WAAW,EAAE,KAAK,EAAE,SAAS,IAAI,EAAE,GAAG,SAAS,CAMxG"}
@@ -0,0 +1,35 @@
1
+ // The optimizer driver: applies a rule set to a block's logical plan until no
2
+ // rule changes it (a fixpoint, bounded), and caches the result per AST node —
3
+ // the analysis is syntactic, so a plan depends only on the node, its static
4
+ // depth, the run's binding count and the rule set.
5
+ import { logicalBlock } from "./ir.js";
6
+ const MAX_PASSES = 8;
7
+ /** Rewrite `logicalBlock(node, depth)` to a fixpoint of `rules`. */
8
+ export function optimizeBlock(node, depth, ctx, rules) {
9
+ let plan = logicalBlock(node, depth);
10
+ for (let pass = 0; pass < MAX_PASSES; pass++) {
11
+ let changed = false;
12
+ for (const rule of rules) {
13
+ const next = rule(plan, ctx);
14
+ if (next !== null && next !== plan) {
15
+ plan = next;
16
+ changed = true;
17
+ }
18
+ }
19
+ if (!changed)
20
+ break;
21
+ }
22
+ return plan;
23
+ }
24
+ const cache = new WeakMap();
25
+ /** `optimizeBlock`, memoized on the node (re-planned when depth, binding count
26
+ * or rule set differ from the cached entry). */
27
+ export function planFor(node, depth, ctx, rules) {
28
+ const hit = cache.get(node);
29
+ if (hit && hit.depth === depth && hit.bindingCount === ctx.bindingCount && hit.rules === rules)
30
+ return hit.plan;
31
+ const plan = optimizeBlock(node, depth, ctx, rules);
32
+ cache.set(node, { depth, bindingCount: ctx.bindingCount, rules, plan });
33
+ return plan;
34
+ }
35
+ //# sourceMappingURL=driver.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"driver.js","sourceRoot":"","sources":["../../src/optimize/driver.ts"],"names":[],"mappings":"AAAA,8EAA8E;AAC9E,8EAA8E;AAC9E,4EAA4E;AAC5E,mDAAmD;AAInD,OAAO,EAAE,YAAY,EAAE,MAAM,SAAS,CAAC;AAEvC,MAAM,UAAU,GAAG,CAAC,CAAC;AAErB,oEAAoE;AACpE,MAAM,UAAU,aAAa,CAAC,IAAY,EAAE,KAAa,EAAE,GAAgB,EAAE,KAAsB;IACjG,IAAI,IAAI,GAAG,YAAY,CAAC,IAAI,EAAE,KAAK,CAAC,CAAC;IACrC,KAAK,IAAI,IAAI,GAAG,CAAC,EAAE,IAAI,GAAG,UAAU,EAAE,IAAI,EAAE,EAAE,CAAC;QAC7C,IAAI,OAAO,GAAG,KAAK,CAAC;QACpB,KAAK,MAAM,IAAI,IAAI,KAAK,EAAE,CAAC;YACzB,MAAM,IAAI,GAAG,IAAI,CAAC,IAAI,EAAE,GAAG,CAAC,CAAC;YAC7B,IAAI,IAAI,KAAK,IAAI,IAAI,IAAI,KAAK,IAAI,EAAE,CAAC;gBAAC,IAAI,GAAG,IAAI,CAAC;gBAAC,OAAO,GAAG,IAAI,CAAC;YAAC,CAAC;QACtE,CAAC;QACD,IAAI,CAAC,OAAO;YAAE,MAAM;IACtB,CAAC;IACD,OAAO,IAAI,CAAC;AACd,CAAC;AAGD,MAAM,KAAK,GAAG,IAAI,OAAO,EAAkB,CAAC;AAE5C;gDACgD;AAChD,MAAM,UAAU,OAAO,CAAC,IAAY,EAAE,KAAa,EAAE,GAAgB,EAAE,KAAsB;IAC3F,MAAM,GAAG,GAAG,KAAK,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC;IAC5B,IAAI,GAAG,IAAI,GAAG,CAAC,KAAK,KAAK,KAAK,IAAI,GAAG,CAAC,YAAY,KAAK,GAAG,CAAC,YAAY,IAAI,GAAG,CAAC,KAAK,KAAK,KAAK;QAAE,OAAO,GAAG,CAAC,IAAI,CAAC;IAChH,MAAM,IAAI,GAAG,aAAa,CAAC,IAAI,EAAE,KAAK,EAAE,GAAG,EAAE,KAAK,CAAC,CAAC;IACpD,KAAK,CAAC,GAAG,CAAC,IAAI,EAAE,EAAE,KAAK,EAAE,YAAY,EAAE,GAAG,CAAC,YAAY,EAAE,KAAK,EAAE,IAAI,EAAE,CAAC,CAAC;IACxE,OAAO,IAAI,CAAC;AACd,CAAC"}
@@ -0,0 +1,34 @@
1
+ /** A pre-built equality index a `DataContext` may expose for a collection
2
+ * (see `DataContext.indexFor`): `lookup(value)` returns the ascending positions
3
+ * (into `toRows(collection)`) of the rows whose key equals `value` under §5.
4
+ *
5
+ * `lookupRows`, when present, answers the same probe with the ROWS themselves —
6
+ * exactly the rows `lookup(value)` would select, in receiver order — without
7
+ * the engine ever materializing `toRows(collection)`. A store-backed context
8
+ * implements it over its own indexes (one indexed statement per probe); the
9
+ * engine prefers it for a statically stable receiver, so the collection is
10
+ * never read whole. A throw is the query's error (as a `get` that throws is). */
11
+ export interface RowIndex {
12
+ lookup(value: unknown): readonly number[];
13
+ lookupRows?(value: unknown): Iterable<unknown>;
14
+ }
15
+ /** The engine's own `RowIndex`: `add` every row's key in row order, then probe. */
16
+ export declare class HashIndex implements RowIndex {
17
+ private prim;
18
+ private refs;
19
+ private count;
20
+ /** Record that the row at `position` has key `value`. Positions must be
21
+ * added in ascending order (row order). A `NaN` key is not indexed. */
22
+ add(value: unknown, position: number): void;
23
+ lookup(value: unknown): readonly number[];
24
+ /** How many rows were indexed (rows with a `NaN` key are not). */
25
+ get size(): number;
26
+ }
27
+ /** The tagged bucket key of a primitive, `null` for `NaN` (no key: it equals
28
+ * nothing), `undefined` for a reference-keyed value (object, array, function,
29
+ * symbol — compared by identity). Mirrors `semantics.equals`: `===` after
30
+ * normalizing `undefined` to `null`. */
31
+ export declare function primitiveKey(v: unknown): string | null | undefined;
32
+ /** Intersect two ascending position lists, ascending. */
33
+ export declare function intersectPositions(a: readonly number[], b: readonly number[]): readonly number[];
34
+ //# sourceMappingURL=hash-index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"hash-index.d.ts","sourceRoot":"","sources":["../../src/optimize/hash-index.ts"],"names":[],"mappings":"AAeA;;;;;;;;;iFASiF;AACjF,MAAM,WAAW,QAAQ;IACvB,MAAM,CAAC,KAAK,EAAE,OAAO,GAAG,SAAS,MAAM,EAAE,CAAC;IAC1C,UAAU,CAAC,CAAC,KAAK,EAAE,OAAO,GAAG,QAAQ,CAAC,OAAO,CAAC,CAAC;CAChD;AAID,mFAAmF;AACnF,qBAAa,SAAU,YAAW,QAAQ;IACxC,OAAO,CAAC,IAAI,CAA+B;IAC3C,OAAO,CAAC,IAAI,CAAgC;IAC5C,OAAO,CAAC,KAAK,CAAK;IAElB;2EACuE;IACvE,GAAG,CAAC,KAAK,EAAE,OAAO,EAAE,QAAQ,EAAE,MAAM,GAAG,IAAI;IAW3C,MAAM,CAAC,KAAK,EAAE,OAAO,GAAG,SAAS,MAAM,EAAE;IAMzC,kEAAkE;IAClE,IAAI,IAAI,IAAI,MAAM,CAEjB;CACF;AAED;;;wCAGwC;AACxC,wBAAgB,YAAY,CAAC,CAAC,EAAE,OAAO,GAAG,MAAM,GAAG,IAAI,GAAG,SAAS,CAUlE;AAED,yDAAyD;AACzD,wBAAgB,kBAAkB,CAAC,CAAC,EAAE,SAAS,MAAM,EAAE,EAAE,CAAC,EAAE,SAAS,MAAM,EAAE,GAAG,SAAS,MAAM,EAAE,CAUhG"}
@@ -0,0 +1,79 @@
1
+ // A hash index over the rows of one collection, keyed under OQX equality
2
+ // (SEMANTICS §5) — the only key discipline that makes an index probe return
3
+ // exactly the rows a `local == value` scan would keep:
4
+ //
5
+ // • absent is one value: `undefined` and `null` share a bucket;
6
+ // • numbers are doubles: `-0` meets `0`; `NaN` equals nothing, so a row whose
7
+ // key is `NaN` is never indexed and a probe for `NaN` finds nothing;
8
+ // • strings, booleans and bigints key by value with a type tag, so `"5"`,
9
+ // `5` and `true` stay apart;
10
+ // • objects, arrays, functions and symbols key by REFERENCE — the reference
11
+ // engine's `===` — never structurally.
12
+ //
13
+ // Buckets hold row POSITIONS in ascending order, so a probe yields rows in the
14
+ // receiver's order (§3 and §13 — a block's rows are the receiver's, in order).
15
+ const NONE = Object.freeze([]);
16
+ /** The engine's own `RowIndex`: `add` every row's key in row order, then probe. */
17
+ export class HashIndex {
18
+ prim = new Map();
19
+ refs = new Map();
20
+ count = 0;
21
+ /** Record that the row at `position` has key `value`. Positions must be
22
+ * added in ascending order (row order). A `NaN` key is not indexed. */
23
+ add(value, position) {
24
+ const k = primitiveKey(value);
25
+ if (k === null)
26
+ return; // NaN: equals nothing
27
+ const map = k === undefined ? this.refs : this.prim;
28
+ const key = k === undefined ? value : k;
29
+ let bucket = map.get(key);
30
+ if (!bucket)
31
+ map.set(key, (bucket = []));
32
+ bucket.push(position);
33
+ this.count++;
34
+ }
35
+ lookup(value) {
36
+ const k = primitiveKey(value);
37
+ if (k === null)
38
+ return NONE;
39
+ return (k === undefined ? this.refs.get(value) : this.prim.get(k)) ?? NONE;
40
+ }
41
+ /** How many rows were indexed (rows with a `NaN` key are not). */
42
+ get size() {
43
+ return this.count;
44
+ }
45
+ }
46
+ /** The tagged bucket key of a primitive, `null` for `NaN` (no key: it equals
47
+ * nothing), `undefined` for a reference-keyed value (object, array, function,
48
+ * symbol — compared by identity). Mirrors `semantics.equals`: `===` after
49
+ * normalizing `undefined` to `null`. */
50
+ export function primitiveKey(v) {
51
+ switch (typeof v) {
52
+ case "undefined": return "∅"; // absent ≡ absent
53
+ case "object": return v === null ? "∅" : undefined;
54
+ case "number": return Number.isNaN(v) ? null : `n${v}`; // `${-0}` is "0": -0 meets 0
55
+ case "string": return `s${v}`;
56
+ case "boolean": return v ? "t" : "f";
57
+ case "bigint": return `i${v}`;
58
+ default: return undefined; // function, symbol: reference identity
59
+ }
60
+ }
61
+ /** Intersect two ascending position lists, ascending. */
62
+ export function intersectPositions(a, b) {
63
+ const out = [];
64
+ let i = 0, j = 0;
65
+ while (i < a.length && j < b.length) {
66
+ const x = a[i], y = b[j];
67
+ if (x === y) {
68
+ out.push(x);
69
+ i++;
70
+ j++;
71
+ }
72
+ else if (x < y)
73
+ i++;
74
+ else
75
+ j++;
76
+ }
77
+ return out;
78
+ }
79
+ //# sourceMappingURL=hash-index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"hash-index.js","sourceRoot":"","sources":["../../src/optimize/hash-index.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,8 @@
1
+ export type { BlockPlan, Correlation, Rule, RuleContext } from "./ir.js";
2
+ export { logicalBlock, conjunction, conjuncts, residualWithout, lookupOrder } from "./ir.js";
3
+ export { optimizeBlock, planFor } from "./driver.js";
4
+ export { correlatedEqualityProbe, stableReceiver, invariantBlock, cardinalityOnly, DEFAULT_RULES } from "./rules.js";
5
+ export type { RowIndex } from "./hash-index.js";
6
+ export { HashIndex, primitiveKey, intersectPositions } from "./hash-index.js";
7
+ export { exprReadsScopeIn, whereReadsScopeIn, opReadsScopeIn, exprReadsCurrentScope, exprRaiseFree, whereRaiseFree, opRaiseFree, opHasLifts, whereHasLifts, localPath, SCOPE_INTRINSICS, } from "./analysis.js";
8
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/optimize/index.ts"],"names":[],"mappings":"AAMA,YAAY,EAAE,SAAS,EAAE,WAAW,EAAE,IAAI,EAAE,WAAW,EAAE,MAAM,SAAS,CAAC;AACzE,OAAO,EAAE,YAAY,EAAE,WAAW,EAAE,SAAS,EAAE,eAAe,EAAE,WAAW,EAAE,MAAM,SAAS,CAAC;AAC7F,OAAO,EAAE,aAAa,EAAE,OAAO,EAAE,MAAM,aAAa,CAAC;AACrD,OAAO,EAAE,uBAAuB,EAAE,cAAc,EAAE,cAAc,EAAE,eAAe,EAAE,aAAa,EAAE,MAAM,YAAY,CAAC;AACrH,YAAY,EAAE,QAAQ,EAAE,MAAM,iBAAiB,CAAC;AAChD,OAAO,EAAE,SAAS,EAAE,YAAY,EAAE,kBAAkB,EAAE,MAAM,iBAAiB,CAAC;AAC9E,OAAO,EACL,gBAAgB,EAAE,iBAAiB,EAAE,cAAc,EAAE,qBAAqB,EAC1E,aAAa,EAAE,cAAc,EAAE,WAAW,EAAE,UAAU,EAAE,aAAa,EAAE,SAAS,EAAE,gBAAgB,GACnG,MAAM,eAAe,CAAC"}
@@ -0,0 +1,11 @@
1
+ // The optimizer: a logical plan per nested block, rules that rewrite it, a
2
+ // driver that applies them to a fixpoint, and the equality-faithful hash index
3
+ // the hash-probe rule executes with. See `rules.ts` for the catalog and each
4
+ // rule's semantics argument; README "Performance: relational patterns" for the
5
+ // user-facing summary.
6
+ export { logicalBlock, conjunction, conjuncts, residualWithout, lookupOrder } from "./ir.js";
7
+ export { optimizeBlock, planFor } from "./driver.js";
8
+ export { correlatedEqualityProbe, stableReceiver, invariantBlock, cardinalityOnly, DEFAULT_RULES } from "./rules.js";
9
+ export { HashIndex, primitiveKey, intersectPositions } from "./hash-index.js";
10
+ export { exprReadsScopeIn, whereReadsScopeIn, opReadsScopeIn, exprReadsCurrentScope, exprRaiseFree, whereRaiseFree, opRaiseFree, opHasLifts, whereHasLifts, localPath, SCOPE_INTRINSICS, } from "./analysis.js";
11
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/optimize/index.ts"],"names":[],"mappings":"AAAA,2EAA2E;AAC3E,+EAA+E;AAC/E,6EAA6E;AAC7E,+EAA+E;AAC/E,uBAAuB;AAGvB,OAAO,EAAE,YAAY,EAAE,WAAW,EAAE,SAAS,EAAE,eAAe,EAAE,WAAW,EAAE,MAAM,SAAS,CAAC;AAC7F,OAAO,EAAE,aAAa,EAAE,OAAO,EAAE,MAAM,aAAa,CAAC;AACrD,OAAO,EAAE,uBAAuB,EAAE,cAAc,EAAE,cAAc,EAAE,eAAe,EAAE,aAAa,EAAE,MAAM,YAAY,CAAC;AAErH,OAAO,EAAE,SAAS,EAAE,YAAY,EAAE,kBAAkB,EAAE,MAAM,iBAAiB,CAAC;AAC9E,OAAO,EACL,gBAAgB,EAAE,iBAAiB,EAAE,cAAc,EAAE,qBAAqB,EAC1E,aAAa,EAAE,cAAc,EAAE,WAAW,EAAE,UAAU,EAAE,aAAa,EAAE,SAAS,EAAE,gBAAgB,GACnG,MAAM,eAAe,CAAC"}
@@ -0,0 +1,84 @@
1
+ import type { Consumer, Expr, Follow, OpNode, OrderSpec, SelectItem, Where } from "../ast.js";
2
+ /** One hoisted equality `local == outer` from the block's top-level `&&`
3
+ * conjunction: `local` reads only the block's row (an identifier or member
4
+ * chain; `path` is its property path, `[]` for `$value`), `outer` reads nothing
5
+ * from the block's row (outer references, bindings, literals, arithmetic over
6
+ * them) and cannot raise. */
7
+ export interface Correlation {
8
+ readonly local: Expr;
9
+ readonly outer: Expr;
10
+ readonly path: readonly string[];
11
+ /** Position of the equality among `conjuncts(where)`, so a probe that
12
+ * answers ONE correlation (a store-backed `lookupRows`) can put the others
13
+ * back in their place as residual conjuncts. */
14
+ readonly index: number;
15
+ }
16
+ /** The plan for one block. `receiver`/`from`/`projection`/`orderBy`/`bound`/
17
+ * `consumer`/`follow`/`distinct` transcribe the AST; `correlated`/`residual`
18
+ * split its `where`; `receiverStable`, `invariant` and `fromCardinality` are the
19
+ * annotations rules add. */
20
+ export interface BlockPlan {
21
+ readonly node: OpNode;
22
+ /** Scope depth of the ENCLOSING scope (root = 0): the receiver is read there;
23
+ * the block's rows are scopes at `depth + 1`. */
24
+ readonly depth: number;
25
+ readonly consumer: Consumer;
26
+ readonly receiver: Expr;
27
+ readonly from: readonly Expr[];
28
+ readonly where: Where | null;
29
+ readonly projection: {
30
+ readonly select: readonly SelectItem[];
31
+ readonly values: boolean;
32
+ };
33
+ readonly orderBy: readonly OrderSpec[] | null;
34
+ readonly distinct: boolean;
35
+ readonly bound: {
36
+ readonly limit: Expr | undefined;
37
+ readonly offset: Expr | undefined;
38
+ };
39
+ readonly follow: Follow | null;
40
+ /** Equalities answered by an index probe on the receiver (in conjunct order). */
41
+ readonly correlated: readonly Correlation[];
42
+ /** The conjuncts still evaluated per row once the probe has selected its
43
+ * bucket; equals `where` when nothing is correlated. */
44
+ readonly residual: Where | null;
45
+ /** The receiver reads nothing from any enclosing row (only the root scope
46
+ * and bindings), so its rows are the same for every enclosing row and can be
47
+ * materialized and indexed once per run. */
48
+ readonly receiverStable: boolean;
49
+ /** The whole block reads nothing from any enclosing row and has no side
50
+ * effects on one (no lifts): its value is the same for every enclosing row
51
+ * and is computed once per run. */
52
+ readonly invariant: boolean;
53
+ /** `exists`/`none`/`count` may be answered from the cardinality of the
54
+ * accessed rows when no residual predicate remains (nothing per row to
55
+ * evaluate, order, dedup or project). */
56
+ readonly fromCardinality: boolean;
57
+ }
58
+ /** What a rule may consult besides the plan. */
59
+ export interface RuleContext {
60
+ /** How many positional bindings this run has (a `${i}` with `i` at or past
61
+ * it raises when evaluated, so it is not raise-free). */
62
+ readonly bindingCount: number;
63
+ }
64
+ /** A rewrite: return an improved plan, or `null` when the rule does not apply
65
+ * (or has already been applied). Rules must be idempotent on their own output. */
66
+ export type Rule = (plan: BlockPlan, ctx: RuleContext) => BlockPlan | null;
67
+ /** The faithful transcription of a block: a scan with every predicate residual. */
68
+ export declare function logicalBlock(node: OpNode, depth: number): BlockPlan;
69
+ /** Rebuild a conjunction from its remaining parts. */
70
+ export declare function conjunction(parts: readonly Where[]): Where | null;
71
+ /** The top-level `&&` conjuncts of a where tree (a non-`and` tree is one). */
72
+ export declare function conjuncts(where: Where): readonly Where[];
73
+ /** The residual when exactly ONE correlation is answered by a probe (a context
74
+ * index's `lookupRows`): every other conjunct, the remaining equalities
75
+ * included, stays in its original place. A kept equality is true by
76
+ * construction for the rows the probe selects and, being two reads, can neither
77
+ * raise nor bind, so the scan's strict left-to-right order and outcome hold. */
78
+ export declare function residualWithout(plan: BlockPlan, answered: Correlation): Where | null;
79
+ /** The order in which a single-correlation probe tries the plan's correlations:
80
+ * those whose outer side varies with the enclosing row first (a literal selects
81
+ * the same rows for every enclosing row and narrows nothing), each group in
82
+ * conjunct order. */
83
+ export declare function lookupOrder(plan: BlockPlan): readonly Correlation[];
84
+ //# sourceMappingURL=ir.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ir.d.ts","sourceRoot":"","sources":["../../src/optimize/ir.ts"],"names":[],"mappings":"AAYA,OAAO,KAAK,EAAE,QAAQ,EAAE,IAAI,EAAE,MAAM,EAAE,MAAM,EAAE,SAAS,EAAE,UAAU,EAAE,KAAK,EAAE,MAAM,WAAW,CAAC;AAE9F;;;;6BAI6B;AAC7B,MAAM,WAAW,WAAW;IAC1B,QAAQ,CAAC,KAAK,EAAE,IAAI,CAAC;IACrB,QAAQ,CAAC,KAAK,EAAE,IAAI,CAAC;IACrB,QAAQ,CAAC,IAAI,EAAE,SAAS,MAAM,EAAE,CAAC;IACjC;;oDAEgD;IAChD,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;CACxB;AAED;;;4BAG4B;AAC5B,MAAM,WAAW,SAAS;IACxB,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB;qDACiD;IACjD,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,QAAQ,CAAC,QAAQ,EAAE,QAAQ,CAAC;IAC5B,QAAQ,CAAC,QAAQ,EAAE,IAAI,CAAC;IACxB,QAAQ,CAAC,IAAI,EAAE,SAAS,IAAI,EAAE,CAAC;IAC/B,QAAQ,CAAC,KAAK,EAAE,KAAK,GAAG,IAAI,CAAC;IAC7B,QAAQ,CAAC,UAAU,EAAE;QAAE,QAAQ,CAAC,MAAM,EAAE,SAAS,UAAU,EAAE,CAAC;QAAC,QAAQ,CAAC,MAAM,EAAE,OAAO,CAAA;KAAE,CAAC;IAC1F,QAAQ,CAAC,OAAO,EAAE,SAAS,SAAS,EAAE,GAAG,IAAI,CAAC;IAC9C,QAAQ,CAAC,QAAQ,EAAE,OAAO,CAAC;IAC3B,QAAQ,CAAC,KAAK,EAAE;QAAE,QAAQ,CAAC,KAAK,EAAE,IAAI,GAAG,SAAS,CAAC;QAAC,QAAQ,CAAC,MAAM,EAAE,IAAI,GAAG,SAAS,CAAA;KAAE,CAAC;IACxF,QAAQ,CAAC,MAAM,EAAE,MAAM,GAAG,IAAI,CAAC;IAC/B,iFAAiF;IACjF,QAAQ,CAAC,UAAU,EAAE,SAAS,WAAW,EAAE,CAAC;IAC5C;4DACwD;IACxD,QAAQ,CAAC,QAAQ,EAAE,KAAK,GAAG,IAAI,CAAC;IAChC;;gDAE4C;IAC5C,QAAQ,CAAC,cAAc,EAAE,OAAO,CAAC;IACjC;;uCAEmC;IACnC,QAAQ,CAAC,SAAS,EAAE,OAAO,CAAC;IAC5B;;6CAEyC;IACzC,QAAQ,CAAC,eAAe,EAAE,OAAO,CAAC;CACnC;AAED,gDAAgD;AAChD,MAAM,WAAW,WAAW;IAC1B;6DACyD;IACzD,QAAQ,CAAC,YAAY,EAAE,MAAM,CAAC;CAC/B;AAED;kFACkF;AAClF,MAAM,MAAM,IAAI,GAAG,CAAC,IAAI,EAAE,SAAS,EAAE,GAAG,EAAE,WAAW,KAAK,SAAS,GAAG,IAAI,CAAC;AAE3E,mFAAmF;AACnF,wBAAgB,YAAY,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,SAAS,CAoBnE;AAED,sDAAsD;AACtD,wBAAgB,WAAW,CAAC,KAAK,EAAE,SAAS,KAAK,EAAE,GAAG,KAAK,GAAG,IAAI,CAEjE;AAED,8EAA8E;AAC9E,wBAAgB,SAAS,CAAC,KAAK,EAAE,KAAK,GAAG,SAAS,KAAK,EAAE,CAExD;AAED;;;;gFAIgF;AAChF,wBAAgB,eAAe,CAAC,IAAI,EAAE,SAAS,EAAE,QAAQ,EAAE,WAAW,GAAG,KAAK,GAAG,IAAI,CAEpF;AAED;;;qBAGqB;AACrB,wBAAgB,WAAW,CAAC,IAAI,EAAE,SAAS,GAAG,SAAS,WAAW,EAAE,CAInE"}
@@ -0,0 +1,60 @@
1
+ // The optimizer's logical representation of one nested block (an `OpNode`):
2
+ // where its rows come from, which predicates correlate them with the enclosing
3
+ // rows, what remains to be checked per row, how they are shaped, and how the
4
+ // consumer reduces them. Rules rewrite a `BlockPlan`; the engine executes it.
5
+ //
6
+ // A plan starts as a faithful transcription of the AST (`logicalBlock`): every
7
+ // predicate residual, nothing correlated, nothing memoized. Rules move work
8
+ // from the per-row path into the per-run or per-probe path only when the
9
+ // semantics argument in `rules.ts` holds; the engine's fallbacks (an index that
10
+ // cannot be built, a receiver that is not stable) always degrade to the plain
11
+ // scan the plan started from.
12
+ /** The faithful transcription of a block: a scan with every predicate residual. */
13
+ export function logicalBlock(node, depth) {
14
+ const sub = node.sub;
15
+ return {
16
+ node,
17
+ depth,
18
+ consumer: node.op,
19
+ receiver: node.receiver,
20
+ from: sub.from,
21
+ where: sub.where,
22
+ projection: { select: sub.select, values: sub.values ?? false },
23
+ orderBy: sub.orderBy,
24
+ distinct: node.distinct ?? false,
25
+ bound: { limit: sub.limit, offset: sub.offset },
26
+ follow: sub.follow,
27
+ correlated: [],
28
+ residual: sub.where,
29
+ receiverStable: false,
30
+ invariant: false,
31
+ fromCardinality: false,
32
+ };
33
+ }
34
+ /** Rebuild a conjunction from its remaining parts. */
35
+ export function conjunction(parts) {
36
+ return parts.length === 0 ? null : parts.length === 1 ? parts[0] : { kind: "and", parts: [...parts] };
37
+ }
38
+ /** The top-level `&&` conjuncts of a where tree (a non-`and` tree is one). */
39
+ export function conjuncts(where) {
40
+ return where.kind === "and" ? where.parts : [where];
41
+ }
42
+ /** The residual when exactly ONE correlation is answered by a probe (a context
43
+ * index's `lookupRows`): every other conjunct, the remaining equalities
44
+ * included, stays in its original place. A kept equality is true by
45
+ * construction for the rows the probe selects and, being two reads, can neither
46
+ * raise nor bind, so the scan's strict left-to-right order and outcome hold. */
47
+ export function residualWithout(plan, answered) {
48
+ return plan.where ? conjunction(conjuncts(plan.where).filter((_, i) => i !== answered.index)) : null;
49
+ }
50
+ /** The order in which a single-correlation probe tries the plan's correlations:
51
+ * those whose outer side varies with the enclosing row first (a literal selects
52
+ * the same rows for every enclosing row and narrows nothing), each group in
53
+ * conjunct order. */
54
+ export function lookupOrder(plan) {
55
+ const all = plan.correlated;
56
+ if (all.length < 2)
57
+ return all;
58
+ return [...all.filter((c) => c.outer.kind !== "lit"), ...all.filter((c) => c.outer.kind === "lit")];
59
+ }
60
+ //# sourceMappingURL=ir.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ir.js","sourceRoot":"","sources":["../../src/optimize/ir.ts"],"names":[],"mappings":"AAAA,4EAA4E;AAC5E,+EAA+E;AAC/E,6EAA6E;AAC7E,8EAA8E;AAC9E,EAAE;AACF,+EAA+E;AAC/E,4EAA4E;AAC5E,yEAAyE;AACzE,gFAAgF;AAChF,8EAA8E;AAC9E,8BAA8B;AAmE9B,mFAAmF;AACnF,MAAM,UAAU,YAAY,CAAC,IAAY,EAAE,KAAa;IACtD,MAAM,GAAG,GAAG,IAAI,CAAC,GAAG,CAAC;IACrB,OAAO;QACL,IAAI;QACJ,KAAK;QACL,QAAQ,EAAE,IAAI,CAAC,EAAE;QACjB,QAAQ,EAAE,IAAI,CAAC,QAAQ;QACvB,IAAI,EAAE,GAAG,CAAC,IAAI;QACd,KAAK,EAAE,GAAG,CAAC,KAAK;QAChB,UAAU,EAAE,EAAE,MAAM,EAAE,GAAG,CAAC,MAAM,EAAE,MAAM,EAAE,GAAG,CAAC,MAAM,IAAI,KAAK,EAAE;QAC/D,OAAO,EAAE,GAAG,CAAC,OAAO;QACpB,QAAQ,EAAE,IAAI,CAAC,QAAQ,IAAI,KAAK;QAChC,KAAK,EAAE,EAAE,KAAK,EAAE,GAAG,CAAC,KAAK,EAAE,MAAM,EAAE,GAAG,CAAC,MAAM,EAAE;QAC/C,MAAM,EAAE,GAAG,CAAC,MAAM;QAClB,UAAU,EAAE,EAAE;QACd,QAAQ,EAAE,GAAG,CAAC,KAAK;QACnB,cAAc,EAAE,KAAK;QACrB,SAAS,EAAE,KAAK;QAChB,eAAe,EAAE,KAAK;KACvB,CAAC;AACJ,CAAC;AAED,sDAAsD;AACtD,MAAM,UAAU,WAAW,CAAC,KAAuB;IACjD,OAAO,KAAK,CAAC,MAAM,KAAK,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,KAAK,CAAC,MAAM,KAAK,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAE,CAAC,CAAC,CAAC,EAAE,IAAI,EAAE,KAAK,EAAE,KAAK,EAAE,CAAC,GAAG,KAAK,CAAC,EAAE,CAAC;AACzG,CAAC;AAED,8EAA8E;AAC9E,MAAM,UAAU,SAAS,CAAC,KAAY;IACpC,OAAO,KAAK,CAAC,IAAI,KAAK,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC;AACtD,CAAC;AAED;;;;gFAIgF;AAChF,MAAM,UAAU,eAAe,CAAC,IAAe,EAAE,QAAqB;IACpE,OAAO,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,WAAW,CAAC,SAAS,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC,KAAK,QAAQ,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC;AACvG,CAAC;AAED;;;qBAGqB;AACrB,MAAM,UAAU,WAAW,CAAC,IAAe;IACzC,MAAM,GAAG,GAAG,IAAI,CAAC,UAAU,CAAC;IAC5B,IAAI,GAAG,CAAC,MAAM,GAAG,CAAC;QAAE,OAAO,GAAG,CAAC;IAC/B,OAAO,CAAC,GAAG,GAAG,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,KAAK,CAAC,IAAI,KAAK,KAAK,CAAC,EAAE,GAAG,GAAG,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,KAAK,CAAC,IAAI,KAAK,KAAK,CAAC,CAAC,CAAC;AACtG,CAAC"}