@malloydata/malloy-tests 0.0.281 → 0.0.282
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 +9 -9
- package/src/core/parameters.spec.ts +26 -0
- package/src/databases/all/db_filter_expressions.spec.ts +1 -2
- package/src/databases/all/expr.spec.ts +56 -58
- package/src/databases/all/functions.spec.ts +39 -29
- package/src/databases/all/orderby.spec.ts +3 -3
- package/src/databases/bigquery/functions.spec.ts +79 -0
- package/src/databases/presto-trino/presto-trino.spec.ts +18 -0
- package/src/render/drill.spec.ts +266 -61
- package/src/util/index.ts +2 -17
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.
|
|
25
|
-
"@malloydata/db-duckdb": "0.0.
|
|
26
|
-
"@malloydata/db-postgres": "0.0.
|
|
27
|
-
"@malloydata/db-snowflake": "0.0.
|
|
28
|
-
"@malloydata/db-trino": "0.0.
|
|
29
|
-
"@malloydata/malloy": "0.0.
|
|
30
|
-
"@malloydata/malloy-tag": "0.0.
|
|
31
|
-
"@malloydata/render": "0.0.
|
|
24
|
+
"@malloydata/db-bigquery": "0.0.282",
|
|
25
|
+
"@malloydata/db-duckdb": "0.0.282",
|
|
26
|
+
"@malloydata/db-postgres": "0.0.282",
|
|
27
|
+
"@malloydata/db-snowflake": "0.0.282",
|
|
28
|
+
"@malloydata/db-trino": "0.0.282",
|
|
29
|
+
"@malloydata/malloy": "0.0.282",
|
|
30
|
+
"@malloydata/malloy-tag": "0.0.282",
|
|
31
|
+
"@malloydata/render": "0.0.282",
|
|
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.
|
|
41
|
+
"version": "0.0.282"
|
|
42
42
|
}
|
|
@@ -192,6 +192,32 @@ describe('parameters', () => {
|
|
|
192
192
|
`
|
|
193
193
|
).malloyResultMatches(runtime, {param_value: 11});
|
|
194
194
|
});
|
|
195
|
+
it('default value modified through extension propagates', async () => {
|
|
196
|
+
await expect(
|
|
197
|
+
`
|
|
198
|
+
##! experimental.parameters
|
|
199
|
+
source: ab_new(param::number is 10) is duckdb.table('malloytest.state_facts') extend {
|
|
200
|
+
dimension: param_value is param
|
|
201
|
+
}
|
|
202
|
+
source: ab_new_new(param::number is 11) is ab_new(param is param + 1) extend {}
|
|
203
|
+
run: ab_new_new -> { group_by: param_value }
|
|
204
|
+
`
|
|
205
|
+
).malloyResultMatches(runtime, {param_value: 12});
|
|
206
|
+
});
|
|
207
|
+
// Fix this with namespaces!
|
|
208
|
+
it.skip('default value modified through extension twice propagates', async () => {
|
|
209
|
+
await expect(
|
|
210
|
+
`
|
|
211
|
+
##! experimental.parameters
|
|
212
|
+
source: ab_plus_0(param::number is 0) is duckdb.table('malloytest.state_facts') extend {
|
|
213
|
+
dimension: param_value is param
|
|
214
|
+
}
|
|
215
|
+
source: ab_plus_one(param::number is 0) is ab_plus_0(param is param + 1) extend {}
|
|
216
|
+
source: ab_plus_two(param::number is 0) is ab_plus_one(param is param + 1) extend {}
|
|
217
|
+
run: ab_plus_two -> { group_by: param_value }
|
|
218
|
+
`
|
|
219
|
+
).malloyResultMatches(runtime, {param_value: 2});
|
|
220
|
+
});
|
|
195
221
|
it('use parameter in nested view', async () => {
|
|
196
222
|
await expect(
|
|
197
223
|
`
|
|
@@ -321,8 +321,7 @@ describe.each(runtimes.runtimeList)('filter expressions %s', (dbName, db) => {
|
|
|
321
321
|
});
|
|
322
322
|
});
|
|
323
323
|
|
|
324
|
-
|
|
325
|
-
const testBoolean = !db.dialect.booleanAsNumbers;
|
|
324
|
+
const testBoolean = db.dialect.booleanType === 'supported';
|
|
326
325
|
describe('boolean filter expressions', () => {
|
|
327
326
|
const facts = db.loadModel(`
|
|
328
327
|
source: facts is ${dbName}.sql("""
|
|
@@ -24,11 +24,7 @@
|
|
|
24
24
|
|
|
25
25
|
import {RuntimeList, allDatabases} from '../../runtimes';
|
|
26
26
|
import '../../util/db-jest-matchers';
|
|
27
|
-
import {
|
|
28
|
-
booleanResult,
|
|
29
|
-
databasesFromEnvironmentOr,
|
|
30
|
-
mkSqlEqWith,
|
|
31
|
-
} from '../../util';
|
|
27
|
+
import {databasesFromEnvironmentOr, mkSqlEqWith} from '../../util';
|
|
32
28
|
|
|
33
29
|
const runtimes = new RuntimeList(databasesFromEnvironmentOr(allDatabases));
|
|
34
30
|
|
|
@@ -534,8 +530,8 @@ describe.each(runtimes.runtimeList)('%s', (databaseName, runtime) => {
|
|
|
534
530
|
group_by: boolean_2 is sql_boolean("\${engines} = 2")
|
|
535
531
|
}
|
|
536
532
|
`).malloyResultMatches(expressionModel, {
|
|
537
|
-
boolean_1:
|
|
538
|
-
boolean_2:
|
|
533
|
+
boolean_1: runtime.dialect.resultBoolean(true),
|
|
534
|
+
boolean_2: runtime.dialect.resultBoolean(false),
|
|
539
535
|
});
|
|
540
536
|
});
|
|
541
537
|
|
|
@@ -867,57 +863,59 @@ describe.each(runtimes.runtimeList)('%s', (databaseName, runtime) => {
|
|
|
867
863
|
});
|
|
868
864
|
});
|
|
869
865
|
|
|
870
|
-
|
|
871
|
-
|
|
872
|
-
|
|
873
|
-
|
|
874
|
-
|
|
875
|
-
|
|
876
|
-
|
|
877
|
-
|
|
878
|
-
|
|
879
|
-
|
|
880
|
-
|
|
881
|
-
|
|
866
|
+
if (runtime.dialect.booleanType !== 'none') {
|
|
867
|
+
describe('null safe booleans', () => {
|
|
868
|
+
const nulls = `${databaseName}.sql("""
|
|
869
|
+
SELECT
|
|
870
|
+
0 as ${q`n`},
|
|
871
|
+
1 as ${q`x`}, 2 as ${q`y`},
|
|
872
|
+
'a' as ${q`a`}, 'b' as ${q`b`},
|
|
873
|
+
(1=1) as ${q`tf`}
|
|
874
|
+
UNION ALL SELECT
|
|
875
|
+
5,
|
|
876
|
+
null, null, null, null, null
|
|
877
|
+
""") extend { where: n > 0 }`;
|
|
878
|
+
const is_true = runtime.dialect.resultBoolean(true);
|
|
882
879
|
|
|
883
|
-
|
|
884
|
-
|
|
885
|
-
|
|
886
|
-
|
|
887
|
-
|
|
888
|
-
|
|
889
|
-
|
|
890
|
-
|
|
891
|
-
|
|
892
|
-
|
|
893
|
-
|
|
894
|
-
|
|
895
|
-
|
|
896
|
-
|
|
897
|
-
|
|
898
|
-
|
|
899
|
-
|
|
900
|
-
|
|
901
|
-
|
|
902
|
-
|
|
903
|
-
|
|
904
|
-
|
|
905
|
-
|
|
906
|
-
|
|
907
|
-
|
|
908
|
-
|
|
909
|
-
|
|
910
|
-
|
|
911
|
-
|
|
912
|
-
|
|
913
|
-
|
|
914
|
-
|
|
915
|
-
|
|
916
|
-
|
|
917
|
-
|
|
918
|
-
|
|
880
|
+
it('select boolean', async () => {
|
|
881
|
+
await expect(`run: ${nulls} -> {
|
|
882
|
+
select:
|
|
883
|
+
null_boolean is tf
|
|
884
|
+
}`).malloyResultMatches(runtime, {null_boolean: null});
|
|
885
|
+
});
|
|
886
|
+
it('not boolean', async () => {
|
|
887
|
+
await expect(`run: ${nulls} -> {
|
|
888
|
+
select:
|
|
889
|
+
not_null_boolean is not tf
|
|
890
|
+
}`).malloyResultMatches(runtime, {not_null_boolean: is_true});
|
|
891
|
+
});
|
|
892
|
+
it('numeric != non-null to null', async () => {
|
|
893
|
+
await expect(
|
|
894
|
+
`run: ${nulls} -> { select: val_ne_null is x != 9 }`
|
|
895
|
+
).malloyResultMatches(runtime, {val_ne_null: is_true});
|
|
896
|
+
});
|
|
897
|
+
it('string !~ non-null to null', async () => {
|
|
898
|
+
await expect(
|
|
899
|
+
`run: ${nulls} -> { select: val_ne_null is a !~ 'z' }`
|
|
900
|
+
).malloyResultMatches(runtime, {val_ne_null: is_true});
|
|
901
|
+
});
|
|
902
|
+
it('regex !~ non-null to null', async () => {
|
|
903
|
+
await expect(
|
|
904
|
+
`run: ${nulls} -> { select: val_ne_null is a !~ r'z' }`
|
|
905
|
+
).malloyResultMatches(runtime, {val_ne_null: is_true});
|
|
906
|
+
});
|
|
907
|
+
it('numeric != null-to-null', async () => {
|
|
908
|
+
await expect(
|
|
909
|
+
`run: ${nulls} -> { select: null_ne_null is x != y }`
|
|
910
|
+
).malloyResultMatches(runtime, {null_ne_null: is_true});
|
|
911
|
+
});
|
|
912
|
+
it('string !~ null-to-null', async () => {
|
|
913
|
+
await expect(
|
|
914
|
+
`run: ${nulls} -> { select: null_ne_null is a !~ b }`
|
|
915
|
+
).malloyResultMatches(runtime, {null_ne_null: is_true});
|
|
916
|
+
});
|
|
919
917
|
});
|
|
920
|
-
}
|
|
918
|
+
}
|
|
921
919
|
|
|
922
920
|
test('dimension expressions expanded with parens properly', async () => {
|
|
923
921
|
await expect(
|
|
@@ -929,8 +927,8 @@ describe.each(runtimes.runtimeList)('%s', (databaseName, runtime) => {
|
|
|
929
927
|
paren is false and (fot)
|
|
930
928
|
}`
|
|
931
929
|
).malloyResultMatches(runtime, {
|
|
932
|
-
paren:
|
|
933
|
-
no_paren:
|
|
930
|
+
paren: runtime.dialect.resultBoolean(false),
|
|
931
|
+
no_paren: runtime.dialect.resultBoolean(false),
|
|
934
932
|
});
|
|
935
933
|
});
|
|
936
934
|
});
|
|
@@ -23,7 +23,7 @@
|
|
|
23
23
|
*/
|
|
24
24
|
|
|
25
25
|
import {RuntimeList, allDatabases} from '../../runtimes';
|
|
26
|
-
import {
|
|
26
|
+
import {brokenIn, databasesFromEnvironmentOr} from '../../util';
|
|
27
27
|
import '../../util/db-jest-matchers';
|
|
28
28
|
import type * as malloy from '@malloydata/malloy';
|
|
29
29
|
|
|
@@ -68,6 +68,8 @@ runtimes.runtimeMap.forEach((runtime, databaseName) =>
|
|
|
68
68
|
expressionModels.forEach((x, databaseName) => {
|
|
69
69
|
const expressionModel = x.expressionModel;
|
|
70
70
|
const runtime = x.runtime;
|
|
71
|
+
const dbTrue = runtime.dialect.resultBoolean(true);
|
|
72
|
+
const dbFalse = runtime.dialect.resultBoolean(false);
|
|
71
73
|
const funcTestGeneral = async (
|
|
72
74
|
expr: string,
|
|
73
75
|
type: 'group_by' | 'aggregate',
|
|
@@ -85,8 +87,12 @@ expressionModels.forEach((x, databaseName) => {
|
|
|
85
87
|
};
|
|
86
88
|
|
|
87
89
|
if (expected.success !== undefined) {
|
|
90
|
+
const expectedSuccess =
|
|
91
|
+
typeof expected.success === 'boolean'
|
|
92
|
+
? runtime.dialect.resultBoolean(expected.success)
|
|
93
|
+
: expected.success;
|
|
88
94
|
const result = await run();
|
|
89
|
-
expect(result.data.path(0, 'f').value).toBe(
|
|
95
|
+
expect(result.data.path(0, 'f').value).toBe(expectedSuccess);
|
|
90
96
|
} else {
|
|
91
97
|
expect(run).rejects.toThrowError(expected.error);
|
|
92
98
|
}
|
|
@@ -623,6 +629,7 @@ expressionModels.forEach((x, databaseName) => {
|
|
|
623
629
|
const result = await expressionModel
|
|
624
630
|
.loadQuery(
|
|
625
631
|
`
|
|
632
|
+
# test.debug
|
|
626
633
|
run: state_facts -> {
|
|
627
634
|
group_by: state
|
|
628
635
|
calculate: lag_val is lag(@2011-11-11 11:11:11, 1, now).year = now.year
|
|
@@ -630,10 +637,10 @@ expressionModels.forEach((x, databaseName) => {
|
|
|
630
637
|
)
|
|
631
638
|
.run();
|
|
632
639
|
expect(result.data.path(0, 'lag_val').value).toBe(
|
|
633
|
-
|
|
640
|
+
runtime.dialect.resultBoolean(true)
|
|
634
641
|
);
|
|
635
642
|
expect(result.data.path(1, 'lag_val').value).toBe(
|
|
636
|
-
|
|
643
|
+
runtime.dialect.resultBoolean(false)
|
|
637
644
|
);
|
|
638
645
|
});
|
|
639
646
|
});
|
|
@@ -856,18 +863,18 @@ expressionModels.forEach((x, databaseName) => {
|
|
|
856
863
|
: "'+inf'::number";
|
|
857
864
|
it.when(databaseName !== 'mysql')(`works - ${databaseName}`, async () => {
|
|
858
865
|
await funcTestMultiple(
|
|
859
|
-
[`is_inf(${inf})`,
|
|
860
|
-
['is_inf(100)',
|
|
861
|
-
['is_inf(null)',
|
|
866
|
+
[`is_inf(${inf})`, dbTrue],
|
|
867
|
+
['is_inf(100)', dbFalse],
|
|
868
|
+
['is_inf(null)', dbFalse]
|
|
862
869
|
);
|
|
863
870
|
});
|
|
864
871
|
});
|
|
865
872
|
describe('is_nan', () => {
|
|
866
873
|
it.when(databaseName !== 'mysql')(`works - ${databaseName}`, async () => {
|
|
867
874
|
await funcTestMultiple(
|
|
868
|
-
["is_nan('NaN'::number)",
|
|
869
|
-
['is_nan(100)',
|
|
870
|
-
['is_nan(null)',
|
|
875
|
+
["is_nan('NaN'::number)", dbTrue],
|
|
876
|
+
['is_nan(100)', dbFalse],
|
|
877
|
+
['is_nan(null)', dbFalse]
|
|
871
878
|
);
|
|
872
879
|
});
|
|
873
880
|
});
|
|
@@ -877,11 +884,11 @@ expressionModels.forEach((x, databaseName) => {
|
|
|
877
884
|
['greatest(1, 10, -100)', 10],
|
|
878
885
|
[
|
|
879
886
|
'greatest(@2003, @2004, @1994) = @2004',
|
|
880
|
-
|
|
887
|
+
runtime.dialect.resultBoolean(true),
|
|
881
888
|
],
|
|
882
889
|
[
|
|
883
890
|
'greatest(@2023-05-26 11:58:00, @2023-05-26 11:59:00) = @2023-05-26 11:59:00',
|
|
884
|
-
|
|
891
|
+
runtime.dialect.resultBoolean(true),
|
|
885
892
|
],
|
|
886
893
|
["greatest('a', 'b')", 'b'],
|
|
887
894
|
['greatest(1, null, 0)', null],
|
|
@@ -895,11 +902,11 @@ expressionModels.forEach((x, databaseName) => {
|
|
|
895
902
|
['least(1, 10, -100)', -100],
|
|
896
903
|
[
|
|
897
904
|
'least(@2003, @2004, @1994) = @1994',
|
|
898
|
-
|
|
905
|
+
runtime.dialect.resultBoolean(true),
|
|
899
906
|
],
|
|
900
907
|
[
|
|
901
908
|
'least(@2023-05-26 11:58:00, @2023-05-26 11:59:00) = @2023-05-26 11:58:00',
|
|
902
|
-
|
|
909
|
+
runtime.dialect.resultBoolean(true),
|
|
903
910
|
],
|
|
904
911
|
["least('a', 'b')", 'a'],
|
|
905
912
|
['least(1, null, 0)', null],
|
|
@@ -931,14 +938,17 @@ expressionModels.forEach((x, databaseName) => {
|
|
|
931
938
|
await funcTestMultiple(
|
|
932
939
|
[
|
|
933
940
|
"starts_with('hello world', 'hello')",
|
|
934
|
-
|
|
941
|
+
runtime.dialect.resultBoolean(true),
|
|
935
942
|
],
|
|
936
943
|
[
|
|
937
944
|
"starts_with('hello world', 'world')",
|
|
938
|
-
|
|
945
|
+
runtime.dialect.resultBoolean(false),
|
|
939
946
|
],
|
|
940
|
-
["starts_with(null, 'world')",
|
|
941
|
-
[
|
|
947
|
+
["starts_with(null, 'world')", runtime.dialect.resultBoolean(false)],
|
|
948
|
+
[
|
|
949
|
+
"starts_with('hello world', null)",
|
|
950
|
+
runtime.dialect.resultBoolean(false),
|
|
951
|
+
]
|
|
942
952
|
);
|
|
943
953
|
});
|
|
944
954
|
});
|
|
@@ -947,14 +957,14 @@ expressionModels.forEach((x, databaseName) => {
|
|
|
947
957
|
await funcTestMultiple(
|
|
948
958
|
[
|
|
949
959
|
"ends_with('hello world', 'world')",
|
|
950
|
-
|
|
960
|
+
runtime.dialect.resultBoolean(true),
|
|
951
961
|
],
|
|
952
962
|
[
|
|
953
963
|
"ends_with('hello world', 'hello')",
|
|
954
|
-
|
|
964
|
+
runtime.dialect.resultBoolean(false),
|
|
955
965
|
],
|
|
956
|
-
["ends_with(null, 'world')",
|
|
957
|
-
["ends_with('hello world', null)",
|
|
966
|
+
["ends_with(null, 'world')", runtime.dialect.resultBoolean(false)],
|
|
967
|
+
["ends_with('hello world', null)", runtime.dialect.resultBoolean(false)]
|
|
958
968
|
);
|
|
959
969
|
});
|
|
960
970
|
});
|
|
@@ -999,14 +1009,14 @@ expressionModels.forEach((x, databaseName) => {
|
|
|
999
1009
|
// There are around a billion values that rand() can be, so if this
|
|
1000
1010
|
// test fails, most likely something is broken. Otherwise, you're the lucky
|
|
1001
1011
|
// one in a billion!
|
|
1002
|
-
await funcTest('rand() = rand()',
|
|
1012
|
+
await funcTest('rand() = rand()', runtime.dialect.resultBoolean(false));
|
|
1003
1013
|
});
|
|
1004
1014
|
});
|
|
1005
1015
|
describe('pi', () => {
|
|
1006
1016
|
it(`is pi - ${databaseName}`, async () => {
|
|
1007
1017
|
await funcTest(
|
|
1008
1018
|
'abs(pi() - 3.141592653589793) < 0.0000000000001',
|
|
1009
|
-
|
|
1019
|
+
runtime.dialect.resultBoolean(true)
|
|
1010
1020
|
);
|
|
1011
1021
|
});
|
|
1012
1022
|
});
|
|
@@ -1169,8 +1179,8 @@ expressionModels.forEach((x, databaseName) => {
|
|
|
1169
1179
|
aggregate: also_passes is abs(count_approx(airport_count)-count(airport_count))/count(airport_count) < 0.3
|
|
1170
1180
|
}
|
|
1171
1181
|
`).malloyResultMatches(runtime, {
|
|
1172
|
-
'passes':
|
|
1173
|
-
'also_passes':
|
|
1182
|
+
'passes': runtime.dialect.resultBoolean(true),
|
|
1183
|
+
'also_passes': runtime.dialect.resultBoolean(true),
|
|
1174
1184
|
});
|
|
1175
1185
|
});
|
|
1176
1186
|
test.when(supported)('works with fanout', async () => {
|
|
@@ -1182,7 +1192,7 @@ expressionModels.forEach((x, databaseName) => {
|
|
|
1182
1192
|
run: state_facts_fanout -> {
|
|
1183
1193
|
aggregate: x is state_facts.state.count_approx() > 0
|
|
1184
1194
|
}
|
|
1185
|
-
`).malloyResultMatches(runtime, {x:
|
|
1195
|
+
`).malloyResultMatches(runtime, {x: runtime.dialect.resultBoolean(true)});
|
|
1186
1196
|
});
|
|
1187
1197
|
});
|
|
1188
1198
|
describe('last_value', () => {
|
|
@@ -1368,7 +1378,7 @@ expressionModels.forEach((x, databaseName) => {
|
|
|
1368
1378
|
it.when(isDuckdb)('to_timestamp', async () => {
|
|
1369
1379
|
await funcTest(
|
|
1370
1380
|
'to_timestamp(1725555835) = @2024-09-05 17:03:55',
|
|
1371
|
-
|
|
1381
|
+
runtime.dialect.resultBoolean(true)
|
|
1372
1382
|
);
|
|
1373
1383
|
});
|
|
1374
1384
|
it.when(isDuckdb)('list_extract', async () => {
|
|
@@ -1384,7 +1394,7 @@ expressionModels.forEach((x, databaseName) => {
|
|
|
1384
1394
|
trino('from_unixtime', async () => {
|
|
1385
1395
|
await funcTest(
|
|
1386
1396
|
'from_unixtime(1725555835) = @2024-09-05 17:03:55',
|
|
1387
|
-
|
|
1397
|
+
runtime.dialect.resultBoolean(true)
|
|
1388
1398
|
);
|
|
1389
1399
|
});
|
|
1390
1400
|
});
|
|
@@ -23,7 +23,7 @@
|
|
|
23
23
|
*/
|
|
24
24
|
|
|
25
25
|
import {RuntimeList, allDatabases} from '../../runtimes';
|
|
26
|
-
import {
|
|
26
|
+
import {databasesFromEnvironmentOr} from '../../util';
|
|
27
27
|
import '../../util/db-jest-matchers';
|
|
28
28
|
|
|
29
29
|
const runtimes = new RuntimeList(databasesFromEnvironmentOr(allDatabases));
|
|
@@ -45,7 +45,7 @@ describe.each(runtimes.runtimeList)('%s', (databaseName, runtime) => {
|
|
|
45
45
|
aggregate: model_count is count()
|
|
46
46
|
}
|
|
47
47
|
`).malloyResultMatches(orderByModel, {
|
|
48
|
-
big:
|
|
48
|
+
big: runtime.dialect.resultBoolean(false),
|
|
49
49
|
model_count: 58451,
|
|
50
50
|
});
|
|
51
51
|
});
|
|
@@ -62,7 +62,7 @@ describe.each(runtimes.runtimeList)('%s', (databaseName, runtime) => {
|
|
|
62
62
|
aggregate: model_count is model_count.sum()
|
|
63
63
|
}
|
|
64
64
|
`).malloyResultMatches(orderByModel, {
|
|
65
|
-
big:
|
|
65
|
+
big: runtime.dialect.resultBoolean(false),
|
|
66
66
|
model_count: 58500,
|
|
67
67
|
});
|
|
68
68
|
});
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* Copyright 2023 Google LLC
|
|
3
|
+
*
|
|
4
|
+
* Permission is hereby granted, free of charge, to any person obtaining
|
|
5
|
+
* a copy of this software and associated documentation files
|
|
6
|
+
* (the "Software"), to deal in the Software without restriction,
|
|
7
|
+
* including without limitation the rights to use, copy, modify, merge,
|
|
8
|
+
* publish, distribute, sublicense, and/or sell copies of the Software,
|
|
9
|
+
* and to permit persons to whom the Software is furnished to do so,
|
|
10
|
+
* subject to the following conditions:
|
|
11
|
+
*
|
|
12
|
+
* The above copyright notice and this permission notice shall be
|
|
13
|
+
* included in all copies or substantial portions of the Software.
|
|
14
|
+
*
|
|
15
|
+
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
|
16
|
+
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
|
17
|
+
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
|
18
|
+
* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
|
|
19
|
+
* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
|
|
20
|
+
* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
|
|
21
|
+
* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
|
22
|
+
*/
|
|
23
|
+
|
|
24
|
+
import {RuntimeList} from '../../runtimes';
|
|
25
|
+
import '../../util/db-jest-matchers';
|
|
26
|
+
import {describeIfDatabaseAvailable} from '../../util';
|
|
27
|
+
|
|
28
|
+
const [describe, databases] = describeIfDatabaseAvailable(['bigquery']);
|
|
29
|
+
const runtimes = new RuntimeList(databases);
|
|
30
|
+
|
|
31
|
+
afterAll(async () => {
|
|
32
|
+
await runtimes.closeAll();
|
|
33
|
+
});
|
|
34
|
+
|
|
35
|
+
describe('dialect specific function tests for standardsql', () => {
|
|
36
|
+
const runtime = runtimes.runtimeMap.get('bigquery');
|
|
37
|
+
|
|
38
|
+
it('runs the max_by function - bigquery', async () => {
|
|
39
|
+
await expect(`run: bigquery.sql("""
|
|
40
|
+
SELECT 1 as y, 55 as x
|
|
41
|
+
UNION ALL SELECT 50 as y, 22 as x
|
|
42
|
+
UNION ALL SELECT 100 as y, 1 as x
|
|
43
|
+
""") -> {
|
|
44
|
+
aggregate:
|
|
45
|
+
m1 is max_by(x, y)
|
|
46
|
+
m2 is max_by(y, x)
|
|
47
|
+
}`).malloyResultMatches(runtime!, {m1: 1, m2: 1});
|
|
48
|
+
});
|
|
49
|
+
|
|
50
|
+
it('runs the max_by function by grouping - bigquery', async () => {
|
|
51
|
+
await expect(`run: bigquery.sql("""
|
|
52
|
+
SELECT 1 as y, 55 as x, 10 as z
|
|
53
|
+
UNION ALL SELECT 50 as y, 22 as x, 10 as z
|
|
54
|
+
UNION ALL SELECT 1 as y, 10 as x, 20 as z
|
|
55
|
+
UNION ALL SELECT 100 as y, 15 as x, 20 as z
|
|
56
|
+
""") -> {
|
|
57
|
+
group_by:
|
|
58
|
+
z
|
|
59
|
+
aggregate:
|
|
60
|
+
m1 is max_by(x, y)
|
|
61
|
+
m2 is max_by(y, x)
|
|
62
|
+
}`).malloyResultMatches(runtime!, [
|
|
63
|
+
{z: 10, m1: 22, m2: 1},
|
|
64
|
+
{z: 20, m1: 15, m2: 100},
|
|
65
|
+
]);
|
|
66
|
+
});
|
|
67
|
+
|
|
68
|
+
it('runs the min_by function - bigquery', async () => {
|
|
69
|
+
await expect(`run: bigquery.sql("""
|
|
70
|
+
SELECT 1 as y, 55 as x
|
|
71
|
+
UNION ALL SELECT 50 as y, 22 as x
|
|
72
|
+
UNION ALL SELECT 100 as y, 1 as x
|
|
73
|
+
""") -> {
|
|
74
|
+
aggregate:
|
|
75
|
+
m1 is min_by(x, y)
|
|
76
|
+
m2 is min_by(y, x)
|
|
77
|
+
}`).malloyResultMatches(runtime!, {m1: 55, m2: 100});
|
|
78
|
+
});
|
|
79
|
+
});
|
|
@@ -256,6 +256,24 @@ describe.each(runtimes.runtimeList)(
|
|
|
256
256
|
}`).malloyResultMatches(runtime, {m1: 1, m2: 1});
|
|
257
257
|
});
|
|
258
258
|
|
|
259
|
+
it(`runs the max_by function by grouping - ${databaseName}`, async () => {
|
|
260
|
+
await expect(`run: ${databaseName}.sql("""
|
|
261
|
+
SELECT 1 as y, 55 as x, 10 as z
|
|
262
|
+
UNION ALL SELECT 50 as y, 22 as x, 10 as z
|
|
263
|
+
UNION ALL SELECT 1 as y, 10 as x, 20 as z
|
|
264
|
+
UNION ALL SELECT 100 as y, 15 as x, 20 as z
|
|
265
|
+
""") -> {
|
|
266
|
+
group_by:
|
|
267
|
+
z
|
|
268
|
+
aggregate:
|
|
269
|
+
m1 is max_by(x, y)
|
|
270
|
+
m2 is max_by(y, x)
|
|
271
|
+
}`).malloyResultMatches(runtime, [
|
|
272
|
+
{z: 10, m1: 22, m2: 1},
|
|
273
|
+
{z: 20, m1: 15, m2: 100},
|
|
274
|
+
]);
|
|
275
|
+
});
|
|
276
|
+
|
|
259
277
|
it(`runs the min_by function - ${databaseName}`, async () => {
|
|
260
278
|
await expect(`run: ${databaseName}.sql("""
|
|
261
279
|
SELECT 1 as y, 55 as x
|
package/src/render/drill.spec.ts
CHANGED
|
@@ -13,51 +13,103 @@ const duckdb = runtimeFor('duckdb');
|
|
|
13
13
|
|
|
14
14
|
describe('drill query', () => {
|
|
15
15
|
const model = `
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
16
|
+
##! experimental { drill parameters }
|
|
17
|
+
source: carriers is duckdb.table('test/data/duckdb/carriers.parquet') extend {
|
|
18
|
+
primary_key: code
|
|
19
|
+
measure: carrier_count is count()
|
|
20
|
+
}
|
|
21
|
+
source: flights is duckdb.table('test/data/duckdb/flights/part.*.parquet') extend {
|
|
22
|
+
primary_key: id2
|
|
23
|
+
// rename some fields as from their physical names
|
|
24
|
+
rename: \`Origin Code\` is origin
|
|
25
|
+
measure: flight_count is count()
|
|
26
|
+
join_one: carriers with carrier
|
|
27
|
+
|
|
28
|
+
view: top_carriers is {
|
|
29
|
+
group_by: carriers.nickname
|
|
30
|
+
aggregate:
|
|
31
|
+
flight_count
|
|
32
|
+
limit: 1
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
view: over_time is {
|
|
36
|
+
group_by: dep_month is month(dep_time)
|
|
37
|
+
aggregate: flight_count
|
|
38
|
+
limit: 1
|
|
19
39
|
}
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
aggregate:
|
|
30
|
-
flight_count
|
|
31
|
-
limit: 1
|
|
32
|
-
}
|
|
33
|
-
|
|
34
|
-
view: over_time is {
|
|
35
|
-
group_by: dep_month is month(dep_time)
|
|
36
|
-
aggregate: flight_count
|
|
37
|
-
limit: 1
|
|
38
|
-
}
|
|
39
|
-
|
|
40
|
-
view: by_origin is {
|
|
41
|
-
group_by: \`Origin Code\`
|
|
42
|
-
aggregate: flight_count
|
|
43
|
-
limit: 1
|
|
44
|
-
}
|
|
45
|
-
|
|
46
|
-
view: no_filter is {
|
|
47
|
-
aggregate: flight_count
|
|
48
|
-
}
|
|
49
|
-
|
|
50
|
-
view: cool_carriers is {
|
|
51
|
-
where: carrier = 'AA' or carrier = 'WN'
|
|
52
|
-
group_by: \`Origin Code\`
|
|
53
|
-
}
|
|
40
|
+
|
|
41
|
+
view: by_origin is {
|
|
42
|
+
group_by: \`Origin Code\`
|
|
43
|
+
aggregate: flight_count
|
|
44
|
+
limit: 1
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
view: no_filter is {
|
|
48
|
+
aggregate: flight_count
|
|
54
49
|
}
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
50
|
+
|
|
51
|
+
view: cool_carriers is {
|
|
52
|
+
where: carrier = 'AA' or carrier = 'WN'
|
|
53
|
+
group_by: \`Origin Code\`
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
view: negative_value is {
|
|
57
|
+
group_by: negative_one is -1
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
source: flights_with_parameters(
|
|
61
|
+
number_param is 1,
|
|
62
|
+
string_param is 'foo',
|
|
63
|
+
boolean_param is true,
|
|
64
|
+
date_param is @2000,
|
|
65
|
+
timestamp_param is @2004-01-01 10:00,
|
|
66
|
+
filter_expression_param::filter<number> is f'> 3'
|
|
67
|
+
) is flights
|
|
68
|
+
source: flights_with_timestamp_param(
|
|
69
|
+
timestamp_param is @2004-01-01 10:00
|
|
70
|
+
) is flights
|
|
71
|
+
query: top_carriers is flights -> top_carriers
|
|
72
|
+
query: over_time is flights -> over_time
|
|
73
|
+
query: by_origin is flights -> by_origin
|
|
74
|
+
query: no_filter is flights -> no_filter
|
|
75
|
+
query: cool_carriers is flights -> cool_carriers
|
|
76
|
+
query: negative_value is flights -> negative_value
|
|
77
|
+
query: literal_with_nested_view_stable is flights -> {
|
|
78
|
+
where:
|
|
79
|
+
\`Origin Code\` ~ f'SFO, ORD',
|
|
80
|
+
destination = 'SJC'
|
|
81
|
+
group_by: carrier
|
|
82
|
+
nest: cool_carriers
|
|
83
|
+
}
|
|
84
|
+
query: literal_with_nested_view_unstable is flights -> {
|
|
85
|
+
where:
|
|
86
|
+
carriers.nickname ~ '%A%',
|
|
87
|
+
distance > 100,
|
|
88
|
+
month(dep_time) = 7
|
|
89
|
+
group_by: carrier
|
|
90
|
+
nest: cool_carriers
|
|
91
|
+
having: flight_count > 100
|
|
92
|
+
}
|
|
93
|
+
query: already_has_some_drills is flights -> {
|
|
94
|
+
drill:
|
|
95
|
+
\`Origin Code\` ~ f\`SFO, ORD\`,
|
|
96
|
+
destination = "SJC",
|
|
97
|
+
carrier = "AA",
|
|
98
|
+
cool_carriers.\`Origin Code\` = "ORD"
|
|
99
|
+
} + over_time
|
|
100
|
+
query: source_has_parameters is flights_with_parameters(
|
|
101
|
+
number_param is 1,
|
|
102
|
+
string_param is 'foo',
|
|
103
|
+
boolean_param is true,
|
|
104
|
+
date_param is @2000,
|
|
105
|
+
timestamp_param is @2004-01-01 10:00,
|
|
106
|
+
filter_expression_param is f'> 3'
|
|
107
|
+
) -> top_carriers
|
|
108
|
+
query: source_has_timezone_param is flights_with_timestamp_param(
|
|
109
|
+
timestamp_param is @2004-01-01 10:00:00[America/Los_Angeles]
|
|
110
|
+
) -> top_carriers
|
|
111
|
+
query: only_default_params is flights_with_parameters -> top_carriers
|
|
112
|
+
`;
|
|
61
113
|
|
|
62
114
|
beforeEach(() => {
|
|
63
115
|
jest.spyOn(console, 'warn').mockImplementation(() => {});
|
|
@@ -70,12 +122,9 @@ describe('drill query', () => {
|
|
|
70
122
|
.run();
|
|
71
123
|
const table = getDataTree(API.util.wrapResult(result));
|
|
72
124
|
const expDrillQuery =
|
|
73
|
-
'run: flights -> {
|
|
74
|
-
' where:\n' +
|
|
75
|
-
" carriers.nickname = 'Southwest'\n" +
|
|
76
|
-
'} + { select: * }';
|
|
125
|
+
'run: flights -> { drill: top_carriers.nickname = "Southwest" } + { select: * }';
|
|
77
126
|
const row = table.rows[0];
|
|
78
|
-
expect(row.
|
|
127
|
+
expect(row.getDrillQueryMalloy()).toEqual(expDrillQuery);
|
|
79
128
|
});
|
|
80
129
|
|
|
81
130
|
test('can handle expression fields', async () => {
|
|
@@ -85,10 +134,9 @@ describe('drill query', () => {
|
|
|
85
134
|
.run();
|
|
86
135
|
const table = getDataTree(API.util.wrapResult(result));
|
|
87
136
|
const expDrillQuery =
|
|
88
|
-
'run: flights -> {
|
|
89
|
-
'month(dep_time) = 8\n} + { select: * }';
|
|
137
|
+
'run: flights -> { drill: over_time.dep_month = 8 } + { select: * }';
|
|
90
138
|
const row = table.rows[0];
|
|
91
|
-
expect(row.
|
|
139
|
+
expect(row.getDrillQueryMalloy()).toEqual(expDrillQuery);
|
|
92
140
|
});
|
|
93
141
|
|
|
94
142
|
test('can handle renamed and multi-word field names', async () => {
|
|
@@ -98,10 +146,9 @@ describe('drill query', () => {
|
|
|
98
146
|
.run();
|
|
99
147
|
const table = getDataTree(API.util.wrapResult(result));
|
|
100
148
|
const expDrillQuery =
|
|
101
|
-
'run: flights -> {
|
|
102
|
-
"`Origin Code` = 'ATL'\n} + { select: * }";
|
|
149
|
+
'run: flights -> { drill: by_origin.`Origin Code` = "ATL" } + { select: * }';
|
|
103
150
|
const row = table.rows[0];
|
|
104
|
-
expect(row.
|
|
151
|
+
expect(row.getDrillQueryMalloy()).toEqual(expDrillQuery);
|
|
105
152
|
});
|
|
106
153
|
|
|
107
154
|
test('can handle queries with no filter', async () => {
|
|
@@ -112,7 +159,7 @@ describe('drill query', () => {
|
|
|
112
159
|
const table = getDataTree(API.util.wrapResult(result));
|
|
113
160
|
const expDrillQuery = 'run: flights -> { select: * }';
|
|
114
161
|
const row = table.rows[0];
|
|
115
|
-
expect(row.
|
|
162
|
+
expect(row.getDrillQueryMalloy()).toEqual(expDrillQuery);
|
|
116
163
|
});
|
|
117
164
|
|
|
118
165
|
test('can handle view filters', async () => {
|
|
@@ -122,12 +169,170 @@ describe('drill query', () => {
|
|
|
122
169
|
.run();
|
|
123
170
|
const table = getDataTree(API.util.wrapResult(result));
|
|
124
171
|
const expDrillQuery =
|
|
125
|
-
'run: flights -> {
|
|
126
|
-
' where:\n' +
|
|
127
|
-
" carrier = 'AA' or carrier = 'WN',\n" +
|
|
128
|
-
" `Origin Code` = 'ABQ'\n" +
|
|
129
|
-
'} + { select: * }';
|
|
172
|
+
'run: flights -> { drill: cool_carriers.`Origin Code` = "ABQ" } + { select: * }';
|
|
130
173
|
const row = table.rows[0];
|
|
131
|
-
expect(row.
|
|
174
|
+
expect(row.getDrillQueryMalloy()).toEqual(expDrillQuery);
|
|
175
|
+
});
|
|
176
|
+
|
|
177
|
+
test('can handle filters that are not in a view (not stable compatible)', async () => {
|
|
178
|
+
const result = await duckdb
|
|
179
|
+
.loadModel(model)
|
|
180
|
+
.loadQueryByName('literal_with_nested_view_unstable')
|
|
181
|
+
.run();
|
|
182
|
+
const table = getDataTree(API.util.wrapResult(result));
|
|
183
|
+
const expDrillQuery = `run: flights -> {
|
|
184
|
+
drill:
|
|
185
|
+
carriers.nickname ~ '%A%',
|
|
186
|
+
distance > 100,
|
|
187
|
+
month(dep_time) = 7,
|
|
188
|
+
carrier = "AA"
|
|
189
|
+
} + { select: * }`;
|
|
190
|
+
const row1 = table.rows[0];
|
|
191
|
+
expect(row1.getDrillQueryMalloy()).toEqual(expDrillQuery);
|
|
192
|
+
const nest = row1.column('cool_carriers');
|
|
193
|
+
expect(nest.isRepeatedRecord()).toBe(true);
|
|
194
|
+
if (nest.isRepeatedRecord()) {
|
|
195
|
+
const expDrillQuery = `run: flights -> {
|
|
196
|
+
drill:
|
|
197
|
+
carriers.nickname ~ '%A%',
|
|
198
|
+
distance > 100,
|
|
199
|
+
month(dep_time) = 7,
|
|
200
|
+
carrier = "AA",
|
|
201
|
+
cool_carriers.\`Origin Code\` = "ABQ"
|
|
202
|
+
} + { select: * }`;
|
|
203
|
+
const row = nest.rows[0];
|
|
204
|
+
expect(row.getDrillQueryMalloy()).toEqual(expDrillQuery);
|
|
205
|
+
}
|
|
206
|
+
});
|
|
207
|
+
|
|
208
|
+
test('can handle filters that are not in a view (stable compatible)', async () => {
|
|
209
|
+
const result = await duckdb
|
|
210
|
+
.loadModel(model)
|
|
211
|
+
.loadQueryByName('literal_with_nested_view_stable')
|
|
212
|
+
.run();
|
|
213
|
+
const table = getDataTree(API.util.wrapResult(result));
|
|
214
|
+
const expDrillQuery = `run: flights -> {
|
|
215
|
+
drill:
|
|
216
|
+
\`Origin Code\` ~ f\`SFO, ORD\`,
|
|
217
|
+
destination = "SJC",
|
|
218
|
+
carrier = "AA"
|
|
219
|
+
} + { select: * }`;
|
|
220
|
+
const row1 = table.rows[0];
|
|
221
|
+
expect(row1.getDrillQueryMalloy()).toEqual(expDrillQuery);
|
|
222
|
+
const nest = row1.column('cool_carriers');
|
|
223
|
+
expect(nest.isRepeatedRecord()).toBe(true);
|
|
224
|
+
if (nest.isRepeatedRecord()) {
|
|
225
|
+
const expDrillQuery = `run: flights -> {
|
|
226
|
+
drill:
|
|
227
|
+
\`Origin Code\` ~ f\`SFO, ORD\`,
|
|
228
|
+
destination = "SJC",
|
|
229
|
+
carrier = "AA",
|
|
230
|
+
cool_carriers.\`Origin Code\` = "ORD"
|
|
231
|
+
} + { select: * }`;
|
|
232
|
+
const row = nest.rows[0];
|
|
233
|
+
expect(row.getDrillQueryMalloy()).toEqual(expDrillQuery);
|
|
234
|
+
}
|
|
235
|
+
});
|
|
236
|
+
|
|
237
|
+
test('can handle drills that are already there', async () => {
|
|
238
|
+
const result = await duckdb
|
|
239
|
+
.loadModel(model)
|
|
240
|
+
.loadQueryByName('already_has_some_drills')
|
|
241
|
+
.run();
|
|
242
|
+
const table = getDataTree(API.util.wrapResult(result));
|
|
243
|
+
const expDrillQuery = `run: flights -> {
|
|
244
|
+
drill:
|
|
245
|
+
\`Origin Code\` ~ f\`SFO, ORD\`,
|
|
246
|
+
destination = "SJC",
|
|
247
|
+
carrier = "AA",
|
|
248
|
+
cool_carriers.\`Origin Code\` = "ORD",
|
|
249
|
+
over_time.dep_month = 5
|
|
250
|
+
} + { select: * }`;
|
|
251
|
+
const row = table.rows[0];
|
|
252
|
+
expect(row.getDrillQueryMalloy()).toEqual(expDrillQuery);
|
|
253
|
+
});
|
|
254
|
+
|
|
255
|
+
test('negative number can be used in stable query filter', async () => {
|
|
256
|
+
const result = await duckdb
|
|
257
|
+
.loadModel(model)
|
|
258
|
+
.loadQueryByName('negative_value')
|
|
259
|
+
.run();
|
|
260
|
+
const table = getDataTree(API.util.wrapResult(result));
|
|
261
|
+
const expDrillQuery =
|
|
262
|
+
'run: flights -> { drill: negative_value.negative_one = -1 } + { select: * }';
|
|
263
|
+
const row = table.rows[0];
|
|
264
|
+
expect(row.getDrillQueryMalloy()).toEqual(expDrillQuery);
|
|
265
|
+
expect(row.getStableDrillQuery()).toMatchObject({
|
|
266
|
+
definition: {
|
|
267
|
+
kind: 'arrow',
|
|
268
|
+
source: {
|
|
269
|
+
kind: 'source_reference',
|
|
270
|
+
name: 'flights',
|
|
271
|
+
},
|
|
272
|
+
view: {
|
|
273
|
+
kind: 'segment',
|
|
274
|
+
operations: [
|
|
275
|
+
{
|
|
276
|
+
filter: {
|
|
277
|
+
field_reference: {
|
|
278
|
+
name: 'negative_one',
|
|
279
|
+
path: ['negative_value'],
|
|
280
|
+
},
|
|
281
|
+
kind: 'literal_equality',
|
|
282
|
+
value: {
|
|
283
|
+
kind: 'number_literal',
|
|
284
|
+
number_value: -1,
|
|
285
|
+
},
|
|
286
|
+
},
|
|
287
|
+
kind: 'drill',
|
|
288
|
+
},
|
|
289
|
+
],
|
|
290
|
+
},
|
|
291
|
+
},
|
|
292
|
+
});
|
|
293
|
+
});
|
|
294
|
+
|
|
295
|
+
describe('source parameters', () => {
|
|
296
|
+
test('can handle source parameters', async () => {
|
|
297
|
+
const result = await duckdb
|
|
298
|
+
.loadModel(model)
|
|
299
|
+
.loadQueryByName('source_has_parameters')
|
|
300
|
+
.run();
|
|
301
|
+
const table = getDataTree(API.util.wrapResult(result));
|
|
302
|
+
const expDrillQuery = `run: flights_with_parameters(
|
|
303
|
+
number_param is 1,
|
|
304
|
+
string_param is "foo",
|
|
305
|
+
boolean_param is true,
|
|
306
|
+
date_param is @2000,
|
|
307
|
+
timestamp_param is @2004-01-01 10:00,
|
|
308
|
+
filter_expression_param is f\`> 3\`
|
|
309
|
+
) -> { drill: top_carriers.nickname = "Southwest" } + { select: * }`;
|
|
310
|
+
const row = table.rows[0];
|
|
311
|
+
expect(row.getDrillQueryMalloy()).toEqual(expDrillQuery);
|
|
312
|
+
});
|
|
313
|
+
|
|
314
|
+
test('can handle timezone in source parameter', async () => {
|
|
315
|
+
const result = await duckdb
|
|
316
|
+
.loadModel(model)
|
|
317
|
+
.loadQueryByName('source_has_timezone_param')
|
|
318
|
+
.run();
|
|
319
|
+
const table = getDataTree(API.util.wrapResult(result));
|
|
320
|
+
const expDrillQuery =
|
|
321
|
+
'run: flights_with_timestamp_param(timestamp_param is @2004-01-01 10:00:00[America/Los_Angeles]) -> { drill: top_carriers.nickname = "Southwest" } + { select: * }';
|
|
322
|
+
const row = table.rows[0];
|
|
323
|
+
expect(row.getDrillQueryMalloy()).toEqual(expDrillQuery);
|
|
324
|
+
});
|
|
325
|
+
|
|
326
|
+
test('default_params_are_not_included', async () => {
|
|
327
|
+
const result = await duckdb
|
|
328
|
+
.loadModel(model)
|
|
329
|
+
.loadQueryByName('only_default_params')
|
|
330
|
+
.run();
|
|
331
|
+
const table = getDataTree(API.util.wrapResult(result));
|
|
332
|
+
const expDrillQuery =
|
|
333
|
+
'run: flights_with_parameters -> { drill: top_carriers.nickname = "Southwest" } + { select: * }';
|
|
334
|
+
const row = table.rows[0];
|
|
335
|
+
expect(row.getDrillQueryMalloy()).toEqual(expDrillQuery);
|
|
336
|
+
});
|
|
132
337
|
});
|
|
133
338
|
});
|
package/src/util/index.ts
CHANGED
|
@@ -29,6 +29,7 @@ import type {
|
|
|
29
29
|
Result,
|
|
30
30
|
Runtime,
|
|
31
31
|
Expr,
|
|
32
|
+
SingleConnectionRuntime,
|
|
32
33
|
} from '@malloydata/malloy';
|
|
33
34
|
import {composeSQLExpr} from '@malloydata/malloy';
|
|
34
35
|
export * from '@malloydata/malloy/test';
|
|
@@ -102,7 +103,7 @@ function sqlSafe(str: string): string {
|
|
|
102
103
|
}
|
|
103
104
|
|
|
104
105
|
export function mkSqlEqWith(
|
|
105
|
-
runtime:
|
|
106
|
+
runtime: SingleConnectionRuntime,
|
|
106
107
|
cName: string,
|
|
107
108
|
initV?: InitValues
|
|
108
109
|
) {
|
|
@@ -198,19 +199,3 @@ export async function runQuery(runtime: Runtime, querySrc: string) {
|
|
|
198
199
|
|
|
199
200
|
return result;
|
|
200
201
|
}
|
|
201
|
-
|
|
202
|
-
export function booleanResult(value: boolean, dbName: string) {
|
|
203
|
-
if (dbName === 'mysql') {
|
|
204
|
-
return value ? 1 : 0;
|
|
205
|
-
} else {
|
|
206
|
-
return value;
|
|
207
|
-
}
|
|
208
|
-
}
|
|
209
|
-
|
|
210
|
-
export function booleanCode(value: boolean, dbName: string) {
|
|
211
|
-
if (dbName === 'mysql') {
|
|
212
|
-
return value ? '1' : '0';
|
|
213
|
-
} else {
|
|
214
|
-
return value ? 'true' : 'false';
|
|
215
|
-
}
|
|
216
|
-
}
|