@malloydata/malloy-tests 0.0.248-dev250325154148 → 0.0.248-dev250325235002
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/package.json
CHANGED
|
@@ -21,14 +21,14 @@
|
|
|
21
21
|
},
|
|
22
22
|
"dependencies": {
|
|
23
23
|
"@jest/globals": "^29.4.3",
|
|
24
|
-
"@malloydata/db-bigquery": "^0.0.248-
|
|
25
|
-
"@malloydata/db-duckdb": "^0.0.248-
|
|
26
|
-
"@malloydata/db-postgres": "^0.0.248-
|
|
27
|
-
"@malloydata/db-snowflake": "^0.0.248-
|
|
28
|
-
"@malloydata/db-trino": "^0.0.248-
|
|
29
|
-
"@malloydata/malloy": "^0.0.248-
|
|
30
|
-
"@malloydata/malloy-tag": "^0.0.248-
|
|
31
|
-
"@malloydata/render": "^0.0.248-
|
|
24
|
+
"@malloydata/db-bigquery": "^0.0.248-dev250325235002",
|
|
25
|
+
"@malloydata/db-duckdb": "^0.0.248-dev250325235002",
|
|
26
|
+
"@malloydata/db-postgres": "^0.0.248-dev250325235002",
|
|
27
|
+
"@malloydata/db-snowflake": "^0.0.248-dev250325235002",
|
|
28
|
+
"@malloydata/db-trino": "^0.0.248-dev250325235002",
|
|
29
|
+
"@malloydata/malloy": "^0.0.248-dev250325235002",
|
|
30
|
+
"@malloydata/malloy-tag": "^0.0.248-dev250325235002",
|
|
31
|
+
"@malloydata/render": "^0.0.248-dev250325235002",
|
|
32
32
|
"events": "^3.3.0",
|
|
33
33
|
"jsdom": "^22.1.0",
|
|
34
34
|
"luxon": "^2.4.0",
|
|
@@ -38,5 +38,5 @@
|
|
|
38
38
|
"@types/jsdom": "^21.1.1",
|
|
39
39
|
"@types/luxon": "^2.4.0"
|
|
40
40
|
},
|
|
41
|
-
"version": "0.0.248-
|
|
41
|
+
"version": "0.0.248-dev250325235002"
|
|
42
42
|
}
|
|
@@ -16,98 +16,94 @@ afterAll(async () => {
|
|
|
16
16
|
await runtimes.closeAll();
|
|
17
17
|
});
|
|
18
18
|
|
|
19
|
-
// mtoy todo sit down with each parser and compiler and make sure there is a test for every case
|
|
20
|
-
|
|
21
19
|
describe.each(runtimes.runtimeList)('filter expressions %s', (dbName, db) => {
|
|
22
20
|
const q = db.getQuoter();
|
|
23
21
|
describe('string filter expressions', () => {
|
|
24
|
-
|
|
22
|
+
function got(s: string) {
|
|
23
|
+
const zipMe = s.split(',');
|
|
24
|
+
return zipMe.map(s => ({nm: s}));
|
|
25
|
+
}
|
|
26
|
+
const xbq = db.dialect.sqlLiteralString('x\\');
|
|
25
27
|
const abc = db.loadModel(`
|
|
26
28
|
source: abc is ${dbName}.sql("""
|
|
27
|
-
SELECT 'abc' as ${q`s`}, '
|
|
28
|
-
UNION ALL SELECT 'def', '
|
|
29
|
-
UNION ALL SELECT
|
|
30
|
-
UNION ALL SELECT '', '
|
|
31
|
-
UNION ALL SELECT
|
|
29
|
+
SELECT 'abc' as ${q`s`}, 'abc' as ${q`nm`}
|
|
30
|
+
UNION ALL SELECT 'def', 'def'
|
|
31
|
+
UNION ALL SELECT ${xbq}, 'xback'
|
|
32
|
+
UNION ALL SELECT '', 'z-empty'
|
|
33
|
+
UNION ALL SELECT null, 'z-null'
|
|
32
34
|
""")
|
|
33
35
|
`);
|
|
34
36
|
|
|
35
|
-
test('abc', async () => {
|
|
37
|
+
test('is abc', async () => {
|
|
36
38
|
await expect(`
|
|
37
39
|
run: abc -> {
|
|
38
40
|
where: s ~ f'abc';
|
|
39
41
|
select: s
|
|
40
42
|
}`).malloyResultMatches(abc, [{s: 'abc'}]);
|
|
41
43
|
});
|
|
44
|
+
test.skip('empty string filter expression', async () => {
|
|
45
|
+
/*
|
|
46
|
+
since the sql generated works when pasted into mysql
|
|
47
|
+
my next suggestion is that there is some funky re-ordering
|
|
48
|
+
happening in the result processing which will test tomorrow
|
|
49
|
+
*/
|
|
50
|
+
await expect(`
|
|
51
|
+
# test.verbose
|
|
52
|
+
run: abc -> {
|
|
53
|
+
where: s ~ f'';
|
|
54
|
+
select: *; order_by: nm asc
|
|
55
|
+
}`).malloyResultMatches(abc, got('abc,def,xback,z-empty,z-null'));
|
|
56
|
+
});
|
|
42
57
|
test('abc,def', async () => {
|
|
43
58
|
await expect(`
|
|
44
59
|
run: abc -> {
|
|
45
60
|
where: s ~ f'abc,def';
|
|
46
|
-
select: nm; order_by: nm
|
|
47
|
-
}`).malloyResultMatches(abc,
|
|
61
|
+
select: nm; order_by: nm asc
|
|
62
|
+
}`).malloyResultMatches(abc, got('abc,def'));
|
|
48
63
|
});
|
|
49
64
|
test('-abc', async () => {
|
|
50
65
|
await expect(`
|
|
66
|
+
# test.verbose
|
|
51
67
|
run: abc -> {
|
|
52
68
|
where: s ~ f'-abc',
|
|
53
69
|
select: nm; order_by: nm asc
|
|
54
|
-
}`).malloyResultMatches(abc,
|
|
55
|
-
{nm: '1 - def'},
|
|
56
|
-
{nm: '2 - null'},
|
|
57
|
-
{nm: '3 - empty'},
|
|
58
|
-
{nm: '4 - xback'},
|
|
59
|
-
]);
|
|
70
|
+
}`).malloyResultMatches(abc, got('def,xback,z-empty,z-null'));
|
|
60
71
|
});
|
|
61
72
|
test('-starts', async () => {
|
|
62
73
|
await expect(`
|
|
74
|
+
# test.verbose
|
|
63
75
|
run: abc -> {
|
|
64
76
|
where: s ~ f'-a%',
|
|
65
77
|
select: nm; order_by: nm asc
|
|
66
|
-
}`).malloyResultMatches(abc,
|
|
67
|
-
{nm: '1 - def'},
|
|
68
|
-
{nm: '2 - null'},
|
|
69
|
-
{nm: '3 - empty'},
|
|
70
|
-
{nm: '4 - xback'},
|
|
71
|
-
]);
|
|
78
|
+
}`).malloyResultMatches(abc, got('def,xback,z-empty,z-null'));
|
|
72
79
|
});
|
|
73
80
|
test('-contains', async () => {
|
|
74
81
|
await expect(`
|
|
82
|
+
# test.verbose
|
|
75
83
|
run: abc -> {
|
|
76
84
|
where: s ~ f'-%b%',
|
|
77
85
|
select: nm; order_by: nm asc
|
|
78
|
-
}`).malloyResultMatches(abc,
|
|
79
|
-
{nm: '1 - def'},
|
|
80
|
-
{nm: '2 - null'},
|
|
81
|
-
{nm: '3 - empty'},
|
|
82
|
-
{nm: '4 - xback'},
|
|
83
|
-
]);
|
|
86
|
+
}`).malloyResultMatches(abc, got('def,xback,z-empty,z-null'));
|
|
84
87
|
});
|
|
85
88
|
test('-end', async () => {
|
|
86
89
|
await expect(`
|
|
90
|
+
# test.verbose
|
|
87
91
|
run: abc -> {
|
|
88
92
|
where: s ~ f'-%c',
|
|
89
93
|
select: nm; order_by: nm asc
|
|
90
|
-
}`).malloyResultMatches(abc,
|
|
91
|
-
{nm: '1 - def'},
|
|
92
|
-
{nm: '2 - null'},
|
|
93
|
-
{nm: '3 - empty'},
|
|
94
|
-
{nm: '4 - xback'},
|
|
95
|
-
]);
|
|
94
|
+
}`).malloyResultMatches(abc, got('def,xback,z-empty,z-null'));
|
|
96
95
|
});
|
|
97
96
|
test('unlike', async () => {
|
|
98
97
|
await expect(`
|
|
98
|
+
# test.verbose
|
|
99
99
|
run: abc -> {
|
|
100
100
|
where: s ~ f'-a%c',
|
|
101
101
|
select: nm; order_by: nm asc
|
|
102
|
-
}`).malloyResultMatches(abc,
|
|
103
|
-
{nm: '1 - def'},
|
|
104
|
-
{nm: '2 - null'},
|
|
105
|
-
{nm: '3 - empty'},
|
|
106
|
-
{nm: '4 - xback'},
|
|
107
|
-
]);
|
|
102
|
+
}`).malloyResultMatches(abc, got('def,xback,z-empty,z-null'));
|
|
108
103
|
});
|
|
109
104
|
test('simple but not ___,-abc', async () => {
|
|
110
105
|
await expect(`
|
|
106
|
+
# test.verbose
|
|
111
107
|
run: abc -> {
|
|
112
108
|
where: s ~ f'___,-abc';
|
|
113
109
|
select: s
|
|
@@ -115,43 +111,39 @@ describe.each(runtimes.runtimeList)('filter expressions %s', (dbName, db) => {
|
|
|
115
111
|
});
|
|
116
112
|
test('empty', async () => {
|
|
117
113
|
await expect(`
|
|
114
|
+
# test.verbose
|
|
118
115
|
run: abc -> {
|
|
119
116
|
where: s ~ f'empty'
|
|
120
117
|
select: nm; order_by: nm asc
|
|
121
|
-
}`).malloyResultMatches(abc,
|
|
118
|
+
}`).malloyResultMatches(abc, got('z-empty,z-null'));
|
|
122
119
|
});
|
|
123
120
|
test('-empty', async () => {
|
|
124
121
|
await expect(`
|
|
122
|
+
# test.verbose
|
|
125
123
|
run: abc -> {
|
|
126
124
|
where: s ~ f'-empty'
|
|
127
125
|
select: nm; order_by: nm asc
|
|
128
|
-
}`).malloyResultMatches(abc,
|
|
129
|
-
{nm: '0 - abc'},
|
|
130
|
-
{nm: '1 - def'},
|
|
131
|
-
{nm: '4 - xback'},
|
|
132
|
-
]);
|
|
126
|
+
}`).malloyResultMatches(abc, got('abc,def,xback'));
|
|
133
127
|
});
|
|
134
128
|
test('null', async () => {
|
|
135
129
|
await expect(`
|
|
130
|
+
# test.verbose
|
|
136
131
|
run: abc -> {
|
|
137
132
|
where: s ~ f'null'
|
|
138
133
|
select: nm
|
|
139
|
-
}`).malloyResultMatches(abc,
|
|
134
|
+
}`).malloyResultMatches(abc, got('z-null'));
|
|
140
135
|
});
|
|
141
136
|
test('-null', async () => {
|
|
142
137
|
await expect(`
|
|
138
|
+
# test.verbose
|
|
143
139
|
run: abc -> {
|
|
144
140
|
where: s ~ f'-null'
|
|
145
141
|
select: nm; order_by: nm asc
|
|
146
|
-
}`).malloyResultMatches(abc,
|
|
147
|
-
{nm: '0 - abc'},
|
|
148
|
-
{nm: '1 - def'},
|
|
149
|
-
{nm: '3 - empty'},
|
|
150
|
-
{nm: '4 - xback'},
|
|
151
|
-
]);
|
|
142
|
+
}`).malloyResultMatches(abc, got('abc,def,xback,z-empty'));
|
|
152
143
|
});
|
|
153
144
|
test('starts', async () => {
|
|
154
145
|
await expect(`
|
|
146
|
+
# test.verbose
|
|
155
147
|
run: abc -> {
|
|
156
148
|
where: s ~ f'a%';
|
|
157
149
|
select: s
|
|
@@ -159,13 +151,15 @@ describe.each(runtimes.runtimeList)('filter expressions %s', (dbName, db) => {
|
|
|
159
151
|
});
|
|
160
152
|
test('contains', async () => {
|
|
161
153
|
await expect(`
|
|
154
|
+
# test.verbose
|
|
162
155
|
run: abc -> {
|
|
163
156
|
where: s ~ f'%b%,%e%';
|
|
164
|
-
select:
|
|
157
|
+
select: *; order_by: nm asc
|
|
165
158
|
}`).malloyResultMatches(abc, [{s: 'abc'}, {s: 'def'}]);
|
|
166
159
|
});
|
|
167
160
|
test('simple ends', async () => {
|
|
168
161
|
await expect(`
|
|
162
|
+
# test.verbose
|
|
169
163
|
run: abc -> {
|
|
170
164
|
where: s ~ f'%c';
|
|
171
165
|
select: s
|
|
@@ -173,17 +167,19 @@ describe.each(runtimes.runtimeList)('filter expressions %s', (dbName, db) => {
|
|
|
173
167
|
});
|
|
174
168
|
test('ends in backslash', async () => {
|
|
175
169
|
await expect(`
|
|
170
|
+
# test.verbose
|
|
176
171
|
run: abc -> {
|
|
177
172
|
where: s ~ f'%\\\\'
|
|
178
173
|
select: nm
|
|
179
|
-
}`).malloyResultMatches(abc,
|
|
174
|
+
}`).malloyResultMatches(abc, got('xback'));
|
|
180
175
|
});
|
|
181
176
|
test('= x backslash', async () => {
|
|
182
177
|
await expect(`
|
|
178
|
+
# test.verbose
|
|
183
179
|
run: abc -> {
|
|
184
180
|
where: s ~ f'x\\\\'
|
|
185
181
|
select: nm
|
|
186
|
-
}`).malloyResultMatches(abc,
|
|
182
|
+
}`).malloyResultMatches(abc, got('xback'));
|
|
187
183
|
});
|
|
188
184
|
});
|
|
189
185
|
|
|
@@ -198,6 +194,20 @@ describe.each(runtimes.runtimeList)('filter expressions %s', (dbName, db) => {
|
|
|
198
194
|
UNION ALL SELECT NULL, 'null'
|
|
199
195
|
""")
|
|
200
196
|
`);
|
|
197
|
+
test.skip('empty numeric filter', async () => {
|
|
198
|
+
await expect(`
|
|
199
|
+
run: nums -> {
|
|
200
|
+
where: n ~ f''
|
|
201
|
+
select: t; order_by: t asc
|
|
202
|
+
}`).malloyResultMatches(nums, [
|
|
203
|
+
{t: '0'},
|
|
204
|
+
{t: '1'},
|
|
205
|
+
{t: '2'},
|
|
206
|
+
{t: '3'},
|
|
207
|
+
{t: '4'},
|
|
208
|
+
{t: 'null'},
|
|
209
|
+
]);
|
|
210
|
+
});
|
|
201
211
|
test('2', async () => {
|
|
202
212
|
await expect(`
|
|
203
213
|
run: nums -> {
|
|
@@ -227,7 +237,6 @@ describe.each(runtimes.runtimeList)('filter expressions %s', (dbName, db) => {
|
|
|
227
237
|
});
|
|
228
238
|
test('not [1 to 3]', async () => {
|
|
229
239
|
await expect(`
|
|
230
|
-
# test.verbose
|
|
231
240
|
run: nums -> {
|
|
232
241
|
where: n ~ f'not [1 to 3]'
|
|
233
242
|
select: t; order_by: t asc
|
|
@@ -299,6 +308,7 @@ describe.each(runtimes.runtimeList)('filter expressions %s', (dbName, db) => {
|
|
|
299
308
|
});
|
|
300
309
|
|
|
301
310
|
// mysql doesn't have true booleans ...
|
|
311
|
+
const testBoolean = !db.dialect.booleanAsNumbers;
|
|
302
312
|
describe('boolean filter expressions', () => {
|
|
303
313
|
const facts = db.loadModel(`
|
|
304
314
|
source: facts is ${dbName}.sql("""
|
|
@@ -307,48 +317,59 @@ describe.each(runtimes.runtimeList)('filter expressions %s', (dbName, db) => {
|
|
|
307
317
|
UNION ALL SELECT NULL, 'null'
|
|
308
318
|
""")
|
|
309
319
|
`);
|
|
310
|
-
test.when(
|
|
320
|
+
test.when(testBoolean)('true', async () => {
|
|
311
321
|
await expect(`
|
|
312
322
|
run: facts -> {
|
|
313
323
|
where: b ~ f'true'
|
|
314
324
|
select: t; order_by: t asc
|
|
315
325
|
}`).malloyResultMatches(facts, [{t: 'true'}]);
|
|
316
326
|
});
|
|
317
|
-
test.when(
|
|
327
|
+
test.when(testBoolean)('true', async () => {
|
|
318
328
|
await expect(`
|
|
319
329
|
run: facts -> {
|
|
320
330
|
where: b ~ f'true'
|
|
321
331
|
select: t; order_by: t asc
|
|
322
332
|
}`).malloyResultMatches(facts, [{t: 'true'}]);
|
|
323
333
|
});
|
|
324
|
-
test.when(
|
|
334
|
+
test.when(testBoolean)('false', async () => {
|
|
325
335
|
await expect(`
|
|
326
336
|
run: facts -> {
|
|
327
337
|
where: b ~ f'false'
|
|
328
338
|
select: t; order_by: t asc
|
|
329
339
|
}`).malloyResultMatches(facts, [{t: 'false'}, {t: 'null'}]);
|
|
330
340
|
});
|
|
331
|
-
test.when(
|
|
341
|
+
test.when(testBoolean)('=false', async () => {
|
|
332
342
|
await expect(`
|
|
333
343
|
run: facts -> {
|
|
334
344
|
where: b ~ f'=false'
|
|
335
345
|
select: t; order_by: t asc
|
|
336
346
|
}`).malloyResultMatches(facts, [{t: 'false'}]);
|
|
337
347
|
});
|
|
338
|
-
test.when(
|
|
348
|
+
test.when(testBoolean)('null', async () => {
|
|
339
349
|
await expect(`
|
|
340
350
|
run: facts -> {
|
|
341
351
|
where: b ~ f'null'
|
|
342
352
|
select: t; order_by: t asc
|
|
343
353
|
}`).malloyResultMatches(facts, [{t: 'null'}]);
|
|
344
354
|
});
|
|
345
|
-
test.when(
|
|
355
|
+
test.when(testBoolean)('not null', async () => {
|
|
346
356
|
await expect(`
|
|
347
357
|
run: facts -> {
|
|
348
358
|
where: b ~ f'not null'
|
|
349
359
|
select: t; order_by: t asc
|
|
350
360
|
}`).malloyResultMatches(facts, [{t: 'false'}, {t: 'true'}]);
|
|
351
361
|
});
|
|
362
|
+
// test.when(testBoolean)('empty boolean filter', async () => {
|
|
363
|
+
// await expect(`
|
|
364
|
+
// run: facts -> {
|
|
365
|
+
// where: b ~ f''
|
|
366
|
+
// select: t; order_by: t asc
|
|
367
|
+
// }`).malloyResultMatches(facts, [
|
|
368
|
+
// {t: 'false'},
|
|
369
|
+
// {t: 'null'},
|
|
370
|
+
// {t: 'true'},
|
|
371
|
+
// ]);
|
|
372
|
+
// });
|
|
352
373
|
});
|
|
353
374
|
|
|
354
375
|
type TL = 'timeLiteral';
|
|
@@ -373,12 +394,12 @@ describe.each(runtimes.runtimeList)('filter expressions %s', (dbName, db) => {
|
|
|
373
394
|
* { t: 1 second before start, n: 'before' }
|
|
374
395
|
* { t: start, n: 'first' }
|
|
375
396
|
* { t: 1 second before end, n: 'last' }
|
|
376
|
-
* { t: end, n: '
|
|
377
|
-
* { t: NULL n: '
|
|
397
|
+
* { t: end, n: 'post-range' }
|
|
398
|
+
* { t: NULL n: 'z-null' }
|
|
378
399
|
* Use malloyResultMatches(range, inRange) or (range, notInRange)
|
|
379
400
|
*/
|
|
380
401
|
const inRange = [{n: 'first'}, {n: 'last'}];
|
|
381
|
-
const notInRange = [{n: 'before'}, {n: '
|
|
402
|
+
const notInRange = [{n: 'before'}, {n: 'post-range'}];
|
|
382
403
|
function mkRange(start: string, end: string) {
|
|
383
404
|
const begin = LuxonDateTime.fromFormat(start, fTimestamp);
|
|
384
405
|
const b4 = begin.minus({second: 1});
|
|
@@ -394,8 +415,8 @@ describe.each(runtimes.runtimeList)('filter expressions %s', (dbName, db) => {
|
|
|
394
415
|
SELECT ${before} AS ${q`t`}, 'before' AS ${q`n`}
|
|
395
416
|
UNION ALL SELECT ${lit(start, 'timestamp')}, 'first'
|
|
396
417
|
UNION ALL SELECT ${last} , 'last'
|
|
397
|
-
UNION ALL SELECT ${lit(end, 'timestamp')}, '
|
|
398
|
-
UNION ALL SELECT NULL, '
|
|
418
|
+
UNION ALL SELECT ${lit(end, 'timestamp')}, 'post-range'
|
|
419
|
+
UNION ALL SELECT NULL, 'z-null'
|
|
399
420
|
""")
|
|
400
421
|
-> {select: *; order_by: n}`;
|
|
401
422
|
return db.loadModel(rangeModel);
|
|
@@ -412,10 +433,10 @@ describe.each(runtimes.runtimeList)('filter expressions %s', (dbName, db) => {
|
|
|
412
433
|
)} AS ${q`t`}, 'before' AS ${q`n`}
|
|
413
434
|
UNION ALL SELECT ${lit(start, 'date')}, 'first'
|
|
414
435
|
UNION ALL SELECT ${lit(last.toFormat(fDate), 'date')} , 'last'
|
|
415
|
-
UNION ALL SELECT ${lit(end, 'date')}, '
|
|
416
|
-
UNION ALL SELECT NULL, '
|
|
436
|
+
UNION ALL SELECT ${lit(end, 'date')}, 'post-range'
|
|
437
|
+
UNION ALL SELECT NULL, 'z-null'
|
|
417
438
|
""")
|
|
418
|
-
-> {select:
|
|
439
|
+
-> {select: t,n; order_by: n}`;
|
|
419
440
|
return db.loadModel(rangeModel);
|
|
420
441
|
}
|
|
421
442
|
|
|
@@ -432,7 +453,7 @@ describe.each(runtimes.runtimeList)('filter expressions %s', (dbName, db) => {
|
|
|
432
453
|
const range = mkDateRange('2001-01-01', '2001-04-01');
|
|
433
454
|
await expect(`
|
|
434
455
|
run: range + { where: t ~ f'after 2001-Q1' }
|
|
435
|
-
`).malloyResultMatches(range, {n: '
|
|
456
|
+
`).malloyResultMatches(range, {n: 'post-range'});
|
|
436
457
|
});
|
|
437
458
|
test('date before month', async () => {
|
|
438
459
|
const range = mkDateRange('2001-01-01', '2001-02-01');
|
|
@@ -497,7 +518,7 @@ describe.each(runtimes.runtimeList)('filter expressions %s', (dbName, db) => {
|
|
|
497
518
|
const range = mkRange('2001-01-01 00:00:00', '2002-01-01 00:00:00');
|
|
498
519
|
await expect(`
|
|
499
520
|
run: range + { where: t ~ f'after 2001' }
|
|
500
|
-
`).malloyResultMatches(range, [{n: '
|
|
521
|
+
`).malloyResultMatches(range, [{n: 'post-range'}]);
|
|
501
522
|
});
|
|
502
523
|
test('y2k for 1 minute', async () => {
|
|
503
524
|
const range = mkRange('2001-01-01 00:00:00', '2001-01-01 00:01:00');
|
|
@@ -545,17 +566,30 @@ describe.each(runtimes.runtimeList)('filter expressions %s', (dbName, db) => {
|
|
|
545
566
|
const range = mkRange('2001-01-01 00:00:00', '2002-01-01 00:00:00');
|
|
546
567
|
await expect(`
|
|
547
568
|
run: range + { where: t ~ f'null' }
|
|
548
|
-
`).malloyResultMatches(range, [{n: '
|
|
569
|
+
`).malloyResultMatches(range, [{n: 'z-null'}]);
|
|
549
570
|
});
|
|
550
571
|
test('not null', async () => {
|
|
551
572
|
const range = mkRange('2001-01-01 00:00:00', '2002-01-01 00:00:00');
|
|
552
573
|
await expect(`
|
|
553
|
-
run: range + { where: t ~ f'not null' }
|
|
574
|
+
run: range + { where: t ~ f'not null'; order_by: n }
|
|
575
|
+
`).malloyResultMatches(range, [
|
|
576
|
+
{n: 'before'},
|
|
577
|
+
{n: 'first'},
|
|
578
|
+
{n: 'last'},
|
|
579
|
+
{n: 'post-range'},
|
|
580
|
+
]);
|
|
581
|
+
});
|
|
582
|
+
test.skip('empty temporal filter', async () => {
|
|
583
|
+
const range = mkRange('2001-01-01 00:00:00', '2002-01-01 00:00:00');
|
|
584
|
+
await expect(`
|
|
585
|
+
# test.verbose
|
|
586
|
+
run: range + { where: t ~ f''; order_by: n }
|
|
554
587
|
`).malloyResultMatches(range, [
|
|
555
588
|
{n: 'before'},
|
|
556
589
|
{n: 'first'},
|
|
557
590
|
{n: 'last'},
|
|
558
|
-
{n: '
|
|
591
|
+
{n: 'post-range'},
|
|
592
|
+
{n: 'z-null'},
|
|
559
593
|
]);
|
|
560
594
|
});
|
|
561
595
|
test('year literal', async () => {
|
|
@@ -550,6 +550,7 @@ runtimes.runtimeMap.forEach((runtime, databaseName) => {
|
|
|
550
550
|
// symmetric aggregate are needed on both sides of the join
|
|
551
551
|
// Check the row count and that sums on each side work properly.
|
|
552
552
|
await expect(`
|
|
553
|
+
# test.verbose
|
|
553
554
|
run: ${databaseName}.table('malloytest.state_facts') -> {
|
|
554
555
|
group_by: state
|
|
555
556
|
nest: ugly is {
|
|
@@ -184,4 +184,166 @@ describe.each(runtimes.runtimeList)('%s', (databaseName, runtime) => {
|
|
|
184
184
|
} -> foo
|
|
185
185
|
`).malloyResultMatches(orderByModel, {});
|
|
186
186
|
});
|
|
187
|
+
|
|
188
|
+
// There's a problem with null ordering in MySQL which we are ignoring for now
|
|
189
|
+
const testNullOrdering = databaseName !== 'mysql';
|
|
190
|
+
const testTimes =
|
|
191
|
+
testNullOrdering && !['presto', 'trino'].includes(databaseName);
|
|
192
|
+
describe('null ordering', () => {
|
|
193
|
+
const q = runtime.getQuoter();
|
|
194
|
+
const a = runtime.dialect.sqlLiteralString('a');
|
|
195
|
+
const b = runtime.dialect.sqlLiteralString('b');
|
|
196
|
+
const c = runtime.dialect.sqlLiteralString('c');
|
|
197
|
+
const abc = `${databaseName}.sql("""
|
|
198
|
+
SELECT ${a} as ${q`l`}, 'a' as ${q`ln`}
|
|
199
|
+
UNION ALL SELECT ${c}, 'c'
|
|
200
|
+
UNION ALL SELECT ${b}, 'b'
|
|
201
|
+
UNION ALL SELECT NULL, 'null'
|
|
202
|
+
""")`;
|
|
203
|
+
test.when(testNullOrdering)(
|
|
204
|
+
'null last in select string with ascending order',
|
|
205
|
+
async () => {
|
|
206
|
+
await expect(
|
|
207
|
+
`run: ${abc} -> { select: *; order_by: l asc }`
|
|
208
|
+
).malloyResultMatches(runtime, [
|
|
209
|
+
{ln: 'a'},
|
|
210
|
+
{ln: 'b'},
|
|
211
|
+
{ln: 'c'},
|
|
212
|
+
{ln: 'null'},
|
|
213
|
+
]);
|
|
214
|
+
}
|
|
215
|
+
);
|
|
216
|
+
test.when(testNullOrdering)(
|
|
217
|
+
'null last in select string with descending order',
|
|
218
|
+
async () => {
|
|
219
|
+
await expect(
|
|
220
|
+
`run: ${abc} -> { select: *; order_by: l desc }`
|
|
221
|
+
).malloyResultMatches(runtime, [
|
|
222
|
+
{ln: 'c'},
|
|
223
|
+
{ln: 'b'},
|
|
224
|
+
{ln: 'a'},
|
|
225
|
+
{ln: 'null'},
|
|
226
|
+
]);
|
|
227
|
+
}
|
|
228
|
+
);
|
|
229
|
+
test.when(testNullOrdering)(
|
|
230
|
+
'null last in reduce string with default ascending order',
|
|
231
|
+
async () => {
|
|
232
|
+
await expect(`
|
|
233
|
+
#! test.verbose
|
|
234
|
+
run: ${abc} -> { group_by: l }`).malloyResultMatches(runtime, [
|
|
235
|
+
{l: 'a'},
|
|
236
|
+
{l: 'b'},
|
|
237
|
+
{l: 'c'},
|
|
238
|
+
{l: null},
|
|
239
|
+
]);
|
|
240
|
+
}
|
|
241
|
+
);
|
|
242
|
+
const nums = `${databaseName}.sql("""
|
|
243
|
+
SELECT 1 as ${q`n`}
|
|
244
|
+
UNION ALL SELECT 9
|
|
245
|
+
UNION ALL SELECT 5
|
|
246
|
+
UNION ALL SELECT NULL
|
|
247
|
+
""")`;
|
|
248
|
+
test.when(testNullOrdering)(
|
|
249
|
+
'null last in numbers with ascending order',
|
|
250
|
+
async () => {
|
|
251
|
+
await expect(
|
|
252
|
+
`run: ${nums} -> { select: n; order_by: n asc }`
|
|
253
|
+
).malloyResultMatches(runtime, [{n: 1}, {n: 5}, {n: 9}, {n: null}]);
|
|
254
|
+
}
|
|
255
|
+
);
|
|
256
|
+
test.when(testNullOrdering)(
|
|
257
|
+
'null last in numbers with descending order',
|
|
258
|
+
async () => {
|
|
259
|
+
await expect(
|
|
260
|
+
`run: ${nums} -> { select: n; order_by: n desc }`
|
|
261
|
+
).malloyResultMatches(runtime, [{n: 9}, {n: 5}, {n: 1}, {n: null}]);
|
|
262
|
+
}
|
|
263
|
+
);
|
|
264
|
+
test.when(testNullOrdering)(
|
|
265
|
+
'null last in reduce measure with default descending order',
|
|
266
|
+
async () => {
|
|
267
|
+
await expect(`
|
|
268
|
+
# test.verbose
|
|
269
|
+
run: ${nums} -> {
|
|
270
|
+
group_by: nstr is n ? pick 'null' when is null else concat('number', n::string)
|
|
271
|
+
aggregate: nTotal is n.sum()
|
|
272
|
+
}`).malloyResultMatches(runtime, [
|
|
273
|
+
{nstr: 'number9'},
|
|
274
|
+
{nstr: 'number5'},
|
|
275
|
+
{nstr: 'number1'},
|
|
276
|
+
{nstr: 'null'},
|
|
277
|
+
]);
|
|
278
|
+
}
|
|
279
|
+
);
|
|
280
|
+
const y2020 = runtime.dialect.sqlLiteralTime(
|
|
281
|
+
{},
|
|
282
|
+
{
|
|
283
|
+
node: 'timeLiteral',
|
|
284
|
+
literal: '2020-01-01 00:00:00',
|
|
285
|
+
typeDef: {type: 'timestamp'},
|
|
286
|
+
}
|
|
287
|
+
);
|
|
288
|
+
const d2020 = new Date('2020-01-01 00:00:00Z');
|
|
289
|
+
const d2022 = new Date('2022-01-01 00:00:00Z');
|
|
290
|
+
const d2025 = new Date('2025-01-01 00:00:00Z');
|
|
291
|
+
const y2025 = runtime.dialect.sqlLiteralTime(
|
|
292
|
+
{},
|
|
293
|
+
{
|
|
294
|
+
node: 'timeLiteral',
|
|
295
|
+
literal: '2025-01-01 00:00:00',
|
|
296
|
+
typeDef: {type: 'timestamp'},
|
|
297
|
+
}
|
|
298
|
+
);
|
|
299
|
+
const y2022 = runtime.dialect.sqlLiteralTime(
|
|
300
|
+
{},
|
|
301
|
+
{
|
|
302
|
+
node: 'timeLiteral',
|
|
303
|
+
literal: '2022-01-01 00:00:00',
|
|
304
|
+
typeDef: {type: 'timestamp'},
|
|
305
|
+
}
|
|
306
|
+
);
|
|
307
|
+
const times = `${databaseName}.sql("""
|
|
308
|
+
SELECT ${y2020} as ${q`t`}
|
|
309
|
+
UNION ALL SELECT ${y2025}
|
|
310
|
+
UNION ALL SELECT ${y2022}
|
|
311
|
+
UNION ALL SELECT NULL
|
|
312
|
+
""")`;
|
|
313
|
+
test.when(testTimes)(
|
|
314
|
+
'null last in timestamps with ascending order',
|
|
315
|
+
async () => {
|
|
316
|
+
await expect(
|
|
317
|
+
`run: ${times} -> { select: t; order_by: t asc }`
|
|
318
|
+
).malloyResultMatches(runtime, [
|
|
319
|
+
{t: d2020},
|
|
320
|
+
{t: d2022},
|
|
321
|
+
{t: d2025},
|
|
322
|
+
{t: null},
|
|
323
|
+
]);
|
|
324
|
+
}
|
|
325
|
+
);
|
|
326
|
+
test.when(testTimes)(
|
|
327
|
+
'null last in timestamps with descending order',
|
|
328
|
+
async () => {
|
|
329
|
+
await expect(
|
|
330
|
+
`run: ${times} -> { select: t; order_by: t desc }`
|
|
331
|
+
).malloyResultMatches(runtime, [
|
|
332
|
+
{t: d2025},
|
|
333
|
+
{t: d2022},
|
|
334
|
+
{t: d2020},
|
|
335
|
+
{t: null},
|
|
336
|
+
]);
|
|
337
|
+
}
|
|
338
|
+
);
|
|
339
|
+
test.when(testTimes)(
|
|
340
|
+
'null last in reduce timestamps with default descending order',
|
|
341
|
+
async () => {
|
|
342
|
+
await expect(`run: ${times} -> { group_by: t }`).malloyResultMatches(
|
|
343
|
+
runtime,
|
|
344
|
+
[{t: d2025}, {t: d2022}, {t: d2020}, {t: null}]
|
|
345
|
+
);
|
|
346
|
+
}
|
|
347
|
+
);
|
|
348
|
+
});
|
|
187
349
|
});
|
|
@@ -100,7 +100,6 @@ describe('Wildcard BigQuery Tables', () => {
|
|
|
100
100
|
)
|
|
101
101
|
.run();
|
|
102
102
|
expect(result.data.value).toStrictEqual([
|
|
103
|
-
{state: null, aircraft_count: 43},
|
|
104
103
|
{state: 'IA', aircraft_count: 1},
|
|
105
104
|
{state: 'KS', aircraft_count: 1},
|
|
106
105
|
{state: 'LA', aircraft_count: 1},
|
|
@@ -109,6 +108,7 @@ describe('Wildcard BigQuery Tables', () => {
|
|
|
109
108
|
{state: 'OK', aircraft_count: 1},
|
|
110
109
|
{state: 'OR', aircraft_count: 2},
|
|
111
110
|
{state: 'TX', aircraft_count: 1},
|
|
111
|
+
{state: null, aircraft_count: 43},
|
|
112
112
|
]);
|
|
113
113
|
}
|
|
114
114
|
});
|
|
@@ -137,11 +137,11 @@ describe('Wildcard BigQuery Tables', () => {
|
|
|
137
137
|
)
|
|
138
138
|
.run();
|
|
139
139
|
expect(result.data.value).toStrictEqual([
|
|
140
|
-
{state: null, aircraft_count: 47},
|
|
141
140
|
{state: 'KS', aircraft_count: 1},
|
|
142
141
|
{state: 'LA', aircraft_count: 1},
|
|
143
142
|
{state: 'OK', aircraft_count: 1},
|
|
144
143
|
{state: 'OR', aircraft_count: 1},
|
|
144
|
+
{state: null, aircraft_count: 47},
|
|
145
145
|
]);
|
|
146
146
|
}
|
|
147
147
|
});
|
|
@@ -197,8 +197,8 @@ describe('Wildcard BigQuery Tables', () => {
|
|
|
197
197
|
)
|
|
198
198
|
.run();
|
|
199
199
|
expect(result.data.value).toStrictEqual([
|
|
200
|
-
{_TABLE_SUFFIX: null, aircraft_count: 47},
|
|
201
200
|
{_TABLE_SUFFIX: '02', aircraft_count: 4},
|
|
201
|
+
{_TABLE_SUFFIX: null, aircraft_count: 47},
|
|
202
202
|
]);
|
|
203
203
|
}
|
|
204
204
|
});
|