@powersync/service-sync-rules 0.17.10 → 0.18.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +126 -0
- package/dist/SqlBucketDescriptor.d.ts +3 -3
- package/dist/SqlBucketDescriptor.js +2 -2
- package/dist/SqlBucketDescriptor.js.map +1 -1
- package/dist/SqlDataQuery.js +7 -5
- package/dist/SqlDataQuery.js.map +1 -1
- package/dist/SqlParameterQuery.d.ts +54 -12
- package/dist/SqlParameterQuery.js +106 -33
- package/dist/SqlParameterQuery.js.map +1 -1
- package/dist/SqlSyncRules.d.ts +2 -2
- package/dist/SqlSyncRules.js +21 -4
- package/dist/SqlSyncRules.js.map +1 -1
- package/dist/StaticSqlParameterQuery.d.ts +9 -6
- package/dist/StaticSqlParameterQuery.js +42 -12
- package/dist/StaticSqlParameterQuery.js.map +1 -1
- package/dist/index.d.ts +1 -0
- package/dist/index.js +1 -0
- package/dist/index.js.map +1 -1
- package/dist/json_schema.js +4 -0
- package/dist/json_schema.js.map +1 -1
- package/dist/request_functions.d.ts +19 -0
- package/dist/request_functions.js +47 -0
- package/dist/request_functions.js.map +1 -0
- package/dist/sql_filters.d.ts +38 -4
- package/dist/sql_filters.js +270 -177
- package/dist/sql_filters.js.map +1 -1
- package/dist/sql_functions.d.ts +30 -19
- package/dist/sql_functions.js +113 -17
- package/dist/sql_functions.js.map +1 -1
- package/dist/sql_support.d.ts +6 -3
- package/dist/sql_support.js +72 -35
- package/dist/sql_support.js.map +1 -1
- package/dist/types.d.ts +100 -12
- package/dist/types.js +18 -0
- package/dist/types.js.map +1 -1
- package/dist/utils.d.ts +1 -2
- package/dist/utils.js +0 -6
- package/dist/utils.js.map +1 -1
- package/package.json +6 -3
package/dist/sql_filters.js
CHANGED
|
@@ -1,8 +1,10 @@
|
|
|
1
|
+
import { ExpressionType, TYPE_NONE } from './ExpressionType.js';
|
|
1
2
|
import { SqlRuleError } from './errors.js';
|
|
2
|
-
import { BASIC_OPERATORS,
|
|
3
|
+
import { BASIC_OPERATORS, OPERATOR_IS_NOT_NULL, OPERATOR_IS_NULL, OPERATOR_JSON_EXTRACT_JSON, OPERATOR_JSON_EXTRACT_SQL, OPERATOR_NOT, SQL_FUNCTIONS, castOperator, sqliteTypeOf } from './sql_functions.js';
|
|
4
|
+
import { SQLITE_FALSE, SQLITE_TRUE, andFilters, compileStaticOperator, getOperatorFunction, isClauseError, isParameterMatchClause, isParameterValueClause, isRowValueClause, isStaticValueClause, orFilters, toBooleanParameterSetClause } from './sql_support.js';
|
|
3
5
|
import { isJsonValue } from './utils.js';
|
|
4
|
-
import {
|
|
5
|
-
import {
|
|
6
|
+
import { JSONBig } from '@powersync/service-jsonbig';
|
|
7
|
+
import { REQUEST_FUNCTIONS } from './request_functions.js';
|
|
6
8
|
export const MATCH_CONST_FALSE = [];
|
|
7
9
|
export const MATCH_CONST_TRUE = [{}];
|
|
8
10
|
Object.freeze(MATCH_CONST_TRUE);
|
|
@@ -24,6 +26,7 @@ export class SqlTools {
|
|
|
24
26
|
this.parameter_tables = options.parameter_tables ?? [];
|
|
25
27
|
this.sql = options.sql;
|
|
26
28
|
this.supports_expanding_parameters = options.supports_expanding_parameters ?? false;
|
|
29
|
+
this.supports_parameter_expressions = options.supports_parameter_expressions ?? false;
|
|
27
30
|
}
|
|
28
31
|
error(message, expr) {
|
|
29
32
|
this.errors.push(new SqlRuleError(message, this.sql, expr));
|
|
@@ -44,33 +47,30 @@ export class SqlTools {
|
|
|
44
47
|
const base = this.compileClause(where);
|
|
45
48
|
return toBooleanParameterSetClause(base);
|
|
46
49
|
}
|
|
47
|
-
|
|
50
|
+
compileRowValueExtractor(expr) {
|
|
48
51
|
const clause = this.compileClause(expr);
|
|
49
|
-
if (!
|
|
50
|
-
|
|
52
|
+
if (!isRowValueClause(clause) && !isClauseError(clause)) {
|
|
53
|
+
return this.error('Parameter match expression is not allowed here', expr ?? undefined);
|
|
51
54
|
}
|
|
52
55
|
return clause;
|
|
53
56
|
}
|
|
57
|
+
compileParameterValueExtractor(expr) {
|
|
58
|
+
const clause = this.compileClause(expr);
|
|
59
|
+
if (isClauseError(clause) || isStaticValueClause(clause) || isParameterValueClause(clause)) {
|
|
60
|
+
return clause;
|
|
61
|
+
}
|
|
62
|
+
return this.error('Parameter match expression is not allowed here', expr ?? undefined);
|
|
63
|
+
}
|
|
54
64
|
/**
|
|
55
65
|
* Given an expression, return a compiled clause.
|
|
56
66
|
*/
|
|
57
67
|
compileClause(expr) {
|
|
58
68
|
if (expr == null) {
|
|
59
|
-
return
|
|
60
|
-
evaluate: () => SQLITE_TRUE,
|
|
61
|
-
getType() {
|
|
62
|
-
return ExpressionType.INTEGER;
|
|
63
|
-
}
|
|
64
|
-
};
|
|
69
|
+
return staticValueClause(SQLITE_TRUE);
|
|
65
70
|
}
|
|
66
71
|
else if (isStatic(expr)) {
|
|
67
72
|
const value = staticValue(expr);
|
|
68
|
-
return
|
|
69
|
-
evaluate: () => value,
|
|
70
|
-
getType() {
|
|
71
|
-
return ExpressionType.fromTypeText(sqliteTypeOf(value));
|
|
72
|
-
}
|
|
73
|
-
};
|
|
73
|
+
return staticValueClause(value);
|
|
74
74
|
}
|
|
75
75
|
else if (expr.type == 'ref') {
|
|
76
76
|
const column = expr.name;
|
|
@@ -81,8 +81,7 @@ export class SqlTools {
|
|
|
81
81
|
return this.error(`Schema is not supported in column references`, expr);
|
|
82
82
|
}
|
|
83
83
|
if (this.isParameterRef(expr)) {
|
|
84
|
-
|
|
85
|
-
return { bucketParameter: param };
|
|
84
|
+
return this.getParameterRefClause(expr);
|
|
86
85
|
}
|
|
87
86
|
else if (this.isTableRef(expr)) {
|
|
88
87
|
const table = this.getTableName(expr);
|
|
@@ -128,15 +127,27 @@ export class SqlTools {
|
|
|
128
127
|
}
|
|
129
128
|
else if (op == '=') {
|
|
130
129
|
// Options:
|
|
131
|
-
// 1.
|
|
132
|
-
// 2.
|
|
130
|
+
// 1. row value, row value
|
|
131
|
+
// 2. row value, parameter value
|
|
133
132
|
// 3. static true, parameterMatch - not supported yet
|
|
133
|
+
// 4. parameter value, parameter value
|
|
134
134
|
let staticFilter1;
|
|
135
135
|
let otherFilter1;
|
|
136
|
-
if (
|
|
136
|
+
if (this.supports_parameter_expressions &&
|
|
137
|
+
isParameterValueClause(leftFilter) &&
|
|
138
|
+
isParameterValueClause(rightFilter)) {
|
|
139
|
+
// 4. parameterValue, parameterValue
|
|
140
|
+
// This includes (static value, parameter value)
|
|
141
|
+
// Not applicable to data queries (composeFunction will error).
|
|
142
|
+
// Some of those cases can still be handled with case (2),
|
|
143
|
+
// so we filter for supports_parameter_expressions above.
|
|
144
|
+
const fnImpl = getOperatorFunction('=');
|
|
145
|
+
return this.composeFunction(fnImpl, [leftFilter, rightFilter], [left, right]);
|
|
146
|
+
}
|
|
147
|
+
if (!isRowValueClause(leftFilter) && !isRowValueClause(rightFilter)) {
|
|
137
148
|
return this.error(`Cannot have bucket parameters on both sides of = operator`, expr);
|
|
138
149
|
}
|
|
139
|
-
else if (
|
|
150
|
+
else if (isRowValueClause(leftFilter)) {
|
|
140
151
|
staticFilter1 = leftFilter;
|
|
141
152
|
otherFilter1 = rightFilter;
|
|
142
153
|
}
|
|
@@ -146,17 +157,18 @@ export class SqlTools {
|
|
|
146
157
|
}
|
|
147
158
|
const staticFilter = staticFilter1;
|
|
148
159
|
const otherFilter = otherFilter1;
|
|
149
|
-
if (
|
|
150
|
-
// 1.
|
|
160
|
+
if (isRowValueClause(otherFilter)) {
|
|
161
|
+
// 1. row value = row value
|
|
151
162
|
return compileStaticOperator(op, leftFilter, rightFilter);
|
|
152
163
|
}
|
|
153
164
|
else if (isParameterValueClause(otherFilter)) {
|
|
154
|
-
// 2.
|
|
165
|
+
// 2. row value = parameter value
|
|
166
|
+
const inputParam = basicInputParameter(otherFilter);
|
|
155
167
|
return {
|
|
156
168
|
error: false,
|
|
157
|
-
|
|
169
|
+
inputParameters: [inputParam],
|
|
158
170
|
unbounded: false,
|
|
159
|
-
|
|
171
|
+
filterRow(tables) {
|
|
160
172
|
const value = staticFilter.evaluate(tables);
|
|
161
173
|
if (value == null) {
|
|
162
174
|
// null never matches on =
|
|
@@ -167,15 +179,17 @@ export class SqlTools {
|
|
|
167
179
|
// Cannot persist this, e.g. BLOB
|
|
168
180
|
return MATCH_CONST_FALSE;
|
|
169
181
|
}
|
|
170
|
-
return [{ [
|
|
171
|
-
}
|
|
182
|
+
return [{ [inputParam.key]: value }];
|
|
183
|
+
},
|
|
184
|
+
usesAuthenticatedRequestParameters: otherFilter.usesAuthenticatedRequestParameters,
|
|
185
|
+
usesUnauthenticatedRequestParameters: otherFilter.usesUnauthenticatedRequestParameters
|
|
172
186
|
};
|
|
173
187
|
}
|
|
174
188
|
else if (isParameterMatchClause(otherFilter)) {
|
|
175
|
-
// 3.
|
|
189
|
+
// 3. row value = parameterMatch
|
|
176
190
|
// (bucket.param = 'something') = staticValue
|
|
177
191
|
// To implement this, we need to ensure the static value here can only be true.
|
|
178
|
-
return this.error(`
|
|
192
|
+
return this.error(`Parameter match clauses cannot be used here`, expr);
|
|
179
193
|
}
|
|
180
194
|
else {
|
|
181
195
|
throw new Error('Unexpected');
|
|
@@ -185,16 +199,19 @@ export class SqlTools {
|
|
|
185
199
|
// Options:
|
|
186
200
|
// static IN static
|
|
187
201
|
// parameterValue IN static
|
|
188
|
-
if (
|
|
202
|
+
if (isRowValueClause(leftFilter) && isRowValueClause(rightFilter)) {
|
|
203
|
+
// static1 IN static2
|
|
189
204
|
return compileStaticOperator(op, leftFilter, rightFilter);
|
|
190
205
|
}
|
|
191
|
-
else if (isParameterValueClause(leftFilter) &&
|
|
192
|
-
|
|
206
|
+
else if (isParameterValueClause(leftFilter) && isRowValueClause(rightFilter)) {
|
|
207
|
+
// token_parameters.value IN table.some_array
|
|
208
|
+
// bucket.param IN table.some_array
|
|
209
|
+
const inputParam = basicInputParameter(leftFilter);
|
|
193
210
|
return {
|
|
194
211
|
error: false,
|
|
195
|
-
|
|
212
|
+
inputParameters: [inputParam],
|
|
196
213
|
unbounded: true,
|
|
197
|
-
|
|
214
|
+
filterRow(tables) {
|
|
198
215
|
const aValue = rightFilter.evaluate(tables);
|
|
199
216
|
if (aValue == null) {
|
|
200
217
|
return MATCH_CONST_FALSE;
|
|
@@ -204,27 +221,44 @@ export class SqlTools {
|
|
|
204
221
|
throw new Error('Not an array');
|
|
205
222
|
}
|
|
206
223
|
return values.map((value) => {
|
|
207
|
-
return { [
|
|
224
|
+
return { [inputParam.key]: value };
|
|
208
225
|
});
|
|
209
|
-
}
|
|
226
|
+
},
|
|
227
|
+
usesAuthenticatedRequestParameters: leftFilter.usesAuthenticatedRequestParameters,
|
|
228
|
+
usesUnauthenticatedRequestParameters: leftFilter.usesUnauthenticatedRequestParameters
|
|
210
229
|
};
|
|
211
230
|
}
|
|
212
231
|
else if (this.supports_expanding_parameters &&
|
|
213
|
-
|
|
232
|
+
isRowValueClause(leftFilter) &&
|
|
214
233
|
isParameterValueClause(rightFilter)) {
|
|
215
|
-
|
|
234
|
+
// table.some_value IN token_parameters.some_array
|
|
235
|
+
// This expands into "table_some_value = <value>" for each value of the array.
|
|
236
|
+
// We only support one such filter per query
|
|
237
|
+
const key = `${rightFilter.key}[*]`;
|
|
238
|
+
const inputParam = {
|
|
239
|
+
key: key,
|
|
240
|
+
expands: true,
|
|
241
|
+
filteredRowToLookupValue: (filterParameters) => {
|
|
242
|
+
return filterParameters[key];
|
|
243
|
+
},
|
|
244
|
+
parametersToLookupValue: (parameters) => {
|
|
245
|
+
return rightFilter.lookupParameterValue(parameters);
|
|
246
|
+
}
|
|
247
|
+
};
|
|
216
248
|
return {
|
|
217
249
|
error: false,
|
|
218
|
-
|
|
250
|
+
inputParameters: [inputParam],
|
|
219
251
|
unbounded: false,
|
|
220
|
-
|
|
252
|
+
filterRow(tables) {
|
|
221
253
|
const value = leftFilter.evaluate(tables);
|
|
222
254
|
if (!isJsonValue(value)) {
|
|
223
255
|
// Cannot persist, e.g. BLOB
|
|
224
256
|
return MATCH_CONST_FALSE;
|
|
225
257
|
}
|
|
226
|
-
return [{ [
|
|
227
|
-
}
|
|
258
|
+
return [{ [inputParam.key]: value }];
|
|
259
|
+
},
|
|
260
|
+
usesAuthenticatedRequestParameters: rightFilter.usesAuthenticatedRequestParameters,
|
|
261
|
+
usesUnauthenticatedRequestParameters: rightFilter.usesUnauthenticatedRequestParameters
|
|
228
262
|
};
|
|
229
263
|
}
|
|
230
264
|
else {
|
|
@@ -232,10 +266,8 @@ export class SqlTools {
|
|
|
232
266
|
}
|
|
233
267
|
}
|
|
234
268
|
else if (BASIC_OPERATORS.has(op)) {
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
}
|
|
238
|
-
return compileStaticOperator(op, leftFilter, rightFilter);
|
|
269
|
+
const fnImpl = getOperatorFunction(op);
|
|
270
|
+
return this.composeFunction(fnImpl, [leftFilter, rightFilter], [left, right]);
|
|
239
271
|
}
|
|
240
272
|
else {
|
|
241
273
|
return this.error(`Operator not supported: ${op}`, expr);
|
|
@@ -243,157 +275,84 @@ export class SqlTools {
|
|
|
243
275
|
}
|
|
244
276
|
else if (expr.type == 'unary') {
|
|
245
277
|
if (expr.op == 'NOT') {
|
|
246
|
-
const
|
|
247
|
-
|
|
248
|
-
return filter;
|
|
249
|
-
}
|
|
250
|
-
else if (!isStaticRowValueClause(filter)) {
|
|
251
|
-
return this.error('Cannot use NOT on bucket parameter filters', expr);
|
|
252
|
-
}
|
|
253
|
-
return {
|
|
254
|
-
evaluate: (tables) => {
|
|
255
|
-
const value = filter.evaluate(tables);
|
|
256
|
-
return sqliteNot(value);
|
|
257
|
-
},
|
|
258
|
-
getType() {
|
|
259
|
-
return ExpressionType.INTEGER;
|
|
260
|
-
}
|
|
261
|
-
};
|
|
278
|
+
const clause = this.compileClause(expr.operand);
|
|
279
|
+
return this.composeFunction(OPERATOR_NOT, [clause], [expr.operand]);
|
|
262
280
|
}
|
|
263
281
|
else if (expr.op == 'IS NULL') {
|
|
264
|
-
const
|
|
265
|
-
|
|
266
|
-
return leftFilter;
|
|
267
|
-
}
|
|
268
|
-
else if (isStaticRowValueClause(leftFilter)) {
|
|
269
|
-
// 1. static IS NULL
|
|
270
|
-
const nullValue = {
|
|
271
|
-
evaluate: () => null,
|
|
272
|
-
getType() {
|
|
273
|
-
return ExpressionType.INTEGER;
|
|
274
|
-
}
|
|
275
|
-
};
|
|
276
|
-
return compileStaticOperator('IS', leftFilter, nullValue);
|
|
277
|
-
}
|
|
278
|
-
else if (isParameterValueClause(leftFilter)) {
|
|
279
|
-
// 2. param IS NULL
|
|
280
|
-
return {
|
|
281
|
-
error: false,
|
|
282
|
-
bucketParameters: [leftFilter.bucketParameter],
|
|
283
|
-
unbounded: false,
|
|
284
|
-
filter(tables) {
|
|
285
|
-
return [{ [leftFilter.bucketParameter]: null }];
|
|
286
|
-
}
|
|
287
|
-
};
|
|
288
|
-
}
|
|
289
|
-
else {
|
|
290
|
-
return this.error(`Cannot use IS NULL here`, expr);
|
|
291
|
-
}
|
|
282
|
+
const clause = this.compileClause(expr.operand);
|
|
283
|
+
return this.composeFunction(OPERATOR_IS_NULL, [clause], [expr.operand]);
|
|
292
284
|
}
|
|
293
285
|
else if (expr.op == 'IS NOT NULL') {
|
|
294
|
-
const
|
|
295
|
-
|
|
296
|
-
return leftFilter;
|
|
297
|
-
}
|
|
298
|
-
else if (isStaticRowValueClause(leftFilter)) {
|
|
299
|
-
// 1. static IS NULL
|
|
300
|
-
const nullValue = {
|
|
301
|
-
evaluate: () => null,
|
|
302
|
-
getType() {
|
|
303
|
-
return ExpressionType.INTEGER;
|
|
304
|
-
}
|
|
305
|
-
};
|
|
306
|
-
return compileStaticOperator('IS NOT', leftFilter, nullValue);
|
|
307
|
-
}
|
|
308
|
-
else {
|
|
309
|
-
return this.error(`Cannot use IS NOT NULL here`, expr);
|
|
310
|
-
}
|
|
286
|
+
const clause = this.compileClause(expr.operand);
|
|
287
|
+
return this.composeFunction(OPERATOR_IS_NOT_NULL, [clause], [expr.operand]);
|
|
311
288
|
}
|
|
312
289
|
else {
|
|
313
290
|
return this.error(`Operator ${expr.op} is not supported`, expr);
|
|
314
291
|
}
|
|
315
292
|
}
|
|
316
293
|
else if (expr.type == 'call' && expr.function?.name != null) {
|
|
294
|
+
const schema = expr.function.schema; // schema.function()
|
|
317
295
|
const fn = expr.function.name;
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
296
|
+
if (schema == null) {
|
|
297
|
+
// Just fn()
|
|
298
|
+
const fnImpl = SQL_FUNCTIONS[fn];
|
|
299
|
+
if (fnImpl == null) {
|
|
300
|
+
return this.error(`Function '${fn}' is not defined`, expr);
|
|
301
|
+
}
|
|
302
|
+
const argClauses = expr.args.map((arg) => this.compileClause(arg));
|
|
303
|
+
const composed = this.composeFunction(fnImpl, argClauses, expr.args);
|
|
304
|
+
return composed;
|
|
321
305
|
}
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
error = true;
|
|
306
|
+
else if (schema == 'request') {
|
|
307
|
+
// Special function
|
|
308
|
+
if (!this.supports_parameter_expressions) {
|
|
309
|
+
return this.error(`${schema} schema is not available in data queries`, expr);
|
|
327
310
|
}
|
|
328
|
-
|
|
329
|
-
error
|
|
330
|
-
return this.error(`Bucket parameters are not supported in function call arguments`, arg);
|
|
311
|
+
if (expr.args.length > 0) {
|
|
312
|
+
return this.error(`Function '${schema}.${fn}' does not take arguments`, expr);
|
|
331
313
|
}
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
getType(schema) {
|
|
343
|
-
const argTypes = argExtractors.map((e) => e.getType(schema));
|
|
344
|
-
return fnImpl.getReturnType(argTypes);
|
|
314
|
+
if (fn in REQUEST_FUNCTIONS) {
|
|
315
|
+
const fnImpl = REQUEST_FUNCTIONS[fn];
|
|
316
|
+
return {
|
|
317
|
+
key: 'request.parameters()',
|
|
318
|
+
lookupParameterValue(parameters) {
|
|
319
|
+
return fnImpl.call(parameters);
|
|
320
|
+
},
|
|
321
|
+
usesAuthenticatedRequestParameters: fnImpl.usesAuthenticatedRequestParameters,
|
|
322
|
+
usesUnauthenticatedRequestParameters: fnImpl.usesUnauthenticatedRequestParameters
|
|
323
|
+
};
|
|
345
324
|
}
|
|
346
|
-
|
|
325
|
+
else {
|
|
326
|
+
return this.error(`Function '${schema}.${fn}' is not defined`, expr);
|
|
327
|
+
}
|
|
328
|
+
}
|
|
329
|
+
else {
|
|
330
|
+
// Unknown function with schema
|
|
331
|
+
return this.error(`Function '${schema}.${fn}' is not defined`, expr);
|
|
332
|
+
}
|
|
347
333
|
}
|
|
348
334
|
else if (expr.type == 'member') {
|
|
349
335
|
const operand = this.compileClause(expr.operand);
|
|
350
|
-
if (
|
|
351
|
-
return
|
|
352
|
-
}
|
|
353
|
-
else if (!isStaticRowValueClause(operand)) {
|
|
354
|
-
return this.error(`Bucket parameters are not supported in member lookups`, expr.operand);
|
|
336
|
+
if (!(typeof expr.member == 'string' && (expr.op == '->>' || expr.op == '->'))) {
|
|
337
|
+
return this.error(`Unsupported member operation ${expr.op}`, expr);
|
|
355
338
|
}
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
return jsonExtract(containerString, expr.member, expr.op);
|
|
361
|
-
},
|
|
362
|
-
getType() {
|
|
363
|
-
return ExpressionType.ANY_JSON;
|
|
364
|
-
}
|
|
365
|
-
};
|
|
339
|
+
const debugArgs = [expr.operand, expr];
|
|
340
|
+
const args = [operand, staticValueClause(expr.member)];
|
|
341
|
+
if (expr.op == '->') {
|
|
342
|
+
return this.composeFunction(OPERATOR_JSON_EXTRACT_JSON, args, debugArgs);
|
|
366
343
|
}
|
|
367
344
|
else {
|
|
368
|
-
return this.
|
|
345
|
+
return this.composeFunction(OPERATOR_JSON_EXTRACT_SQL, args, debugArgs);
|
|
369
346
|
}
|
|
370
347
|
}
|
|
371
348
|
else if (expr.type == 'cast') {
|
|
372
349
|
const operand = this.compileClause(expr.operand);
|
|
373
|
-
if (isClauseError(operand)) {
|
|
374
|
-
return operand;
|
|
375
|
-
}
|
|
376
|
-
else if (!isStaticRowValueClause(operand)) {
|
|
377
|
-
return this.error(`Bucket parameters are not supported in cast expressions`, expr.operand);
|
|
378
|
-
}
|
|
379
350
|
const to = expr.to?.name?.toLowerCase();
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
evaluate: (tables) => {
|
|
383
|
-
const value = operand.evaluate(tables);
|
|
384
|
-
if (value == null) {
|
|
385
|
-
return null;
|
|
386
|
-
}
|
|
387
|
-
return cast(value, to);
|
|
388
|
-
},
|
|
389
|
-
getType() {
|
|
390
|
-
return ExpressionType.fromTypeText(to);
|
|
391
|
-
}
|
|
392
|
-
};
|
|
393
|
-
}
|
|
394
|
-
else {
|
|
351
|
+
const castFn = castOperator(to);
|
|
352
|
+
if (castFn == null) {
|
|
395
353
|
return this.error(`CAST not supported for '${to}'`, expr);
|
|
396
354
|
}
|
|
355
|
+
return this.composeFunction(castFn, [operand], [expr.operand]);
|
|
397
356
|
}
|
|
398
357
|
else {
|
|
399
358
|
return this.error(`${expr.type} not supported here`, expr);
|
|
@@ -458,10 +417,18 @@ export class SqlTools {
|
|
|
458
417
|
}
|
|
459
418
|
}
|
|
460
419
|
}
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
420
|
+
getParameterRefClause(expr) {
|
|
421
|
+
const table = expr.table.name;
|
|
422
|
+
const column = expr.name;
|
|
423
|
+
return {
|
|
424
|
+
key: `${table}.${column}`,
|
|
425
|
+
lookupParameterValue: (parameters) => {
|
|
426
|
+
const pt = parameters[table];
|
|
427
|
+
return pt?.[column] ?? null;
|
|
428
|
+
},
|
|
429
|
+
usesAuthenticatedRequestParameters: table == 'token_parameters',
|
|
430
|
+
usesUnauthenticatedRequestParameters: table == 'user_parameters'
|
|
431
|
+
};
|
|
465
432
|
}
|
|
466
433
|
refHasSchema(ref) {
|
|
467
434
|
return ref.table?.schema != null;
|
|
@@ -488,6 +455,103 @@ export class SqlTools {
|
|
|
488
455
|
throw new SqlRuleError(`Undefined table ${ref.table?.name}`, this.sql, ref);
|
|
489
456
|
}
|
|
490
457
|
}
|
|
458
|
+
/**
|
|
459
|
+
* Given a function, compile a clause with the function over compiled arguments.
|
|
460
|
+
*
|
|
461
|
+
* For functions with multiple arguments, the following combinations are supported:
|
|
462
|
+
* fn(StaticValueClause, StaticValueClause) => StaticValueClause
|
|
463
|
+
* fn(ParameterValueClause, ParameterValueClause) => ParameterValueClause
|
|
464
|
+
* fn(RowValueClause, RowValueClause) => RowValueClause
|
|
465
|
+
* fn(ParameterValueClause, StaticValueClause) => ParameterValueClause
|
|
466
|
+
* fn(RowValueClause, StaticValueClause) => RowValueClause
|
|
467
|
+
*
|
|
468
|
+
* This is not supported, and will likely never be supported:
|
|
469
|
+
* fn(ParameterValueClause, RowValueClause) => error
|
|
470
|
+
*
|
|
471
|
+
* @param fnImpl The function or operator implementation
|
|
472
|
+
* @param argClauses The compiled argument clauses
|
|
473
|
+
* @param debugArgExpressions The original parsed expressions, for debug info only
|
|
474
|
+
* @returns a compiled function clause
|
|
475
|
+
*/
|
|
476
|
+
composeFunction(fnImpl, argClauses, debugArgExpressions) {
|
|
477
|
+
let argsType = 'static';
|
|
478
|
+
for (let i = 0; i < argClauses.length; i++) {
|
|
479
|
+
const debugArg = debugArgExpressions[i];
|
|
480
|
+
const clause = argClauses[i];
|
|
481
|
+
if (isClauseError(clause)) {
|
|
482
|
+
// Return immediately on error
|
|
483
|
+
return clause;
|
|
484
|
+
}
|
|
485
|
+
else if (isStaticValueClause(clause)) {
|
|
486
|
+
// argsType unchanged
|
|
487
|
+
}
|
|
488
|
+
else if (isParameterValueClause(clause)) {
|
|
489
|
+
if (!this.supports_parameter_expressions) {
|
|
490
|
+
return this.error(`Cannot use bucket parameters in expressions`, debugArg);
|
|
491
|
+
}
|
|
492
|
+
if (argsType == 'static' || argsType == 'param') {
|
|
493
|
+
argsType = 'param';
|
|
494
|
+
}
|
|
495
|
+
else {
|
|
496
|
+
return this.error(`Cannot use table values and parameters in the same clauses`, debugArg);
|
|
497
|
+
}
|
|
498
|
+
}
|
|
499
|
+
else if (isRowValueClause(clause)) {
|
|
500
|
+
if (argsType == 'static' || argsType == 'row') {
|
|
501
|
+
argsType = 'row';
|
|
502
|
+
}
|
|
503
|
+
else {
|
|
504
|
+
return this.error(`Cannot use table values and parameters in the same clauses`, debugArg);
|
|
505
|
+
}
|
|
506
|
+
}
|
|
507
|
+
else {
|
|
508
|
+
return this.error(`Parameter match clauses cannot be used here`, debugArg);
|
|
509
|
+
}
|
|
510
|
+
}
|
|
511
|
+
if (argsType == 'row' || argsType == 'static') {
|
|
512
|
+
return {
|
|
513
|
+
evaluate: (tables) => {
|
|
514
|
+
const args = argClauses.map((e) => e.evaluate(tables));
|
|
515
|
+
return fnImpl.call(...args);
|
|
516
|
+
},
|
|
517
|
+
getType(schema) {
|
|
518
|
+
const argTypes = argClauses.map((e) => e.getType(schema));
|
|
519
|
+
return fnImpl.getReturnType(argTypes);
|
|
520
|
+
}
|
|
521
|
+
};
|
|
522
|
+
}
|
|
523
|
+
else if (argsType == 'param') {
|
|
524
|
+
const argStrings = argClauses.map((e) => e.key);
|
|
525
|
+
const name = `${fnImpl.debugName}(${argStrings.join(',')})`;
|
|
526
|
+
const usesAuthenticatedRequestParameters = argClauses.find((clause) => isParameterValueClause(clause) && clause.usesAuthenticatedRequestParameters) !=
|
|
527
|
+
null;
|
|
528
|
+
const usesUnauthenticatedRequestParameters = argClauses.find((clause) => isParameterValueClause(clause) && clause.usesUnauthenticatedRequestParameters) !=
|
|
529
|
+
null;
|
|
530
|
+
return {
|
|
531
|
+
key: name,
|
|
532
|
+
lookupParameterValue: (parameters) => {
|
|
533
|
+
const args = argClauses.map((e) => {
|
|
534
|
+
if (isParameterValueClause(e)) {
|
|
535
|
+
return e.lookupParameterValue(parameters);
|
|
536
|
+
}
|
|
537
|
+
else if (isStaticValueClause(e)) {
|
|
538
|
+
return e.value;
|
|
539
|
+
}
|
|
540
|
+
else {
|
|
541
|
+
throw new Error('unreachable condition');
|
|
542
|
+
}
|
|
543
|
+
});
|
|
544
|
+
return fnImpl.call(...args);
|
|
545
|
+
},
|
|
546
|
+
usesAuthenticatedRequestParameters,
|
|
547
|
+
usesUnauthenticatedRequestParameters
|
|
548
|
+
};
|
|
549
|
+
}
|
|
550
|
+
else {
|
|
551
|
+
throw new Error('unreachable condition');
|
|
552
|
+
}
|
|
553
|
+
}
|
|
554
|
+
parameterFunction() { }
|
|
491
555
|
}
|
|
492
556
|
function isStatic(expr) {
|
|
493
557
|
return ['integer', 'string', 'numeric', 'boolean', 'null'].includes(expr.type);
|
|
@@ -503,4 +567,33 @@ function staticValue(expr) {
|
|
|
503
567
|
return expr.value;
|
|
504
568
|
}
|
|
505
569
|
}
|
|
570
|
+
function staticValueClause(value) {
|
|
571
|
+
return {
|
|
572
|
+
value: value,
|
|
573
|
+
// RowValueClause compatibility
|
|
574
|
+
evaluate: () => value,
|
|
575
|
+
getType() {
|
|
576
|
+
return ExpressionType.fromTypeText(sqliteTypeOf(value));
|
|
577
|
+
},
|
|
578
|
+
// ParamterValueClause compatibility
|
|
579
|
+
key: JSONBig.stringify(value),
|
|
580
|
+
lookupParameterValue(_parameters) {
|
|
581
|
+
return value;
|
|
582
|
+
},
|
|
583
|
+
usesAuthenticatedRequestParameters: false,
|
|
584
|
+
usesUnauthenticatedRequestParameters: false
|
|
585
|
+
};
|
|
586
|
+
}
|
|
587
|
+
function basicInputParameter(clause) {
|
|
588
|
+
return {
|
|
589
|
+
key: clause.key,
|
|
590
|
+
expands: false,
|
|
591
|
+
filteredRowToLookupValue: (filterParameters) => {
|
|
592
|
+
return filterParameters[clause.key];
|
|
593
|
+
},
|
|
594
|
+
parametersToLookupValue: (parameters) => {
|
|
595
|
+
return clause.lookupParameterValue(parameters);
|
|
596
|
+
}
|
|
597
|
+
};
|
|
598
|
+
}
|
|
506
599
|
//# sourceMappingURL=sql_filters.js.map
|