@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.
Files changed (169) hide show
  1. package/README.md +29 -0
  2. package/bin/dump_malloytest.sh +1 -0
  3. package/bin/postgres_init.sh +9 -0
  4. package/bin/postgres_start.sh +2 -0
  5. package/bin/postgres_stop.sh +2 -0
  6. package/data/duckdb/README.md +4 -0
  7. package/data/duckdb/aircraft.parquet +0 -0
  8. package/data/duckdb/aircraft_models.parquet +0 -0
  9. package/data/duckdb/airports.parquet +0 -0
  10. package/data/duckdb/alltypes.parquet +0 -0
  11. package/data/duckdb/alltypes2.parquet +0 -0
  12. package/data/duckdb/bq_medicare_test.parquet +0 -0
  13. package/data/duckdb/carriers.parquet +0 -0
  14. package/data/duckdb/flights/part.0.parquet +0 -0
  15. package/data/duckdb/flights/part.1.parquet +0 -0
  16. package/data/duckdb/flights/part.2.parquet +0 -0
  17. package/data/duckdb/flights.parquet +0 -0
  18. package/data/duckdb/flights_partitioned.parquet +0 -0
  19. package/data/duckdb/ga_sample.parquet +0 -0
  20. package/data/duckdb/numbers.parquet +0 -0
  21. package/data/duckdb/state_facts.parquet +0 -0
  22. package/data/duckdb/test.json +3 -0
  23. package/data/duckdb/words.parquet +0 -0
  24. package/data/duckdb/words_bigger.parquet +0 -0
  25. package/data/postgres/malloytest-postgres.sql.gz +0 -0
  26. package/data/postgres/state_facts.sql +366 -0
  27. package/dist/api.spec.d.ts +1 -0
  28. package/dist/api.spec.js +77 -0
  29. package/dist/api.spec.js.map +1 -0
  30. package/dist/databases/all/expr.spec.d.ts +3 -0
  31. package/dist/databases/all/expr.spec.js +581 -0
  32. package/dist/databases/all/expr.spec.js.map +1 -0
  33. package/dist/databases/all/functions.spec.d.ts +2 -0
  34. package/dist/databases/all/functions.spec.js +784 -0
  35. package/dist/databases/all/functions.spec.js.map +1 -0
  36. package/dist/databases/all/index.spec.d.ts +2 -0
  37. package/dist/databases/all/index.spec.js +154 -0
  38. package/dist/databases/all/index.spec.js.map +1 -0
  39. package/dist/databases/all/join.spec.d.ts +2 -0
  40. package/dist/databases/all/join.spec.js +309 -0
  41. package/dist/databases/all/join.spec.js.map +1 -0
  42. package/dist/databases/all/nomodel.spec.d.ts +2 -0
  43. package/dist/databases/all/nomodel.spec.js +957 -0
  44. package/dist/databases/all/nomodel.spec.js.map +1 -0
  45. package/dist/databases/all/orderby.spec.d.ts +2 -0
  46. package/dist/databases/all/orderby.spec.js +224 -0
  47. package/dist/databases/all/orderby.spec.js.map +1 -0
  48. package/dist/databases/all/problems.spec.d.ts +2 -0
  49. package/dist/databases/all/problems.spec.js +113 -0
  50. package/dist/databases/all/problems.spec.js.map +1 -0
  51. package/dist/databases/all/sql_expressions.spec.d.ts +2 -0
  52. package/dist/databases/all/sql_expressions.spec.js +96 -0
  53. package/dist/databases/all/sql_expressions.spec.js.map +1 -0
  54. package/dist/databases/all/time.spec.d.ts +3 -0
  55. package/dist/databases/all/time.spec.js +647 -0
  56. package/dist/databases/all/time.spec.js.map +1 -0
  57. package/dist/databases/bigquery/double_truncation.spec.d.ts +1 -0
  58. package/dist/databases/bigquery/double_truncation.spec.js +50 -0
  59. package/dist/databases/bigquery/double_truncation.spec.js.map +1 -0
  60. package/dist/databases/bigquery/handexpr.spec.d.ts +1 -0
  61. package/dist/databases/bigquery/handexpr.spec.js +713 -0
  62. package/dist/databases/bigquery/handexpr.spec.js.map +1 -0
  63. package/dist/databases/bigquery/injestion_time_partitioning.spec.d.ts +1 -0
  64. package/dist/databases/bigquery/injestion_time_partitioning.spec.js +235 -0
  65. package/dist/databases/bigquery/injestion_time_partitioning.spec.js.map +1 -0
  66. package/dist/databases/bigquery/joined_filters.spec.d.ts +1 -0
  67. package/dist/databases/bigquery/joined_filters.spec.js +72 -0
  68. package/dist/databases/bigquery/joined_filters.spec.js.map +1 -0
  69. package/dist/databases/bigquery/json.spec.d.ts +1 -0
  70. package/dist/databases/bigquery/json.spec.js +86 -0
  71. package/dist/databases/bigquery/json.spec.js.map +1 -0
  72. package/dist/databases/bigquery/malloy_query.spec.d.ts +1 -0
  73. package/dist/databases/bigquery/malloy_query.spec.js +906 -0
  74. package/dist/databases/bigquery/malloy_query.spec.js.map +1 -0
  75. package/dist/databases/bigquery/performance.skipped.spec.d.ts +1 -0
  76. package/dist/databases/bigquery/performance.skipped.spec.js +70 -0
  77. package/dist/databases/bigquery/performance.skipped.spec.js.map +1 -0
  78. package/dist/databases/bigquery/time.spec.d.ts +1 -0
  79. package/dist/databases/bigquery/time.spec.js +55 -0
  80. package/dist/databases/bigquery/time.spec.js.map +1 -0
  81. package/dist/databases/bigquery/wildcard_table_names.spec.d.ts +1 -0
  82. package/dist/databases/bigquery/wildcard_table_names.spec.js +212 -0
  83. package/dist/databases/bigquery/wildcard_table_names.spec.js.map +1 -0
  84. package/dist/databases/bigquery-duckdb/nested_source_table.spec.d.ts +1 -0
  85. package/dist/databases/bigquery-duckdb/nested_source_table.spec.js +247 -0
  86. package/dist/databases/bigquery-duckdb/nested_source_table.spec.js.map +1 -0
  87. package/dist/databases/bigquery-postgres/multi_connection.spec.d.ts +1 -0
  88. package/dist/databases/bigquery-postgres/multi_connection.spec.js +138 -0
  89. package/dist/databases/bigquery-postgres/multi_connection.spec.js.map +1 -0
  90. package/dist/databases/bigquery-postgres/streaming.spec.d.ts +1 -0
  91. package/dist/databases/bigquery-postgres/streaming.spec.js +88 -0
  92. package/dist/databases/bigquery-postgres/streaming.spec.js.map +1 -0
  93. package/dist/databases/duckdb/duckdb.spec.d.ts +1 -0
  94. package/dist/databases/duckdb/duckdb.spec.js +116 -0
  95. package/dist/databases/duckdb/duckdb.spec.js.map +1 -0
  96. package/dist/databases/postgres/postgres.spec.d.ts +1 -0
  97. package/dist/databases/postgres/postgres.spec.js +137 -0
  98. package/dist/databases/postgres/postgres.spec.js.map +1 -0
  99. package/dist/databases/shared/test_list.d.ts +3 -0
  100. package/dist/databases/shared/test_list.js +24 -0
  101. package/dist/databases/shared/test_list.js.map +1 -0
  102. package/dist/dependencies.spec.d.ts +1 -0
  103. package/dist/dependencies.spec.js +63 -0
  104. package/dist/dependencies.spec.js.map +1 -0
  105. package/dist/index.d.ts +11 -0
  106. package/dist/index.js +49 -0
  107. package/dist/index.js.map +1 -0
  108. package/dist/model/utils.spec.d.ts +1 -0
  109. package/dist/model/utils.spec.js +38 -0
  110. package/dist/model/utils.spec.js.map +1 -0
  111. package/dist/models/faa_model.d.ts +5 -0
  112. package/dist/models/faa_model.js +977 -0
  113. package/dist/models/faa_model.js.map +1 -0
  114. package/dist/models/medicare_model.d.ts +4 -0
  115. package/dist/models/medicare_model.js +243 -0
  116. package/dist/models/medicare_model.js.map +1 -0
  117. package/dist/render/render.spec.d.ts +1 -0
  118. package/dist/render/render.spec.js +551 -0
  119. package/dist/render/render.spec.js.map +1 -0
  120. package/dist/runtimes.d.ts +33 -0
  121. package/dist/runtimes.js +147 -0
  122. package/dist/runtimes.js.map +1 -0
  123. package/dist/tags.spec.d.ts +1 -0
  124. package/dist/tags.spec.js +259 -0
  125. package/dist/tags.spec.js.map +1 -0
  126. package/dist/util/db-jest-matchers.d.ts +19 -0
  127. package/dist/util/db-jest-matchers.js +90 -0
  128. package/dist/util/db-jest-matchers.js.map +1 -0
  129. package/dist/util/index.d.ts +16 -0
  130. package/dist/util/index.js +171 -0
  131. package/dist/util/index.js.map +1 -0
  132. package/package.json +35 -0
  133. package/src/api.spec.ts +84 -0
  134. package/src/databases/all/expr.spec.ts +704 -0
  135. package/src/databases/all/functions.spec.ts +1135 -0
  136. package/src/databases/all/index.spec.ts +178 -0
  137. package/src/databases/all/join.spec.ts +350 -0
  138. package/src/databases/all/nomodel.spec.ts +1159 -0
  139. package/src/databases/all/orderby.spec.ts +268 -0
  140. package/src/databases/all/problems.spec.ts +121 -0
  141. package/src/databases/all/sql_expressions.spec.ts +107 -0
  142. package/src/databases/all/time.spec.ts +793 -0
  143. package/src/databases/bigquery/double_truncation.spec.ts +51 -0
  144. package/src/databases/bigquery/handexpr.spec.ts +782 -0
  145. package/src/databases/bigquery/injestion_time_partitioning.spec.ts +268 -0
  146. package/src/databases/bigquery/joined_filters.spec.ts +75 -0
  147. package/src/databases/bigquery/json.spec.ts +95 -0
  148. package/src/databases/bigquery/malloy_query.spec.ts +1036 -0
  149. package/src/databases/bigquery/performance.skipped.spec.ts +73 -0
  150. package/src/databases/bigquery/time.spec.ts +59 -0
  151. package/src/databases/bigquery/wildcard_table_names.spec.ts +233 -0
  152. package/src/databases/bigquery-duckdb/nested_source_table.spec.ts +268 -0
  153. package/src/databases/bigquery-postgres/multi_connection.spec.ts +143 -0
  154. package/src/databases/bigquery-postgres/streaming.spec.ts +100 -0
  155. package/src/databases/duckdb/duckdb.spec.ts +131 -0
  156. package/src/databases/postgres/postgres.spec.ts +167 -0
  157. package/src/databases/shared/test_list.ts +24 -0
  158. package/src/dependencies.spec.ts +62 -0
  159. package/src/index.ts +36 -0
  160. package/src/model/utils.spec.ts +37 -0
  161. package/src/models/faa_model.ts +995 -0
  162. package/src/models/medicare_model.ts +246 -0
  163. package/src/render/__snapshots__/render.spec.ts.snap +12574 -0
  164. package/src/render/render.spec.ts +670 -0
  165. package/src/runtimes.ts +182 -0
  166. package/src/tags.spec.ts +277 -0
  167. package/src/util/db-jest-matchers.ts +128 -0
  168. package/src/util/index.ts +191 -0
  169. package/tsconfig.json +25 -0
@@ -0,0 +1,1036 @@
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 {test} from '@jest/globals';
25
+ import * as malloy from '@malloydata/malloy';
26
+ import {Query} from '@malloydata/malloy';
27
+ import {testModel} from '../../models/faa_model';
28
+ import {BigQueryTestConnection, RuntimeList} from '../../runtimes';
29
+ import {describeIfDatabaseAvailable, fStringEq} from '../../util';
30
+
31
+ const runtimeList = new RuntimeList(['bigquery']);
32
+ const runtime = runtimeList.runtimeMap.get('bigquery');
33
+ if (runtime === undefined) {
34
+ throw new Error("Couldn't build runtime");
35
+ }
36
+ const bq = runtime.connection as BigQueryTestConnection;
37
+
38
+ function compileQueryFromQueryDef(
39
+ model: malloy.ModelMaterializer,
40
+ query: Query
41
+ ) {
42
+ return model._loadQueryFromQueryDef(query).getSQL();
43
+ }
44
+
45
+ async function compileQuery(model: malloy.ModelMaterializer, query: string) {
46
+ return await model.loadQuery(query).getSQL();
47
+ }
48
+
49
+ async function runQuery(model: malloy.ModelMaterializer, query: string) {
50
+ return await model.loadQuery(query).run();
51
+ }
52
+
53
+ async function bqCompile(sql: string): Promise<boolean> {
54
+ try {
55
+ await bq.executeSQLRaw(`WITH test AS(\n${sql}) SELECT 1`);
56
+ } catch (e) {
57
+ malloy.Malloy.log.error(`SQL: didn't compile\n=============\n${sql}`);
58
+ throw e;
59
+ }
60
+ return true;
61
+ }
62
+
63
+ const [describe] = describeIfDatabaseAvailable(['bigquery']);
64
+
65
+ describe('BigQuery expression tests', () => {
66
+ const faa = runtime._loadModelFromModelDef(testModel);
67
+
68
+ it('simple_pipeline', async () => {
69
+ const sql = await compileQueryFromQueryDef(faa, {
70
+ structRef: 'flights',
71
+ pipeHead: {name: 'flights_by_carrier'},
72
+ pipeline: [{fields: ['name', 'flight_count'], type: 'reduce'}],
73
+ });
74
+ await bqCompile(sql);
75
+ });
76
+
77
+ // EXPLORE flights
78
+ // ->{
79
+ // carrier,
80
+ // flight_count,
81
+ // routes is { origin_code, destination_code, route_flights is flight_count
82
+ // ORDER BY route_flights DESC
83
+ // LIMIT 5 )
84
+ // | PROJECT
85
+ // carrier,
86
+ // routes.origin_code,
87
+ // routes.route_flights,
88
+ // flight_count / routes.route_flights as percent_of_carrier_flights
89
+ it('turtle_requery', async () => {
90
+ const sql = await compileQueryFromQueryDef(faa, {
91
+ structRef: 'flights',
92
+ pipeline: [
93
+ // top 5 routes per carrier
94
+ {
95
+ type: 'reduce',
96
+ fields: [
97
+ 'carrier',
98
+ 'flight_count',
99
+ {
100
+ type: 'turtle',
101
+ name: 'routes',
102
+ pipeline: [
103
+ {
104
+ type: 'reduce',
105
+ fields: [
106
+ 'origin_code',
107
+ 'destination_code',
108
+ 'flight_count',
109
+ {
110
+ type: 'number',
111
+ name: 'route_flights',
112
+ expressionType: 'aggregate',
113
+ e: [
114
+ {
115
+ type: 'aggregate',
116
+ function: 'count',
117
+ e: [],
118
+ },
119
+ ],
120
+ },
121
+ ],
122
+ },
123
+ ],
124
+ },
125
+ ],
126
+ limit: 5,
127
+ orderBy: [{dir: 'desc', field: 'carrier'}],
128
+ },
129
+ // carrier top routes
130
+ {
131
+ type: 'project',
132
+ fields: [
133
+ 'carrier',
134
+ 'flight_count',
135
+ 'routes.origin_code',
136
+ 'routes.route_flights',
137
+ ],
138
+ },
139
+ ],
140
+ });
141
+ await bqCompile(sql);
142
+ });
143
+
144
+ it('step_0', async () => {
145
+ const sql = await compileQueryFromQueryDef(faa, {
146
+ structRef: 'flights',
147
+ pipeline: [{type: 'reduce', fields: ['carriers.name', 'flight_count']}],
148
+ });
149
+ await bqCompile(sql);
150
+ });
151
+
152
+ it('filtered_measures', async () => {
153
+ const sql = await compileQueryFromQueryDef(faa, {
154
+ structRef: 'flights',
155
+ filterList: [
156
+ fStringEq('origin.state', 'CA'),
157
+ fStringEq('destination.state', 'NY'),
158
+ ],
159
+ pipeline: [{type: 'reduce', fields: ['carriers.name', 'flight_count']}],
160
+ });
161
+ await bqCompile(sql);
162
+ });
163
+
164
+ it('timestamp', async () => {
165
+ const sql = await compileQueryFromQueryDef(faa, {
166
+ structRef: 'flights',
167
+ pipeline: [
168
+ {
169
+ fields: [
170
+ {
171
+ as: 'dep_year',
172
+ name: 'dep_time',
173
+ timeframe: 'year',
174
+ type: 'timestamp',
175
+ },
176
+ {
177
+ as: 'dep_month',
178
+ name: 'dep_time',
179
+ timeframe: 'month',
180
+ type: 'timestamp',
181
+ },
182
+ {
183
+ as: 'dep_week',
184
+ name: 'dep_time',
185
+ timeframe: 'week',
186
+ type: 'timestamp',
187
+ },
188
+ {
189
+ as: 'dep_date',
190
+ name: 'dep_time',
191
+ timeframe: 'day',
192
+ type: 'timestamp',
193
+ },
194
+ {
195
+ as: 'dep_hour',
196
+ name: 'dep_time',
197
+ timeframe: 'hour',
198
+ type: 'timestamp',
199
+ },
200
+ {
201
+ as: 'dep_minute',
202
+ name: 'dep_time',
203
+ timeframe: 'minute',
204
+ type: 'timestamp',
205
+ },
206
+ {
207
+ as: 'dep_second',
208
+ name: 'dep_time',
209
+ timeframe: 'second',
210
+ type: 'timestamp',
211
+ },
212
+ {
213
+ type: 'number',
214
+ name: 'total_distance_ca',
215
+ expressionType: 'aggregate',
216
+ e: [
217
+ {
218
+ type: 'filterExpression',
219
+ filterList: [fStringEq('origin.state', 'CA')],
220
+ e: [
221
+ {
222
+ type: 'aggregate',
223
+ function: 'sum',
224
+ e: [{type: 'field', path: 'distance'}],
225
+ },
226
+ ],
227
+ },
228
+ ],
229
+ },
230
+ ],
231
+ limit: 20,
232
+ type: 'reduce',
233
+ },
234
+ ],
235
+ });
236
+ await bqCompile(sql);
237
+ });
238
+
239
+ it('bucket_test', async () => {
240
+ const sql = await compileQueryFromQueryDef(faa, {
241
+ pipeline: [
242
+ {
243
+ fields: [
244
+ {
245
+ bucketFilter: 'AA,WN,DL',
246
+ bucketOther: 'Other Carrier',
247
+ name: 'carrier',
248
+ type: 'string',
249
+ },
250
+ 'flight_count',
251
+ ],
252
+ orderBy: [{dir: 'asc', field: 2}],
253
+ type: 'reduce',
254
+ },
255
+ ],
256
+ structRef: 'flights',
257
+ });
258
+ await bqCompile(sql);
259
+ });
260
+
261
+ it('flights_by_carrier', async () => {
262
+ const sql = await compileQuery(faa, 'query: flights->flights_by_carrier');
263
+ await bqCompile(sql);
264
+ });
265
+
266
+ it('simple_reduce', async () => {
267
+ const sql = await compileQueryFromQueryDef(faa, {
268
+ structRef: 'flights',
269
+ pipeline: [{type: 'reduce', fields: ['carrier', 'flight_count']}],
270
+ });
271
+ await bqCompile(sql);
272
+ });
273
+
274
+ it('two_sums', async () => {
275
+ const sql = await compileQueryFromQueryDef(faa, {
276
+ structRef: 'flights',
277
+ pipeline: [
278
+ {
279
+ type: 'reduce',
280
+ fields: [
281
+ {
282
+ type: 'number',
283
+ expressionType: 'aggregate',
284
+ name: 'total_distance',
285
+ e: [
286
+ {
287
+ type: 'aggregate',
288
+ function: 'sum',
289
+ e: [{type: 'field', path: 'distance'}],
290
+ },
291
+ ],
292
+ },
293
+ 'aircraft.aircraft_models.total_seats',
294
+ ],
295
+ },
296
+ ],
297
+ });
298
+ await bqCompile(sql);
299
+ });
300
+
301
+ it('first_fragment', async () => {
302
+ const sql = await compileQueryFromQueryDef(faa, {
303
+ structRef: 'flights',
304
+ pipeline: [
305
+ {
306
+ type: 'reduce',
307
+ fields: [
308
+ {
309
+ type: 'string',
310
+ name: 'carrier',
311
+ e: ['UPPER(', {type: 'field', path: 'carriers.nickname'}, ')'],
312
+ },
313
+ 'flight_count',
314
+ ],
315
+ },
316
+ ],
317
+ });
318
+ await bqCompile(sql);
319
+ });
320
+
321
+ it('sum_in_expr', async () => {
322
+ const sql = await compileQueryFromQueryDef(faa, {
323
+ structRef: 'flights',
324
+ pipeline: [
325
+ {
326
+ fields: [
327
+ 'carriers.name',
328
+ {
329
+ type: 'number',
330
+ expressionType: 'aggregate',
331
+ name: 'total_distance',
332
+ e: [
333
+ {
334
+ type: 'aggregate',
335
+ function: 'sum',
336
+ e: [{type: 'field', path: 'distance'}],
337
+ },
338
+ ],
339
+ },
340
+ ],
341
+ type: 'reduce',
342
+ },
343
+ ],
344
+ });
345
+ await bqCompile(sql);
346
+ });
347
+
348
+ it('filtered_sum_in_expr', async () => {
349
+ const sql = await compileQueryFromQueryDef(faa, {
350
+ structRef: 'flights',
351
+ pipeline: [
352
+ {
353
+ type: 'reduce',
354
+ fields: [
355
+ 'aircraft.aircraft_models.manufacturer',
356
+ {
357
+ type: 'number',
358
+ expressionType: 'aggregate',
359
+ name: 'total_distance',
360
+ e: [
361
+ {
362
+ type: 'filterExpression',
363
+ filterList: [fStringEq('origin_code', 'SFO')],
364
+ e: [
365
+ {
366
+ type: 'aggregate',
367
+ function: 'sum',
368
+ e: [{type: 'field', path: 'distance'}],
369
+ },
370
+ ],
371
+ },
372
+ ],
373
+ },
374
+ ],
375
+ },
376
+ ],
377
+ });
378
+ await bqCompile(sql);
379
+ });
380
+
381
+ it('dynamic_measure', async () => {
382
+ const sql = await compileQueryFromQueryDef(faa, {
383
+ structRef: 'flights',
384
+ pipeline: [
385
+ {
386
+ type: 'reduce',
387
+ fields: [
388
+ 'origin.state',
389
+ 'flight_count',
390
+ {
391
+ type: 'number',
392
+ expressionType: 'aggregate',
393
+ name: 'total_distance',
394
+ e: [
395
+ {
396
+ type: 'filterExpression',
397
+ filterList: [fStringEq('origin_code', 'SFO')],
398
+ e: [
399
+ {
400
+ type: 'aggregate',
401
+ function: 'sum',
402
+ e: [{type: 'field', path: 'distance'}],
403
+ },
404
+ ],
405
+ },
406
+ ],
407
+ },
408
+ ],
409
+ filterList: [fStringEq('carriers.code', 'WN')],
410
+ },
411
+ ],
412
+ });
413
+ await bqCompile(sql);
414
+ });
415
+
416
+ it('add_filter_to_named_query', async () => {
417
+ const sql = await compileQueryFromQueryDef(faa, {
418
+ structRef: 'flights',
419
+ filterList: [fStringEq('destination_code', 'AL')],
420
+ pipeHead: {name: 'flights_by_city_top_5'},
421
+ pipeline: [],
422
+ });
423
+ await bqCompile(sql);
424
+ });
425
+
426
+ it('flights.flights_by_model', async () => {
427
+ const sql = await compileQuery(faa, 'query: flights->flights_by_model');
428
+ await bqCompile(sql);
429
+ });
430
+
431
+ it('flights.aircraft_facts_test', async () => {
432
+ const sql = await compileQuery(faa, 'query: flights->aircraft_facts_test');
433
+ await bqCompile(sql);
434
+ });
435
+
436
+ it('flights.measures_first', async () => {
437
+ const sql = await compileQuery(faa, 'query:flights->measures_first');
438
+ await bqCompile(sql);
439
+ });
440
+
441
+ it('flights.carriers_by_total_engines', async () => {
442
+ const sql = await compileQuery(
443
+ faa,
444
+ 'query: flights->carriers_by_total_engines'
445
+ );
446
+ await bqCompile(sql);
447
+ });
448
+
449
+ it('flights.first_turtle', async () => {
450
+ const sql = await compileQuery(faa, 'query: flights->first_turtle');
451
+ await bqCompile(sql);
452
+ });
453
+
454
+ it('flights.top_5_routes_carriers', async () => {
455
+ const sql = await compileQuery(
456
+ faa,
457
+ 'query: flights->top_5_routes_carriers'
458
+ );
459
+ await bqCompile(sql);
460
+ });
461
+
462
+ it('flights.new_york_airports', async () => {
463
+ const sql = await compileQuery(faa, 'query: flights->new_york_airports');
464
+ await bqCompile(sql);
465
+ });
466
+
467
+ it('flights.flights_by_carrier_with_totals', async () => {
468
+ const sql = await compileQuery(
469
+ faa,
470
+ 'query: flights->flights_by_carrier_with_totals'
471
+ );
472
+ await bqCompile(sql);
473
+ });
474
+
475
+ it('lotsoturtles', async () => {
476
+ const sql = await compileQueryFromQueryDef(faa, {
477
+ structRef: 'flights',
478
+ pipeline: [
479
+ {
480
+ fields: [
481
+ 'origin.state',
482
+ 'flight_count',
483
+ 'flights_by_model',
484
+ 'flights_by_carrier',
485
+ 'measures_first',
486
+ 'first_turtle',
487
+ ],
488
+ type: 'reduce',
489
+ },
490
+ ],
491
+ });
492
+ await bqCompile(sql);
493
+ });
494
+
495
+ it('add_filter_to_def', async () => {
496
+ const sql = await compileQueryFromQueryDef(faa, {
497
+ structRef: 'flights',
498
+ filterList: [fStringEq('destination_code', 'AL')],
499
+ pipeHead: {name: 'flights_by_carrier_with_totals'},
500
+ pipeline: [
501
+ {
502
+ type: 'reduce',
503
+ fields: [
504
+ 'main.name',
505
+ 'main.flight_count',
506
+ {
507
+ name: 'total_flights',
508
+ type: 'number',
509
+ expressionType: 'scalar',
510
+ e: [{type: 'field', path: 'totals.flight_count'}],
511
+ },
512
+ ],
513
+ },
514
+ ],
515
+ });
516
+ await bqCompile(sql);
517
+ });
518
+
519
+ it('flights.search_index', async () => {
520
+ const sql = await compileQuery(faa, 'query: flights->search_index');
521
+ await bqCompile(sql);
522
+ });
523
+
524
+ it('turtle_turtle_filter', async () => {
525
+ const result = await runQuery(
526
+ faa,
527
+ `
528
+ query: table('malloytest.airports')->{
529
+ where: faa_region ? ~'A%'
530
+ order_by: 1
531
+ group_by: faa_region
532
+ aggregate: airport_count is COUNT(*)
533
+ nest: state is {
534
+ where: state ?'CA'|'NY'
535
+ group_by: state
536
+ nest: code is {
537
+ where: major='Y'
538
+ top: 10
539
+ order_by: 1
540
+ group_by: code
541
+ }
542
+ }
543
+ }
544
+ -> {
545
+ where: state.code.code != null
546
+ group_by: state.code.code
547
+ }
548
+ `
549
+ );
550
+ expect(result.data.value[0]['code']).toBe('ACV');
551
+ });
552
+
553
+ it('flights.search_index', async () => {
554
+ const sql = await compileQuery(faa, 'query: flights->search_index');
555
+ await bqCompile(sql);
556
+ });
557
+
558
+ it('medicare_test.turtle_city_zip', async () => {
559
+ const sql = await compileQuery(
560
+ faa,
561
+ 'query: medicare_test->turtle_city_zip'
562
+ );
563
+ await bqCompile(sql);
564
+ });
565
+
566
+ it('medicare_test.triple_turtle', async () => {
567
+ const sql = await compileQuery(faa, 'query: medicare_test->triple_turtle');
568
+ await bqCompile(sql);
569
+ });
570
+
571
+ it('medicare_test.rollup_by_location', async () => {
572
+ const sql = await compileQuery(
573
+ faa,
574
+ 'query: medicare_test->rollup_by_location'
575
+ );
576
+ await bqCompile(sql);
577
+ });
578
+
579
+ it('flights.flights_routes_sessionized', async () => {
580
+ const sql = await compileQuery(
581
+ faa,
582
+ 'query: flights->flights_routes_sessionized'
583
+ );
584
+ await bqCompile(sql);
585
+ });
586
+
587
+ it('flights.flights_aircraft_sessionized', async () => {
588
+ const sql = await compileQuery(
589
+ faa,
590
+ 'query: flights->flights_aircraft_sessionized'
591
+ );
592
+ await bqCompile(sql);
593
+ });
594
+
595
+ it('flights.flights_by_manufacturer', async () => {
596
+ const sql = await compileQuery(
597
+ faa,
598
+ 'query: flights->flights_by_manufacturer'
599
+ );
600
+ await bqCompile(sql);
601
+ });
602
+
603
+ it('flights.flights_by_carrier_2001_2002', async () => {
604
+ const sql = await compileQuery(
605
+ faa,
606
+ 'query: flights->flights_by_carrier_2001_2002'
607
+ );
608
+ await bqCompile(sql);
609
+ });
610
+
611
+ it('timeframes aliased', async () => {
612
+ const sql = await compileQuery(
613
+ faa,
614
+ `
615
+ query: flights->{
616
+ group_by: mon is dep_time.month
617
+ }
618
+ `
619
+ );
620
+ await bqCompile(sql);
621
+ });
622
+
623
+ it('count distinct', async () => {
624
+ const sql = await compileQuery(
625
+ faa,
626
+ `
627
+ query: flights->{
628
+ aggregate: carrier_count is count(distinct carrier)
629
+ }
630
+ `
631
+ );
632
+ // console.log(result.sql);
633
+ await bqCompile(sql);
634
+ });
635
+
636
+ it('table_base_on_query', async () => {
637
+ const result = await faa
638
+ ._loadQueryFromQueryDef({
639
+ structRef: 'medicare_state_facts',
640
+ pipeline: [
641
+ {
642
+ type: 'reduce',
643
+ fields: ['provider_state', 'num_providers'],
644
+ orderBy: [{dir: 'desc', field: 2}],
645
+ },
646
+ ],
647
+ })
648
+ .run();
649
+ expect(result.data.value[0]['num_providers']).toBe(296);
650
+ });
651
+
652
+ // const faa2: TestDeclaration[] = [
653
+
654
+ it('table_base_on_query2', async () => {
655
+ const result = await faa
656
+ ._loadQueryFromQueryDef({
657
+ structRef: {
658
+ type: 'struct',
659
+ name: 'malloy-data.malloytest.bq_medicare_test',
660
+ dialect: 'standardsql',
661
+ as: 'mtest',
662
+ structRelationship: {
663
+ type: 'basetable',
664
+ connectionName: 'bigquery',
665
+ },
666
+ structSource: {
667
+ type: 'table',
668
+ tablePath: 'malloy-data.malloytest.bq_medicare_test',
669
+ },
670
+ fields: [
671
+ {
672
+ type: 'number',
673
+ name: 'c',
674
+ expressionType: 'aggregate',
675
+ e: [{type: 'aggregate', function: 'count', e: []}],
676
+ },
677
+ {
678
+ type: 'turtle',
679
+ name: 'get_count',
680
+ pipeline: [{type: 'reduce', fields: ['c']}],
681
+ },
682
+ ],
683
+ },
684
+ pipeHead: {name: 'get_count'},
685
+ pipeline: [],
686
+ })
687
+ .run();
688
+ expect(result.data.value[0]['c']).toBe(202656);
689
+ });
690
+ });
691
+
692
+ const airportModelText = `
693
+ source: airports is table('malloy-data.malloytest.airports'){
694
+ primary_key: code
695
+ measure: airport_count is count(*)
696
+
697
+ query: by_fac_type is {
698
+ group_by: fac_type
699
+ aggregate: airport_count
700
+ }
701
+
702
+ query: by_state is {
703
+ group_by: state
704
+ aggregate: airport_count
705
+ }
706
+
707
+ query: by_county is {
708
+ group_by: county
709
+ aggregate: airport_count
710
+ }
711
+ }
712
+
713
+ query: ca_airports is airports->by_fac_type{? state ? 'CA' | 'NY'}
714
+ `;
715
+
716
+ describe('airport_tests', () => {
717
+ let model: malloy.ModelMaterializer;
718
+ beforeAll(async () => {
719
+ model = runtime.loadModel(airportModelText);
720
+ });
721
+
722
+ it('airport_count', async () => {
723
+ const result = await runQuery(
724
+ model,
725
+ `
726
+ query: airports->{
727
+ aggregate: a is count(*)
728
+ }
729
+ `
730
+ );
731
+ expect(result.data.value[0]['a']).toBe(19793);
732
+ });
733
+
734
+ it('turtle_from_hell', async () => {
735
+ const result = await runQuery(
736
+ model,
737
+ `
738
+ query: airports-> {
739
+ nest: zero is {
740
+ nest: by_faa_region_i is { where: county ~'I%' and state != NULL
741
+ group_by: faa_region
742
+ aggregate: airport_count
743
+ nest: by_state is {
744
+ group_by: state
745
+ aggregate: airport_count
746
+ nest: by_county is {
747
+ group_by: county
748
+ aggregate: airport_count
749
+ }
750
+ }
751
+ }
752
+ nest: by_faa_region_Z is { where: county ~'Z%' and state !=NULL
753
+ group_by: faa_region
754
+ aggregate: airport_count
755
+ nest: by_state is {
756
+ group_by: state
757
+ aggregate: airport_count
758
+ nest: by_county is {
759
+ group_by: county
760
+ aggregate: airport_count
761
+ }
762
+ }
763
+ }
764
+ }
765
+ } -> { limit: 1
766
+ project: zero.by_faa_region_Z.by_state.by_county.county
767
+ }
768
+
769
+ `
770
+ );
771
+ expect(result.data.value[0]['county']).toBe('ZAVALA');
772
+ });
773
+
774
+ it('nested_project', async () => {
775
+ const result = await runQuery(
776
+ model,
777
+ `
778
+ query: airports -> {
779
+ group_by: county
780
+ nest: stuff is {
781
+ project: elevation
782
+ order_by: 1 desc
783
+ limit: 10
784
+ }
785
+ order_by: 1
786
+ } -> {
787
+ project: stuff.elevation
788
+ limit: 1
789
+ }
790
+ `
791
+ );
792
+ expect(result.data.value[0]['elevation']).toBe(1836);
793
+ });
794
+
795
+ it('nested_sums', async () => {
796
+ const result = await runQuery(
797
+ model,
798
+ `
799
+ query: airports->{
800
+ aggregate: airport_count
801
+ nest: by_state is {
802
+ group_by: state
803
+ aggregate: airport_count
804
+ nest: by_fac_type is {
805
+ group_by: fac_type
806
+ aggregate: airport_count
807
+ }
808
+ }
809
+ } -> {
810
+ group_by: airport_count
811
+ aggregate:
812
+ sum_state is by_state.sum(by_state.airport_count),
813
+ sum_fac is by_state.by_fac_type.sum(by_state.by_fac_type.airport_count)
814
+ }
815
+ `
816
+ );
817
+ // console.log(result.sql);
818
+ expect(result.data.value[0]['sum_state']).toBe(19793);
819
+ expect(result.data.value[0]['sum_fac']).toBe(19793);
820
+ });
821
+
822
+ it('pipeline_as_declared_turtle', async () => {
823
+ const result = await runQuery(
824
+ model,
825
+ `
826
+ source: my_airports is airports {
827
+ query: pipe_turtle is {
828
+ aggregate: a is airport_count
829
+ } -> {
830
+ project: a
831
+ }
832
+ }
833
+ query: my_airports->pipe_turtle
834
+ `
835
+ );
836
+ expect(result.data.value[0]['a']).toBe(19793);
837
+ });
838
+
839
+ it('pipeline Turtle', async () => {
840
+ const result = await runQuery(
841
+ model,
842
+ `
843
+ query: table('malloytest.airports')->{
844
+ aggregate: airport_count is count()
845
+ nest: pipe_turtle is {
846
+ group_by:
847
+ state
848
+ county
849
+ aggregate: a is count()
850
+ } -> {
851
+ project:
852
+ state is upper(state)
853
+ a
854
+ } -> {
855
+ group_by: state
856
+ aggregate: total_airports is a.sum()
857
+ }
858
+ }
859
+ `
860
+ );
861
+
862
+ expect(
863
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
864
+ (result.data.value[0] as any).pipe_turtle[0].total_airports
865
+ ).toBe(1845);
866
+ });
867
+
868
+ it.skip('crossjoined turtles', async () => {
869
+ // const result = await runQuery(model,`
870
+ // explore airports
871
+ // ->{
872
+ // top_seaplane is { limit 5 : [fac_type: 'SEAPLANE BASE']
873
+ // state
874
+ // airport_count
875
+ // )
876
+ // by_state is {
877
+ // state
878
+ // airport_count
879
+ // )
880
+ // | project : [top_seaplane.state = by_state.state]
881
+ // by_state.*
882
+ // `);
883
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
884
+ // expect((result.data.value[0] as any).pipe_turtle[0].total_airports).toBe(1845);
885
+ });
886
+
887
+ it.skip('crossjoined turtles as turtle', async () => {
888
+ // const result = await runQuery(model,`
889
+ // explore airports
890
+ // ->{
891
+ // airport_count
892
+ // tp is {
893
+ // top_seaplane is { limit 5 : [fac_type: 'SEAPLANE BASE']
894
+ // state
895
+ // airport_count
896
+ // )
897
+ // by_state is {
898
+ // state
899
+ // airport_count
900
+ // )
901
+ // | project : [top_seaplane.state = by_state.state]
902
+ // by_state.*
903
+ // )
904
+ // `);
905
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
906
+ // expect((result.data.value[0] as any).pipe_turtle[0].total_airports).toBe(1845);
907
+ });
908
+
909
+ it('string_expressions', async () => {
910
+ const result = await runQuery(
911
+ model,
912
+ `
913
+ query: airports->{
914
+ group_by: lower_state is lower(state)
915
+ order_by: 1 DESC
916
+ limit: 10
917
+ }
918
+ `
919
+ );
920
+ expect(result.data.value[0]['lower_state']).toBe('wy');
921
+ });
922
+
923
+ it('half_count', async () => {
924
+ const result = await runQuery(
925
+ model,
926
+ `
927
+ query: airports->{
928
+ aggregate: half is airport_count/2.0
929
+ }
930
+ `
931
+ );
932
+ expect(result.data.value[0]['half']).toBe(9896.5);
933
+ });
934
+ });
935
+
936
+ describe('sql injection tests', () => {
937
+ const model = runtime._loadModelFromModelDef(testModel);
938
+ jest.setTimeout(100000);
939
+
940
+ test('string literal escapes quotes', async () => {
941
+ const result = await runQuery(
942
+ model,
943
+ `
944
+ query: table('malloytest.state_facts')->{ group_by: test is 'foo\\''
945
+ }
946
+ `
947
+ );
948
+ expect(result.data.value[0]['test']).toBe("foo'");
949
+ });
950
+
951
+ test('string filter escapes quotes', async () => {
952
+ const result = await runQuery(
953
+ model,
954
+ `
955
+ query: table('malloytest.state_facts')->{ aggregate: test is count() {? state ? 'foo\\'' } }
956
+ `
957
+ );
958
+ expect(result.data.value[0]['test']).toBe(0);
959
+ });
960
+
961
+ test('string literal escapes backslashes', async () => {
962
+ const result = await runQuery(
963
+ model,
964
+ `
965
+ query: table('malloytest.state_facts')->{ group_by: test is 'foo\\\\\\''
966
+ }
967
+ `
968
+ );
969
+ expect(result.data.value[0]['test']).toBe("foo\\'");
970
+ });
971
+
972
+ test('string filter escapes backslashes', async () => {
973
+ const result = await runQuery(
974
+ model,
975
+ `
976
+ query: table('malloytest.state_facts')->{ aggregate: test is count() {? state ? 'foo\\\\\\'' }}
977
+ `
978
+ );
979
+ expect(result.data.value[0]['test']).toBe(0);
980
+ });
981
+
982
+ test('comment in string', async () => {
983
+ const result = await runQuery(
984
+ model,
985
+ `
986
+ query: table('malloytest.state_facts')->{ group_by: test is 'foo \\\\'--'
987
+ }
988
+ `
989
+ );
990
+ expect(result.data.value[0]['test']).toBe('foo \\');
991
+ });
992
+
993
+ test('comment in string filter', async () => {
994
+ let error;
995
+ try {
996
+ await runQuery(
997
+ model,
998
+ `
999
+ query: table('malloytest.state_facts')->{ aggregate: test is count() {? state ? 'foo \\\\' THEN 0 else 1 END) as test--'
1000
+ }} `
1001
+ );
1002
+ } catch (e) {
1003
+ error = e;
1004
+ }
1005
+ expect(error).not.toBeUndefined();
1006
+ });
1007
+
1008
+ test.todo("'malloytest\\'.tables' produces the wrong error...");
1009
+
1010
+ test('comment in literal', async () => {
1011
+ const result = await runQuery(
1012
+ model,
1013
+ `
1014
+ query: flights->{ group_by: test is 'foo \\\\'--'
1015
+ }
1016
+ `
1017
+ );
1018
+ expect(result.data.value[0]['test']).toBe('foo \\');
1019
+ });
1020
+ });
1021
+
1022
+ describe('unsupported type tests', () => {
1023
+ it('can read unsupported types in schema', async () => {
1024
+ const result = await runtime
1025
+ .loadQuery(
1026
+ `
1027
+ sql: badType is {
1028
+ select: """SELECT ST_GEOGFROMTEXT('LINESTRING(1 2, 3 4)') as geo"""
1029
+ }
1030
+ query: from_sql(badType)->{ project: *}
1031
+ `
1032
+ )
1033
+ .run();
1034
+ expect(result.data.value[0]['geo']).toBeDefined();
1035
+ });
1036
+ });