@tanstack/db 0.0.27 → 0.0.29
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.
- package/dist/cjs/change-events.cjs +141 -0
- package/dist/cjs/change-events.cjs.map +1 -0
- package/dist/cjs/change-events.d.cts +49 -0
- package/dist/cjs/collection.cjs +234 -86
- package/dist/cjs/collection.cjs.map +1 -1
- package/dist/cjs/collection.d.cts +95 -20
- package/dist/cjs/errors.cjs +509 -1
- package/dist/cjs/errors.cjs.map +1 -1
- package/dist/cjs/errors.d.cts +225 -1
- package/dist/cjs/index.cjs +82 -3
- package/dist/cjs/index.cjs.map +1 -1
- package/dist/cjs/index.d.cts +5 -1
- package/dist/cjs/indexes/auto-index.cjs +64 -0
- package/dist/cjs/indexes/auto-index.cjs.map +1 -0
- package/dist/cjs/indexes/auto-index.d.cts +9 -0
- package/dist/cjs/indexes/base-index.cjs +46 -0
- package/dist/cjs/indexes/base-index.cjs.map +1 -0
- package/dist/cjs/indexes/base-index.d.cts +54 -0
- package/dist/cjs/indexes/index-options.d.cts +13 -0
- package/dist/cjs/indexes/lazy-index.cjs +193 -0
- package/dist/cjs/indexes/lazy-index.cjs.map +1 -0
- package/dist/cjs/indexes/lazy-index.d.cts +96 -0
- package/dist/cjs/indexes/ordered-index.cjs +227 -0
- package/dist/cjs/indexes/ordered-index.cjs.map +1 -0
- package/dist/cjs/indexes/ordered-index.d.cts +72 -0
- package/dist/cjs/local-storage.cjs +9 -15
- package/dist/cjs/local-storage.cjs.map +1 -1
- package/dist/cjs/query/builder/functions.cjs +11 -0
- package/dist/cjs/query/builder/functions.cjs.map +1 -1
- package/dist/cjs/query/builder/functions.d.cts +4 -0
- package/dist/cjs/query/builder/index.cjs +6 -7
- package/dist/cjs/query/builder/index.cjs.map +1 -1
- package/dist/cjs/query/builder/ref-proxy.cjs +37 -0
- package/dist/cjs/query/builder/ref-proxy.cjs.map +1 -1
- package/dist/cjs/query/builder/ref-proxy.d.cts +12 -0
- package/dist/cjs/query/compiler/evaluators.cjs +83 -58
- package/dist/cjs/query/compiler/evaluators.cjs.map +1 -1
- package/dist/cjs/query/compiler/evaluators.d.cts +8 -0
- package/dist/cjs/query/compiler/expressions.cjs +61 -0
- package/dist/cjs/query/compiler/expressions.cjs.map +1 -0
- package/dist/cjs/query/compiler/expressions.d.cts +25 -0
- package/dist/cjs/query/compiler/group-by.cjs +5 -10
- package/dist/cjs/query/compiler/group-by.cjs.map +1 -1
- package/dist/cjs/query/compiler/index.cjs +23 -17
- package/dist/cjs/query/compiler/index.cjs.map +1 -1
- package/dist/cjs/query/compiler/index.d.cts +12 -3
- package/dist/cjs/query/compiler/joins.cjs +61 -12
- package/dist/cjs/query/compiler/joins.cjs.map +1 -1
- package/dist/cjs/query/compiler/order-by.cjs +4 -34
- package/dist/cjs/query/compiler/order-by.cjs.map +1 -1
- package/dist/cjs/query/compiler/types.d.cts +2 -2
- package/dist/cjs/query/live-query-collection.cjs +54 -12
- package/dist/cjs/query/live-query-collection.cjs.map +1 -1
- package/dist/cjs/query/optimizer.cjs +45 -7
- package/dist/cjs/query/optimizer.cjs.map +1 -1
- package/dist/cjs/query/optimizer.d.cts +13 -3
- package/dist/cjs/transactions.cjs +5 -4
- package/dist/cjs/transactions.cjs.map +1 -1
- package/dist/cjs/types.d.cts +31 -0
- package/dist/cjs/utils/array-utils.cjs +18 -0
- package/dist/cjs/utils/array-utils.cjs.map +1 -0
- package/dist/cjs/utils/array-utils.d.cts +8 -0
- package/dist/cjs/utils/comparison.cjs +52 -0
- package/dist/cjs/utils/comparison.cjs.map +1 -0
- package/dist/cjs/utils/comparison.d.cts +11 -0
- package/dist/cjs/utils/index-optimization.cjs +270 -0
- package/dist/cjs/utils/index-optimization.cjs.map +1 -0
- package/dist/cjs/utils/index-optimization.d.cts +29 -0
- package/dist/esm/change-events.d.ts +49 -0
- package/dist/esm/change-events.js +141 -0
- package/dist/esm/change-events.js.map +1 -0
- package/dist/esm/collection.d.ts +95 -20
- package/dist/esm/collection.js +232 -84
- package/dist/esm/collection.js.map +1 -1
- package/dist/esm/errors.d.ts +225 -1
- package/dist/esm/errors.js +510 -2
- package/dist/esm/errors.js.map +1 -1
- package/dist/esm/index.d.ts +5 -1
- package/dist/esm/index.js +81 -2
- package/dist/esm/index.js.map +1 -1
- package/dist/esm/indexes/auto-index.d.ts +9 -0
- package/dist/esm/indexes/auto-index.js +64 -0
- package/dist/esm/indexes/auto-index.js.map +1 -0
- package/dist/esm/indexes/base-index.d.ts +54 -0
- package/dist/esm/indexes/base-index.js +46 -0
- package/dist/esm/indexes/base-index.js.map +1 -0
- package/dist/esm/indexes/index-options.d.ts +13 -0
- package/dist/esm/indexes/lazy-index.d.ts +96 -0
- package/dist/esm/indexes/lazy-index.js +193 -0
- package/dist/esm/indexes/lazy-index.js.map +1 -0
- package/dist/esm/indexes/ordered-index.d.ts +72 -0
- package/dist/esm/indexes/ordered-index.js +227 -0
- package/dist/esm/indexes/ordered-index.js.map +1 -0
- package/dist/esm/local-storage.js +9 -15
- package/dist/esm/local-storage.js.map +1 -1
- package/dist/esm/query/builder/functions.d.ts +4 -0
- package/dist/esm/query/builder/functions.js +11 -0
- package/dist/esm/query/builder/functions.js.map +1 -1
- package/dist/esm/query/builder/index.js +6 -7
- package/dist/esm/query/builder/index.js.map +1 -1
- package/dist/esm/query/builder/ref-proxy.d.ts +12 -0
- package/dist/esm/query/builder/ref-proxy.js +37 -0
- package/dist/esm/query/builder/ref-proxy.js.map +1 -1
- package/dist/esm/query/compiler/evaluators.d.ts +8 -0
- package/dist/esm/query/compiler/evaluators.js +84 -59
- package/dist/esm/query/compiler/evaluators.js.map +1 -1
- package/dist/esm/query/compiler/expressions.d.ts +25 -0
- package/dist/esm/query/compiler/expressions.js +61 -0
- package/dist/esm/query/compiler/expressions.js.map +1 -0
- package/dist/esm/query/compiler/group-by.js +5 -10
- package/dist/esm/query/compiler/group-by.js.map +1 -1
- package/dist/esm/query/compiler/index.d.ts +12 -3
- package/dist/esm/query/compiler/index.js +23 -17
- package/dist/esm/query/compiler/index.js.map +1 -1
- package/dist/esm/query/compiler/joins.js +61 -12
- package/dist/esm/query/compiler/joins.js.map +1 -1
- package/dist/esm/query/compiler/order-by.js +1 -31
- package/dist/esm/query/compiler/order-by.js.map +1 -1
- package/dist/esm/query/compiler/types.d.ts +2 -2
- package/dist/esm/query/live-query-collection.js +54 -12
- package/dist/esm/query/live-query-collection.js.map +1 -1
- package/dist/esm/query/optimizer.d.ts +13 -3
- package/dist/esm/query/optimizer.js +40 -2
- package/dist/esm/query/optimizer.js.map +1 -1
- package/dist/esm/transactions.js +5 -4
- package/dist/esm/transactions.js.map +1 -1
- package/dist/esm/types.d.ts +31 -0
- package/dist/esm/utils/array-utils.d.ts +8 -0
- package/dist/esm/utils/array-utils.js +18 -0
- package/dist/esm/utils/array-utils.js.map +1 -0
- package/dist/esm/utils/comparison.d.ts +11 -0
- package/dist/esm/utils/comparison.js +52 -0
- package/dist/esm/utils/comparison.js.map +1 -0
- package/dist/esm/utils/index-optimization.d.ts +29 -0
- package/dist/esm/utils/index-optimization.js +270 -0
- package/dist/esm/utils/index-optimization.js.map +1 -0
- package/package.json +1 -1
- package/src/change-events.ts +257 -0
- package/src/collection.ts +318 -105
- package/src/errors.ts +545 -1
- package/src/index.ts +7 -1
- package/src/indexes/auto-index.ts +108 -0
- package/src/indexes/base-index.ts +119 -0
- package/src/indexes/index-options.ts +42 -0
- package/src/indexes/lazy-index.ts +251 -0
- package/src/indexes/ordered-index.ts +305 -0
- package/src/local-storage.ts +16 -17
- package/src/query/builder/functions.ts +14 -0
- package/src/query/builder/index.ts +12 -7
- package/src/query/builder/ref-proxy.ts +65 -0
- package/src/query/compiler/evaluators.ts +114 -62
- package/src/query/compiler/expressions.ts +92 -0
- package/src/query/compiler/group-by.ts +10 -10
- package/src/query/compiler/index.ts +52 -22
- package/src/query/compiler/joins.ts +114 -15
- package/src/query/compiler/order-by.ts +1 -45
- package/src/query/compiler/types.ts +2 -2
- package/src/query/live-query-collection.ts +95 -15
- package/src/query/optimizer.ts +94 -5
- package/src/transactions.ts +10 -4
- package/src/types.ts +38 -0
- package/src/utils/array-utils.ts +28 -0
- package/src/utils/comparison.ts +79 -0
- package/src/utils/index-optimization.ts +546 -0
|
@@ -1,25 +1,34 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
|
|
3
|
+
const errors = require("../../errors.cjs");
|
|
3
4
|
function compileExpression(expr) {
|
|
5
|
+
const compiledFn = compileExpressionInternal(expr, false);
|
|
6
|
+
return compiledFn;
|
|
7
|
+
}
|
|
8
|
+
function compileSingleRowExpression(expr) {
|
|
9
|
+
const compiledFn = compileExpressionInternal(expr, true);
|
|
10
|
+
return compiledFn;
|
|
11
|
+
}
|
|
12
|
+
function compileExpressionInternal(expr, isSingleRow) {
|
|
4
13
|
switch (expr.type) {
|
|
5
14
|
case `val`: {
|
|
6
15
|
const value = expr.value;
|
|
7
16
|
return () => value;
|
|
8
17
|
}
|
|
9
18
|
case `ref`: {
|
|
10
|
-
return compileRef(expr);
|
|
19
|
+
return isSingleRow ? compileSingleRowRef(expr) : compileRef(expr);
|
|
11
20
|
}
|
|
12
21
|
case `func`: {
|
|
13
|
-
return compileFunction(expr);
|
|
22
|
+
return compileFunction(expr, isSingleRow);
|
|
14
23
|
}
|
|
15
24
|
default:
|
|
16
|
-
throw new
|
|
25
|
+
throw new errors.UnknownExpressionTypeError(expr.type);
|
|
17
26
|
}
|
|
18
27
|
}
|
|
19
28
|
function compileRef(ref) {
|
|
20
29
|
const [tableAlias, ...propertyPath] = ref.path;
|
|
21
30
|
if (!tableAlias) {
|
|
22
|
-
throw new
|
|
31
|
+
throw new errors.EmptyReferencePathError();
|
|
23
32
|
}
|
|
24
33
|
if (propertyPath.length === 0) {
|
|
25
34
|
return (namespacedRow) => namespacedRow[tableAlias];
|
|
@@ -46,69 +55,84 @@ function compileRef(ref) {
|
|
|
46
55
|
};
|
|
47
56
|
}
|
|
48
57
|
}
|
|
49
|
-
function
|
|
50
|
-
const
|
|
58
|
+
function compileSingleRowRef(ref) {
|
|
59
|
+
const propertyPath = ref.path;
|
|
60
|
+
return (item) => {
|
|
61
|
+
let value = item;
|
|
62
|
+
for (const prop of propertyPath) {
|
|
63
|
+
if (value == null) {
|
|
64
|
+
return value;
|
|
65
|
+
}
|
|
66
|
+
value = value[prop];
|
|
67
|
+
}
|
|
68
|
+
return value;
|
|
69
|
+
};
|
|
70
|
+
}
|
|
71
|
+
function compileFunction(func, isSingleRow) {
|
|
72
|
+
const compiledArgs = func.args.map(
|
|
73
|
+
(arg) => compileExpressionInternal(arg, isSingleRow)
|
|
74
|
+
);
|
|
51
75
|
switch (func.name) {
|
|
52
76
|
// Comparison operators
|
|
53
77
|
case `eq`: {
|
|
54
78
|
const argA = compiledArgs[0];
|
|
55
79
|
const argB = compiledArgs[1];
|
|
56
|
-
return (
|
|
57
|
-
const a = argA(
|
|
58
|
-
const b = argB(
|
|
80
|
+
return (data) => {
|
|
81
|
+
const a = argA(data);
|
|
82
|
+
const b = argB(data);
|
|
59
83
|
return a === b;
|
|
60
84
|
};
|
|
61
85
|
}
|
|
62
86
|
case `gt`: {
|
|
63
87
|
const argA = compiledArgs[0];
|
|
64
88
|
const argB = compiledArgs[1];
|
|
65
|
-
return (
|
|
66
|
-
const a = argA(
|
|
67
|
-
const b = argB(
|
|
89
|
+
return (data) => {
|
|
90
|
+
const a = argA(data);
|
|
91
|
+
const b = argB(data);
|
|
68
92
|
return a > b;
|
|
69
93
|
};
|
|
70
94
|
}
|
|
71
95
|
case `gte`: {
|
|
72
96
|
const argA = compiledArgs[0];
|
|
73
97
|
const argB = compiledArgs[1];
|
|
74
|
-
return (
|
|
75
|
-
const a = argA(
|
|
76
|
-
const b = argB(
|
|
98
|
+
return (data) => {
|
|
99
|
+
const a = argA(data);
|
|
100
|
+
const b = argB(data);
|
|
77
101
|
return a >= b;
|
|
78
102
|
};
|
|
79
103
|
}
|
|
80
104
|
case `lt`: {
|
|
81
105
|
const argA = compiledArgs[0];
|
|
82
106
|
const argB = compiledArgs[1];
|
|
83
|
-
return (
|
|
84
|
-
const a = argA(
|
|
85
|
-
const b = argB(
|
|
107
|
+
return (data) => {
|
|
108
|
+
const a = argA(data);
|
|
109
|
+
const b = argB(data);
|
|
86
110
|
return a < b;
|
|
87
111
|
};
|
|
88
112
|
}
|
|
89
113
|
case `lte`: {
|
|
90
114
|
const argA = compiledArgs[0];
|
|
91
115
|
const argB = compiledArgs[1];
|
|
92
|
-
return (
|
|
93
|
-
const a = argA(
|
|
94
|
-
const b = argB(
|
|
116
|
+
return (data) => {
|
|
117
|
+
const a = argA(data);
|
|
118
|
+
const b = argB(data);
|
|
95
119
|
return a <= b;
|
|
96
120
|
};
|
|
97
121
|
}
|
|
98
122
|
// Boolean operators
|
|
99
123
|
case `and`:
|
|
100
|
-
return (
|
|
124
|
+
return (data) => {
|
|
101
125
|
for (const compiledArg of compiledArgs) {
|
|
102
|
-
if (!compiledArg(
|
|
126
|
+
if (!compiledArg(data)) {
|
|
103
127
|
return false;
|
|
104
128
|
}
|
|
105
129
|
}
|
|
106
130
|
return true;
|
|
107
131
|
};
|
|
108
132
|
case `or`:
|
|
109
|
-
return (
|
|
133
|
+
return (data) => {
|
|
110
134
|
for (const compiledArg of compiledArgs) {
|
|
111
|
-
if (compiledArg(
|
|
135
|
+
if (compiledArg(data)) {
|
|
112
136
|
return true;
|
|
113
137
|
}
|
|
114
138
|
}
|
|
@@ -116,15 +140,15 @@ function compileFunction(func) {
|
|
|
116
140
|
};
|
|
117
141
|
case `not`: {
|
|
118
142
|
const arg = compiledArgs[0];
|
|
119
|
-
return (
|
|
143
|
+
return (data) => !arg(data);
|
|
120
144
|
}
|
|
121
145
|
// Array operators
|
|
122
146
|
case `in`: {
|
|
123
147
|
const valueEvaluator = compiledArgs[0];
|
|
124
148
|
const arrayEvaluator = compiledArgs[1];
|
|
125
|
-
return (
|
|
126
|
-
const value = valueEvaluator(
|
|
127
|
-
const array = arrayEvaluator(
|
|
149
|
+
return (data) => {
|
|
150
|
+
const value = valueEvaluator(data);
|
|
151
|
+
const array = arrayEvaluator(data);
|
|
128
152
|
if (!Array.isArray(array)) {
|
|
129
153
|
return false;
|
|
130
154
|
}
|
|
@@ -135,40 +159,40 @@ function compileFunction(func) {
|
|
|
135
159
|
case `like`: {
|
|
136
160
|
const valueEvaluator = compiledArgs[0];
|
|
137
161
|
const patternEvaluator = compiledArgs[1];
|
|
138
|
-
return (
|
|
139
|
-
const value = valueEvaluator(
|
|
140
|
-
const pattern = patternEvaluator(
|
|
162
|
+
return (data) => {
|
|
163
|
+
const value = valueEvaluator(data);
|
|
164
|
+
const pattern = patternEvaluator(data);
|
|
141
165
|
return evaluateLike(value, pattern, false);
|
|
142
166
|
};
|
|
143
167
|
}
|
|
144
168
|
case `ilike`: {
|
|
145
169
|
const valueEvaluator = compiledArgs[0];
|
|
146
170
|
const patternEvaluator = compiledArgs[1];
|
|
147
|
-
return (
|
|
148
|
-
const value = valueEvaluator(
|
|
149
|
-
const pattern = patternEvaluator(
|
|
171
|
+
return (data) => {
|
|
172
|
+
const value = valueEvaluator(data);
|
|
173
|
+
const pattern = patternEvaluator(data);
|
|
150
174
|
return evaluateLike(value, pattern, true);
|
|
151
175
|
};
|
|
152
176
|
}
|
|
153
177
|
// String functions
|
|
154
178
|
case `upper`: {
|
|
155
179
|
const arg = compiledArgs[0];
|
|
156
|
-
return (
|
|
157
|
-
const value = arg(
|
|
180
|
+
return (data) => {
|
|
181
|
+
const value = arg(data);
|
|
158
182
|
return typeof value === `string` ? value.toUpperCase() : value;
|
|
159
183
|
};
|
|
160
184
|
}
|
|
161
185
|
case `lower`: {
|
|
162
186
|
const arg = compiledArgs[0];
|
|
163
|
-
return (
|
|
164
|
-
const value = arg(
|
|
187
|
+
return (data) => {
|
|
188
|
+
const value = arg(data);
|
|
165
189
|
return typeof value === `string` ? value.toLowerCase() : value;
|
|
166
190
|
};
|
|
167
191
|
}
|
|
168
192
|
case `length`: {
|
|
169
193
|
const arg = compiledArgs[0];
|
|
170
|
-
return (
|
|
171
|
-
const value = arg(
|
|
194
|
+
return (data) => {
|
|
195
|
+
const value = arg(data);
|
|
172
196
|
if (typeof value === `string`) {
|
|
173
197
|
return value.length;
|
|
174
198
|
}
|
|
@@ -179,9 +203,9 @@ function compileFunction(func) {
|
|
|
179
203
|
};
|
|
180
204
|
}
|
|
181
205
|
case `concat`:
|
|
182
|
-
return (
|
|
206
|
+
return (data) => {
|
|
183
207
|
return compiledArgs.map((evaluator) => {
|
|
184
|
-
const arg = evaluator(
|
|
208
|
+
const arg = evaluator(data);
|
|
185
209
|
try {
|
|
186
210
|
return String(arg ?? ``);
|
|
187
211
|
} catch {
|
|
@@ -194,9 +218,9 @@ function compileFunction(func) {
|
|
|
194
218
|
}).join(``);
|
|
195
219
|
};
|
|
196
220
|
case `coalesce`:
|
|
197
|
-
return (
|
|
221
|
+
return (data) => {
|
|
198
222
|
for (const evaluator of compiledArgs) {
|
|
199
|
-
const value = evaluator(
|
|
223
|
+
const value = evaluator(data);
|
|
200
224
|
if (value !== null && value !== void 0) {
|
|
201
225
|
return value;
|
|
202
226
|
}
|
|
@@ -207,42 +231,42 @@ function compileFunction(func) {
|
|
|
207
231
|
case `add`: {
|
|
208
232
|
const argA = compiledArgs[0];
|
|
209
233
|
const argB = compiledArgs[1];
|
|
210
|
-
return (
|
|
211
|
-
const a = argA(
|
|
212
|
-
const b = argB(
|
|
234
|
+
return (data) => {
|
|
235
|
+
const a = argA(data);
|
|
236
|
+
const b = argB(data);
|
|
213
237
|
return (a ?? 0) + (b ?? 0);
|
|
214
238
|
};
|
|
215
239
|
}
|
|
216
240
|
case `subtract`: {
|
|
217
241
|
const argA = compiledArgs[0];
|
|
218
242
|
const argB = compiledArgs[1];
|
|
219
|
-
return (
|
|
220
|
-
const a = argA(
|
|
221
|
-
const b = argB(
|
|
243
|
+
return (data) => {
|
|
244
|
+
const a = argA(data);
|
|
245
|
+
const b = argB(data);
|
|
222
246
|
return (a ?? 0) - (b ?? 0);
|
|
223
247
|
};
|
|
224
248
|
}
|
|
225
249
|
case `multiply`: {
|
|
226
250
|
const argA = compiledArgs[0];
|
|
227
251
|
const argB = compiledArgs[1];
|
|
228
|
-
return (
|
|
229
|
-
const a = argA(
|
|
230
|
-
const b = argB(
|
|
252
|
+
return (data) => {
|
|
253
|
+
const a = argA(data);
|
|
254
|
+
const b = argB(data);
|
|
231
255
|
return (a ?? 0) * (b ?? 0);
|
|
232
256
|
};
|
|
233
257
|
}
|
|
234
258
|
case `divide`: {
|
|
235
259
|
const argA = compiledArgs[0];
|
|
236
260
|
const argB = compiledArgs[1];
|
|
237
|
-
return (
|
|
238
|
-
const a = argA(
|
|
239
|
-
const b = argB(
|
|
261
|
+
return (data) => {
|
|
262
|
+
const a = argA(data);
|
|
263
|
+
const b = argB(data);
|
|
240
264
|
const divisor = b ?? 0;
|
|
241
265
|
return divisor !== 0 ? (a ?? 0) / divisor : null;
|
|
242
266
|
};
|
|
243
267
|
}
|
|
244
268
|
default:
|
|
245
|
-
throw new
|
|
269
|
+
throw new errors.UnknownFunctionError(func.name);
|
|
246
270
|
}
|
|
247
271
|
}
|
|
248
272
|
function evaluateLike(value, pattern, caseInsensitive) {
|
|
@@ -258,4 +282,5 @@ function evaluateLike(value, pattern, caseInsensitive) {
|
|
|
258
282
|
return regex.test(searchValue);
|
|
259
283
|
}
|
|
260
284
|
exports.compileExpression = compileExpression;
|
|
285
|
+
exports.compileSingleRowExpression = compileSingleRowExpression;
|
|
261
286
|
//# sourceMappingURL=evaluators.cjs.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"evaluators.cjs","sources":["../../../../src/query/compiler/evaluators.ts"],"sourcesContent":["import type { BasicExpression, Func, PropRef } from \"../ir.js\"\nimport type { NamespacedRow } from \"../../types.js\"\n\n/**\n * Compiled expression evaluator function type\n */\nexport type CompiledExpression = (namespacedRow: NamespacedRow) => any\n\n/**\n * Compiles an expression into an optimized evaluator function.\n * This eliminates branching during evaluation by pre-compiling the expression structure.\n */\nexport function compileExpression(expr: BasicExpression): CompiledExpression {\n switch (expr.type) {\n case `val`: {\n // For constant values, return a function that just returns the value\n const value = expr.value\n return () => value\n }\n\n case `ref`: {\n // For references, pre-compile the property path navigation\n return compileRef(expr)\n }\n\n case `func`: {\n // For functions, pre-compile the function and its arguments\n return compileFunction(expr)\n }\n\n default:\n throw new Error(`Unknown expression type: ${(expr as any).type}`)\n }\n}\n\n/**\n * Compiles a reference expression into an optimized evaluator\n */\nfunction compileRef(ref: PropRef): CompiledExpression {\n const [tableAlias, ...propertyPath] = ref.path\n\n if (!tableAlias) {\n throw new Error(`Reference path cannot be empty`)\n }\n\n // Pre-compile the property path navigation\n if (propertyPath.length === 0) {\n // Simple table reference\n return (namespacedRow) => namespacedRow[tableAlias]\n } else if (propertyPath.length === 1) {\n // Single property access - most common case\n const prop = propertyPath[0]!\n return (namespacedRow) => {\n const tableData = namespacedRow[tableAlias]\n return tableData?.[prop]\n }\n } else {\n // Multiple property navigation\n return (namespacedRow) => {\n const tableData = namespacedRow[tableAlias]\n if (tableData === undefined) {\n return undefined\n }\n\n let value: any = tableData\n for (const prop of propertyPath) {\n if (value == null) {\n return value\n }\n value = value[prop]\n }\n return value\n }\n }\n}\n\n/**\n * Compiles a function expression into an optimized evaluator\n */\nfunction compileFunction(func: Func): CompiledExpression {\n // Pre-compile all arguments\n const compiledArgs = func.args.map(compileExpression)\n\n switch (func.name) {\n // Comparison operators\n case `eq`: {\n const argA = compiledArgs[0]!\n const argB = compiledArgs[1]!\n return (namespacedRow) => {\n const a = argA(namespacedRow)\n const b = argB(namespacedRow)\n return a === b\n }\n }\n case `gt`: {\n const argA = compiledArgs[0]!\n const argB = compiledArgs[1]!\n return (namespacedRow) => {\n const a = argA(namespacedRow)\n const b = argB(namespacedRow)\n return a > b\n }\n }\n case `gte`: {\n const argA = compiledArgs[0]!\n const argB = compiledArgs[1]!\n return (namespacedRow) => {\n const a = argA(namespacedRow)\n const b = argB(namespacedRow)\n return a >= b\n }\n }\n case `lt`: {\n const argA = compiledArgs[0]!\n const argB = compiledArgs[1]!\n return (namespacedRow) => {\n const a = argA(namespacedRow)\n const b = argB(namespacedRow)\n return a < b\n }\n }\n case `lte`: {\n const argA = compiledArgs[0]!\n const argB = compiledArgs[1]!\n return (namespacedRow) => {\n const a = argA(namespacedRow)\n const b = argB(namespacedRow)\n return a <= b\n }\n }\n\n // Boolean operators\n case `and`:\n return (namespacedRow) => {\n for (const compiledArg of compiledArgs) {\n if (!compiledArg(namespacedRow)) {\n return false\n }\n }\n return true\n }\n case `or`:\n return (namespacedRow) => {\n for (const compiledArg of compiledArgs) {\n if (compiledArg(namespacedRow)) {\n return true\n }\n }\n return false\n }\n case `not`: {\n const arg = compiledArgs[0]!\n return (namespacedRow) => !arg(namespacedRow)\n }\n\n // Array operators\n case `in`: {\n const valueEvaluator = compiledArgs[0]!\n const arrayEvaluator = compiledArgs[1]!\n return (namespacedRow) => {\n const value = valueEvaluator(namespacedRow)\n const array = arrayEvaluator(namespacedRow)\n if (!Array.isArray(array)) {\n return false\n }\n return array.includes(value)\n }\n }\n\n // String operators\n case `like`: {\n const valueEvaluator = compiledArgs[0]!\n const patternEvaluator = compiledArgs[1]!\n return (namespacedRow) => {\n const value = valueEvaluator(namespacedRow)\n const pattern = patternEvaluator(namespacedRow)\n return evaluateLike(value, pattern, false)\n }\n }\n case `ilike`: {\n const valueEvaluator = compiledArgs[0]!\n const patternEvaluator = compiledArgs[1]!\n return (namespacedRow) => {\n const value = valueEvaluator(namespacedRow)\n const pattern = patternEvaluator(namespacedRow)\n return evaluateLike(value, pattern, true)\n }\n }\n\n // String functions\n case `upper`: {\n const arg = compiledArgs[0]!\n return (namespacedRow) => {\n const value = arg(namespacedRow)\n return typeof value === `string` ? value.toUpperCase() : value\n }\n }\n case `lower`: {\n const arg = compiledArgs[0]!\n return (namespacedRow) => {\n const value = arg(namespacedRow)\n return typeof value === `string` ? value.toLowerCase() : value\n }\n }\n case `length`: {\n const arg = compiledArgs[0]!\n return (namespacedRow) => {\n const value = arg(namespacedRow)\n if (typeof value === `string`) {\n return value.length\n }\n if (Array.isArray(value)) {\n return value.length\n }\n return 0\n }\n }\n case `concat`:\n return (namespacedRow) => {\n return compiledArgs\n .map((evaluator) => {\n const arg = evaluator(namespacedRow)\n try {\n return String(arg ?? ``)\n } catch {\n try {\n return JSON.stringify(arg) || ``\n } catch {\n return `[object]`\n }\n }\n })\n .join(``)\n }\n case `coalesce`:\n return (namespacedRow) => {\n for (const evaluator of compiledArgs) {\n const value = evaluator(namespacedRow)\n if (value !== null && value !== undefined) {\n return value\n }\n }\n return null\n }\n\n // Math functions\n case `add`: {\n const argA = compiledArgs[0]!\n const argB = compiledArgs[1]!\n return (namespacedRow) => {\n const a = argA(namespacedRow)\n const b = argB(namespacedRow)\n return (a ?? 0) + (b ?? 0)\n }\n }\n case `subtract`: {\n const argA = compiledArgs[0]!\n const argB = compiledArgs[1]!\n return (namespacedRow) => {\n const a = argA(namespacedRow)\n const b = argB(namespacedRow)\n return (a ?? 0) - (b ?? 0)\n }\n }\n case `multiply`: {\n const argA = compiledArgs[0]!\n const argB = compiledArgs[1]!\n return (namespacedRow) => {\n const a = argA(namespacedRow)\n const b = argB(namespacedRow)\n return (a ?? 0) * (b ?? 0)\n }\n }\n case `divide`: {\n const argA = compiledArgs[0]!\n const argB = compiledArgs[1]!\n return (namespacedRow) => {\n const a = argA(namespacedRow)\n const b = argB(namespacedRow)\n const divisor = b ?? 0\n return divisor !== 0 ? (a ?? 0) / divisor : null\n }\n }\n\n default:\n throw new Error(`Unknown function: ${func.name}`)\n }\n}\n\n/**\n * Evaluates LIKE/ILIKE patterns\n */\nfunction evaluateLike(\n value: any,\n pattern: any,\n caseInsensitive: boolean\n): boolean {\n if (typeof value !== `string` || typeof pattern !== `string`) {\n return false\n }\n\n const searchValue = caseInsensitive ? value.toLowerCase() : value\n const searchPattern = caseInsensitive ? pattern.toLowerCase() : pattern\n\n // Convert SQL LIKE pattern to regex\n // First escape all regex special chars except % and _\n let regexPattern = searchPattern.replace(/[.*+?^${}()|[\\]\\\\]/g, `\\\\$&`)\n\n // Then convert SQL wildcards to regex\n regexPattern = regexPattern.replace(/%/g, `.*`) // % matches any sequence\n regexPattern = regexPattern.replace(/_/g, `.`) // _ matches any single char\n\n const regex = new RegExp(`^${regexPattern}$`)\n return regex.test(searchValue)\n}\n"],"names":[],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"evaluators.cjs","sources":["../../../../src/query/compiler/evaluators.ts"],"sourcesContent":["import {\n EmptyReferencePathError,\n UnknownExpressionTypeError,\n UnknownFunctionError,\n} from \"../../errors.js\"\nimport type { BasicExpression, Func, PropRef } from \"../ir.js\"\nimport type { NamespacedRow } from \"../../types.js\"\n\n/**\n * Compiled expression evaluator function type\n */\nexport type CompiledExpression = (namespacedRow: NamespacedRow) => any\n\n/**\n * Compiled single-row expression evaluator function type\n */\nexport type CompiledSingleRowExpression = (item: Record<string, unknown>) => any\n\n/**\n * Compiles an expression into an optimized evaluator function.\n * This eliminates branching during evaluation by pre-compiling the expression structure.\n */\nexport function compileExpression(expr: BasicExpression): CompiledExpression {\n const compiledFn = compileExpressionInternal(expr, false)\n return compiledFn as CompiledExpression\n}\n\n/**\n * Compiles a single-row expression into an optimized evaluator function.\n */\nexport function compileSingleRowExpression(\n expr: BasicExpression\n): CompiledSingleRowExpression {\n const compiledFn = compileExpressionInternal(expr, true)\n return compiledFn as CompiledSingleRowExpression\n}\n\n/**\n * Internal unified expression compiler that handles both namespaced and single-row evaluation\n */\nfunction compileExpressionInternal(\n expr: BasicExpression,\n isSingleRow: boolean\n): (data: any) => any {\n switch (expr.type) {\n case `val`: {\n // For constant values, return a function that just returns the value\n const value = expr.value\n return () => value\n }\n\n case `ref`: {\n // For references, compile based on evaluation mode\n return isSingleRow ? compileSingleRowRef(expr) : compileRef(expr)\n }\n\n case `func`: {\n // For functions, use the unified compiler\n return compileFunction(expr, isSingleRow)\n }\n\n default:\n throw new UnknownExpressionTypeError((expr as any).type)\n }\n}\n\n/**\n * Compiles a reference expression into an optimized evaluator\n */\nfunction compileRef(ref: PropRef): CompiledExpression {\n const [tableAlias, ...propertyPath] = ref.path\n\n if (!tableAlias) {\n throw new EmptyReferencePathError()\n }\n\n // Pre-compile the property path navigation\n if (propertyPath.length === 0) {\n // Simple table reference\n return (namespacedRow) => namespacedRow[tableAlias]\n } else if (propertyPath.length === 1) {\n // Single property access - most common case\n const prop = propertyPath[0]!\n return (namespacedRow) => {\n const tableData = namespacedRow[tableAlias]\n return tableData?.[prop]\n }\n } else {\n // Multiple property navigation\n return (namespacedRow) => {\n const tableData = namespacedRow[tableAlias]\n if (tableData === undefined) {\n return undefined\n }\n\n let value: any = tableData\n for (const prop of propertyPath) {\n if (value == null) {\n return value\n }\n value = value[prop]\n }\n return value\n }\n }\n}\n\n/**\n * Compiles a reference expression for single-row evaluation\n */\nfunction compileSingleRowRef(ref: PropRef): CompiledSingleRowExpression {\n const propertyPath = ref.path\n\n // This function works for all path lengths including empty path\n return (item) => {\n let value: any = item\n for (const prop of propertyPath) {\n if (value == null) {\n return value\n }\n value = value[prop]\n }\n return value\n }\n}\n\n/**\n * Compiles a function expression for both namespaced and single-row evaluation\n */\nfunction compileFunction(func: Func, isSingleRow: boolean): (data: any) => any {\n // Pre-compile all arguments using the appropriate compiler\n const compiledArgs = func.args.map((arg) =>\n compileExpressionInternal(arg, isSingleRow)\n )\n\n switch (func.name) {\n // Comparison operators\n case `eq`: {\n const argA = compiledArgs[0]!\n const argB = compiledArgs[1]!\n return (data) => {\n const a = argA(data)\n const b = argB(data)\n return a === b\n }\n }\n case `gt`: {\n const argA = compiledArgs[0]!\n const argB = compiledArgs[1]!\n return (data) => {\n const a = argA(data)\n const b = argB(data)\n return a > b\n }\n }\n case `gte`: {\n const argA = compiledArgs[0]!\n const argB = compiledArgs[1]!\n return (data) => {\n const a = argA(data)\n const b = argB(data)\n return a >= b\n }\n }\n case `lt`: {\n const argA = compiledArgs[0]!\n const argB = compiledArgs[1]!\n return (data) => {\n const a = argA(data)\n const b = argB(data)\n return a < b\n }\n }\n case `lte`: {\n const argA = compiledArgs[0]!\n const argB = compiledArgs[1]!\n return (data) => {\n const a = argA(data)\n const b = argB(data)\n return a <= b\n }\n }\n\n // Boolean operators\n case `and`:\n return (data) => {\n for (const compiledArg of compiledArgs) {\n if (!compiledArg(data)) {\n return false\n }\n }\n return true\n }\n case `or`:\n return (data) => {\n for (const compiledArg of compiledArgs) {\n if (compiledArg(data)) {\n return true\n }\n }\n return false\n }\n case `not`: {\n const arg = compiledArgs[0]!\n return (data) => !arg(data)\n }\n\n // Array operators\n case `in`: {\n const valueEvaluator = compiledArgs[0]!\n const arrayEvaluator = compiledArgs[1]!\n return (data) => {\n const value = valueEvaluator(data)\n const array = arrayEvaluator(data)\n if (!Array.isArray(array)) {\n return false\n }\n return array.includes(value)\n }\n }\n\n // String operators\n case `like`: {\n const valueEvaluator = compiledArgs[0]!\n const patternEvaluator = compiledArgs[1]!\n return (data) => {\n const value = valueEvaluator(data)\n const pattern = patternEvaluator(data)\n return evaluateLike(value, pattern, false)\n }\n }\n case `ilike`: {\n const valueEvaluator = compiledArgs[0]!\n const patternEvaluator = compiledArgs[1]!\n return (data) => {\n const value = valueEvaluator(data)\n const pattern = patternEvaluator(data)\n return evaluateLike(value, pattern, true)\n }\n }\n\n // String functions\n case `upper`: {\n const arg = compiledArgs[0]!\n return (data) => {\n const value = arg(data)\n return typeof value === `string` ? value.toUpperCase() : value\n }\n }\n case `lower`: {\n const arg = compiledArgs[0]!\n return (data) => {\n const value = arg(data)\n return typeof value === `string` ? value.toLowerCase() : value\n }\n }\n case `length`: {\n const arg = compiledArgs[0]!\n return (data) => {\n const value = arg(data)\n if (typeof value === `string`) {\n return value.length\n }\n if (Array.isArray(value)) {\n return value.length\n }\n return 0\n }\n }\n case `concat`:\n return (data) => {\n return compiledArgs\n .map((evaluator) => {\n const arg = evaluator(data)\n try {\n return String(arg ?? ``)\n } catch {\n try {\n return JSON.stringify(arg) || ``\n } catch {\n return `[object]`\n }\n }\n })\n .join(``)\n }\n case `coalesce`:\n return (data) => {\n for (const evaluator of compiledArgs) {\n const value = evaluator(data)\n if (value !== null && value !== undefined) {\n return value\n }\n }\n return null\n }\n\n // Math functions\n case `add`: {\n const argA = compiledArgs[0]!\n const argB = compiledArgs[1]!\n return (data) => {\n const a = argA(data)\n const b = argB(data)\n return (a ?? 0) + (b ?? 0)\n }\n }\n case `subtract`: {\n const argA = compiledArgs[0]!\n const argB = compiledArgs[1]!\n return (data) => {\n const a = argA(data)\n const b = argB(data)\n return (a ?? 0) - (b ?? 0)\n }\n }\n case `multiply`: {\n const argA = compiledArgs[0]!\n const argB = compiledArgs[1]!\n return (data) => {\n const a = argA(data)\n const b = argB(data)\n return (a ?? 0) * (b ?? 0)\n }\n }\n case `divide`: {\n const argA = compiledArgs[0]!\n const argB = compiledArgs[1]!\n return (data) => {\n const a = argA(data)\n const b = argB(data)\n const divisor = b ?? 0\n return divisor !== 0 ? (a ?? 0) / divisor : null\n }\n }\n\n default:\n throw new UnknownFunctionError(func.name)\n }\n}\n\n/**\n * Evaluates LIKE/ILIKE patterns\n */\nfunction evaluateLike(\n value: any,\n pattern: any,\n caseInsensitive: boolean\n): boolean {\n if (typeof value !== `string` || typeof pattern !== `string`) {\n return false\n }\n\n const searchValue = caseInsensitive ? value.toLowerCase() : value\n const searchPattern = caseInsensitive ? pattern.toLowerCase() : pattern\n\n // Convert SQL LIKE pattern to regex\n // First escape all regex special chars except % and _\n let regexPattern = searchPattern.replace(/[.*+?^${}()|[\\]\\\\]/g, `\\\\$&`)\n\n // Then convert SQL wildcards to regex\n regexPattern = regexPattern.replace(/%/g, `.*`) // % matches any sequence\n regexPattern = regexPattern.replace(/_/g, `.`) // _ matches any single char\n\n const regex = new RegExp(`^${regexPattern}$`)\n return regex.test(searchValue)\n}\n"],"names":["UnknownExpressionTypeError","EmptyReferencePathError","UnknownFunctionError"],"mappings":"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"}
|
|
@@ -4,8 +4,16 @@ import { NamespacedRow } from '../../types.js';
|
|
|
4
4
|
* Compiled expression evaluator function type
|
|
5
5
|
*/
|
|
6
6
|
export type CompiledExpression = (namespacedRow: NamespacedRow) => any;
|
|
7
|
+
/**
|
|
8
|
+
* Compiled single-row expression evaluator function type
|
|
9
|
+
*/
|
|
10
|
+
export type CompiledSingleRowExpression = (item: Record<string, unknown>) => any;
|
|
7
11
|
/**
|
|
8
12
|
* Compiles an expression into an optimized evaluator function.
|
|
9
13
|
* This eliminates branching during evaluation by pre-compiling the expression structure.
|
|
10
14
|
*/
|
|
11
15
|
export declare function compileExpression(expr: BasicExpression): CompiledExpression;
|
|
16
|
+
/**
|
|
17
|
+
* Compiles a single-row expression into an optimized evaluator function.
|
|
18
|
+
*/
|
|
19
|
+
export declare function compileSingleRowExpression(expr: BasicExpression): CompiledSingleRowExpression;
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
|
|
3
|
+
const ir = require("../ir.cjs");
|
|
4
|
+
const SUPPORTED_COLLECTION_FUNCS = /* @__PURE__ */ new Set([
|
|
5
|
+
`eq`,
|
|
6
|
+
`gt`,
|
|
7
|
+
`lt`,
|
|
8
|
+
`gte`,
|
|
9
|
+
`lte`,
|
|
10
|
+
`and`,
|
|
11
|
+
`or`,
|
|
12
|
+
`in`
|
|
13
|
+
]);
|
|
14
|
+
function isConvertibleToCollectionFilter(whereClause) {
|
|
15
|
+
const tpe = whereClause.type;
|
|
16
|
+
if (tpe === `func`) {
|
|
17
|
+
if (!SUPPORTED_COLLECTION_FUNCS.has(whereClause.name)) {
|
|
18
|
+
return false;
|
|
19
|
+
}
|
|
20
|
+
return whereClause.args.every(
|
|
21
|
+
(arg) => isConvertibleToCollectionFilter(arg)
|
|
22
|
+
);
|
|
23
|
+
}
|
|
24
|
+
return [`val`, `ref`].includes(tpe);
|
|
25
|
+
}
|
|
26
|
+
function convertToBasicExpression(whereClause, collectionAlias) {
|
|
27
|
+
const tpe = whereClause.type;
|
|
28
|
+
if (tpe === `val`) {
|
|
29
|
+
return new ir.Value(whereClause.value);
|
|
30
|
+
} else if (tpe === `ref`) {
|
|
31
|
+
const path = whereClause.path;
|
|
32
|
+
if (Array.isArray(path)) {
|
|
33
|
+
if (path[0] === collectionAlias && path.length > 1) {
|
|
34
|
+
return new ir.PropRef(path.slice(1));
|
|
35
|
+
} else if (path.length === 1 && path[0] !== void 0) {
|
|
36
|
+
return new ir.PropRef([path[0]]);
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
return new ir.PropRef(Array.isArray(path) ? path : [String(path)]);
|
|
40
|
+
} else {
|
|
41
|
+
if (!SUPPORTED_COLLECTION_FUNCS.has(whereClause.name)) {
|
|
42
|
+
return null;
|
|
43
|
+
}
|
|
44
|
+
const args = [];
|
|
45
|
+
for (const arg of whereClause.args) {
|
|
46
|
+
const convertedArg = convertToBasicExpression(
|
|
47
|
+
arg,
|
|
48
|
+
collectionAlias
|
|
49
|
+
);
|
|
50
|
+
if (convertedArg == null) {
|
|
51
|
+
return null;
|
|
52
|
+
}
|
|
53
|
+
args.push(convertedArg);
|
|
54
|
+
}
|
|
55
|
+
return new ir.Func(whereClause.name, args);
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
exports.SUPPORTED_COLLECTION_FUNCS = SUPPORTED_COLLECTION_FUNCS;
|
|
59
|
+
exports.convertToBasicExpression = convertToBasicExpression;
|
|
60
|
+
exports.isConvertibleToCollectionFilter = isConvertibleToCollectionFilter;
|
|
61
|
+
//# sourceMappingURL=expressions.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"expressions.cjs","sources":["../../../../src/query/compiler/expressions.ts"],"sourcesContent":["import { Func, PropRef, Value } from \"../ir.js\"\nimport type { BasicExpression } from \"../ir.js\"\n\n/**\n * Functions supported by the collection index system.\n * These are the only functions that can be used in WHERE clauses\n * that are pushed down to collection subscriptions for index optimization.\n */\nexport const SUPPORTED_COLLECTION_FUNCS = new Set([\n `eq`,\n `gt`,\n `lt`,\n `gte`,\n `lte`,\n `and`,\n `or`,\n `in`,\n])\n\n/**\n * Determines if a WHERE clause can be converted to collection-compatible BasicExpression format.\n * This checks if the expression only uses functions supported by the collection index system.\n *\n * @param whereClause - The WHERE clause to check\n * @returns True if the clause can be converted for collection index optimization\n */\nexport function isConvertibleToCollectionFilter(\n whereClause: BasicExpression<boolean>\n): boolean {\n const tpe = whereClause.type\n if (tpe === `func`) {\n // Check if this function is supported\n if (!SUPPORTED_COLLECTION_FUNCS.has(whereClause.name)) {\n return false\n }\n // Recursively check all arguments\n return whereClause.args.every((arg) =>\n isConvertibleToCollectionFilter(arg as BasicExpression<boolean>)\n )\n }\n return [`val`, `ref`].includes(tpe)\n}\n\n/**\n * Converts a WHERE clause to BasicExpression format compatible with collection indexes.\n * This function creates proper BasicExpression class instances that the collection\n * index system can understand.\n *\n * @param whereClause - The WHERE clause to convert\n * @param collectionAlias - The alias of the collection being filtered\n * @returns The converted BasicExpression or null if conversion fails\n */\nexport function convertToBasicExpression(\n whereClause: BasicExpression<boolean>,\n collectionAlias: string\n): BasicExpression<boolean> | null {\n const tpe = whereClause.type\n if (tpe === `val`) {\n return new Value(whereClause.value)\n } else if (tpe === `ref`) {\n const path = whereClause.path\n if (Array.isArray(path)) {\n if (path[0] === collectionAlias && path.length > 1) {\n // Remove the table alias from the path for single-collection queries\n return new PropRef(path.slice(1))\n } else if (path.length === 1 && path[0] !== undefined) {\n // Single field reference\n return new PropRef([path[0]])\n }\n }\n // Fallback for non-array paths\n return new PropRef(Array.isArray(path) ? path : [String(path)])\n } else {\n // Check if this function is supported\n if (!SUPPORTED_COLLECTION_FUNCS.has(whereClause.name)) {\n return null\n }\n // Recursively convert all arguments\n const args: Array<BasicExpression> = []\n for (const arg of whereClause.args) {\n const convertedArg = convertToBasicExpression(\n arg as BasicExpression<boolean>,\n collectionAlias\n )\n if (convertedArg == null) {\n return null\n }\n args.push(convertedArg)\n }\n return new Func(whereClause.name, args)\n }\n}\n"],"names":["Value","PropRef","Func"],"mappings":";;;AAQO,MAAM,iDAAiC,IAAI;AAAA,EAChD;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF,CAAC;AASM,SAAS,gCACd,aACS;AACT,QAAM,MAAM,YAAY;AACxB,MAAI,QAAQ,QAAQ;AAElB,QAAI,CAAC,2BAA2B,IAAI,YAAY,IAAI,GAAG;AACrD,aAAO;AAAA,IACT;AAEA,WAAO,YAAY,KAAK;AAAA,MAAM,CAAC,QAC7B,gCAAgC,GAA+B;AAAA,IAAA;AAAA,EAEnE;AACA,SAAO,CAAC,OAAO,KAAK,EAAE,SAAS,GAAG;AACpC;AAWO,SAAS,yBACd,aACA,iBACiC;AACjC,QAAM,MAAM,YAAY;AACxB,MAAI,QAAQ,OAAO;AACjB,WAAO,IAAIA,GAAAA,MAAM,YAAY,KAAK;AAAA,EACpC,WAAW,QAAQ,OAAO;AACxB,UAAM,OAAO,YAAY;AACzB,QAAI,MAAM,QAAQ,IAAI,GAAG;AACvB,UAAI,KAAK,CAAC,MAAM,mBAAmB,KAAK,SAAS,GAAG;AAElD,eAAO,IAAIC,GAAAA,QAAQ,KAAK,MAAM,CAAC,CAAC;AAAA,MAClC,WAAW,KAAK,WAAW,KAAK,KAAK,CAAC,MAAM,QAAW;AAErD,eAAO,IAAIA,GAAAA,QAAQ,CAAC,KAAK,CAAC,CAAC,CAAC;AAAA,MAC9B;AAAA,IACF;AAEA,WAAO,IAAIA,GAAAA,QAAQ,MAAM,QAAQ,IAAI,IAAI,OAAO,CAAC,OAAO,IAAI,CAAC,CAAC;AAAA,EAChE,OAAO;AAEL,QAAI,CAAC,2BAA2B,IAAI,YAAY,IAAI,GAAG;AACrD,aAAO;AAAA,IACT;AAEA,UAAM,OAA+B,CAAA;AACrC,eAAW,OAAO,YAAY,MAAM;AAClC,YAAM,eAAe;AAAA,QACnB;AAAA,QACA;AAAA,MAAA;AAEF,UAAI,gBAAgB,MAAM;AACxB,eAAO;AAAA,MACT;AACA,WAAK,KAAK,YAAY;AAAA,IACxB;AACA,WAAO,IAAIC,GAAAA,KAAK,YAAY,MAAM,IAAI;AAAA,EACxC;AACF;;;;"}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import { BasicExpression } from '../ir.js';
|
|
2
|
+
/**
|
|
3
|
+
* Functions supported by the collection index system.
|
|
4
|
+
* These are the only functions that can be used in WHERE clauses
|
|
5
|
+
* that are pushed down to collection subscriptions for index optimization.
|
|
6
|
+
*/
|
|
7
|
+
export declare const SUPPORTED_COLLECTION_FUNCS: Set<string>;
|
|
8
|
+
/**
|
|
9
|
+
* Determines if a WHERE clause can be converted to collection-compatible BasicExpression format.
|
|
10
|
+
* This checks if the expression only uses functions supported by the collection index system.
|
|
11
|
+
*
|
|
12
|
+
* @param whereClause - The WHERE clause to check
|
|
13
|
+
* @returns True if the clause can be converted for collection index optimization
|
|
14
|
+
*/
|
|
15
|
+
export declare function isConvertibleToCollectionFilter(whereClause: BasicExpression<boolean>): boolean;
|
|
16
|
+
/**
|
|
17
|
+
* Converts a WHERE clause to BasicExpression format compatible with collection indexes.
|
|
18
|
+
* This function creates proper BasicExpression class instances that the collection
|
|
19
|
+
* index system can understand.
|
|
20
|
+
*
|
|
21
|
+
* @param whereClause - The WHERE clause to convert
|
|
22
|
+
* @param collectionAlias - The alias of the collection being filtered
|
|
23
|
+
* @returns The converted BasicExpression or null if conversion fails
|
|
24
|
+
*/
|
|
25
|
+
export declare function convertToBasicExpression(whereClause: BasicExpression<boolean>, collectionAlias: string): BasicExpression<boolean> | null;
|
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
|
|
3
3
|
const d2mini = require("@electric-sql/d2mini");
|
|
4
4
|
const ir = require("../ir.cjs");
|
|
5
|
+
const errors = require("../../errors.cjs");
|
|
5
6
|
const evaluators = require("./evaluators.cjs");
|
|
6
7
|
const { sum, count, avg, min, max } = d2mini.groupByOperators;
|
|
7
8
|
function validateAndCreateMapping(groupByClause, selectClause) {
|
|
@@ -18,9 +19,7 @@ function validateAndCreateMapping(groupByClause, selectClause) {
|
|
|
18
19
|
(groupExpr) => expressionsEqual(expr, groupExpr)
|
|
19
20
|
);
|
|
20
21
|
if (groupIndex === -1) {
|
|
21
|
-
throw new
|
|
22
|
-
`Non-aggregate expression '${alias}' in SELECT must also appear in GROUP BY clause`
|
|
23
|
-
);
|
|
22
|
+
throw new errors.NonAggregateExpressionNotInGroupByError(alias);
|
|
24
23
|
}
|
|
25
24
|
selectToGroupByIndex.set(alias, groupIndex);
|
|
26
25
|
}
|
|
@@ -223,7 +222,7 @@ function getAggregateFunction(aggExpr) {
|
|
|
223
222
|
case `max`:
|
|
224
223
|
return max(valueExtractor);
|
|
225
224
|
default:
|
|
226
|
-
throw new
|
|
225
|
+
throw new errors.UnsupportedAggregateFunctionError(aggExpr.name);
|
|
227
226
|
}
|
|
228
227
|
}
|
|
229
228
|
function transformHavingClause(havingExpr, selectClause) {
|
|
@@ -235,9 +234,7 @@ function transformHavingClause(havingExpr, selectClause) {
|
|
|
235
234
|
return new ir.PropRef([`result`, alias]);
|
|
236
235
|
}
|
|
237
236
|
}
|
|
238
|
-
throw new
|
|
239
|
-
`Aggregate function in HAVING clause must also be in SELECT clause: ${aggExpr.name}`
|
|
240
|
-
);
|
|
237
|
+
throw new errors.AggregateFunctionNotInSelectError(aggExpr.name);
|
|
241
238
|
}
|
|
242
239
|
case `func`: {
|
|
243
240
|
const funcExpr = havingExpr;
|
|
@@ -259,9 +256,7 @@ function transformHavingClause(havingExpr, selectClause) {
|
|
|
259
256
|
case `val`:
|
|
260
257
|
return havingExpr;
|
|
261
258
|
default:
|
|
262
|
-
throw new
|
|
263
|
-
`Unknown expression type in HAVING clause: ${havingExpr.type}`
|
|
264
|
-
);
|
|
259
|
+
throw new errors.UnknownHavingExpressionTypeError(havingExpr.type);
|
|
265
260
|
}
|
|
266
261
|
}
|
|
267
262
|
function aggregatesEqual(agg1, agg2) {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"group-by.cjs","sources":["../../../../src/query/compiler/group-by.ts"],"sourcesContent":["import { filter, groupBy, groupByOperators, map } from \"@electric-sql/d2mini\"\nimport { Func, PropRef } from \"../ir.js\"\nimport { compileExpression } from \"./evaluators.js\"\nimport type {\n Aggregate,\n BasicExpression,\n GroupBy,\n Having,\n Select,\n} from \"../ir.js\"\nimport type { NamespacedAndKeyedStream, NamespacedRow } from \"../../types.js\"\n\nconst { sum, count, avg, min, max } = groupByOperators\n\n/**\n * Interface for caching the mapping between GROUP BY expressions and SELECT expressions\n */\ninterface GroupBySelectMapping {\n selectToGroupByIndex: Map<string, number> // Maps SELECT alias to GROUP BY expression index\n groupByExpressions: Array<any> // The GROUP BY expressions for reference\n}\n\n/**\n * Validates that all non-aggregate expressions in SELECT are present in GROUP BY\n * and creates a cached mapping for efficient lookup during processing\n */\nfunction validateAndCreateMapping(\n groupByClause: GroupBy,\n selectClause?: Select\n): GroupBySelectMapping {\n const selectToGroupByIndex = new Map<string, number>()\n const groupByExpressions = [...groupByClause]\n\n if (!selectClause) {\n return { selectToGroupByIndex, groupByExpressions }\n }\n\n // Validate each SELECT expression\n for (const [alias, expr] of Object.entries(selectClause)) {\n if (expr.type === `agg`) {\n // Aggregate expressions are allowed and don't need to be in GROUP BY\n continue\n }\n\n // Non-aggregate expression must be in GROUP BY\n const groupIndex = groupByExpressions.findIndex((groupExpr) =>\n expressionsEqual(expr, groupExpr)\n )\n\n if (groupIndex === -1) {\n throw new Error(\n `Non-aggregate expression '${alias}' in SELECT must also appear in GROUP BY clause`\n )\n }\n\n // Cache the mapping\n selectToGroupByIndex.set(alias, groupIndex)\n }\n\n return { selectToGroupByIndex, groupByExpressions }\n}\n\n/**\n * Processes the GROUP BY clause with optional HAVING and SELECT\n * Works with the new __select_results structure from early SELECT processing\n */\nexport function processGroupBy(\n pipeline: NamespacedAndKeyedStream,\n groupByClause: GroupBy,\n havingClauses?: Array<Having>,\n selectClause?: Select,\n fnHavingClauses?: Array<(row: any) => any>\n): NamespacedAndKeyedStream {\n // Handle empty GROUP BY (single-group aggregation)\n if (groupByClause.length === 0) {\n // For single-group aggregation, create a single group with all data\n const aggregates: Record<string, any> = {}\n\n if (selectClause) {\n // Scan the SELECT clause for aggregate functions\n for (const [alias, expr] of Object.entries(selectClause)) {\n if (expr.type === `agg`) {\n const aggExpr = expr\n aggregates[alias] = getAggregateFunction(aggExpr)\n }\n }\n }\n\n // Use a constant key for single group\n const keyExtractor = () => ({ __singleGroup: true })\n\n // Apply the groupBy operator with single group\n pipeline = pipeline.pipe(\n groupBy(keyExtractor, aggregates)\n ) as NamespacedAndKeyedStream\n\n // Update __select_results to include aggregate values\n pipeline = pipeline.pipe(\n map(([, aggregatedRow]) => {\n // Start with the existing __select_results from early SELECT processing\n const selectResults = (aggregatedRow as any).__select_results || {}\n const finalResults: Record<string, any> = { ...selectResults }\n\n if (selectClause) {\n // Update with aggregate results\n for (const [alias, expr] of Object.entries(selectClause)) {\n if (expr.type === `agg`) {\n finalResults[alias] = aggregatedRow[alias]\n }\n // Non-aggregates keep their original values from early SELECT processing\n }\n }\n\n // Use a single key for the result and update __select_results\n return [\n `single_group`,\n {\n ...aggregatedRow,\n __select_results: finalResults,\n },\n ] as [unknown, Record<string, any>]\n })\n )\n\n // Apply HAVING clauses if present\n if (havingClauses && havingClauses.length > 0) {\n for (const havingClause of havingClauses) {\n const transformedHavingClause = transformHavingClause(\n havingClause,\n selectClause || {}\n )\n const compiledHaving = compileExpression(transformedHavingClause)\n\n pipeline = pipeline.pipe(\n filter(([, row]) => {\n // Create a namespaced row structure for HAVING evaluation\n const namespacedRow = { result: (row as any).__select_results }\n return compiledHaving(namespacedRow)\n })\n )\n }\n }\n\n // Apply functional HAVING clauses if present\n if (fnHavingClauses && fnHavingClauses.length > 0) {\n for (const fnHaving of fnHavingClauses) {\n pipeline = pipeline.pipe(\n filter(([, row]) => {\n // Create a namespaced row structure for functional HAVING evaluation\n const namespacedRow = { result: (row as any).__select_results }\n return fnHaving(namespacedRow)\n })\n )\n }\n }\n\n return pipeline\n }\n\n // Multi-group aggregation logic...\n // Validate and create mapping for non-aggregate expressions in SELECT\n const mapping = validateAndCreateMapping(groupByClause, selectClause)\n\n // Pre-compile groupBy expressions\n const compiledGroupByExpressions = groupByClause.map(compileExpression)\n\n // Create a key extractor function using simple __key_X format\n const keyExtractor = ([, row]: [\n string,\n NamespacedRow & { __select_results?: any },\n ]) => {\n // Use the original namespaced row for GROUP BY expressions, not __select_results\n const namespacedRow = { ...row }\n delete (namespacedRow as any).__select_results\n\n const key: Record<string, unknown> = {}\n\n // Use simple __key_X format for each groupBy expression\n for (let i = 0; i < groupByClause.length; i++) {\n const compiledExpr = compiledGroupByExpressions[i]!\n const value = compiledExpr(namespacedRow)\n key[`__key_${i}`] = value\n }\n\n return key\n }\n\n // Create aggregate functions for any aggregated columns in the SELECT clause\n const aggregates: Record<string, any> = {}\n\n if (selectClause) {\n // Scan the SELECT clause for aggregate functions\n for (const [alias, expr] of Object.entries(selectClause)) {\n if (expr.type === `agg`) {\n const aggExpr = expr\n aggregates[alias] = getAggregateFunction(aggExpr)\n }\n }\n }\n\n // Apply the groupBy operator\n pipeline = pipeline.pipe(groupBy(keyExtractor, aggregates))\n\n // Update __select_results to handle GROUP BY results\n pipeline = pipeline.pipe(\n map(([, aggregatedRow]) => {\n // Start with the existing __select_results from early SELECT processing\n const selectResults = (aggregatedRow as any).__select_results || {}\n const finalResults: Record<string, any> = {}\n\n if (selectClause) {\n // Process each SELECT expression\n for (const [alias, expr] of Object.entries(selectClause)) {\n if (expr.type !== `agg`) {\n // Use cached mapping to get the corresponding __key_X for non-aggregates\n const groupIndex = mapping.selectToGroupByIndex.get(alias)\n if (groupIndex !== undefined) {\n finalResults[alias] = aggregatedRow[`__key_${groupIndex}`]\n } else {\n // Fallback to original SELECT results\n finalResults[alias] = selectResults[alias]\n }\n } else {\n // Use aggregate results\n finalResults[alias] = aggregatedRow[alias]\n }\n }\n } else {\n // No SELECT clause - just use the group keys\n for (let i = 0; i < groupByClause.length; i++) {\n finalResults[`__key_${i}`] = aggregatedRow[`__key_${i}`]\n }\n }\n\n // Generate a simple key for the live collection using group values\n let finalKey: unknown\n if (groupByClause.length === 1) {\n finalKey = aggregatedRow[`__key_0`]\n } else {\n const keyParts: Array<unknown> = []\n for (let i = 0; i < groupByClause.length; i++) {\n keyParts.push(aggregatedRow[`__key_${i}`])\n }\n finalKey = JSON.stringify(keyParts)\n }\n\n return [\n finalKey,\n {\n ...aggregatedRow,\n __select_results: finalResults,\n },\n ] as [unknown, Record<string, any>]\n })\n )\n\n // Apply HAVING clauses if present\n if (havingClauses && havingClauses.length > 0) {\n for (const havingClause of havingClauses) {\n const transformedHavingClause = transformHavingClause(\n havingClause,\n selectClause || {}\n )\n const compiledHaving = compileExpression(transformedHavingClause)\n\n pipeline = pipeline.pipe(\n filter(([, row]) => {\n // Create a namespaced row structure for HAVING evaluation\n const namespacedRow = { result: (row as any).__select_results }\n return compiledHaving(namespacedRow)\n })\n )\n }\n }\n\n // Apply functional HAVING clauses if present\n if (fnHavingClauses && fnHavingClauses.length > 0) {\n for (const fnHaving of fnHavingClauses) {\n pipeline = pipeline.pipe(\n filter(([, row]) => {\n // Create a namespaced row structure for functional HAVING evaluation\n const namespacedRow = { result: (row as any).__select_results }\n return fnHaving(namespacedRow)\n })\n )\n }\n }\n\n return pipeline\n}\n\n/**\n * Helper function to check if two expressions are equal\n */\nfunction expressionsEqual(expr1: any, expr2: any): boolean {\n if (!expr1 || !expr2) return false\n if (expr1.type !== expr2.type) return false\n\n switch (expr1.type) {\n case `ref`:\n // Compare paths as arrays\n if (!expr1.path || !expr2.path) return false\n if (expr1.path.length !== expr2.path.length) return false\n return expr1.path.every(\n (segment: string, i: number) => segment === expr2.path[i]\n )\n case `val`:\n return expr1.value === expr2.value\n case `func`:\n return (\n expr1.name === expr2.name &&\n expr1.args?.length === expr2.args?.length &&\n (expr1.args || []).every((arg: any, i: number) =>\n expressionsEqual(arg, expr2.args[i])\n )\n )\n case `agg`:\n return (\n expr1.name === expr2.name &&\n expr1.args?.length === expr2.args?.length &&\n (expr1.args || []).every((arg: any, i: number) =>\n expressionsEqual(arg, expr2.args[i])\n )\n )\n default:\n return false\n }\n}\n\n/**\n * Helper function to get an aggregate function based on the Agg expression\n */\nfunction getAggregateFunction(aggExpr: Aggregate) {\n // Pre-compile the value extractor expression\n const compiledExpr = compileExpression(aggExpr.args[0]!)\n\n // Create a value extractor function for the expression to aggregate\n const valueExtractor = ([, namespacedRow]: [string, NamespacedRow]) => {\n const value = compiledExpr(namespacedRow)\n // Ensure we return a number for numeric aggregate functions\n return typeof value === `number` ? value : value != null ? Number(value) : 0\n }\n\n // Return the appropriate aggregate function\n switch (aggExpr.name.toLowerCase()) {\n case `sum`:\n return sum(valueExtractor)\n case `count`:\n return count() // count() doesn't need a value extractor\n case `avg`:\n return avg(valueExtractor)\n case `min`:\n return min(valueExtractor)\n case `max`:\n return max(valueExtractor)\n default:\n throw new Error(`Unsupported aggregate function: ${aggExpr.name}`)\n }\n}\n\n/**\n * Transforms a HAVING clause to replace Agg expressions with references to computed values\n */\nfunction transformHavingClause(\n havingExpr: BasicExpression | Aggregate,\n selectClause: Select\n): BasicExpression {\n switch (havingExpr.type) {\n case `agg`: {\n const aggExpr = havingExpr\n // Find matching aggregate in SELECT clause\n for (const [alias, selectExpr] of Object.entries(selectClause)) {\n if (selectExpr.type === `agg` && aggregatesEqual(aggExpr, selectExpr)) {\n // Replace with a reference to the computed aggregate\n return new PropRef([`result`, alias])\n }\n }\n // If no matching aggregate found in SELECT, throw error\n throw new Error(\n `Aggregate function in HAVING clause must also be in SELECT clause: ${aggExpr.name}`\n )\n }\n\n case `func`: {\n const funcExpr = havingExpr\n // Transform function arguments recursively\n const transformedArgs = funcExpr.args.map(\n (arg: BasicExpression | Aggregate) =>\n transformHavingClause(arg, selectClause)\n )\n return new Func(funcExpr.name, transformedArgs)\n }\n\n case `ref`: {\n const refExpr = havingExpr\n // Check if this is a direct reference to a SELECT alias\n if (refExpr.path.length === 1) {\n const alias = refExpr.path[0]!\n if (selectClause[alias]) {\n // This is a reference to a SELECT alias, convert to result.alias\n return new PropRef([`result`, alias])\n }\n }\n // Return as-is for other refs\n return havingExpr as BasicExpression\n }\n\n case `val`:\n // Return as-is\n return havingExpr as BasicExpression\n\n default:\n throw new Error(\n `Unknown expression type in HAVING clause: ${(havingExpr as any).type}`\n )\n }\n}\n\n/**\n * Checks if two aggregate expressions are equal\n */\nfunction aggregatesEqual(agg1: Aggregate, agg2: Aggregate): boolean {\n return (\n agg1.name === agg2.name &&\n agg1.args.length === agg2.args.length &&\n agg1.args.every((arg, i) => expressionsEqual(arg, agg2.args[i]))\n )\n}\n"],"names":["groupByOperators","aggregates","keyExtractor","groupBy","map","compileExpression","filter","PropRef","Func"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"group-by.cjs","sources":["../../../../src/query/compiler/group-by.ts"],"sourcesContent":["import { filter, groupBy, groupByOperators, map } from \"@electric-sql/d2mini\"\nimport { Func, PropRef } from \"../ir.js\"\nimport {\n AggregateFunctionNotInSelectError,\n NonAggregateExpressionNotInGroupByError,\n UnknownHavingExpressionTypeError,\n UnsupportedAggregateFunctionError,\n} from \"../../errors.js\"\nimport { compileExpression } from \"./evaluators.js\"\nimport type {\n Aggregate,\n BasicExpression,\n GroupBy,\n Having,\n Select,\n} from \"../ir.js\"\nimport type { NamespacedAndKeyedStream, NamespacedRow } from \"../../types.js\"\n\nconst { sum, count, avg, min, max } = groupByOperators\n\n/**\n * Interface for caching the mapping between GROUP BY expressions and SELECT expressions\n */\ninterface GroupBySelectMapping {\n selectToGroupByIndex: Map<string, number> // Maps SELECT alias to GROUP BY expression index\n groupByExpressions: Array<any> // The GROUP BY expressions for reference\n}\n\n/**\n * Validates that all non-aggregate expressions in SELECT are present in GROUP BY\n * and creates a cached mapping for efficient lookup during processing\n */\nfunction validateAndCreateMapping(\n groupByClause: GroupBy,\n selectClause?: Select\n): GroupBySelectMapping {\n const selectToGroupByIndex = new Map<string, number>()\n const groupByExpressions = [...groupByClause]\n\n if (!selectClause) {\n return { selectToGroupByIndex, groupByExpressions }\n }\n\n // Validate each SELECT expression\n for (const [alias, expr] of Object.entries(selectClause)) {\n if (expr.type === `agg`) {\n // Aggregate expressions are allowed and don't need to be in GROUP BY\n continue\n }\n\n // Non-aggregate expression must be in GROUP BY\n const groupIndex = groupByExpressions.findIndex((groupExpr) =>\n expressionsEqual(expr, groupExpr)\n )\n\n if (groupIndex === -1) {\n throw new NonAggregateExpressionNotInGroupByError(alias)\n }\n\n // Cache the mapping\n selectToGroupByIndex.set(alias, groupIndex)\n }\n\n return { selectToGroupByIndex, groupByExpressions }\n}\n\n/**\n * Processes the GROUP BY clause with optional HAVING and SELECT\n * Works with the new __select_results structure from early SELECT processing\n */\nexport function processGroupBy(\n pipeline: NamespacedAndKeyedStream,\n groupByClause: GroupBy,\n havingClauses?: Array<Having>,\n selectClause?: Select,\n fnHavingClauses?: Array<(row: any) => any>\n): NamespacedAndKeyedStream {\n // Handle empty GROUP BY (single-group aggregation)\n if (groupByClause.length === 0) {\n // For single-group aggregation, create a single group with all data\n const aggregates: Record<string, any> = {}\n\n if (selectClause) {\n // Scan the SELECT clause for aggregate functions\n for (const [alias, expr] of Object.entries(selectClause)) {\n if (expr.type === `agg`) {\n const aggExpr = expr\n aggregates[alias] = getAggregateFunction(aggExpr)\n }\n }\n }\n\n // Use a constant key for single group\n const keyExtractor = () => ({ __singleGroup: true })\n\n // Apply the groupBy operator with single group\n pipeline = pipeline.pipe(\n groupBy(keyExtractor, aggregates)\n ) as NamespacedAndKeyedStream\n\n // Update __select_results to include aggregate values\n pipeline = pipeline.pipe(\n map(([, aggregatedRow]) => {\n // Start with the existing __select_results from early SELECT processing\n const selectResults = (aggregatedRow as any).__select_results || {}\n const finalResults: Record<string, any> = { ...selectResults }\n\n if (selectClause) {\n // Update with aggregate results\n for (const [alias, expr] of Object.entries(selectClause)) {\n if (expr.type === `agg`) {\n finalResults[alias] = aggregatedRow[alias]\n }\n // Non-aggregates keep their original values from early SELECT processing\n }\n }\n\n // Use a single key for the result and update __select_results\n return [\n `single_group`,\n {\n ...aggregatedRow,\n __select_results: finalResults,\n },\n ] as [unknown, Record<string, any>]\n })\n )\n\n // Apply HAVING clauses if present\n if (havingClauses && havingClauses.length > 0) {\n for (const havingClause of havingClauses) {\n const transformedHavingClause = transformHavingClause(\n havingClause,\n selectClause || {}\n )\n const compiledHaving = compileExpression(transformedHavingClause)\n\n pipeline = pipeline.pipe(\n filter(([, row]) => {\n // Create a namespaced row structure for HAVING evaluation\n const namespacedRow = { result: (row as any).__select_results }\n return compiledHaving(namespacedRow)\n })\n )\n }\n }\n\n // Apply functional HAVING clauses if present\n if (fnHavingClauses && fnHavingClauses.length > 0) {\n for (const fnHaving of fnHavingClauses) {\n pipeline = pipeline.pipe(\n filter(([, row]) => {\n // Create a namespaced row structure for functional HAVING evaluation\n const namespacedRow = { result: (row as any).__select_results }\n return fnHaving(namespacedRow)\n })\n )\n }\n }\n\n return pipeline\n }\n\n // Multi-group aggregation logic...\n // Validate and create mapping for non-aggregate expressions in SELECT\n const mapping = validateAndCreateMapping(groupByClause, selectClause)\n\n // Pre-compile groupBy expressions\n const compiledGroupByExpressions = groupByClause.map(compileExpression)\n\n // Create a key extractor function using simple __key_X format\n const keyExtractor = ([, row]: [\n string,\n NamespacedRow & { __select_results?: any },\n ]) => {\n // Use the original namespaced row for GROUP BY expressions, not __select_results\n const namespacedRow = { ...row }\n delete (namespacedRow as any).__select_results\n\n const key: Record<string, unknown> = {}\n\n // Use simple __key_X format for each groupBy expression\n for (let i = 0; i < groupByClause.length; i++) {\n const compiledExpr = compiledGroupByExpressions[i]!\n const value = compiledExpr(namespacedRow)\n key[`__key_${i}`] = value\n }\n\n return key\n }\n\n // Create aggregate functions for any aggregated columns in the SELECT clause\n const aggregates: Record<string, any> = {}\n\n if (selectClause) {\n // Scan the SELECT clause for aggregate functions\n for (const [alias, expr] of Object.entries(selectClause)) {\n if (expr.type === `agg`) {\n const aggExpr = expr\n aggregates[alias] = getAggregateFunction(aggExpr)\n }\n }\n }\n\n // Apply the groupBy operator\n pipeline = pipeline.pipe(groupBy(keyExtractor, aggregates))\n\n // Update __select_results to handle GROUP BY results\n pipeline = pipeline.pipe(\n map(([, aggregatedRow]) => {\n // Start with the existing __select_results from early SELECT processing\n const selectResults = (aggregatedRow as any).__select_results || {}\n const finalResults: Record<string, any> = {}\n\n if (selectClause) {\n // Process each SELECT expression\n for (const [alias, expr] of Object.entries(selectClause)) {\n if (expr.type !== `agg`) {\n // Use cached mapping to get the corresponding __key_X for non-aggregates\n const groupIndex = mapping.selectToGroupByIndex.get(alias)\n if (groupIndex !== undefined) {\n finalResults[alias] = aggregatedRow[`__key_${groupIndex}`]\n } else {\n // Fallback to original SELECT results\n finalResults[alias] = selectResults[alias]\n }\n } else {\n // Use aggregate results\n finalResults[alias] = aggregatedRow[alias]\n }\n }\n } else {\n // No SELECT clause - just use the group keys\n for (let i = 0; i < groupByClause.length; i++) {\n finalResults[`__key_${i}`] = aggregatedRow[`__key_${i}`]\n }\n }\n\n // Generate a simple key for the live collection using group values\n let finalKey: unknown\n if (groupByClause.length === 1) {\n finalKey = aggregatedRow[`__key_0`]\n } else {\n const keyParts: Array<unknown> = []\n for (let i = 0; i < groupByClause.length; i++) {\n keyParts.push(aggregatedRow[`__key_${i}`])\n }\n finalKey = JSON.stringify(keyParts)\n }\n\n return [\n finalKey,\n {\n ...aggregatedRow,\n __select_results: finalResults,\n },\n ] as [unknown, Record<string, any>]\n })\n )\n\n // Apply HAVING clauses if present\n if (havingClauses && havingClauses.length > 0) {\n for (const havingClause of havingClauses) {\n const transformedHavingClause = transformHavingClause(\n havingClause,\n selectClause || {}\n )\n const compiledHaving = compileExpression(transformedHavingClause)\n\n pipeline = pipeline.pipe(\n filter(([, row]) => {\n // Create a namespaced row structure for HAVING evaluation\n const namespacedRow = { result: (row as any).__select_results }\n return compiledHaving(namespacedRow)\n })\n )\n }\n }\n\n // Apply functional HAVING clauses if present\n if (fnHavingClauses && fnHavingClauses.length > 0) {\n for (const fnHaving of fnHavingClauses) {\n pipeline = pipeline.pipe(\n filter(([, row]) => {\n // Create a namespaced row structure for functional HAVING evaluation\n const namespacedRow = { result: (row as any).__select_results }\n return fnHaving(namespacedRow)\n })\n )\n }\n }\n\n return pipeline\n}\n\n/**\n * Helper function to check if two expressions are equal\n */\nfunction expressionsEqual(expr1: any, expr2: any): boolean {\n if (!expr1 || !expr2) return false\n if (expr1.type !== expr2.type) return false\n\n switch (expr1.type) {\n case `ref`:\n // Compare paths as arrays\n if (!expr1.path || !expr2.path) return false\n if (expr1.path.length !== expr2.path.length) return false\n return expr1.path.every(\n (segment: string, i: number) => segment === expr2.path[i]\n )\n case `val`:\n return expr1.value === expr2.value\n case `func`:\n return (\n expr1.name === expr2.name &&\n expr1.args?.length === expr2.args?.length &&\n (expr1.args || []).every((arg: any, i: number) =>\n expressionsEqual(arg, expr2.args[i])\n )\n )\n case `agg`:\n return (\n expr1.name === expr2.name &&\n expr1.args?.length === expr2.args?.length &&\n (expr1.args || []).every((arg: any, i: number) =>\n expressionsEqual(arg, expr2.args[i])\n )\n )\n default:\n return false\n }\n}\n\n/**\n * Helper function to get an aggregate function based on the Agg expression\n */\nfunction getAggregateFunction(aggExpr: Aggregate) {\n // Pre-compile the value extractor expression\n const compiledExpr = compileExpression(aggExpr.args[0]!)\n\n // Create a value extractor function for the expression to aggregate\n const valueExtractor = ([, namespacedRow]: [string, NamespacedRow]) => {\n const value = compiledExpr(namespacedRow)\n // Ensure we return a number for numeric aggregate functions\n return typeof value === `number` ? value : value != null ? Number(value) : 0\n }\n\n // Return the appropriate aggregate function\n switch (aggExpr.name.toLowerCase()) {\n case `sum`:\n return sum(valueExtractor)\n case `count`:\n return count() // count() doesn't need a value extractor\n case `avg`:\n return avg(valueExtractor)\n case `min`:\n return min(valueExtractor)\n case `max`:\n return max(valueExtractor)\n default:\n throw new UnsupportedAggregateFunctionError(aggExpr.name)\n }\n}\n\n/**\n * Transforms a HAVING clause to replace Agg expressions with references to computed values\n */\nfunction transformHavingClause(\n havingExpr: BasicExpression | Aggregate,\n selectClause: Select\n): BasicExpression {\n switch (havingExpr.type) {\n case `agg`: {\n const aggExpr = havingExpr\n // Find matching aggregate in SELECT clause\n for (const [alias, selectExpr] of Object.entries(selectClause)) {\n if (selectExpr.type === `agg` && aggregatesEqual(aggExpr, selectExpr)) {\n // Replace with a reference to the computed aggregate\n return new PropRef([`result`, alias])\n }\n }\n // If no matching aggregate found in SELECT, throw error\n throw new AggregateFunctionNotInSelectError(aggExpr.name)\n }\n\n case `func`: {\n const funcExpr = havingExpr\n // Transform function arguments recursively\n const transformedArgs = funcExpr.args.map(\n (arg: BasicExpression | Aggregate) =>\n transformHavingClause(arg, selectClause)\n )\n return new Func(funcExpr.name, transformedArgs)\n }\n\n case `ref`: {\n const refExpr = havingExpr\n // Check if this is a direct reference to a SELECT alias\n if (refExpr.path.length === 1) {\n const alias = refExpr.path[0]!\n if (selectClause[alias]) {\n // This is a reference to a SELECT alias, convert to result.alias\n return new PropRef([`result`, alias])\n }\n }\n // Return as-is for other refs\n return havingExpr as BasicExpression\n }\n\n case `val`:\n // Return as-is\n return havingExpr as BasicExpression\n\n default:\n throw new UnknownHavingExpressionTypeError((havingExpr as any).type)\n }\n}\n\n/**\n * Checks if two aggregate expressions are equal\n */\nfunction aggregatesEqual(agg1: Aggregate, agg2: Aggregate): boolean {\n return (\n agg1.name === agg2.name &&\n agg1.args.length === agg2.args.length &&\n agg1.args.every((arg, i) => expressionsEqual(arg, agg2.args[i]))\n )\n}\n"],"names":["groupByOperators","NonAggregateExpressionNotInGroupByError","aggregates","keyExtractor","groupBy","map","compileExpression","filter","UnsupportedAggregateFunctionError","PropRef","AggregateFunctionNotInSelectError","Func","UnknownHavingExpressionTypeError"],"mappings":";;;;;;AAkBA,MAAM,EAAE,KAAK,OAAO,KAAK,KAAK,QAAQA,OAAAA;AActC,SAAS,yBACP,eACA,cACsB;AACtB,QAAM,2CAA2B,IAAA;AACjC,QAAM,qBAAqB,CAAC,GAAG,aAAa;AAE5C,MAAI,CAAC,cAAc;AACjB,WAAO,EAAE,sBAAsB,mBAAA;AAAA,EACjC;AAGA,aAAW,CAAC,OAAO,IAAI,KAAK,OAAO,QAAQ,YAAY,GAAG;AACxD,QAAI,KAAK,SAAS,OAAO;AAEvB;AAAA,IACF;AAGA,UAAM,aAAa,mBAAmB;AAAA,MAAU,CAAC,cAC/C,iBAAiB,MAAM,SAAS;AAAA,IAAA;AAGlC,QAAI,eAAe,IAAI;AACrB,YAAM,IAAIC,OAAAA,wCAAwC,KAAK;AAAA,IACzD;AAGA,yBAAqB,IAAI,OAAO,UAAU;AAAA,EAC5C;AAEA,SAAO,EAAE,sBAAsB,mBAAA;AACjC;AAMO,SAAS,eACd,UACA,eACA,eACA,cACA,iBAC0B;AAE1B,MAAI,cAAc,WAAW,GAAG;AAE9B,UAAMC,cAAkC,CAAA;AAExC,QAAI,cAAc;AAEhB,iBAAW,CAAC,OAAO,IAAI,KAAK,OAAO,QAAQ,YAAY,GAAG;AACxD,YAAI,KAAK,SAAS,OAAO;AACvB,gBAAM,UAAU;AAChBA,sBAAW,KAAK,IAAI,qBAAqB,OAAO;AAAA,QAClD;AAAA,MACF;AAAA,IACF;AAGA,UAAMC,gBAAe,OAAO,EAAE,eAAe,KAAA;AAG7C,eAAW,SAAS;AAAA,MAClBC,OAAAA,QAAQD,eAAcD,WAAU;AAAA,IAAA;AAIlC,eAAW,SAAS;AAAA,MAClBG,WAAI,CAAC,CAAA,EAAG,aAAa,MAAM;AAEzB,cAAM,gBAAiB,cAAsB,oBAAoB,CAAA;AACjE,cAAM,eAAoC,EAAE,GAAG,cAAA;AAE/C,YAAI,cAAc;AAEhB,qBAAW,CAAC,OAAO,IAAI,KAAK,OAAO,QAAQ,YAAY,GAAG;AACxD,gBAAI,KAAK,SAAS,OAAO;AACvB,2BAAa,KAAK,IAAI,cAAc,KAAK;AAAA,YAC3C;AAAA,UAEF;AAAA,QACF;AAGA,eAAO;AAAA,UACL;AAAA,UACA;AAAA,YACE,GAAG;AAAA,YACH,kBAAkB;AAAA,UAAA;AAAA,QACpB;AAAA,MAEJ,CAAC;AAAA,IAAA;AAIH,QAAI,iBAAiB,cAAc,SAAS,GAAG;AAC7C,iBAAW,gBAAgB,eAAe;AACxC,cAAM,0BAA0B;AAAA,UAC9B;AAAA,UACA,gBAAgB,CAAA;AAAA,QAAC;AAEnB,cAAM,iBAAiBC,WAAAA,kBAAkB,uBAAuB;AAEhE,mBAAW,SAAS;AAAA,UAClBC,cAAO,CAAC,CAAA,EAAG,GAAG,MAAM;AAElB,kBAAM,gBAAgB,EAAE,QAAS,IAAY,iBAAA;AAC7C,mBAAO,eAAe,aAAa;AAAA,UACrC,CAAC;AAAA,QAAA;AAAA,MAEL;AAAA,IACF;AAGA,QAAI,mBAAmB,gBAAgB,SAAS,GAAG;AACjD,iBAAW,YAAY,iBAAiB;AACtC,mBAAW,SAAS;AAAA,UAClBA,cAAO,CAAC,CAAA,EAAG,GAAG,MAAM;AAElB,kBAAM,gBAAgB,EAAE,QAAS,IAAY,iBAAA;AAC7C,mBAAO,SAAS,aAAa;AAAA,UAC/B,CAAC;AAAA,QAAA;AAAA,MAEL;AAAA,IACF;AAEA,WAAO;AAAA,EACT;AAIA,QAAM,UAAU,yBAAyB,eAAe,YAAY;AAGpE,QAAM,6BAA6B,cAAc,IAAID,4BAAiB;AAGtE,QAAM,eAAe,CAAC,CAAA,EAAG,GAAG,MAGtB;AAEJ,UAAM,gBAAgB,EAAE,GAAG,IAAA;AAC3B,WAAQ,cAAsB;AAE9B,UAAM,MAA+B,CAAA;AAGrC,aAAS,IAAI,GAAG,IAAI,cAAc,QAAQ,KAAK;AAC7C,YAAM,eAAe,2BAA2B,CAAC;AACjD,YAAM,QAAQ,aAAa,aAAa;AACxC,UAAI,SAAS,CAAC,EAAE,IAAI;AAAA,IACtB;AAEA,WAAO;AAAA,EACT;AAGA,QAAM,aAAkC,CAAA;AAExC,MAAI,cAAc;AAEhB,eAAW,CAAC,OAAO,IAAI,KAAK,OAAO,QAAQ,YAAY,GAAG;AACxD,UAAI,KAAK,SAAS,OAAO;AACvB,cAAM,UAAU;AAChB,mBAAW,KAAK,IAAI,qBAAqB,OAAO;AAAA,MAClD;AAAA,IACF;AAAA,EACF;AAGA,aAAW,SAAS,KAAKF,OAAAA,QAAQ,cAAc,UAAU,CAAC;AAG1D,aAAW,SAAS;AAAA,IAClBC,WAAI,CAAC,CAAA,EAAG,aAAa,MAAM;AAEzB,YAAM,gBAAiB,cAAsB,oBAAoB,CAAA;AACjE,YAAM,eAAoC,CAAA;AAE1C,UAAI,cAAc;AAEhB,mBAAW,CAAC,OAAO,IAAI,KAAK,OAAO,QAAQ,YAAY,GAAG;AACxD,cAAI,KAAK,SAAS,OAAO;AAEvB,kBAAM,aAAa,QAAQ,qBAAqB,IAAI,KAAK;AACzD,gBAAI,eAAe,QAAW;AAC5B,2BAAa,KAAK,IAAI,cAAc,SAAS,UAAU,EAAE;AAAA,YAC3D,OAAO;AAEL,2BAAa,KAAK,IAAI,cAAc,KAAK;AAAA,YAC3C;AAAA,UACF,OAAO;AAEL,yBAAa,KAAK,IAAI,cAAc,KAAK;AAAA,UAC3C;AAAA,QACF;AAAA,MACF,OAAO;AAEL,iBAAS,IAAI,GAAG,IAAI,cAAc,QAAQ,KAAK;AAC7C,uBAAa,SAAS,CAAC,EAAE,IAAI,cAAc,SAAS,CAAC,EAAE;AAAA,QACzD;AAAA,MACF;AAGA,UAAI;AACJ,UAAI,cAAc,WAAW,GAAG;AAC9B,mBAAW,cAAc,SAAS;AAAA,MACpC,OAAO;AACL,cAAM,WAA2B,CAAA;AACjC,iBAAS,IAAI,GAAG,IAAI,cAAc,QAAQ,KAAK;AAC7C,mBAAS,KAAK,cAAc,SAAS,CAAC,EAAE,CAAC;AAAA,QAC3C;AACA,mBAAW,KAAK,UAAU,QAAQ;AAAA,MACpC;AAEA,aAAO;AAAA,QACL;AAAA,QACA;AAAA,UACE,GAAG;AAAA,UACH,kBAAkB;AAAA,QAAA;AAAA,MACpB;AAAA,IAEJ,CAAC;AAAA,EAAA;AAIH,MAAI,iBAAiB,cAAc,SAAS,GAAG;AAC7C,eAAW,gBAAgB,eAAe;AACxC,YAAM,0BAA0B;AAAA,QAC9B;AAAA,QACA,gBAAgB,CAAA;AAAA,MAAC;AAEnB,YAAM,iBAAiBC,WAAAA,kBAAkB,uBAAuB;AAEhE,iBAAW,SAAS;AAAA,QAClBC,cAAO,CAAC,CAAA,EAAG,GAAG,MAAM;AAElB,gBAAM,gBAAgB,EAAE,QAAS,IAAY,iBAAA;AAC7C,iBAAO,eAAe,aAAa;AAAA,QACrC,CAAC;AAAA,MAAA;AAAA,IAEL;AAAA,EACF;AAGA,MAAI,mBAAmB,gBAAgB,SAAS,GAAG;AACjD,eAAW,YAAY,iBAAiB;AACtC,iBAAW,SAAS;AAAA,QAClBA,cAAO,CAAC,CAAA,EAAG,GAAG,MAAM;AAElB,gBAAM,gBAAgB,EAAE,QAAS,IAAY,iBAAA;AAC7C,iBAAO,SAAS,aAAa;AAAA,QAC/B,CAAC;AAAA,MAAA;AAAA,IAEL;AAAA,EACF;AAEA,SAAO;AACT;AAKA,SAAS,iBAAiB,OAAY,OAAqB;;AACzD,MAAI,CAAC,SAAS,CAAC,MAAO,QAAO;AAC7B,MAAI,MAAM,SAAS,MAAM,KAAM,QAAO;AAEtC,UAAQ,MAAM,MAAA;AAAA,IACZ,KAAK;AAEH,UAAI,CAAC,MAAM,QAAQ,CAAC,MAAM,KAAM,QAAO;AACvC,UAAI,MAAM,KAAK,WAAW,MAAM,KAAK,OAAQ,QAAO;AACpD,aAAO,MAAM,KAAK;AAAA,QAChB,CAAC,SAAiB,MAAc,YAAY,MAAM,KAAK,CAAC;AAAA,MAAA;AAAA,IAE5D,KAAK;AACH,aAAO,MAAM,UAAU,MAAM;AAAA,IAC/B,KAAK;AACH,aACE,MAAM,SAAS,MAAM,UACrB,WAAM,SAAN,mBAAY,cAAW,WAAM,SAAN,mBAAY,YAClC,MAAM,QAAQ,CAAA,GAAI;AAAA,QAAM,CAAC,KAAU,MAClC,iBAAiB,KAAK,MAAM,KAAK,CAAC,CAAC;AAAA,MAAA;AAAA,IAGzC,KAAK;AACH,aACE,MAAM,SAAS,MAAM,UACrB,WAAM,SAAN,mBAAY,cAAW,WAAM,SAAN,mBAAY,YAClC,MAAM,QAAQ,CAAA,GAAI;AAAA,QAAM,CAAC,KAAU,MAClC,iBAAiB,KAAK,MAAM,KAAK,CAAC,CAAC;AAAA,MAAA;AAAA,IAGzC;AACE,aAAO;AAAA,EAAA;AAEb;AAKA,SAAS,qBAAqB,SAAoB;AAEhD,QAAM,eAAeD,WAAAA,kBAAkB,QAAQ,KAAK,CAAC,CAAE;AAGvD,QAAM,iBAAiB,CAAC,CAAA,EAAG,aAAa,MAA+B;AACrE,UAAM,QAAQ,aAAa,aAAa;AAExC,WAAO,OAAO,UAAU,WAAW,QAAQ,SAAS,OAAO,OAAO,KAAK,IAAI;AAAA,EAC7E;AAGA,UAAQ,QAAQ,KAAK,YAAA,GAAY;AAAA,IAC/B,KAAK;AACH,aAAO,IAAI,cAAc;AAAA,IAC3B,KAAK;AACH,aAAO,MAAA;AAAA;AAAA,IACT,KAAK;AACH,aAAO,IAAI,cAAc;AAAA,IAC3B,KAAK;AACH,aAAO,IAAI,cAAc;AAAA,IAC3B,KAAK;AACH,aAAO,IAAI,cAAc;AAAA,IAC3B;AACE,YAAM,IAAIE,OAAAA,kCAAkC,QAAQ,IAAI;AAAA,EAAA;AAE9D;AAKA,SAAS,sBACP,YACA,cACiB;AACjB,UAAQ,WAAW,MAAA;AAAA,IACjB,KAAK,OAAO;AACV,YAAM,UAAU;AAEhB,iBAAW,CAAC,OAAO,UAAU,KAAK,OAAO,QAAQ,YAAY,GAAG;AAC9D,YAAI,WAAW,SAAS,SAAS,gBAAgB,SAAS,UAAU,GAAG;AAErE,iBAAO,IAAIC,GAAAA,QAAQ,CAAC,UAAU,KAAK,CAAC;AAAA,QACtC;AAAA,MACF;AAEA,YAAM,IAAIC,OAAAA,kCAAkC,QAAQ,IAAI;AAAA,IAC1D;AAAA,IAEA,KAAK,QAAQ;AACX,YAAM,WAAW;AAEjB,YAAM,kBAAkB,SAAS,KAAK;AAAA,QACpC,CAAC,QACC,sBAAsB,KAAK,YAAY;AAAA,MAAA;AAE3C,aAAO,IAAIC,GAAAA,KAAK,SAAS,MAAM,eAAe;AAAA,IAChD;AAAA,IAEA,KAAK,OAAO;AACV,YAAM,UAAU;AAEhB,UAAI,QAAQ,KAAK,WAAW,GAAG;AAC7B,cAAM,QAAQ,QAAQ,KAAK,CAAC;AAC5B,YAAI,aAAa,KAAK,GAAG;AAEvB,iBAAO,IAAIF,GAAAA,QAAQ,CAAC,UAAU,KAAK,CAAC;AAAA,QACtC;AAAA,MACF;AAEA,aAAO;AAAA,IACT;AAAA,IAEA,KAAK;AAEH,aAAO;AAAA,IAET;AACE,YAAM,IAAIG,OAAAA,iCAAkC,WAAmB,IAAI;AAAA,EAAA;AAEzE;AAKA,SAAS,gBAAgB,MAAiB,MAA0B;AAClE,SACE,KAAK,SAAS,KAAK,QACnB,KAAK,KAAK,WAAW,KAAK,KAAK,UAC/B,KAAK,KAAK,MAAM,CAAC,KAAK,MAAM,iBAAiB,KAAK,KAAK,KAAK,CAAC,CAAC,CAAC;AAEnE;;"}
|