@malloydata/malloy-tests 0.0.66-dev230807232724
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 +29 -0
- package/bin/dump_malloytest.sh +1 -0
- package/bin/postgres_init.sh +9 -0
- package/bin/postgres_start.sh +2 -0
- package/bin/postgres_stop.sh +2 -0
- package/data/duckdb/README.md +4 -0
- package/data/duckdb/aircraft.parquet +0 -0
- package/data/duckdb/aircraft_models.parquet +0 -0
- package/data/duckdb/airports.parquet +0 -0
- package/data/duckdb/alltypes.parquet +0 -0
- package/data/duckdb/alltypes2.parquet +0 -0
- package/data/duckdb/bq_medicare_test.parquet +0 -0
- package/data/duckdb/carriers.parquet +0 -0
- package/data/duckdb/flights/part.0.parquet +0 -0
- package/data/duckdb/flights/part.1.parquet +0 -0
- package/data/duckdb/flights/part.2.parquet +0 -0
- package/data/duckdb/flights.parquet +0 -0
- package/data/duckdb/flights_partitioned.parquet +0 -0
- package/data/duckdb/ga_sample.parquet +0 -0
- package/data/duckdb/numbers.parquet +0 -0
- package/data/duckdb/state_facts.parquet +0 -0
- package/data/duckdb/test.json +3 -0
- package/data/duckdb/words.parquet +0 -0
- package/data/duckdb/words_bigger.parquet +0 -0
- package/data/postgres/malloytest-postgres.sql.gz +0 -0
- package/data/postgres/state_facts.sql +366 -0
- package/dist/api.spec.d.ts +1 -0
- package/dist/api.spec.js +77 -0
- package/dist/api.spec.js.map +1 -0
- package/dist/databases/all/expr.spec.d.ts +3 -0
- package/dist/databases/all/expr.spec.js +581 -0
- package/dist/databases/all/expr.spec.js.map +1 -0
- package/dist/databases/all/functions.spec.d.ts +2 -0
- package/dist/databases/all/functions.spec.js +784 -0
- package/dist/databases/all/functions.spec.js.map +1 -0
- package/dist/databases/all/index.spec.d.ts +2 -0
- package/dist/databases/all/index.spec.js +154 -0
- package/dist/databases/all/index.spec.js.map +1 -0
- package/dist/databases/all/join.spec.d.ts +2 -0
- package/dist/databases/all/join.spec.js +309 -0
- package/dist/databases/all/join.spec.js.map +1 -0
- package/dist/databases/all/nomodel.spec.d.ts +2 -0
- package/dist/databases/all/nomodel.spec.js +957 -0
- package/dist/databases/all/nomodel.spec.js.map +1 -0
- package/dist/databases/all/orderby.spec.d.ts +2 -0
- package/dist/databases/all/orderby.spec.js +224 -0
- package/dist/databases/all/orderby.spec.js.map +1 -0
- package/dist/databases/all/problems.spec.d.ts +2 -0
- package/dist/databases/all/problems.spec.js +113 -0
- package/dist/databases/all/problems.spec.js.map +1 -0
- package/dist/databases/all/sql_expressions.spec.d.ts +2 -0
- package/dist/databases/all/sql_expressions.spec.js +96 -0
- package/dist/databases/all/sql_expressions.spec.js.map +1 -0
- package/dist/databases/all/time.spec.d.ts +3 -0
- package/dist/databases/all/time.spec.js +647 -0
- package/dist/databases/all/time.spec.js.map +1 -0
- package/dist/databases/bigquery/double_truncation.spec.d.ts +1 -0
- package/dist/databases/bigquery/double_truncation.spec.js +50 -0
- package/dist/databases/bigquery/double_truncation.spec.js.map +1 -0
- package/dist/databases/bigquery/handexpr.spec.d.ts +1 -0
- package/dist/databases/bigquery/handexpr.spec.js +713 -0
- package/dist/databases/bigquery/handexpr.spec.js.map +1 -0
- package/dist/databases/bigquery/injestion_time_partitioning.spec.d.ts +1 -0
- package/dist/databases/bigquery/injestion_time_partitioning.spec.js +235 -0
- package/dist/databases/bigquery/injestion_time_partitioning.spec.js.map +1 -0
- package/dist/databases/bigquery/joined_filters.spec.d.ts +1 -0
- package/dist/databases/bigquery/joined_filters.spec.js +72 -0
- package/dist/databases/bigquery/joined_filters.spec.js.map +1 -0
- package/dist/databases/bigquery/json.spec.d.ts +1 -0
- package/dist/databases/bigquery/json.spec.js +86 -0
- package/dist/databases/bigquery/json.spec.js.map +1 -0
- package/dist/databases/bigquery/malloy_query.spec.d.ts +1 -0
- package/dist/databases/bigquery/malloy_query.spec.js +906 -0
- package/dist/databases/bigquery/malloy_query.spec.js.map +1 -0
- package/dist/databases/bigquery/performance.skipped.spec.d.ts +1 -0
- package/dist/databases/bigquery/performance.skipped.spec.js +70 -0
- package/dist/databases/bigquery/performance.skipped.spec.js.map +1 -0
- package/dist/databases/bigquery/time.spec.d.ts +1 -0
- package/dist/databases/bigquery/time.spec.js +55 -0
- package/dist/databases/bigquery/time.spec.js.map +1 -0
- package/dist/databases/bigquery/wildcard_table_names.spec.d.ts +1 -0
- package/dist/databases/bigquery/wildcard_table_names.spec.js +212 -0
- package/dist/databases/bigquery/wildcard_table_names.spec.js.map +1 -0
- package/dist/databases/bigquery-duckdb/nested_source_table.spec.d.ts +1 -0
- package/dist/databases/bigquery-duckdb/nested_source_table.spec.js +247 -0
- package/dist/databases/bigquery-duckdb/nested_source_table.spec.js.map +1 -0
- package/dist/databases/bigquery-postgres/multi_connection.spec.d.ts +1 -0
- package/dist/databases/bigquery-postgres/multi_connection.spec.js +138 -0
- package/dist/databases/bigquery-postgres/multi_connection.spec.js.map +1 -0
- package/dist/databases/bigquery-postgres/streaming.spec.d.ts +1 -0
- package/dist/databases/bigquery-postgres/streaming.spec.js +88 -0
- package/dist/databases/bigquery-postgres/streaming.spec.js.map +1 -0
- package/dist/databases/duckdb/duckdb.spec.d.ts +1 -0
- package/dist/databases/duckdb/duckdb.spec.js +116 -0
- package/dist/databases/duckdb/duckdb.spec.js.map +1 -0
- package/dist/databases/postgres/postgres.spec.d.ts +1 -0
- package/dist/databases/postgres/postgres.spec.js +137 -0
- package/dist/databases/postgres/postgres.spec.js.map +1 -0
- package/dist/databases/shared/test_list.d.ts +3 -0
- package/dist/databases/shared/test_list.js +24 -0
- package/dist/databases/shared/test_list.js.map +1 -0
- package/dist/dependencies.spec.d.ts +1 -0
- package/dist/dependencies.spec.js +63 -0
- package/dist/dependencies.spec.js.map +1 -0
- package/dist/index.d.ts +11 -0
- package/dist/index.js +49 -0
- package/dist/index.js.map +1 -0
- package/dist/model/utils.spec.d.ts +1 -0
- package/dist/model/utils.spec.js +38 -0
- package/dist/model/utils.spec.js.map +1 -0
- package/dist/models/faa_model.d.ts +5 -0
- package/dist/models/faa_model.js +977 -0
- package/dist/models/faa_model.js.map +1 -0
- package/dist/models/medicare_model.d.ts +4 -0
- package/dist/models/medicare_model.js +243 -0
- package/dist/models/medicare_model.js.map +1 -0
- package/dist/render/render.spec.d.ts +1 -0
- package/dist/render/render.spec.js +551 -0
- package/dist/render/render.spec.js.map +1 -0
- package/dist/runtimes.d.ts +33 -0
- package/dist/runtimes.js +147 -0
- package/dist/runtimes.js.map +1 -0
- package/dist/tags.spec.d.ts +1 -0
- package/dist/tags.spec.js +259 -0
- package/dist/tags.spec.js.map +1 -0
- package/dist/util/db-jest-matchers.d.ts +19 -0
- package/dist/util/db-jest-matchers.js +90 -0
- package/dist/util/db-jest-matchers.js.map +1 -0
- package/dist/util/index.d.ts +16 -0
- package/dist/util/index.js +171 -0
- package/dist/util/index.js.map +1 -0
- package/package.json +35 -0
- package/src/api.spec.ts +84 -0
- package/src/databases/all/expr.spec.ts +704 -0
- package/src/databases/all/functions.spec.ts +1135 -0
- package/src/databases/all/index.spec.ts +178 -0
- package/src/databases/all/join.spec.ts +350 -0
- package/src/databases/all/nomodel.spec.ts +1159 -0
- package/src/databases/all/orderby.spec.ts +268 -0
- package/src/databases/all/problems.spec.ts +121 -0
- package/src/databases/all/sql_expressions.spec.ts +107 -0
- package/src/databases/all/time.spec.ts +793 -0
- package/src/databases/bigquery/double_truncation.spec.ts +51 -0
- package/src/databases/bigquery/handexpr.spec.ts +782 -0
- package/src/databases/bigquery/injestion_time_partitioning.spec.ts +268 -0
- package/src/databases/bigquery/joined_filters.spec.ts +75 -0
- package/src/databases/bigquery/json.spec.ts +95 -0
- package/src/databases/bigquery/malloy_query.spec.ts +1036 -0
- package/src/databases/bigquery/performance.skipped.spec.ts +73 -0
- package/src/databases/bigquery/time.spec.ts +59 -0
- package/src/databases/bigquery/wildcard_table_names.spec.ts +233 -0
- package/src/databases/bigquery-duckdb/nested_source_table.spec.ts +268 -0
- package/src/databases/bigquery-postgres/multi_connection.spec.ts +143 -0
- package/src/databases/bigquery-postgres/streaming.spec.ts +100 -0
- package/src/databases/duckdb/duckdb.spec.ts +131 -0
- package/src/databases/postgres/postgres.spec.ts +167 -0
- package/src/databases/shared/test_list.ts +24 -0
- package/src/dependencies.spec.ts +62 -0
- package/src/index.ts +36 -0
- package/src/model/utils.spec.ts +37 -0
- package/src/models/faa_model.ts +995 -0
- package/src/models/medicare_model.ts +246 -0
- package/src/render/__snapshots__/render.spec.ts.snap +12574 -0
- package/src/render/render.spec.ts +670 -0
- package/src/runtimes.ts +182 -0
- package/src/tags.spec.ts +277 -0
- package/src/util/db-jest-matchers.ts +128 -0
- package/src/util/index.ts +191 -0
- package/tsconfig.json +25 -0
|
@@ -0,0 +1,704 @@
|
|
|
1
|
+
/* eslint-disable no-console */
|
|
2
|
+
/*
|
|
3
|
+
* Copyright 2023 Google LLC
|
|
4
|
+
*
|
|
5
|
+
* Permission is hereby granted, free of charge, to any person obtaining
|
|
6
|
+
* a copy of this software and associated documentation files
|
|
7
|
+
* (the "Software"), to deal in the Software without restriction,
|
|
8
|
+
* including without limitation the rights to use, copy, modify, merge,
|
|
9
|
+
* publish, distribute, sublicense, and/or sell copies of the Software,
|
|
10
|
+
* and to permit persons to whom the Software is furnished to do so,
|
|
11
|
+
* subject to the following conditions:
|
|
12
|
+
*
|
|
13
|
+
* The above copyright notice and this permission notice shall be
|
|
14
|
+
* included in all copies or substantial portions of the Software.
|
|
15
|
+
*
|
|
16
|
+
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
|
17
|
+
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
|
18
|
+
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
|
19
|
+
* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
|
|
20
|
+
* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
|
|
21
|
+
* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
|
|
22
|
+
* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
|
23
|
+
*/
|
|
24
|
+
|
|
25
|
+
import {RuntimeList, allDatabases} from '../../runtimes';
|
|
26
|
+
import '../../util/db-jest-matchers';
|
|
27
|
+
import {databasesFromEnvironmentOr, mkSqlEqWith, testIf} from '../../util';
|
|
28
|
+
|
|
29
|
+
const runtimes = new RuntimeList(databasesFromEnvironmentOr(allDatabases));
|
|
30
|
+
|
|
31
|
+
const expressionModelText = `
|
|
32
|
+
source: aircraft_models is table('malloytest.aircraft_models') extend {
|
|
33
|
+
primary_key: aircraft_model_code
|
|
34
|
+
measure:
|
|
35
|
+
airport_count is count(*),
|
|
36
|
+
aircraft_model_count is count(),
|
|
37
|
+
total_seats is sum(seats),
|
|
38
|
+
boeing_seats is sum(seats) {? manufacturer ? 'BOEING'},
|
|
39
|
+
percent_boeing is boeing_seats / total_seats * 100,
|
|
40
|
+
percent_boeing_floor is floor(boeing_seats / total_seats * 100),
|
|
41
|
+
dimension: seats_bucketed is floor(seats/20)*20.0
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
source: aircraft is table('malloytest.aircraft') extend {
|
|
45
|
+
primary_key: tail_num
|
|
46
|
+
join_one: aircraft_models with aircraft_model_code
|
|
47
|
+
measure: aircraft_count is count(*)
|
|
48
|
+
query: by_manufacturer is {
|
|
49
|
+
top: 5
|
|
50
|
+
group_by: aircraft_models.manufacturer
|
|
51
|
+
aggregate: aircraft_count
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
`;
|
|
55
|
+
|
|
56
|
+
export const exprSharedTests = (
|
|
57
|
+
runtimes: RuntimeList,
|
|
58
|
+
_splitFunction?: (column: string, splitChar: string) => string
|
|
59
|
+
) => {
|
|
60
|
+
describe.each(runtimes.runtimeList)('%s', (databaseName, runtime) => {
|
|
61
|
+
const expressionModel = runtime.loadModel(expressionModelText);
|
|
62
|
+
// basic calculations for sum, filtered sum, without a join.
|
|
63
|
+
it('basic calculations', async () => {
|
|
64
|
+
const result = await expressionModel
|
|
65
|
+
.loadQuery(
|
|
66
|
+
`
|
|
67
|
+
query: aircraft_models->{
|
|
68
|
+
aggregate:
|
|
69
|
+
total_seats,
|
|
70
|
+
total_seats2 is sum(seats),
|
|
71
|
+
boeing_seats,
|
|
72
|
+
boeing_seats2 is sum(seats) {? manufacturer ? 'BOEING'},
|
|
73
|
+
boeing_seats3 is total_seats {? manufacturer ? 'BOEING'},
|
|
74
|
+
percent_boeing,
|
|
75
|
+
percent_boeing2 is boeing_seats / total_seats * 100,
|
|
76
|
+
-- percent_boeing_floor,
|
|
77
|
+
-- percent_boeing_floor2 is FLOOR(boeing_seats / total_seats * 100)
|
|
78
|
+
}
|
|
79
|
+
`
|
|
80
|
+
)
|
|
81
|
+
.run();
|
|
82
|
+
expect(result.data.path(0, 'total_seats').value).toBe(452415);
|
|
83
|
+
expect(result.data.path(0, 'total_seats2').value).toBe(452415);
|
|
84
|
+
expect(result.data.path(0, 'boeing_seats').value).toBe(252771);
|
|
85
|
+
expect(result.data.path(0, 'boeing_seats2').value).toBe(252771);
|
|
86
|
+
expect(result.data.path(0, 'boeing_seats3').value).toBe(252771);
|
|
87
|
+
expect(
|
|
88
|
+
Math.floor(result.data.path(0, 'percent_boeing').number.value)
|
|
89
|
+
).toBe(55);
|
|
90
|
+
expect(
|
|
91
|
+
Math.floor(result.data.path(0, 'percent_boeing2').number.value)
|
|
92
|
+
).toBe(55);
|
|
93
|
+
// expect(result.data.path(0, "percent_boeing_floor").value).toBe(55);
|
|
94
|
+
// expect(result.data.path(0, "percent_boeing_floor2").value).toBe(55);
|
|
95
|
+
});
|
|
96
|
+
|
|
97
|
+
// Floor is broken (doesn't compile because the expression returned isn't an aggregate.)
|
|
98
|
+
it('Floor() -or any function bustage with aggregates', async () => {
|
|
99
|
+
const result = await expressionModel
|
|
100
|
+
.loadQuery(
|
|
101
|
+
`
|
|
102
|
+
query: aircraft_models->{
|
|
103
|
+
aggregate:
|
|
104
|
+
percent_boeing_floor
|
|
105
|
+
percent_boeing_floor2 is FLOOR(boeing_seats / total_seats * 100)
|
|
106
|
+
}
|
|
107
|
+
`
|
|
108
|
+
)
|
|
109
|
+
.run();
|
|
110
|
+
expect(result.data.path(0, 'percent_boeing_floor').value).toBe(55);
|
|
111
|
+
expect(result.data.path(0, 'percent_boeing_floor2').value).toBe(55);
|
|
112
|
+
});
|
|
113
|
+
|
|
114
|
+
// Model based version of sums.
|
|
115
|
+
it('model: expression fixups.', async () => {
|
|
116
|
+
const result = await expressionModel
|
|
117
|
+
.loadQuery(
|
|
118
|
+
`
|
|
119
|
+
query: aircraft->{
|
|
120
|
+
aggregate:
|
|
121
|
+
aircraft_models.total_seats
|
|
122
|
+
aircraft_models.boeing_seats
|
|
123
|
+
}
|
|
124
|
+
`
|
|
125
|
+
)
|
|
126
|
+
.run();
|
|
127
|
+
expect(result.data.path(0, 'total_seats').value).toBe(18294);
|
|
128
|
+
expect(result.data.path(0, 'boeing_seats').value).toBe(6244);
|
|
129
|
+
});
|
|
130
|
+
|
|
131
|
+
// turtle expressions
|
|
132
|
+
it('model: turtle', async () => {
|
|
133
|
+
const result = await expressionModel
|
|
134
|
+
.loadQuery(
|
|
135
|
+
`
|
|
136
|
+
query: aircraft->by_manufacturer
|
|
137
|
+
`
|
|
138
|
+
)
|
|
139
|
+
.run();
|
|
140
|
+
expect(result.data.path(0, 'manufacturer').value).toBe('CESSNA');
|
|
141
|
+
});
|
|
142
|
+
|
|
143
|
+
// filtered turtle expressions
|
|
144
|
+
testIf(runtime.supportsNesting)('model: filtered turtle', async () => {
|
|
145
|
+
const result = await expressionModel
|
|
146
|
+
.loadQuery(
|
|
147
|
+
`
|
|
148
|
+
query: aircraft->{
|
|
149
|
+
nest: b is by_manufacturer{? aircraft_models.manufacturer ?~'B%'}
|
|
150
|
+
}
|
|
151
|
+
`
|
|
152
|
+
)
|
|
153
|
+
.run();
|
|
154
|
+
expect(result.data.path(0, 'b', 0, 'manufacturer').value).toBe('BEECH');
|
|
155
|
+
});
|
|
156
|
+
|
|
157
|
+
// having.
|
|
158
|
+
it('model: simple having', async () => {
|
|
159
|
+
const result = await expressionModel
|
|
160
|
+
.loadQuery(
|
|
161
|
+
`
|
|
162
|
+
query: aircraft->{
|
|
163
|
+
having: aircraft_count >90
|
|
164
|
+
group_by: state
|
|
165
|
+
aggregate: aircraft_count
|
|
166
|
+
order_by: 2
|
|
167
|
+
}
|
|
168
|
+
`
|
|
169
|
+
)
|
|
170
|
+
.run();
|
|
171
|
+
expect(result.data.path(0, 'aircraft_count').value).toBe(91);
|
|
172
|
+
});
|
|
173
|
+
|
|
174
|
+
testIf(runtime.supportsNesting)('model: turtle having2', async () => {
|
|
175
|
+
const result = await expressionModel
|
|
176
|
+
.loadQuery(
|
|
177
|
+
`
|
|
178
|
+
-- hacking a null test for now
|
|
179
|
+
query: aircraft->{
|
|
180
|
+
top: 10
|
|
181
|
+
order_by: 1
|
|
182
|
+
where: region != NULL
|
|
183
|
+
group_by: region
|
|
184
|
+
nest: by_state is {
|
|
185
|
+
top: 10
|
|
186
|
+
order_by: 1 desc
|
|
187
|
+
having: aircraft_count > 50
|
|
188
|
+
group_by: state
|
|
189
|
+
aggregate: aircraft_count
|
|
190
|
+
}
|
|
191
|
+
}
|
|
192
|
+
`
|
|
193
|
+
)
|
|
194
|
+
.run();
|
|
195
|
+
expect(result.data.path(0, 'by_state', 0, 'state').value).toBe('VA');
|
|
196
|
+
});
|
|
197
|
+
|
|
198
|
+
testIf(runtime.supportsNesting)(
|
|
199
|
+
'model: turtle having on main',
|
|
200
|
+
async () => {
|
|
201
|
+
const result = await expressionModel
|
|
202
|
+
.loadQuery(
|
|
203
|
+
`
|
|
204
|
+
query: aircraft->{
|
|
205
|
+
order_by: 2 asc
|
|
206
|
+
having: aircraft_count ? >500
|
|
207
|
+
group_by: region
|
|
208
|
+
aggregate: aircraft_count
|
|
209
|
+
nest: by_state is {
|
|
210
|
+
order_by: 2 asc
|
|
211
|
+
having: aircraft_count >45
|
|
212
|
+
group_by: state
|
|
213
|
+
aggregate: aircraft_count
|
|
214
|
+
nest: by_city is {
|
|
215
|
+
order_by: 2 asc
|
|
216
|
+
having: aircraft_count ? >5
|
|
217
|
+
group_by: city
|
|
218
|
+
aggregate: aircraft_count
|
|
219
|
+
}
|
|
220
|
+
}
|
|
221
|
+
}
|
|
222
|
+
`
|
|
223
|
+
)
|
|
224
|
+
.run();
|
|
225
|
+
expect(
|
|
226
|
+
result.data.path(0, 'by_state', 0, 'by_city', 0, 'city').value
|
|
227
|
+
).toBe('ALBUQUERQUE');
|
|
228
|
+
}
|
|
229
|
+
);
|
|
230
|
+
|
|
231
|
+
// bigquery doesn't like to partition by floats,
|
|
232
|
+
testIf(runtime.supportsNesting)(
|
|
233
|
+
'model: having float group by partition',
|
|
234
|
+
async () => {
|
|
235
|
+
await expect(runtime).queryMatches(
|
|
236
|
+
`${expressionModelText}
|
|
237
|
+
query: aircraft_models->{
|
|
238
|
+
order_by: 1
|
|
239
|
+
where: seats_bucketed > 0
|
|
240
|
+
having: aircraft_model_count > 400
|
|
241
|
+
group_by: seats_bucketed
|
|
242
|
+
aggregate: aircraft_model_count
|
|
243
|
+
nest: foo is {
|
|
244
|
+
group_by: engines
|
|
245
|
+
aggregate: aircraft_model_count
|
|
246
|
+
}
|
|
247
|
+
}`,
|
|
248
|
+
{aircraft_model_count: 448}
|
|
249
|
+
);
|
|
250
|
+
}
|
|
251
|
+
);
|
|
252
|
+
|
|
253
|
+
it('model: aggregate functions distinct min max', async () => {
|
|
254
|
+
const result = await expressionModel
|
|
255
|
+
.loadQuery(
|
|
256
|
+
`
|
|
257
|
+
query: aircraft_models->{
|
|
258
|
+
aggregate:
|
|
259
|
+
distinct_seats is count(distinct seats),
|
|
260
|
+
boeing_distinct_seats is count(distinct seats) {?manufacturer ? 'BOEING'},
|
|
261
|
+
min_seats is min(seats),
|
|
262
|
+
cessna_min_seats is min(seats) {? manufacturer ? 'CESSNA'},
|
|
263
|
+
max_seats is max(seats),
|
|
264
|
+
cessna_max_seats is max(seats) {? manufacturer ? 'CESSNA'},
|
|
265
|
+
min_code is min(aircraft_model_code),
|
|
266
|
+
boeing_min_model is min(model) {? manufacturer ? 'BOEING'},
|
|
267
|
+
max_model is max(model),
|
|
268
|
+
boeing_max_model is max(model) {? manufacturer ? 'BOEING'},
|
|
269
|
+
}
|
|
270
|
+
`
|
|
271
|
+
)
|
|
272
|
+
.run();
|
|
273
|
+
expect(result.data.path(0, 'distinct_seats').value).toBe(187);
|
|
274
|
+
expect(result.data.path(0, 'boeing_distinct_seats').value).toBe(85);
|
|
275
|
+
expect(result.data.path(0, 'min_seats').value).toBe(0);
|
|
276
|
+
expect(result.data.path(0, 'cessna_min_seats').value).toBe(1);
|
|
277
|
+
expect(result.data.path(0, 'max_seats').value).toBe(660);
|
|
278
|
+
expect(result.data.path(0, 'min_code').value).toBe('0030109');
|
|
279
|
+
expect(result.data.path(0, 'cessna_max_seats').value).toBe(14);
|
|
280
|
+
expect(result.data.path(0, 'boeing_min_model').value).toBe('100');
|
|
281
|
+
expect(result.data.path(0, 'max_model').value).toBe('ZWEIFEL PA18');
|
|
282
|
+
expect(result.data.path(0, 'boeing_max_model').value).toBe('YL-15');
|
|
283
|
+
});
|
|
284
|
+
|
|
285
|
+
(databaseName !== 'bigquery' ? it.skip : it)(
|
|
286
|
+
'model: dates named',
|
|
287
|
+
async () => {
|
|
288
|
+
const result = await expressionModel
|
|
289
|
+
.loadQuery(
|
|
290
|
+
`
|
|
291
|
+
query: table('malloytest.alltypes')->{
|
|
292
|
+
group_by:
|
|
293
|
+
t_date,
|
|
294
|
+
t_date_month is t_date.month,
|
|
295
|
+
t_date_year is t_date.year,
|
|
296
|
+
t_timestamp,
|
|
297
|
+
t_timestamp_date is t_timestamp.day,
|
|
298
|
+
t_timestamp_hour is t_timestamp.hour,
|
|
299
|
+
t_timestamp_minute is t_timestamp.minute,
|
|
300
|
+
t_timestamp_second is t_timestamp.second,
|
|
301
|
+
t_timestamp_month is t_timestamp.month,
|
|
302
|
+
t_timestamp_year is t_timestamp.year,
|
|
303
|
+
}
|
|
304
|
+
|
|
305
|
+
`
|
|
306
|
+
)
|
|
307
|
+
.run();
|
|
308
|
+
expect(result.data.path(0, 't_date').value).toEqual(
|
|
309
|
+
new Date('2020-03-02')
|
|
310
|
+
);
|
|
311
|
+
expect(result.data.path(0, 't_date_month').value).toEqual(
|
|
312
|
+
new Date('2020-03-01')
|
|
313
|
+
);
|
|
314
|
+
expect(result.data.path(0, 't_date_year').value).toEqual(
|
|
315
|
+
new Date('2020-01-01')
|
|
316
|
+
);
|
|
317
|
+
expect(result.data.path(0, 't_timestamp').value).toEqual(
|
|
318
|
+
new Date('2020-03-02T12:35:56.000Z')
|
|
319
|
+
);
|
|
320
|
+
expect(result.data.path(0, 't_timestamp_second').value).toEqual(
|
|
321
|
+
new Date('2020-03-02T12:35:56.000Z')
|
|
322
|
+
);
|
|
323
|
+
expect(result.data.path(0, 't_timestamp_minute').value).toEqual(
|
|
324
|
+
new Date('2020-03-02T12:35:00.000Z')
|
|
325
|
+
);
|
|
326
|
+
expect(result.data.path(0, 't_timestamp_hour').value).toEqual(
|
|
327
|
+
new Date('2020-03-02T12:00:00.000Z')
|
|
328
|
+
);
|
|
329
|
+
expect(result.data.path(0, 't_timestamp_date').value).toEqual(
|
|
330
|
+
new Date('2020-03-02')
|
|
331
|
+
);
|
|
332
|
+
expect(result.data.path(0, 't_timestamp_month').value).toEqual(
|
|
333
|
+
new Date('2020-03-01')
|
|
334
|
+
);
|
|
335
|
+
expect(result.data.path(0, 't_timestamp_year').value).toEqual(
|
|
336
|
+
new Date('2020-01-01')
|
|
337
|
+
);
|
|
338
|
+
}
|
|
339
|
+
);
|
|
340
|
+
|
|
341
|
+
it.skip('defines in model', async () => {
|
|
342
|
+
// const result1 = await model.makeQuery(`
|
|
343
|
+
// define a is ('malloytest.alltypes');
|
|
344
|
+
// explore a | reduce x is count(*)
|
|
345
|
+
// `);
|
|
346
|
+
// const result = await model.makeQuery(`
|
|
347
|
+
// define a is ('malloytest.alltypes');
|
|
348
|
+
// explore a | reduce x is count(*)
|
|
349
|
+
// `);
|
|
350
|
+
});
|
|
351
|
+
|
|
352
|
+
it('named query metadata undefined', async () => {
|
|
353
|
+
const result = await expressionModel
|
|
354
|
+
.loadQuery(
|
|
355
|
+
`
|
|
356
|
+
query: aircraft->{
|
|
357
|
+
aggregate: aircraft_count is count()
|
|
358
|
+
}
|
|
359
|
+
`
|
|
360
|
+
)
|
|
361
|
+
.run();
|
|
362
|
+
// TODO The result explore should really be unnamed. This test currently
|
|
363
|
+
// inspects inner information because we have no way to have unnamed
|
|
364
|
+
// explores today.
|
|
365
|
+
// expect(result.getResultExplore().name).toBe(undefined);
|
|
366
|
+
expect(result._queryResult.queryName).toBe(undefined);
|
|
367
|
+
});
|
|
368
|
+
|
|
369
|
+
it('named query metadata named', async () => {
|
|
370
|
+
const result = await expressionModel
|
|
371
|
+
.loadQuery(
|
|
372
|
+
`
|
|
373
|
+
query: aircraft->by_manufacturer
|
|
374
|
+
`
|
|
375
|
+
)
|
|
376
|
+
.run();
|
|
377
|
+
expect(result.resultExplore.name).toBe('by_manufacturer');
|
|
378
|
+
});
|
|
379
|
+
|
|
380
|
+
it('named query metadata named head of pipeline', async () => {
|
|
381
|
+
const result = await expressionModel
|
|
382
|
+
.loadQuery(
|
|
383
|
+
`
|
|
384
|
+
query: aircraft->by_manufacturer->{ aggregate: c is count()}
|
|
385
|
+
`
|
|
386
|
+
)
|
|
387
|
+
.run();
|
|
388
|
+
// TODO Same as above -- this test should check the explore name
|
|
389
|
+
// expect(result.getResultExplore().name).toBe(undefined);
|
|
390
|
+
expect(result._queryResult.queryName).toBe(undefined);
|
|
391
|
+
});
|
|
392
|
+
|
|
393
|
+
it('filtered explores basic', async () => {
|
|
394
|
+
const result = await expressionModel
|
|
395
|
+
.loadQuery(
|
|
396
|
+
`
|
|
397
|
+
source: b is aircraft{ where: aircraft_models.manufacturer ? ~'B%' }
|
|
398
|
+
|
|
399
|
+
query: b->{aggregate: m_count is count(distinct aircraft_models.manufacturer) }
|
|
400
|
+
`
|
|
401
|
+
)
|
|
402
|
+
.run();
|
|
403
|
+
expect(result.data.path(0, 'm_count').value).toBe(63);
|
|
404
|
+
});
|
|
405
|
+
|
|
406
|
+
testIf(runtime.supportsNesting)(
|
|
407
|
+
'query with aliasname used twice',
|
|
408
|
+
async () => {
|
|
409
|
+
const result = await expressionModel
|
|
410
|
+
.loadQuery(
|
|
411
|
+
`
|
|
412
|
+
query: aircraft->{
|
|
413
|
+
group_by: first is substr(city,1,1)
|
|
414
|
+
aggregate: aircraft_count is count()
|
|
415
|
+
nest: aircraft is {
|
|
416
|
+
group_by: first_two is substr(city,1,2)
|
|
417
|
+
aggregate: aircraft_count is count()
|
|
418
|
+
nest: aircraft is {
|
|
419
|
+
group_by: first_three is substr(city,1,3)
|
|
420
|
+
aggregate: aircraft_count is count()
|
|
421
|
+
}
|
|
422
|
+
}
|
|
423
|
+
} -> {
|
|
424
|
+
project:
|
|
425
|
+
aircraft.aircraft.first_three
|
|
426
|
+
aircraft_count
|
|
427
|
+
order_by: 2 desc, 1
|
|
428
|
+
}
|
|
429
|
+
`
|
|
430
|
+
)
|
|
431
|
+
.run();
|
|
432
|
+
expect(result.data.path(0, 'first_three').value).toBe('SAB');
|
|
433
|
+
}
|
|
434
|
+
);
|
|
435
|
+
|
|
436
|
+
it.skip('join foreign_key reverse', async () => {
|
|
437
|
+
const result = await expressionModel
|
|
438
|
+
.loadQuery(
|
|
439
|
+
`
|
|
440
|
+
source: a is table('malloytest.aircraft') {
|
|
441
|
+
primary_key: tail_num
|
|
442
|
+
measure: aircraft_count is count()
|
|
443
|
+
}
|
|
444
|
+
query: table('malloytest.aircraft_models') {
|
|
445
|
+
primary_key: aircraft_model_code
|
|
446
|
+
join_many: a on a.aircraft_model_code
|
|
447
|
+
|
|
448
|
+
some_measures is {
|
|
449
|
+
aggregate: am_count is count()
|
|
450
|
+
aggregate: a.aircraft_count
|
|
451
|
+
}
|
|
452
|
+
} -> some_measure
|
|
453
|
+
`
|
|
454
|
+
)
|
|
455
|
+
.run();
|
|
456
|
+
expect(result.data.path(0, 'first_three').value).toBe('SAN');
|
|
457
|
+
});
|
|
458
|
+
|
|
459
|
+
it('joined filtered explores', async () => {
|
|
460
|
+
const result = await expressionModel
|
|
461
|
+
.loadQuery(
|
|
462
|
+
`
|
|
463
|
+
source: a_models is table('malloytest.aircraft_models'){
|
|
464
|
+
where: manufacturer ? ~'B%'
|
|
465
|
+
primary_key: aircraft_model_code
|
|
466
|
+
measure:model_count is count()
|
|
467
|
+
}
|
|
468
|
+
|
|
469
|
+
source: aircraft2 is table('malloytest.aircraft'){
|
|
470
|
+
join_one: model is a_models with aircraft_model_code
|
|
471
|
+
measure: aircraft_count is count()
|
|
472
|
+
}
|
|
473
|
+
|
|
474
|
+
query: aircraft2->{
|
|
475
|
+
aggregate:
|
|
476
|
+
model.model_count
|
|
477
|
+
aircraft_count
|
|
478
|
+
}
|
|
479
|
+
`
|
|
480
|
+
)
|
|
481
|
+
.run();
|
|
482
|
+
expect(result.data.path(0, 'model_count').value).toBe(244);
|
|
483
|
+
expect(result.data.path(0, 'aircraft_count').value).toBe(3599);
|
|
484
|
+
});
|
|
485
|
+
|
|
486
|
+
it('joined filtered explores with dependancies', async () => {
|
|
487
|
+
const result = await expressionModel
|
|
488
|
+
.loadQuery(
|
|
489
|
+
`
|
|
490
|
+
source: bo_models is
|
|
491
|
+
from(
|
|
492
|
+
table('malloytest.aircraft_models') {? manufacturer ? ~ 'BO%' }
|
|
493
|
+
-> { project: aircraft_model_code, manufacturer, seats }
|
|
494
|
+
) {
|
|
495
|
+
primary_key: aircraft_model_code
|
|
496
|
+
measure: bo_count is count()
|
|
497
|
+
}
|
|
498
|
+
|
|
499
|
+
source: b_models is
|
|
500
|
+
from(
|
|
501
|
+
table('malloytest.aircraft_models') {? manufacturer ? ~ 'B%' }
|
|
502
|
+
-> { project: aircraft_model_code, manufacturer, seats }
|
|
503
|
+
) {
|
|
504
|
+
where: bo_models.seats > 200
|
|
505
|
+
primary_key: aircraft_model_code
|
|
506
|
+
measure: b_count is count()
|
|
507
|
+
join_one: bo_models with aircraft_model_code
|
|
508
|
+
}
|
|
509
|
+
|
|
510
|
+
source: models is table('malloytest.aircraft_models') {
|
|
511
|
+
join_one: b_models with aircraft_model_code
|
|
512
|
+
measure: model_count is count()
|
|
513
|
+
}
|
|
514
|
+
|
|
515
|
+
query: models -> {
|
|
516
|
+
aggregate: model_count
|
|
517
|
+
aggregate: b_models.b_count
|
|
518
|
+
-- aggregate: b_models.bo_models.bo_count
|
|
519
|
+
}
|
|
520
|
+
`
|
|
521
|
+
)
|
|
522
|
+
.run();
|
|
523
|
+
expect(result.data.path(0, 'model_count').value).toBe(60461);
|
|
524
|
+
expect(result.data.path(0, 'b_count').value).toBe(355);
|
|
525
|
+
});
|
|
526
|
+
|
|
527
|
+
it('group by explore - simple group by', async () => {
|
|
528
|
+
const result = await expressionModel
|
|
529
|
+
.loadQuery(
|
|
530
|
+
`
|
|
531
|
+
query: aircraft->{
|
|
532
|
+
group_by: aircraft_models
|
|
533
|
+
aggregate: aircraft_count
|
|
534
|
+
}
|
|
535
|
+
`
|
|
536
|
+
)
|
|
537
|
+
.run();
|
|
538
|
+
expect(result.data.path(0, 'aircraft_count').value).toBe(58);
|
|
539
|
+
expect(result.data.path(0, 'aircraft_models_id').value).toBe('7102802');
|
|
540
|
+
});
|
|
541
|
+
|
|
542
|
+
it('group by explore - pipeline', async () => {
|
|
543
|
+
const result = await expressionModel
|
|
544
|
+
.loadQuery(
|
|
545
|
+
`
|
|
546
|
+
query: aircraft->{
|
|
547
|
+
group_by: aircraft_models
|
|
548
|
+
aggregate: aircraft_count
|
|
549
|
+
} -> {
|
|
550
|
+
group_by: aircraft_models.manufacturer
|
|
551
|
+
aggregate: aircraft_count is aircraft_count.sum()
|
|
552
|
+
}
|
|
553
|
+
`
|
|
554
|
+
)
|
|
555
|
+
.run();
|
|
556
|
+
expect(result.data.path(0, 'aircraft_count').value).toBe(1048);
|
|
557
|
+
expect(result.data.path(0, 'manufacturer').value).toBe('CESSNA');
|
|
558
|
+
});
|
|
559
|
+
|
|
560
|
+
it('group by explore - pipeline 2 levels', async () => {
|
|
561
|
+
const result = await expressionModel
|
|
562
|
+
.loadQuery(
|
|
563
|
+
`
|
|
564
|
+
source: f is table('malloytest.flights'){
|
|
565
|
+
join_one: a is table('malloytest.aircraft') {
|
|
566
|
+
join_one: state_facts is table('malloytest.state_facts'){primary_key: state} with state
|
|
567
|
+
} on tail_num = a.tail_num
|
|
568
|
+
}
|
|
569
|
+
|
|
570
|
+
query: f-> {
|
|
571
|
+
group_by: a.state_facts
|
|
572
|
+
aggregate: flight_count is count()
|
|
573
|
+
} -> {
|
|
574
|
+
group_by: state_facts.popular_name
|
|
575
|
+
aggregate: flight_count is flight_count.sum()
|
|
576
|
+
}
|
|
577
|
+
`
|
|
578
|
+
)
|
|
579
|
+
.run();
|
|
580
|
+
// console.log(result.data.toObject());
|
|
581
|
+
expect(result.data.path(0, 'flight_count').value).toBe(199726);
|
|
582
|
+
expect(result.data.path(0, 'popular_name').value).toBe('Isabella');
|
|
583
|
+
});
|
|
584
|
+
});
|
|
585
|
+
|
|
586
|
+
describe.each(runtimes.runtimeList)('%s', (databaseName, runtime) => {
|
|
587
|
+
const sqlEq = mkSqlEqWith(runtime, {
|
|
588
|
+
malloy: `+ {
|
|
589
|
+
dimension: friName is 'friday'
|
|
590
|
+
dimension: friDay is 5
|
|
591
|
+
dimension: satName is 'saturday'
|
|
592
|
+
dimension: satDay is 6
|
|
593
|
+
}`,
|
|
594
|
+
});
|
|
595
|
+
|
|
596
|
+
describe.skip('alternations with not-eq', () => {
|
|
597
|
+
/*
|
|
598
|
+
Here's the desired truth table ...
|
|
599
|
+
|
|
600
|
+
x x != y | z
|
|
601
|
+
====== ============
|
|
602
|
+
y false
|
|
603
|
+
z false
|
|
604
|
+
^[yz] true
|
|
605
|
+
*/
|
|
606
|
+
test('x not-eq y or z : x eq y', async () => {
|
|
607
|
+
const result = await sqlEq('6 != (6|7)', false);
|
|
608
|
+
expect(result).isSqlEq();
|
|
609
|
+
});
|
|
610
|
+
test('x not-eq y or z : x eq z', async () => {
|
|
611
|
+
const result = await sqlEq('7 != (6|7)', false);
|
|
612
|
+
expect(result).isSqlEq();
|
|
613
|
+
});
|
|
614
|
+
test('x not-eq y or z : else', async () => {
|
|
615
|
+
const result = await sqlEq('5 != (6|7)', true);
|
|
616
|
+
expect(result).isSqlEq();
|
|
617
|
+
});
|
|
618
|
+
/*
|
|
619
|
+
Writing this the old way, should have the same truth table ...
|
|
620
|
+
x != y & != z
|
|
621
|
+
*/
|
|
622
|
+
test('x not-eq y and not-eq z : x eq y', async () => {
|
|
623
|
+
const result = await sqlEq('6 != (6 & !=7)', false);
|
|
624
|
+
expect(result).isSqlEq();
|
|
625
|
+
});
|
|
626
|
+
test('x not-eq y and not-eq z : x eq z', async () => {
|
|
627
|
+
const result = await sqlEq('7 != (6 & != 7)', false);
|
|
628
|
+
expect(result).isSqlEq();
|
|
629
|
+
});
|
|
630
|
+
test('x not-eq y and not-eq z : else', async () => {
|
|
631
|
+
const result = await sqlEq('5 != (6 & !=7)', true);
|
|
632
|
+
expect(result).isSqlEq();
|
|
633
|
+
});
|
|
634
|
+
});
|
|
635
|
+
|
|
636
|
+
describe('string literal quoting', () => {
|
|
637
|
+
const dq = '"';
|
|
638
|
+
const tick = "'";
|
|
639
|
+
const back = '\\';
|
|
640
|
+
test('quote single character', async () => {
|
|
641
|
+
expect(await sqlEq(`'${back}x'`, 'x')).isSqlEq();
|
|
642
|
+
});
|
|
643
|
+
test('quote single quote', async () => {
|
|
644
|
+
expect(await sqlEq(`'${back}${tick}'`, tick)).isSqlEq();
|
|
645
|
+
});
|
|
646
|
+
test('quote double quote', async () => {
|
|
647
|
+
await expect(runtime).queryMatches(
|
|
648
|
+
`sql: x is {connection:"${databaseName}" select:"""SELECT 1 as x"""}
|
|
649
|
+
query: from_sql(x) -> {
|
|
650
|
+
project: double_quote is "${back}${dq}"
|
|
651
|
+
}
|
|
652
|
+
`,
|
|
653
|
+
{double_quote: '"'}
|
|
654
|
+
);
|
|
655
|
+
});
|
|
656
|
+
test('quote backslash', async () => {
|
|
657
|
+
expect(await sqlEq(`'${back}${back}'`, back)).isSqlEq();
|
|
658
|
+
});
|
|
659
|
+
});
|
|
660
|
+
|
|
661
|
+
test('nullish ?? operator', async () => {
|
|
662
|
+
await expect(runtime).queryMatches(
|
|
663
|
+
`sql: nullTest is { connection: "${databaseName}" select: """
|
|
664
|
+
SELECT '' as null_value, '' as string_value
|
|
665
|
+
UNION ALL SELECT null, 'correct'
|
|
666
|
+
""" }
|
|
667
|
+
query: from_sql(nullTest) -> {
|
|
668
|
+
where: null_value = null
|
|
669
|
+
project:
|
|
670
|
+
found_null is null_value ?? 'correct',
|
|
671
|
+
else_pass is string_value ?? 'incorrect'
|
|
672
|
+
literal_null is null ?? 'correct'
|
|
673
|
+
}`,
|
|
674
|
+
{found_null: 'correct', else_pass: 'correct', literal_null: 'correct'}
|
|
675
|
+
);
|
|
676
|
+
});
|
|
677
|
+
|
|
678
|
+
test('dimension expressions expanded with parens properly', async () => {
|
|
679
|
+
await expect(runtime).queryMatches(
|
|
680
|
+
`
|
|
681
|
+
sql: tbl is { connection: "${databaseName}" select: """SELECT 1 as n"""}
|
|
682
|
+
query: from_sql(tbl) + {
|
|
683
|
+
dimension: fot is (false) or (true)
|
|
684
|
+
} -> {
|
|
685
|
+
project:
|
|
686
|
+
no_paren is false and fot
|
|
687
|
+
paren is false and (fot)
|
|
688
|
+
}
|
|
689
|
+
`,
|
|
690
|
+
{paren: false, no_paren: false}
|
|
691
|
+
);
|
|
692
|
+
});
|
|
693
|
+
});
|
|
694
|
+
|
|
695
|
+
afterAll(async () => {
|
|
696
|
+
await runtimes.closeAll();
|
|
697
|
+
});
|
|
698
|
+
};
|
|
699
|
+
|
|
700
|
+
/*
|
|
701
|
+
* This test file reuses common tests definitions.
|
|
702
|
+
* For actual test development please go to: test/src/databases/shared/expr.spec.ts
|
|
703
|
+
*/
|
|
704
|
+
exprSharedTests(runtimes);
|