@tinqerjs/tinqer 0.0.21 → 0.0.23

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.
@@ -0,0 +1,811 @@
1
+ import { isValueExpression } from "../expressions/expression.js";
2
+ import { parseJavaScript } from "../parser/oxc-parser.js";
3
+ import { createBaseContext } from "../visitors/types.js";
4
+ import { visitWhereOperation } from "../visitors/where/index.js";
5
+ const ROW_FILTER_CONTEXT_PARAM_PREFIX = "__tinqer_row_filter_ctx__";
6
+ export function applyRowFiltersToSelectOperation(operation, rowFilters, params, autoParamCounterStart) {
7
+ if (!rowFilters) {
8
+ return { operation, params };
9
+ }
10
+ const filters = rowFilters.filters;
11
+ const context = rowFilters.context;
12
+ if (!context) {
13
+ throw new Error("Row filters require context binding. Call schema.withContext(context).");
14
+ }
15
+ let autoParamCounter = autoParamCounterStart;
16
+ const filterCache = new Map();
17
+ const filterAutoParams = {};
18
+ const requiredContextKeys = new Set();
19
+ function getParsedFilter(table, schema, kind) {
20
+ const resolved = resolveTableConfig(filters, table, schema);
21
+ const cacheKey = `${resolved.key}|${kind}`;
22
+ const cached = filterCache.get(cacheKey);
23
+ if (cached !== undefined) {
24
+ return cached;
25
+ }
26
+ const predicateFn = resolvePredicateForOperation(resolved.config, kind);
27
+ if (!predicateFn) {
28
+ filterCache.set(cacheKey, null);
29
+ return null;
30
+ }
31
+ const parsed = parseRowFilter(predicateFn, resolved.key, kind, autoParamCounter);
32
+ autoParamCounter = parsed.autoParamCounter;
33
+ Object.assign(filterAutoParams, parsed.autoParams);
34
+ for (const key of parsed.contextKeys) {
35
+ requiredContextKeys.add(key);
36
+ }
37
+ filterCache.set(cacheKey, parsed);
38
+ return parsed;
39
+ }
40
+ function transform(current) {
41
+ switch (current.operationType) {
42
+ case "from": {
43
+ const fromOp = current;
44
+ if (fromOp.subquery) {
45
+ const nextSubquery = transform(fromOp.subquery);
46
+ if (nextSubquery === fromOp.subquery) {
47
+ return fromOp;
48
+ }
49
+ const nextFrom = { ...fromOp, subquery: nextSubquery };
50
+ return nextFrom;
51
+ }
52
+ if (!fromOp.table) {
53
+ return fromOp;
54
+ }
55
+ const parsed = getParsedFilter(fromOp.table, fromOp.schema, "select");
56
+ if (!parsed) {
57
+ return fromOp;
58
+ }
59
+ const baseFrom = {
60
+ type: "queryOperation",
61
+ operationType: "from",
62
+ table: fromOp.table,
63
+ ...(fromOp.schema ? { schema: fromOp.schema } : {}),
64
+ };
65
+ const whereOp = {
66
+ type: "queryOperation",
67
+ operationType: "where",
68
+ source: baseFrom,
69
+ predicate: parsed.predicate,
70
+ };
71
+ const wrappedFrom = {
72
+ type: "queryOperation",
73
+ operationType: "from",
74
+ subquery: whereOp,
75
+ aliasHint: fromOp.aliasHint ?? fromOp.table,
76
+ };
77
+ return wrappedFrom;
78
+ }
79
+ case "where": {
80
+ const whereOp = current;
81
+ const nextSource = transform(whereOp.source);
82
+ if (nextSource === whereOp.source) {
83
+ return whereOp;
84
+ }
85
+ const nextWhere = { ...whereOp, source: nextSource };
86
+ return nextWhere;
87
+ }
88
+ case "select": {
89
+ const selectOp = current;
90
+ const nextSource = transform(selectOp.source);
91
+ if (nextSource === selectOp.source) {
92
+ return selectOp;
93
+ }
94
+ const nextSelect = { ...selectOp, source: nextSource };
95
+ return nextSelect;
96
+ }
97
+ case "join": {
98
+ const joinOp = current;
99
+ const nextSource = transform(joinOp.source);
100
+ const nextInner = transform(joinOp.inner);
101
+ if (nextSource === joinOp.source && nextInner === joinOp.inner) {
102
+ return joinOp;
103
+ }
104
+ const nextJoin = { ...joinOp, source: nextSource, inner: nextInner };
105
+ return nextJoin;
106
+ }
107
+ case "groupJoin": {
108
+ const groupJoinOp = current;
109
+ const nextSource = transform(groupJoinOp.source);
110
+ const nextInner = transform(groupJoinOp.inner);
111
+ if (nextSource === groupJoinOp.source && nextInner === groupJoinOp.inner) {
112
+ return groupJoinOp;
113
+ }
114
+ const nextGroupJoin = {
115
+ ...groupJoinOp,
116
+ source: nextSource,
117
+ inner: nextInner,
118
+ };
119
+ return nextGroupJoin;
120
+ }
121
+ case "selectMany": {
122
+ const selectManyOp = current;
123
+ const nextSource = transform(selectManyOp.source);
124
+ const nextCollection = typeof selectManyOp.collection === "object" &&
125
+ selectManyOp.collection !== null &&
126
+ "operationType" in selectManyOp.collection
127
+ ? transform(selectManyOp.collection)
128
+ : selectManyOp.collection;
129
+ if (nextSource === selectManyOp.source && nextCollection === selectManyOp.collection) {
130
+ return selectManyOp;
131
+ }
132
+ const nextSelectMany = {
133
+ ...selectManyOp,
134
+ source: nextSource,
135
+ collection: nextCollection,
136
+ };
137
+ return nextSelectMany;
138
+ }
139
+ case "defaultIfEmpty": {
140
+ const defaultIfEmptyOp = current;
141
+ const nextSource = transform(defaultIfEmptyOp.source);
142
+ if (nextSource === defaultIfEmptyOp.source) {
143
+ return defaultIfEmptyOp;
144
+ }
145
+ const nextDefaultIfEmpty = {
146
+ ...defaultIfEmptyOp,
147
+ source: nextSource,
148
+ };
149
+ return nextDefaultIfEmpty;
150
+ }
151
+ case "groupBy": {
152
+ const groupByOp = current;
153
+ const nextSource = transform(groupByOp.source);
154
+ if (nextSource === groupByOp.source) {
155
+ return groupByOp;
156
+ }
157
+ const nextGroupBy = { ...groupByOp, source: nextSource };
158
+ return nextGroupBy;
159
+ }
160
+ case "orderBy": {
161
+ const orderByOp = current;
162
+ const nextSource = transform(orderByOp.source);
163
+ if (nextSource === orderByOp.source) {
164
+ return orderByOp;
165
+ }
166
+ const nextOrderBy = { ...orderByOp, source: nextSource };
167
+ return nextOrderBy;
168
+ }
169
+ case "thenBy": {
170
+ const thenByOp = current;
171
+ const nextSource = transform(thenByOp.source);
172
+ if (nextSource === thenByOp.source) {
173
+ return thenByOp;
174
+ }
175
+ const nextThenBy = { ...thenByOp, source: nextSource };
176
+ return nextThenBy;
177
+ }
178
+ case "distinct": {
179
+ const distinctOp = current;
180
+ const nextSource = transform(distinctOp.source);
181
+ if (nextSource === distinctOp.source) {
182
+ return distinctOp;
183
+ }
184
+ const nextDistinct = { ...distinctOp, source: nextSource };
185
+ return nextDistinct;
186
+ }
187
+ case "take": {
188
+ const takeOp = current;
189
+ const nextSource = transform(takeOp.source);
190
+ if (nextSource === takeOp.source) {
191
+ return takeOp;
192
+ }
193
+ const nextTake = { ...takeOp, source: nextSource };
194
+ return nextTake;
195
+ }
196
+ case "skip": {
197
+ const skipOp = current;
198
+ const nextSource = transform(skipOp.source);
199
+ if (nextSource === skipOp.source) {
200
+ return skipOp;
201
+ }
202
+ const nextSkip = { ...skipOp, source: nextSource };
203
+ return nextSkip;
204
+ }
205
+ case "reverse": {
206
+ const reverseOp = current;
207
+ const nextSource = transform(reverseOp.source);
208
+ if (nextSource === reverseOp.source) {
209
+ return reverseOp;
210
+ }
211
+ const nextReverse = { ...reverseOp, source: nextSource };
212
+ return nextReverse;
213
+ }
214
+ case "first":
215
+ case "firstOrDefault": {
216
+ const op = current;
217
+ const nextSource = transform(op.source);
218
+ if (nextSource === op.source) {
219
+ return op;
220
+ }
221
+ const nextOp = { ...op, source: nextSource };
222
+ return nextOp;
223
+ }
224
+ case "single":
225
+ case "singleOrDefault": {
226
+ const op = current;
227
+ const nextSource = transform(op.source);
228
+ if (nextSource === op.source) {
229
+ return op;
230
+ }
231
+ const nextOp = { ...op, source: nextSource };
232
+ return nextOp;
233
+ }
234
+ case "last":
235
+ case "lastOrDefault": {
236
+ const op = current;
237
+ const nextSource = transform(op.source);
238
+ if (nextSource === op.source) {
239
+ return op;
240
+ }
241
+ const nextOp = { ...op, source: nextSource };
242
+ return nextOp;
243
+ }
244
+ case "contains": {
245
+ const containsOp = current;
246
+ const nextSource = transform(containsOp.source);
247
+ if (nextSource === containsOp.source) {
248
+ return containsOp;
249
+ }
250
+ const nextContains = { ...containsOp, source: nextSource };
251
+ return nextContains;
252
+ }
253
+ case "any": {
254
+ const anyOp = current;
255
+ const nextSource = transform(anyOp.source);
256
+ if (nextSource === anyOp.source) {
257
+ return anyOp;
258
+ }
259
+ const nextAny = { ...anyOp, source: nextSource };
260
+ return nextAny;
261
+ }
262
+ case "all": {
263
+ const allOp = current;
264
+ const nextSource = transform(allOp.source);
265
+ if (nextSource === allOp.source) {
266
+ return allOp;
267
+ }
268
+ const nextAll = { ...allOp, source: nextSource };
269
+ return nextAll;
270
+ }
271
+ case "count":
272
+ case "longCount": {
273
+ const op = current;
274
+ const nextSource = transform(op.source);
275
+ if (nextSource === op.source) {
276
+ return op;
277
+ }
278
+ const nextOp = { ...op, source: nextSource };
279
+ return nextOp;
280
+ }
281
+ case "sum": {
282
+ const sumOp = current;
283
+ const nextSource = transform(sumOp.source);
284
+ if (nextSource === sumOp.source) {
285
+ return sumOp;
286
+ }
287
+ const nextSum = { ...sumOp, source: nextSource };
288
+ return nextSum;
289
+ }
290
+ case "average": {
291
+ const averageOp = current;
292
+ const nextSource = transform(averageOp.source);
293
+ if (nextSource === averageOp.source) {
294
+ return averageOp;
295
+ }
296
+ const nextAverage = { ...averageOp, source: nextSource };
297
+ return nextAverage;
298
+ }
299
+ case "min": {
300
+ const minOp = current;
301
+ const nextSource = transform(minOp.source);
302
+ if (nextSource === minOp.source) {
303
+ return minOp;
304
+ }
305
+ const nextMin = { ...minOp, source: nextSource };
306
+ return nextMin;
307
+ }
308
+ case "max": {
309
+ const maxOp = current;
310
+ const nextSource = transform(maxOp.source);
311
+ if (nextSource === maxOp.source) {
312
+ return maxOp;
313
+ }
314
+ const nextMax = { ...maxOp, source: nextSource };
315
+ return nextMax;
316
+ }
317
+ case "toDictionary": {
318
+ const toDictionaryOp = current;
319
+ const nextSource = transform(toDictionaryOp.source);
320
+ if (nextSource === toDictionaryOp.source) {
321
+ return toDictionaryOp;
322
+ }
323
+ const nextToDictionary = {
324
+ ...toDictionaryOp,
325
+ source: nextSource,
326
+ };
327
+ return nextToDictionary;
328
+ }
329
+ case "toLookup": {
330
+ const toLookupOp = current;
331
+ const nextSource = transform(toLookupOp.source);
332
+ if (nextSource === toLookupOp.source) {
333
+ return toLookupOp;
334
+ }
335
+ const nextToLookup = { ...toLookupOp, source: nextSource };
336
+ return nextToLookup;
337
+ }
338
+ default:
339
+ return current;
340
+ }
341
+ }
342
+ const nextOperation = transform(operation);
343
+ const contextParams = buildContextParams(context, requiredContextKeys);
344
+ const withAutoParams = mergeParamsStrict(params, filterAutoParams, "Row filter auto-params");
345
+ const nextParams = mergeParamsStrict(withAutoParams, contextParams, "Row filter context params");
346
+ return { operation: nextOperation, params: nextParams };
347
+ }
348
+ export function applyRowFiltersToUpdateOperation(operation, rowFilters, params, autoParamCounterStart) {
349
+ if (!rowFilters) {
350
+ return { operation, params };
351
+ }
352
+ const context = rowFilters.context;
353
+ if (!context) {
354
+ throw new Error("Row filters require context binding. Call schema.withContext(context).");
355
+ }
356
+ const resolved = resolveTableConfig(rowFilters.filters, operation.table, operation.schema);
357
+ const predicateFn = resolvePredicateForOperation(resolved.config, "update");
358
+ if (!predicateFn) {
359
+ return { operation, params };
360
+ }
361
+ const parsed = parseRowFilter(predicateFn, resolved.key, "update", autoParamCounterStart);
362
+ const contextParams = buildContextParams(context, parsed.contextKeys);
363
+ const withAutoParams = mergeParamsStrict(params, parsed.autoParams, "Row filter auto-params");
364
+ const nextParams = mergeParamsStrict(withAutoParams, contextParams, "Row filter context params");
365
+ const includedAssignments = selectIncludedAssignments(operation.assignments, nextParams);
366
+ const checkPredicate = substituteUpdatedColumnsInPredicate(parsed.predicate, includedAssignments);
367
+ const nextPredicate = andPredicates(andPredicates(operation.predicate, parsed.predicate), checkPredicate);
368
+ return {
369
+ operation: {
370
+ ...operation,
371
+ predicate: nextPredicate ?? operation.predicate,
372
+ },
373
+ params: nextParams,
374
+ };
375
+ }
376
+ export function applyRowFiltersToDeleteOperation(operation, rowFilters, params, autoParamCounterStart) {
377
+ if (!rowFilters) {
378
+ return { operation, params };
379
+ }
380
+ const context = rowFilters.context;
381
+ if (!context) {
382
+ throw new Error("Row filters require context binding. Call schema.withContext(context).");
383
+ }
384
+ const resolved = resolveTableConfig(rowFilters.filters, operation.table, operation.schema);
385
+ const predicateFn = resolvePredicateForOperation(resolved.config, "delete");
386
+ if (!predicateFn) {
387
+ return { operation, params };
388
+ }
389
+ const parsed = parseRowFilter(predicateFn, resolved.key, "delete", autoParamCounterStart);
390
+ const contextParams = buildContextParams(context, parsed.contextKeys);
391
+ const withAutoParams = mergeParamsStrict(params, parsed.autoParams, "Row filter auto-params");
392
+ const nextParams = mergeParamsStrict(withAutoParams, contextParams, "Row filter context params");
393
+ const nextPredicate = andPredicates(operation.predicate, parsed.predicate);
394
+ return {
395
+ operation: {
396
+ ...operation,
397
+ predicate: nextPredicate ?? operation.predicate,
398
+ },
399
+ params: nextParams,
400
+ };
401
+ }
402
+ function resolveTableConfig(filters, table, schema) {
403
+ const schemaQualified = schema ? `${schema}.${table}` : null;
404
+ if (schemaQualified && Object.prototype.hasOwnProperty.call(filters, schemaQualified)) {
405
+ return {
406
+ key: schemaQualified,
407
+ config: filters[schemaQualified],
408
+ };
409
+ }
410
+ if (Object.prototype.hasOwnProperty.call(filters, table)) {
411
+ return {
412
+ key: table,
413
+ config: filters[table],
414
+ };
415
+ }
416
+ throw new Error(`Row filter schema is missing configuration for table "${schemaQualified || table}".`);
417
+ }
418
+ function resolvePredicateForOperation(config, kind) {
419
+ if (config === null) {
420
+ return null;
421
+ }
422
+ if (typeof config === "function") {
423
+ return config;
424
+ }
425
+ const opConfig = config;
426
+ const value = opConfig[kind];
427
+ if (value === null) {
428
+ return null;
429
+ }
430
+ if (typeof value !== "function") {
431
+ throw new Error(`Row filter config for ${kind} must be a function or null.`);
432
+ }
433
+ return value;
434
+ }
435
+ function parseRowFilter(predicateFn, table, kind, autoParamCounterStart) {
436
+ const lambda = parseArrowFunctionExpression(predicateFn, `${table}.${kind}`);
437
+ const ctxParamName = getIdentifierParamName(lambda, 1);
438
+ const helpersParamName = getIdentifierParamName(lambda, 2);
439
+ const visitorContext = createBaseContext();
440
+ visitorContext.autoParamCounter = autoParamCounterStart;
441
+ visitorContext.currentTable = table;
442
+ visitorContext.helpersParam = helpersParamName;
443
+ const source = {
444
+ type: "queryOperation",
445
+ operationType: "from",
446
+ table,
447
+ };
448
+ const call = createMethodCall("where", lambda);
449
+ const result = visitWhereOperation(call, source, visitorContext);
450
+ if (!result) {
451
+ throw new Error(`Failed to parse row filter for ${table}.${kind}.`);
452
+ }
453
+ const contextKeys = new Set();
454
+ const predicate = bindContextParams(result.operation.predicate, ctxParamName, contextKeys);
455
+ return {
456
+ predicate,
457
+ autoParams: result.autoParams,
458
+ contextKeys,
459
+ autoParamCounter: visitorContext.autoParamCounter,
460
+ };
461
+ }
462
+ function bindContextParams(expr, ctxParamName, contextKeys) {
463
+ return rewriteExpression(expr, ctxParamName, contextKeys);
464
+ }
465
+ function rewriteExpression(expr, ctxParamName, contextKeys) {
466
+ switch (expr.type) {
467
+ case "param": {
468
+ const paramExpr = expr;
469
+ if (!ctxParamName || paramExpr.param !== ctxParamName) {
470
+ return paramExpr;
471
+ }
472
+ if (!paramExpr.property || paramExpr.index !== undefined) {
473
+ throw new Error("Row filter context parameters must use direct property access (ctx.key).");
474
+ }
475
+ contextKeys.add(paramExpr.property);
476
+ return {
477
+ type: "param",
478
+ param: `${ROW_FILTER_CONTEXT_PARAM_PREFIX}${paramExpr.property}`,
479
+ };
480
+ }
481
+ case "column":
482
+ case "constant":
483
+ case "reference":
484
+ case "allColumns":
485
+ case "booleanColumn":
486
+ case "booleanConstant":
487
+ return expr;
488
+ case "comparison": {
489
+ return {
490
+ ...expr,
491
+ left: rewriteExpression(expr.left, ctxParamName, contextKeys),
492
+ right: rewriteExpression(expr.right, ctxParamName, contextKeys),
493
+ };
494
+ }
495
+ case "logical": {
496
+ return {
497
+ ...expr,
498
+ left: rewriteExpression(expr.left, ctxParamName, contextKeys),
499
+ right: rewriteExpression(expr.right, ctxParamName, contextKeys),
500
+ };
501
+ }
502
+ case "not": {
503
+ return {
504
+ ...expr,
505
+ expression: rewriteExpression(expr.expression, ctxParamName, contextKeys),
506
+ };
507
+ }
508
+ case "booleanMethod": {
509
+ return {
510
+ ...expr,
511
+ object: rewriteExpression(expr.object, ctxParamName, contextKeys),
512
+ arguments: expr.arguments.map((arg) => rewriteExpression(arg, ctxParamName, contextKeys)),
513
+ };
514
+ }
515
+ case "caseInsensitiveFunction": {
516
+ return {
517
+ ...expr,
518
+ arguments: expr.arguments.map((arg) => rewriteExpression(arg, ctxParamName, contextKeys)),
519
+ };
520
+ }
521
+ case "in": {
522
+ return {
523
+ ...expr,
524
+ value: rewriteExpression(expr.value, ctxParamName, contextKeys),
525
+ list: Array.isArray(expr.list)
526
+ ? expr.list.map((item) => rewriteExpression(item, ctxParamName, contextKeys))
527
+ : rewriteExpression(expr.list, ctxParamName, contextKeys),
528
+ };
529
+ }
530
+ case "isNull": {
531
+ return {
532
+ ...expr,
533
+ expression: rewriteExpression(expr.expression, ctxParamName, contextKeys),
534
+ };
535
+ }
536
+ case "arithmetic":
537
+ case "concat": {
538
+ return {
539
+ ...expr,
540
+ left: rewriteExpression(expr.left, ctxParamName, contextKeys),
541
+ right: rewriteExpression(expr.right, ctxParamName, contextKeys),
542
+ };
543
+ }
544
+ case "stringMethod": {
545
+ return {
546
+ ...expr,
547
+ object: rewriteExpression(expr.object, ctxParamName, contextKeys),
548
+ arguments: expr.arguments
549
+ ? expr.arguments.map((arg) => rewriteExpression(arg, ctxParamName, contextKeys))
550
+ : undefined,
551
+ };
552
+ }
553
+ case "case": {
554
+ return {
555
+ ...expr,
556
+ conditions: expr.conditions.map((c) => ({
557
+ when: rewriteExpression(c.when, ctxParamName, contextKeys),
558
+ then: rewriteExpression(c.then, ctxParamName, contextKeys),
559
+ })),
560
+ else: expr.else
561
+ ? rewriteExpression(expr.else, ctxParamName, contextKeys)
562
+ : undefined,
563
+ };
564
+ }
565
+ case "coalesce": {
566
+ return {
567
+ ...expr,
568
+ expressions: expr.expressions.map((e) => rewriteExpression(e, ctxParamName, contextKeys)),
569
+ };
570
+ }
571
+ case "aggregate": {
572
+ return {
573
+ ...expr,
574
+ expression: expr.expression
575
+ ? rewriteExpression(expr.expression, ctxParamName, contextKeys)
576
+ : undefined,
577
+ };
578
+ }
579
+ case "windowFunction": {
580
+ return {
581
+ ...expr,
582
+ partitionBy: expr.partitionBy.map((e) => rewriteExpression(e, ctxParamName, contextKeys)),
583
+ orderBy: expr.orderBy.map((o) => ({
584
+ ...o,
585
+ expression: rewriteExpression(o.expression, ctxParamName, contextKeys),
586
+ })),
587
+ };
588
+ }
589
+ case "object": {
590
+ const nextProperties = {};
591
+ for (const [key, value] of Object.entries(expr.properties)) {
592
+ nextProperties[key] = rewriteExpression(value, ctxParamName, contextKeys);
593
+ }
594
+ return { ...expr, properties: nextProperties };
595
+ }
596
+ case "array": {
597
+ return {
598
+ ...expr,
599
+ elements: expr.elements.map((e) => rewriteExpression(e, ctxParamName, contextKeys)),
600
+ };
601
+ }
602
+ case "memberAccess": {
603
+ return {
604
+ ...expr,
605
+ object: rewriteExpression(expr.object, ctxParamName, contextKeys),
606
+ };
607
+ }
608
+ case "methodCall": {
609
+ return {
610
+ ...expr,
611
+ object: rewriteExpression(expr.object, ctxParamName, contextKeys),
612
+ arguments: expr.arguments.map((a) => rewriteExpression(a, ctxParamName, contextKeys)),
613
+ };
614
+ }
615
+ case "conditional": {
616
+ return {
617
+ ...expr,
618
+ condition: rewriteExpression(expr.condition, ctxParamName, contextKeys),
619
+ then: rewriteExpression(expr.then, ctxParamName, contextKeys),
620
+ else: rewriteExpression(expr.else, ctxParamName, contextKeys),
621
+ };
622
+ }
623
+ case "functionCall": {
624
+ return {
625
+ ...expr,
626
+ arguments: expr.arguments.map((a) => rewriteExpression(a, ctxParamName, contextKeys)),
627
+ };
628
+ }
629
+ case "new": {
630
+ return {
631
+ ...expr,
632
+ arguments: expr.arguments.map((a) => rewriteExpression(a, ctxParamName, contextKeys)),
633
+ };
634
+ }
635
+ case "lambda": {
636
+ return {
637
+ ...expr,
638
+ body: rewriteExpression(expr.body, ctxParamName, contextKeys),
639
+ };
640
+ }
641
+ default:
642
+ return expr;
643
+ }
644
+ }
645
+ function buildContextParams(context, requiredKeys) {
646
+ const params = {};
647
+ for (const key of requiredKeys) {
648
+ if (!Object.prototype.hasOwnProperty.call(context, key)) {
649
+ throw new Error(`Row filter context is missing required key "${key}".`);
650
+ }
651
+ const value = context[key];
652
+ if (value === undefined) {
653
+ throw new Error(`Row filter context key "${key}" must not be undefined.`);
654
+ }
655
+ params[`${ROW_FILTER_CONTEXT_PARAM_PREFIX}${key}`] = value;
656
+ }
657
+ return params;
658
+ }
659
+ function mergeParamsStrict(base, additions, label) {
660
+ for (const key of Object.keys(additions)) {
661
+ if (Object.prototype.hasOwnProperty.call(base, key)) {
662
+ throw new Error(`${label} collided with existing parameter "${key}".`);
663
+ }
664
+ }
665
+ return { ...base, ...additions };
666
+ }
667
+ function andPredicates(left, right) {
668
+ if (!left)
669
+ return right;
670
+ if (!right)
671
+ return left;
672
+ return {
673
+ type: "logical",
674
+ operator: "and",
675
+ left,
676
+ right,
677
+ };
678
+ }
679
+ function selectIncludedAssignments(assignments, params) {
680
+ if (assignments.type !== "object") {
681
+ return {};
682
+ }
683
+ const included = {};
684
+ for (const [column, valueExpr] of Object.entries(assignments.properties)) {
685
+ if (shouldSkipAssignment(valueExpr, params)) {
686
+ continue;
687
+ }
688
+ included[column] = valueExpr;
689
+ }
690
+ return included;
691
+ }
692
+ function shouldSkipAssignment(valueExpr, params) {
693
+ if (valueExpr.type === "param") {
694
+ const paramExpr = valueExpr;
695
+ const paramName = paramExpr.property || paramExpr.param;
696
+ if (Object.prototype.hasOwnProperty.call(params, paramName)) {
697
+ return params[paramName] === undefined;
698
+ }
699
+ return true;
700
+ }
701
+ if (valueExpr.type === "constant") {
702
+ const constant = valueExpr;
703
+ return constant.value === undefined;
704
+ }
705
+ return false;
706
+ }
707
+ function substituteUpdatedColumnsInPredicate(predicate, assignments) {
708
+ function rewriteValue(value) {
709
+ if (value.type !== "column") {
710
+ return rewriteExpression(value, undefined, new Set());
711
+ }
712
+ const colExpr = value;
713
+ if (colExpr.table || colExpr.source) {
714
+ return colExpr;
715
+ }
716
+ const replacement = assignments[colExpr.name];
717
+ if (!replacement) {
718
+ return colExpr;
719
+ }
720
+ if (!isValueExpression(replacement)) {
721
+ throw new Error(`Row filter column "${colExpr.name}" cannot be substituted with a non-value assignment.`);
722
+ }
723
+ return replacement;
724
+ }
725
+ function rewriteBoolean(expr) {
726
+ switch (expr.type) {
727
+ case "comparison":
728
+ return {
729
+ ...expr,
730
+ left: rewriteValue(expr.left),
731
+ right: rewriteValue(expr.right),
732
+ };
733
+ case "logical":
734
+ return {
735
+ ...expr,
736
+ left: rewriteBoolean(expr.left),
737
+ right: rewriteBoolean(expr.right),
738
+ };
739
+ case "not":
740
+ return { ...expr, expression: rewriteBoolean(expr.expression) };
741
+ case "booleanMethod":
742
+ return {
743
+ ...expr,
744
+ object: rewriteValue(expr.object),
745
+ arguments: expr.arguments.map((a) => rewriteValue(a)),
746
+ };
747
+ case "caseInsensitiveFunction":
748
+ return {
749
+ ...expr,
750
+ arguments: [rewriteValue(expr.arguments[0]), rewriteValue(expr.arguments[1])],
751
+ };
752
+ case "in":
753
+ return {
754
+ ...expr,
755
+ value: rewriteValue(expr.value),
756
+ list: Array.isArray(expr.list)
757
+ ? expr.list.map((item) => rewriteValue(item))
758
+ : rewriteExpression(expr.list, undefined, new Set()),
759
+ };
760
+ case "isNull":
761
+ return {
762
+ ...expr,
763
+ expression: rewriteValue(expr.expression),
764
+ };
765
+ default:
766
+ return expr;
767
+ }
768
+ }
769
+ return rewriteBoolean(predicate);
770
+ }
771
+ function parseArrowFunctionExpression(lambda, label) {
772
+ const source = lambda.toString();
773
+ const program = parseJavaScript(source);
774
+ const body = program.body?.[0];
775
+ if (!body || body.type !== "ExpressionStatement") {
776
+ throw new Error(`${label} expects an arrow function expression`);
777
+ }
778
+ const expression = body.expression;
779
+ if (expression.type !== "ArrowFunctionExpression") {
780
+ throw new Error(`${label} expects an arrow function expression`);
781
+ }
782
+ return expression;
783
+ }
784
+ function getIdentifierParamName(lambda, index) {
785
+ const param = lambda.params?.[index];
786
+ if (!param || param.type !== "Identifier") {
787
+ return undefined;
788
+ }
789
+ return param.name;
790
+ }
791
+ function createMethodCall(methodName, argument) {
792
+ return {
793
+ type: "CallExpression",
794
+ callee: {
795
+ type: "MemberExpression",
796
+ object: {
797
+ type: "Identifier",
798
+ name: "__plan",
799
+ },
800
+ property: {
801
+ type: "Identifier",
802
+ name: methodName,
803
+ },
804
+ computed: false,
805
+ optional: false,
806
+ },
807
+ arguments: argument ? [argument] : [],
808
+ optional: false,
809
+ };
810
+ }
811
+ //# sourceMappingURL=row-filters.js.map