@malloydata/malloy 0.0.242 → 0.0.243-dev250314032737
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/dialect/dialect.js +12 -3
- package/dist/dialect/postgres/postgres.d.ts +1 -0
- package/dist/dialect/postgres/postgres.js +1 -0
- package/dist/lang/ast/expressions/expr-filter-expr.js +39 -25
- package/dist/model/filter_compilers.d.ts +5 -1
- package/dist/model/filter_compilers.js +104 -17
- package/dist/model/malloy_query.js +5 -3
- package/dist/version.d.ts +1 -1
- package/dist/version.js +1 -1
- package/package.json +4 -4
package/dist/dialect/dialect.js
CHANGED
|
@@ -243,9 +243,18 @@ class Dialect {
|
|
|
243
243
|
escapeClause = true;
|
|
244
244
|
}
|
|
245
245
|
else {
|
|
246
|
-
if (escapeActive
|
|
247
|
-
|
|
248
|
-
|
|
246
|
+
if (escapeActive) {
|
|
247
|
+
if (this.likeEscape) {
|
|
248
|
+
if (c === '%' || c === '_') {
|
|
249
|
+
escaped += '^';
|
|
250
|
+
escapeClause = true;
|
|
251
|
+
}
|
|
252
|
+
}
|
|
253
|
+
else {
|
|
254
|
+
if (c === '%' || c === '_' || c === '\\') {
|
|
255
|
+
escaped += '\\';
|
|
256
|
+
}
|
|
257
|
+
}
|
|
249
258
|
}
|
|
250
259
|
escaped += c;
|
|
251
260
|
escapeActive = false;
|
|
@@ -25,6 +25,7 @@ export declare class PostgresDialect extends PostgresBase {
|
|
|
25
25
|
readsNestedData: boolean;
|
|
26
26
|
supportsComplexFilteredSources: boolean;
|
|
27
27
|
compoundObjectInSchema: boolean;
|
|
28
|
+
likeEscape: boolean;
|
|
28
29
|
quoteTablePath(tablePath: string): string;
|
|
29
30
|
sqlGroupSetTable(groupSetCount: number): string;
|
|
30
31
|
sqlAnyValue(groupSet: number, fieldName: string): string;
|
|
@@ -26,33 +26,47 @@ class ExprFilterExpression extends expression_def_1.ExpressionDef {
|
|
|
26
26
|
if (matchExpr.type === 'error') {
|
|
27
27
|
return matchExpr;
|
|
28
28
|
}
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
29
|
+
let fParse;
|
|
30
|
+
switch (matchExpr.type) {
|
|
31
|
+
case 'string':
|
|
32
|
+
fParse = malloy_filter_1.StringFilterExpression.parse(this.filterText);
|
|
33
|
+
break;
|
|
34
|
+
case 'number':
|
|
35
|
+
fParse = malloy_filter_1.NumberFilterExpression.parse(this.filterText);
|
|
36
|
+
break;
|
|
37
|
+
case 'boolean':
|
|
38
|
+
fParse = malloy_filter_1.BooleanFilterExpression.parse(this.filterText);
|
|
39
|
+
break;
|
|
40
|
+
case 'date':
|
|
41
|
+
case 'timestamp':
|
|
42
|
+
fParse = malloy_filter_1.TemporalFilterExpression.parse(this.filterText);
|
|
43
|
+
break;
|
|
44
|
+
default:
|
|
45
|
+
return left.loggedErrorExpr('filter-expression-type', `Cannot use filter expressions with type '${matchExpr.type}'`);
|
|
46
|
+
}
|
|
47
|
+
if (fParse.log.length > 0) {
|
|
48
|
+
for (const _err of fParse.log) {
|
|
49
|
+
// mtoy todo actuall get error and report correct position and error type
|
|
50
|
+
return this.loggedErrorExpr('filter-expression-type', `Filter parse error: ${fParse.log[0].message}`);
|
|
48
51
|
}
|
|
49
|
-
return (0, expr_value_1.computedExprValue)({
|
|
50
|
-
value: filterMatch,
|
|
51
|
-
dataType: { type: 'boolean' },
|
|
52
|
-
from: [matchExpr],
|
|
53
|
-
});
|
|
54
52
|
}
|
|
55
|
-
|
|
53
|
+
if (!fParse.parsed) {
|
|
54
|
+
return this.loggedErrorExpr('filter-expression-type', 'FJKLD:JDKSL: expression parsed to null');
|
|
55
|
+
}
|
|
56
|
+
const filterMatch = {
|
|
57
|
+
node: 'filterMatch',
|
|
58
|
+
dataType: matchExpr.type,
|
|
59
|
+
filter: fParse.parsed,
|
|
60
|
+
e: matchExpr.value,
|
|
61
|
+
};
|
|
62
|
+
if (op === '!~') {
|
|
63
|
+
filterMatch.notMatch = true;
|
|
64
|
+
}
|
|
65
|
+
return (0, expr_value_1.computedExprValue)({
|
|
66
|
+
value: filterMatch,
|
|
67
|
+
dataType: { type: 'boolean' },
|
|
68
|
+
from: [matchExpr],
|
|
69
|
+
});
|
|
56
70
|
}
|
|
57
71
|
return this.loggedErrorExpr('filter-expression-type', `Cannot use the '${op}' operator with a filter expression`);
|
|
58
72
|
}
|
|
@@ -1,5 +1,9 @@
|
|
|
1
|
-
import { StringClause } from '@malloydata/malloy-filter';
|
|
1
|
+
import { BooleanClause, ClauseBase, NumberClause, StringClause, TemporalClause } from '@malloydata/malloy-filter';
|
|
2
2
|
import { Dialect } from '../dialect';
|
|
3
3
|
export declare const FilterCompilers: {
|
|
4
|
+
compile(t: string, c: ClauseBase, x: string, d: Dialect): string;
|
|
5
|
+
numberCompile(nc: NumberClause, x: string, d: Dialect): string;
|
|
6
|
+
booleanCompile(bc: BooleanClause, x: string, _d: Dialect): string;
|
|
7
|
+
temporalCompile(tc: TemporalClause, x: string, d: Dialect): string;
|
|
4
8
|
stringCompile(sc: StringClause, x: string, d: Dialect): string;
|
|
5
9
|
};
|
|
@@ -7,15 +7,25 @@
|
|
|
7
7
|
*/
|
|
8
8
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
9
9
|
exports.FilterCompilers = void 0;
|
|
10
|
-
|
|
11
|
-
|
|
10
|
+
const malloy_filter_1 = require("@malloydata/malloy-filter");
|
|
11
|
+
function escapeForLike(v) {
|
|
12
|
+
return v.replace(/([%_\\])/g, '\\$1');
|
|
13
|
+
}
|
|
14
|
+
function invertCompare(no) {
|
|
15
|
+
if (no === '>')
|
|
16
|
+
return '<=';
|
|
17
|
+
else if (no === '<')
|
|
18
|
+
return '>=';
|
|
19
|
+
else if (no === '>=')
|
|
20
|
+
return '<';
|
|
21
|
+
return '>';
|
|
12
22
|
}
|
|
13
23
|
function unlike(disLiked, x) {
|
|
14
|
-
const orNull = `OR ${x} IS NULL`;
|
|
24
|
+
const orNull = ` OR ${x} IS NULL`;
|
|
15
25
|
if (disLiked.length === 1) {
|
|
16
|
-
return `${disLiked[0]}
|
|
26
|
+
return `${disLiked[0]}${orNull}`;
|
|
17
27
|
}
|
|
18
|
-
return `(${disLiked.join('
|
|
28
|
+
return `(${disLiked.join(' AND ')})${orNull}`;
|
|
19
29
|
}
|
|
20
30
|
/*
|
|
21
31
|
* These compilers from filter expression to SQL actually belong in malloy-filters but
|
|
@@ -25,6 +35,83 @@ function unlike(disLiked, x) {
|
|
|
25
35
|
* XXXXFilterCompiler.compile() will move to XXXFilterExpression.compile()
|
|
26
36
|
*/
|
|
27
37
|
exports.FilterCompilers = {
|
|
38
|
+
compile(t, c, x, d) {
|
|
39
|
+
if (t === 'string' && (0, malloy_filter_1.isStringClause)(c)) {
|
|
40
|
+
return exports.FilterCompilers.stringCompile(c, x, d);
|
|
41
|
+
}
|
|
42
|
+
else if (t === 'number' && (0, malloy_filter_1.isNumberClause)(c)) {
|
|
43
|
+
return exports.FilterCompilers.numberCompile(c, x, d);
|
|
44
|
+
}
|
|
45
|
+
else if (t === 'boolean' && (0, malloy_filter_1.isBooleanClause)(c)) {
|
|
46
|
+
return exports.FilterCompilers.booleanCompile(c, x, d);
|
|
47
|
+
}
|
|
48
|
+
else if ((t === 'date' || t === 'timestamp') && (0, malloy_filter_1.isTemporalClause)(c)) {
|
|
49
|
+
return exports.FilterCompilers.temporalCompile(c, x, d);
|
|
50
|
+
}
|
|
51
|
+
throw new Error('INTERNAL ERROR: No filter compiler for ' + t);
|
|
52
|
+
},
|
|
53
|
+
numberCompile(nc, x, d) {
|
|
54
|
+
switch (nc.operator) {
|
|
55
|
+
case '!=':
|
|
56
|
+
case '=': {
|
|
57
|
+
const notEqual = (nc.operator === '=' && nc.not) || (nc.operator === '!=' && !nc.not);
|
|
58
|
+
const optList = nc.values.join(', ');
|
|
59
|
+
if (nc.values.length === 1) {
|
|
60
|
+
if (notEqual)
|
|
61
|
+
return `${x} != ${optList} OR ${x} IS NULL`;
|
|
62
|
+
return `${x} = ${optList}`;
|
|
63
|
+
}
|
|
64
|
+
if (notEqual)
|
|
65
|
+
return `${x} NOT IN (${optList}) OR ${x} IS NULL`;
|
|
66
|
+
return `${x} IN (${optList})`;
|
|
67
|
+
}
|
|
68
|
+
case '>':
|
|
69
|
+
case '<':
|
|
70
|
+
case '>=':
|
|
71
|
+
case '<=':
|
|
72
|
+
return nc.values
|
|
73
|
+
.map(v => `${x} ${nc.operator} ${v}`)
|
|
74
|
+
.join(nc.not ? ' AND ' : ' OR ');
|
|
75
|
+
case 'range': {
|
|
76
|
+
let startOp = nc.startOperator;
|
|
77
|
+
let endOp = nc.endOperator;
|
|
78
|
+
let plus = ' AND ';
|
|
79
|
+
if (nc.not) {
|
|
80
|
+
startOp = invertCompare(startOp);
|
|
81
|
+
endOp = invertCompare(endOp);
|
|
82
|
+
plus = ' OR ';
|
|
83
|
+
}
|
|
84
|
+
return `${x} ${startOp} ${nc.startValue} ${plus} ${x} ${endOp} ${nc.endValue}`;
|
|
85
|
+
}
|
|
86
|
+
case 'null':
|
|
87
|
+
return nc.not ? `${x} IS NOT NULL` : `${x} IS NULL`;
|
|
88
|
+
case '()': {
|
|
89
|
+
const wrapped = '(' + exports.FilterCompilers.numberCompile(nc.expr, x, d) + ')';
|
|
90
|
+
return nc.not ? `NOT ${wrapped}` : wrapped;
|
|
91
|
+
}
|
|
92
|
+
case 'and':
|
|
93
|
+
case 'or':
|
|
94
|
+
return nc.members
|
|
95
|
+
.map(m => exports.FilterCompilers.numberCompile(m, x, d))
|
|
96
|
+
.join(` ${nc.operator.toUpperCase()}`);
|
|
97
|
+
}
|
|
98
|
+
},
|
|
99
|
+
booleanCompile(bc, x, _d) {
|
|
100
|
+
switch (bc.operator) {
|
|
101
|
+
case 'false':
|
|
102
|
+
return `${x} = false`;
|
|
103
|
+
case 'false_or_null':
|
|
104
|
+
return `${x} IS NULL OR ${x} = false`;
|
|
105
|
+
case 'null':
|
|
106
|
+
return bc.not ? `${x} IS NOT NULL` : `${x} IS NULL`;
|
|
107
|
+
case 'true':
|
|
108
|
+
return x;
|
|
109
|
+
}
|
|
110
|
+
},
|
|
111
|
+
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
112
|
+
temporalCompile(tc, x, d) {
|
|
113
|
+
return 'false';
|
|
114
|
+
},
|
|
28
115
|
stringCompile(sc, x, d) {
|
|
29
116
|
switch (sc.operator) {
|
|
30
117
|
case 'null':
|
|
@@ -47,38 +134,38 @@ exports.FilterCompilers = {
|
|
|
47
134
|
return sc.not ? `not ${wrapped}` : wrapped;
|
|
48
135
|
}
|
|
49
136
|
case 'contains': {
|
|
50
|
-
const matches = sc.values.map(v => '%' +
|
|
137
|
+
const matches = sc.values.map(v => '%' + escapeForLike(v) + '%');
|
|
51
138
|
if (sc.not) {
|
|
52
139
|
return unlike(matches.map(m => d.sqlLike('NOT LIKE', x, m)), x);
|
|
53
140
|
}
|
|
54
|
-
return matches.map(m => d.sqlLike('LIKE', x, m)).join('
|
|
141
|
+
return matches.map(m => d.sqlLike('LIKE', x, m)).join(' OR ');
|
|
55
142
|
}
|
|
56
143
|
case 'starts': {
|
|
57
|
-
const matches = sc.values.map(v =>
|
|
144
|
+
const matches = sc.values.map(v => escapeForLike(v) + '%');
|
|
58
145
|
if (sc.not) {
|
|
59
146
|
return unlike(matches.map(m => d.sqlLike('NOT LIKE', x, m)), x);
|
|
60
147
|
}
|
|
61
|
-
return matches.map(m => d.sqlLike('LIKE', x, m)).join('
|
|
148
|
+
return matches.map(m => d.sqlLike('LIKE', x, m)).join(' OR ');
|
|
62
149
|
}
|
|
63
150
|
case 'ends': {
|
|
64
|
-
const matches = sc.values.map(v => '%' +
|
|
151
|
+
const matches = sc.values.map(v => '%' + escapeForLike(v));
|
|
65
152
|
if (sc.not) {
|
|
66
153
|
return unlike(matches.map(m => d.sqlLike('NOT LIKE', x, m)), x);
|
|
67
154
|
}
|
|
68
|
-
return matches.map(m => d.sqlLike('LIKE', x, m)).join('
|
|
155
|
+
return matches.map(m => d.sqlLike('LIKE', x, m)).join(' OR ');
|
|
69
156
|
}
|
|
70
157
|
case '~':
|
|
71
158
|
if (sc.not) {
|
|
72
159
|
return unlike(sc.escaped_values.map(m => d.sqlLike('NOT LIKE', x, m)), x);
|
|
73
160
|
}
|
|
74
|
-
return sc.escaped_values.map(m => d.sqlLike('LIKE', x, m)).join('
|
|
161
|
+
return sc.escaped_values.map(m => d.sqlLike('LIKE', x, m)).join(' OR ');
|
|
75
162
|
case 'and': {
|
|
76
163
|
const clauses = sc.members.map(c => exports.FilterCompilers.stringCompile(c, x, d));
|
|
77
|
-
return clauses.join('
|
|
164
|
+
return clauses.join(' AND ');
|
|
78
165
|
}
|
|
79
166
|
case 'or': {
|
|
80
167
|
const clauses = sc.members.map(c => exports.FilterCompilers.stringCompile(c, x, d));
|
|
81
|
-
return clauses.join('
|
|
168
|
+
return clauses.join(' AND ');
|
|
82
169
|
}
|
|
83
170
|
case ',': {
|
|
84
171
|
/*
|
|
@@ -134,7 +221,7 @@ exports.FilterCompilers = {
|
|
|
134
221
|
if (includeNull) {
|
|
135
222
|
includeExprs.push(`${x} IS NULL`);
|
|
136
223
|
}
|
|
137
|
-
includeSQL = includeExprs.join('
|
|
224
|
+
includeSQL = includeExprs.join(' OR ');
|
|
138
225
|
}
|
|
139
226
|
let excludeSQL = '';
|
|
140
227
|
if (excludes.length > 0 || excludeEmpty || excludeNull) {
|
|
@@ -145,11 +232,11 @@ exports.FilterCompilers = {
|
|
|
145
232
|
if (excludeNull) {
|
|
146
233
|
excludeExprs.push(`${x} IS NOT NULL`);
|
|
147
234
|
}
|
|
148
|
-
excludeSQL = excludeExprs.join('
|
|
235
|
+
excludeSQL = excludeExprs.join(' AND ');
|
|
149
236
|
}
|
|
150
237
|
if (includeSQL) {
|
|
151
238
|
return excludeSQL !== ''
|
|
152
|
-
? includeSQL
|
|
239
|
+
? `(${includeSQL}) AND (${excludeSQL})`
|
|
153
240
|
: includeSQL;
|
|
154
241
|
}
|
|
155
242
|
return excludeSQL !== '' ? excludeSQL : 'true';
|
|
@@ -883,10 +883,12 @@ class QueryField extends QueryNode {
|
|
|
883
883
|
case 'compositeField':
|
|
884
884
|
return '{COMPOSITE_FIELD}';
|
|
885
885
|
case 'filterMatch':
|
|
886
|
-
if (expr.dataType === 'string'
|
|
887
|
-
|
|
886
|
+
if (expr.dataType === 'string' ||
|
|
887
|
+
expr.dataType === 'number' ||
|
|
888
|
+
expr.dataType === 'boolean') {
|
|
889
|
+
return filter_compilers_1.FilterCompilers.compile(expr.dataType, expr.filter, expr.e.sql || '', this.parent.dialect);
|
|
888
890
|
}
|
|
889
|
-
throw new Error(`Internal Error: FCU ${expr.dataType}`);
|
|
891
|
+
throw new Error(`Internal Error: Filter Compiler Undefined (FCU) ${expr.dataType}`);
|
|
890
892
|
default:
|
|
891
893
|
throw new Error(`Internal Error: Unknown expression node '${expr.node}' ${JSON.stringify(expr, undefined, 2)}`);
|
|
892
894
|
}
|
package/dist/version.d.ts
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
export declare const MALLOY_VERSION = "0.0.
|
|
1
|
+
export declare const MALLOY_VERSION = "0.0.243";
|
package/dist/version.js
CHANGED
|
@@ -2,5 +2,5 @@
|
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.MALLOY_VERSION = void 0;
|
|
4
4
|
// generated with 'generate-version-file' script; do not edit manually
|
|
5
|
-
exports.MALLOY_VERSION = '0.0.
|
|
5
|
+
exports.MALLOY_VERSION = '0.0.243';
|
|
6
6
|
//# sourceMappingURL=version.js.map
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@malloydata/malloy",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.243-dev250314032737",
|
|
4
4
|
"license": "MIT",
|
|
5
5
|
"exports": {
|
|
6
6
|
".": "./dist/index.js",
|
|
@@ -41,9 +41,9 @@
|
|
|
41
41
|
"generate-version-file": "VERSION=$(npm pkg get version --workspaces=false | tr -d \\\")\necho \"// generated with 'generate-version-file' script; do not edit manually\\nexport const MALLOY_VERSION = '$VERSION';\" > src/version.ts"
|
|
42
42
|
},
|
|
43
43
|
"dependencies": {
|
|
44
|
-
"@malloydata/malloy-filter": "^0.0.
|
|
45
|
-
"@malloydata/malloy-interfaces": "^0.0.
|
|
46
|
-
"@malloydata/malloy-tag": "^0.0.
|
|
44
|
+
"@malloydata/malloy-filter": "^0.0.243-dev250314032737",
|
|
45
|
+
"@malloydata/malloy-interfaces": "^0.0.243-dev250314032737",
|
|
46
|
+
"@malloydata/malloy-tag": "^0.0.243-dev250314032737",
|
|
47
47
|
"antlr4ts": "^0.5.0-alpha.4",
|
|
48
48
|
"assert": "^2.0.0",
|
|
49
49
|
"jest-diff": "^29.6.2",
|