@malloydata/malloy-tests 0.0.95-dev231019211822 → 0.0.95
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 +213 -1
- package/dist/api.spec.d.ts +1 -1
- package/dist/api.spec.js +6 -13
- package/dist/api.spec.js.map +1 -1
- package/dist/databases/all/db_index.spec.js +21 -41
- package/dist/databases/all/db_index.spec.js.map +1 -1
- package/dist/databases/all/expr.spec.js +262 -339
- package/dist/databases/all/expr.spec.js.map +1 -1
- package/dist/databases/all/functions.spec.js +37 -35
- package/dist/databases/all/functions.spec.js.map +1 -1
- package/dist/databases/all/join.spec.js +125 -169
- package/dist/databases/all/join.spec.js.map +1 -1
- package/dist/databases/all/nomodel.spec.js +335 -594
- package/dist/databases/all/nomodel.spec.js.map +1 -1
- package/dist/databases/all/orderby.spec.js +82 -128
- package/dist/databases/all/orderby.spec.js.map +1 -1
- package/dist/databases/all/sql_expressions.spec.js +27 -43
- package/dist/databases/all/sql_expressions.spec.js.map +1 -1
- package/dist/databases/all/time.spec.js +63 -103
- package/dist/databases/all/time.spec.js.map +1 -1
- package/dist/databases/bigquery/double_truncation.spec.js +1 -1
- package/dist/databases/bigquery/handexpr.spec.js +12 -12
- package/dist/databases/bigquery/injestion_time_partitioning.spec.js +22 -22
- package/dist/databases/bigquery/joined_filters.spec.js +3 -3
- package/dist/databases/bigquery/json.spec.d.ts +1 -1
- package/dist/databases/bigquery/json.spec.js +25 -45
- package/dist/databases/bigquery/json.spec.js.map +1 -1
- package/dist/databases/bigquery/malloy_query.spec.d.ts +1 -1
- package/dist/databases/bigquery/malloy_query.spec.js +47 -48
- package/dist/databases/bigquery/malloy_query.spec.js.map +1 -1
- package/dist/databases/bigquery/time.spec.js +9 -13
- package/dist/databases/bigquery/time.spec.js.map +1 -1
- package/dist/databases/bigquery/wildcard_table_names.spec.js +19 -19
- package/dist/databases/bigquery-duckdb/nested_source_table.spec.js +53 -87
- package/dist/databases/bigquery-duckdb/nested_source_table.spec.js.map +1 -1
- package/dist/databases/bigquery-postgres/multi_connection.spec.js +5 -20
- package/dist/databases/bigquery-postgres/multi_connection.spec.js.map +1 -1
- package/dist/databases/bigquery-postgres/streaming.spec.js +6 -6
- package/dist/databases/bigquery-postgres/streaming.spec.js.map +1 -1
- package/dist/databases/duckdb/duckdb.spec.js +24 -33
- package/dist/databases/duckdb/duckdb.spec.js.map +1 -1
- package/dist/databases/postgres/postgres.spec.js +46 -67
- package/dist/databases/postgres/postgres.spec.js.map +1 -1
- package/dist/jestMatcher.spec.d.ts +1 -0
- package/dist/jestMatcher.spec.js +81 -0
- package/dist/jestMatcher.spec.js.map +1 -0
- package/dist/render/render.spec.js +10 -12
- package/dist/render/render.spec.js.map +1 -1
- package/dist/tags.spec.js +22 -2
- package/dist/tags.spec.js.map +1 -1
- package/dist/util/db-jest-matchers.d.ts +17 -6
- package/dist/util/db-jest-matchers.js +81 -20
- package/dist/util/db-jest-matchers.js.map +1 -1
- package/dist/util/index.d.ts +1 -2
- package/dist/util/index.js +11 -13
- package/dist/util/index.js.map +1 -1
- package/package.json +6 -6
- package/src/api.spec.ts +7 -16
- package/src/databases/all/db_index.spec.ts +22 -48
- package/src/databases/all/expr.spec.ts +273 -431
- package/src/databases/all/functions.spec.ts +37 -35
- package/src/databases/all/join.spec.ts +130 -196
- package/src/databases/all/nomodel.spec.ts +333 -689
- package/src/databases/all/orderby.spec.ts +87 -161
- package/src/databases/all/sql_expressions.spec.ts +29 -49
- package/src/databases/all/time.spec.ts +73 -130
- package/src/databases/bigquery/double_truncation.spec.ts +1 -1
- package/src/databases/bigquery/handexpr.spec.ts +12 -12
- package/src/databases/bigquery/injestion_time_partitioning.spec.ts +22 -22
- package/src/databases/bigquery/joined_filters.spec.ts +3 -3
- package/src/databases/bigquery/json.spec.ts +25 -49
- package/src/databases/bigquery/malloy_query.spec.ts +47 -54
- package/src/databases/bigquery/time.spec.ts +13 -17
- package/src/databases/bigquery/wildcard_table_names.spec.ts +19 -19
- package/src/databases/bigquery-duckdb/nested_source_table.spec.ts +56 -98
- package/src/databases/bigquery-postgres/multi_connection.spec.ts +5 -23
- package/src/databases/bigquery-postgres/streaming.spec.ts +12 -6
- package/src/databases/duckdb/duckdb.spec.ts +31 -43
- package/src/databases/postgres/postgres.spec.ts +54 -84
- package/src/jestMatcher.spec.ts +88 -0
- package/src/render/render.spec.ts +10 -12
- package/src/tags.spec.ts +22 -2
- package/src/util/db-jest-matchers.ts +106 -32
- package/src/util/index.ts +16 -14
|
@@ -35,8 +35,9 @@ const [describe, databases] = describeIfDatabaseAvailable([
|
|
|
35
35
|
'duckdb',
|
|
36
36
|
]);
|
|
37
37
|
|
|
38
|
-
|
|
39
|
-
|
|
38
|
+
function modelText(databaseName: string) {
|
|
39
|
+
return `
|
|
40
|
+
source: ga_sessions is ${databaseName}.table('malloytest.ga_sample') extend {
|
|
40
41
|
|
|
41
42
|
measure:
|
|
42
43
|
user_count is count(fullVisitorId)
|
|
@@ -49,41 +50,41 @@ source:ga_sessions is table('malloytest.ga_sample'){
|
|
|
49
50
|
hits_count is hits.count()
|
|
50
51
|
sold_count is hits.count() { where: hits.product.productQuantity > 0 }
|
|
51
52
|
|
|
52
|
-
|
|
53
|
+
view: by_source is {
|
|
53
54
|
where: trafficSource.\`source\` != '(direct)'
|
|
54
55
|
group_by: trafficSource.\`source\`
|
|
55
56
|
aggregate: hits_count
|
|
56
57
|
limit: 10
|
|
57
58
|
}
|
|
58
|
-
|
|
59
|
+
view: by_adContent_bar_chart is {
|
|
59
60
|
group_by: device.browser
|
|
60
61
|
aggregate: user_count
|
|
61
62
|
group_by: device.deviceCategory
|
|
62
63
|
}
|
|
63
|
-
|
|
64
|
+
view: by_region is {
|
|
64
65
|
where: geoNetwork.region !~ '%demo%'
|
|
65
66
|
group_by: geoNetwork.region
|
|
66
67
|
aggregate: user_count
|
|
67
68
|
limit: 10
|
|
68
69
|
}
|
|
69
|
-
|
|
70
|
+
view: by_device is {
|
|
70
71
|
group_by: device.browser
|
|
71
72
|
aggregate: user_count
|
|
72
73
|
group_by: device.deviceCategory
|
|
73
74
|
}
|
|
74
|
-
|
|
75
|
+
view: by_category is {
|
|
75
76
|
group_by: category is hits.product.v2ProductCategory
|
|
76
77
|
aggregate: total_productRevenue
|
|
77
78
|
aggregate: sold_count
|
|
78
79
|
limit: 10
|
|
79
80
|
}
|
|
80
|
-
|
|
81
|
+
view: by_hour_of_day is {
|
|
81
82
|
// group_by: gsession_hour is hour(start_time::timestamp)
|
|
82
83
|
aggregate: session_count
|
|
83
84
|
order_by: 1
|
|
84
85
|
}
|
|
85
86
|
|
|
86
|
-
|
|
87
|
+
view: page_load_times is {
|
|
87
88
|
group_by: hits.page.pageTitle
|
|
88
89
|
aggregate: hit_count is hits.count()
|
|
89
90
|
nest: load_bar_chart is {
|
|
@@ -93,20 +94,20 @@ source:ga_sessions is table('malloytest.ga_sample'){
|
|
|
93
94
|
limit: 10
|
|
94
95
|
}
|
|
95
96
|
|
|
96
|
-
|
|
97
|
+
view: by_page_title is { where: totals.transactionRevenue > 0
|
|
97
98
|
group_by: hits.page.pageTitle
|
|
98
99
|
aggregate: hits_count
|
|
99
100
|
aggregate: sold_count
|
|
100
101
|
}
|
|
101
102
|
|
|
102
|
-
|
|
103
|
+
view: by_all is {
|
|
103
104
|
nest: by_source
|
|
104
105
|
nest: by_adContent_bar_chart
|
|
105
106
|
nest: by_region
|
|
106
107
|
nest: by_category
|
|
107
108
|
}
|
|
108
109
|
|
|
109
|
-
|
|
110
|
+
view: search_index is {
|
|
110
111
|
index: *, hits.*, customDimensions.* totals.*, trafficSource.*, hits.product.*
|
|
111
112
|
sample: 1%
|
|
112
113
|
}
|
|
@@ -124,26 +125,21 @@ query: sessions_dashboard is ga_sessions -> {
|
|
|
124
125
|
}
|
|
125
126
|
}
|
|
126
127
|
`;
|
|
128
|
+
}
|
|
127
129
|
|
|
128
130
|
const runtimes = new RuntimeList(databases);
|
|
129
131
|
describe.each(runtimes.runtimeList)(
|
|
130
132
|
'Nested Source Table - %s',
|
|
131
133
|
(databaseName, runtime) => {
|
|
134
|
+
const gaModel = runtime.loadModel(modelText(databaseName));
|
|
132
135
|
test(`repeated child of record - ${databaseName}`, async () => {
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
query: ga_sessions->by_page_title
|
|
138
|
-
`
|
|
139
|
-
)
|
|
140
|
-
.run();
|
|
141
|
-
// console.log(result.data.toObject());
|
|
142
|
-
// console.log(result.sql);
|
|
143
|
-
expect(result.data.path(0, 'pageTitle').value).toBe('Shopping Cart');
|
|
136
|
+
await expect('run: ga_sessions->by_page_title').malloyResultMatches(
|
|
137
|
+
gaModel,
|
|
138
|
+
{pageTitle: 'Shopping Cart'}
|
|
139
|
+
);
|
|
144
140
|
});
|
|
145
141
|
|
|
146
|
-
//
|
|
142
|
+
// Tests intermittently fail and lloyd said
|
|
147
143
|
// "I bet it has to do with my sampling test on indexing. Intermittently
|
|
148
144
|
// getting different results. I'd comment out the test and I'll take a
|
|
149
145
|
// look at it when I get back." and that seems reasonable.
|
|
@@ -155,35 +151,28 @@ describe.each(runtimes.runtimeList)(
|
|
|
155
151
|
//
|
|
156
152
|
// Expected: "Organic Search"
|
|
157
153
|
// Received: "Referral"
|
|
158
|
-
test
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
.loadQuery(
|
|
162
|
-
`
|
|
163
|
-
query: ga_sessions->search_index -> {
|
|
154
|
+
test(`search_index - ${databaseName}`, async () => {
|
|
155
|
+
await expect(`
|
|
156
|
+
run: ga_sessions->search_index -> {
|
|
164
157
|
where: fieldName != null
|
|
165
158
|
select: *
|
|
166
159
|
order_by: fieldName, weight desc
|
|
167
160
|
limit: 10
|
|
168
161
|
}
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
expect(result.data.path(0, 'fieldValue').value).toBe('Organic Search');
|
|
175
|
-
// expect(result.data.path(0, "weight").value).toBe(18);
|
|
162
|
+
`).malloyResultMatches(gaModel, {
|
|
163
|
+
fieldName: 'channelGrouping',
|
|
164
|
+
// fieldValue: 'Organic Search',
|
|
165
|
+
// weight: 10,
|
|
166
|
+
});
|
|
176
167
|
});
|
|
177
168
|
|
|
178
|
-
test
|
|
169
|
+
test(`manual index - ${databaseName}`, async () => {
|
|
179
170
|
let sampleSize = '10';
|
|
180
171
|
if (databaseName === 'bigquery') {
|
|
181
172
|
sampleSize = 'false';
|
|
182
173
|
}
|
|
183
|
-
|
|
184
|
-
.
|
|
185
|
-
`
|
|
186
|
-
query: table('malloytest.ga_sample')-> {
|
|
174
|
+
await expect(`
|
|
175
|
+
run: ${databaseName}.table('malloytest.ga_sample')-> {
|
|
187
176
|
index: everything
|
|
188
177
|
sample: ${sampleSize}
|
|
189
178
|
}
|
|
@@ -195,70 +184,39 @@ describe.each(runtimes.runtimeList)(
|
|
|
195
184
|
limit: 100
|
|
196
185
|
}
|
|
197
186
|
}
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
.
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
// expect(result.data.path(0, "fieldValue").value).toBe("Organic Search");
|
|
204
|
-
// expect(result.data.path(0, "weight").value).toBe(18);
|
|
187
|
+
`).malloyResultMatches(runtime, {
|
|
188
|
+
// 'top_fields.fieldName': 'channelGrouping',
|
|
189
|
+
// 'top_fields.fieldValue': 'Organic Search',
|
|
190
|
+
// 'top_fields.weight': 18,
|
|
191
|
+
});
|
|
205
192
|
});
|
|
206
193
|
|
|
207
194
|
test(`autobin - ${databaseName}`, async () => {
|
|
208
|
-
|
|
209
|
-
.
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
aggregate:
|
|
216
|
-
nest: data is {
|
|
217
|
-
group_by: elevation
|
|
218
|
-
aggregate: row_count is count()
|
|
219
|
-
}
|
|
220
|
-
}
|
|
221
|
-
-> {
|
|
222
|
-
group_by: elevation is floor(data.elevation/bin_size)*bin_size + bin_size/2
|
|
223
|
-
aggregate: airport_count is data.row_count.sum()
|
|
224
|
-
order_by: elevation
|
|
195
|
+
await expect(`
|
|
196
|
+
source: airports is ${databaseName}.table('malloytest.airports') extend {
|
|
197
|
+
measure: airport_count is count()
|
|
198
|
+
view: by_elevation is {
|
|
199
|
+
aggregate: bin_size is NULLIF((max(elevation) - min(elevation))/30,0)
|
|
200
|
+
nest: data is {
|
|
201
|
+
group_by: elevation
|
|
202
|
+
aggregate: row_count is count()
|
|
225
203
|
}
|
|
226
204
|
}
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
nest: by_elevation_bar_chart is by_elevation
|
|
205
|
+
-> {
|
|
206
|
+
group_by: elevation is floor(data.elevation/bin_size)*bin_size + bin_size/2
|
|
207
|
+
aggregate: airport_count is data.row_count.sum()
|
|
208
|
+
order_by: elevation
|
|
232
209
|
}
|
|
233
|
-
|
|
234
|
-
)
|
|
235
|
-
.run();
|
|
236
|
-
// console.log(result.sql);
|
|
237
|
-
// console.log(result.data.toObject());
|
|
238
|
-
// expect(result.data.path(0, 'fieldName').value).toBe('channelGrouping');
|
|
239
|
-
// expect(result.data.path(0, 'fieldValue').value).toBe('Organic Search');
|
|
240
|
-
// expect(result.data.path(0, "weight").value).toBe(18);
|
|
241
|
-
});
|
|
242
|
-
|
|
243
|
-
// this really shouldn't be here, we need to be able to test dialect capiblities
|
|
244
|
-
// from the connection.
|
|
245
|
-
test(`safe_cast - ${databaseName}`, async () => {
|
|
246
|
-
const result = await runtime
|
|
247
|
-
.loadQuery(
|
|
248
|
-
`
|
|
249
|
-
source: eone is table('malloytest.airports') {}
|
|
210
|
+
}
|
|
250
211
|
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
limit: 1
|
|
212
|
+
run: airports -> {
|
|
213
|
+
group_by: state is state
|
|
214
|
+
aggregate: airport_count
|
|
215
|
+
nest: by_elevation_bar_chart is by_elevation
|
|
256
216
|
}
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
expect(result.data.value[0]['bad_date']).toBe(null);
|
|
261
|
-
expect(result.data.value[0]['bad_number']).toBe(null);
|
|
217
|
+
`).malloyResultMatches(runtime, {
|
|
218
|
+
// don't know what to expect ...
|
|
219
|
+
});
|
|
262
220
|
});
|
|
263
221
|
}
|
|
264
222
|
);
|
|
@@ -66,40 +66,22 @@ describe('Multi-connection', () => {
|
|
|
66
66
|
});
|
|
67
67
|
|
|
68
68
|
const expressionModelText = `
|
|
69
|
-
source:
|
|
70
|
-
measure: aircraft_count is count(tail_num)
|
|
71
|
-
}
|
|
72
|
-
|
|
73
|
-
source: bigquery_state_facts is table('malloytest.state_facts'){
|
|
69
|
+
source: bigquery_state_facts is bigquery.table('malloytest.state_facts') extend {
|
|
74
70
|
measure: state_count is count(state)+2
|
|
75
71
|
}
|
|
76
72
|
|
|
77
|
-
source: postgres_aircraft is table('
|
|
73
|
+
source: postgres_aircraft is postgres.table('malloytest.aircraft') extend {
|
|
78
74
|
measure: aircraft_count is count(tail_num)+4
|
|
79
75
|
}
|
|
80
76
|
`;
|
|
81
77
|
|
|
82
78
|
const expressionModel = runtime.loadModel(expressionModelText);
|
|
83
79
|
|
|
84
|
-
it('default query', async () => {
|
|
85
|
-
const result = await expressionModel
|
|
86
|
-
.loadQuery(
|
|
87
|
-
`
|
|
88
|
-
query: default_aircraft-> {
|
|
89
|
-
aggregate: aircraft_count
|
|
90
|
-
}
|
|
91
|
-
`
|
|
92
|
-
)
|
|
93
|
-
.run();
|
|
94
|
-
// console.log(result.sql);
|
|
95
|
-
expect(result.data.path(0, 'aircraft_count').value).toBe(3599);
|
|
96
|
-
});
|
|
97
|
-
|
|
98
80
|
it('bigquery query', async () => {
|
|
99
81
|
const result = await expressionModel
|
|
100
82
|
.loadQuery(
|
|
101
83
|
`
|
|
102
|
-
|
|
84
|
+
run: bigquery_state_facts-> {
|
|
103
85
|
aggregate: state_count
|
|
104
86
|
}
|
|
105
87
|
`
|
|
@@ -113,7 +95,7 @@ source: postgres_aircraft is table('postgres:malloytest.aircraft'){
|
|
|
113
95
|
const result = await expressionModel
|
|
114
96
|
.loadQuery(
|
|
115
97
|
`
|
|
116
|
-
|
|
98
|
+
run: postgres_aircraft-> {
|
|
117
99
|
aggregate: aircraft_count
|
|
118
100
|
}
|
|
119
101
|
`
|
|
@@ -126,7 +108,7 @@ source: postgres_aircraft is table('postgres:malloytest.aircraft'){
|
|
|
126
108
|
const result = await runtime
|
|
127
109
|
.loadQuery(
|
|
128
110
|
`
|
|
129
|
-
|
|
111
|
+
run: postgres.table('malloytest.airports')->{
|
|
130
112
|
group_by:
|
|
131
113
|
version is version!()
|
|
132
114
|
aggregate:
|
|
@@ -57,8 +57,10 @@ describe('Streaming tests', () => {
|
|
|
57
57
|
runtimes.runtimeMap.forEach((runtime, databaseName) => {
|
|
58
58
|
it(`basic stream test - ${databaseName}`, async () => {
|
|
59
59
|
const stream = runtime
|
|
60
|
-
.loadModel(
|
|
61
|
-
|
|
60
|
+
.loadModel(
|
|
61
|
+
`source: airports is ${databaseName}.table('malloytest.airports')`
|
|
62
|
+
)
|
|
63
|
+
.loadQuery('run: airports -> { select: code }')
|
|
62
64
|
.runStream({rowLimit: 10});
|
|
63
65
|
const rows: DataRecord[] = [];
|
|
64
66
|
for await (const row of stream) {
|
|
@@ -70,8 +72,10 @@ describe('Streaming tests', () => {
|
|
|
70
72
|
|
|
71
73
|
it(`stream to JSON - ${databaseName}`, async () => {
|
|
72
74
|
const stream = runtime
|
|
73
|
-
.loadModel(
|
|
74
|
-
|
|
75
|
+
.loadModel(
|
|
76
|
+
`source: airports is ${databaseName}.table('malloytest.airports')`
|
|
77
|
+
)
|
|
78
|
+
.loadQuery('run: airports -> { select: code }')
|
|
75
79
|
.runStream({rowLimit: 1});
|
|
76
80
|
const accummulator = new StringAccumulator();
|
|
77
81
|
const jsonWriter = new JSONWriter(accummulator);
|
|
@@ -88,8 +92,10 @@ describe('Streaming tests', () => {
|
|
|
88
92
|
|
|
89
93
|
it(`stream to CSV - ${databaseName}`, async () => {
|
|
90
94
|
const stream = runtime
|
|
91
|
-
.loadModel(
|
|
92
|
-
|
|
95
|
+
.loadModel(
|
|
96
|
+
`source: airports is ${databaseName}.table('malloytest.airports')`
|
|
97
|
+
)
|
|
98
|
+
.loadQuery('run: airports -> { select: code }')
|
|
93
99
|
.runStream({rowLimit: 1});
|
|
94
100
|
const accummulator = new StringAccumulator();
|
|
95
101
|
const csvWriter = new CSVWriter(accummulator);
|
|
@@ -26,6 +26,7 @@ import {RuntimeList} from '../../runtimes';
|
|
|
26
26
|
import '../../util/db-jest-matchers';
|
|
27
27
|
import {describeIfDatabaseAvailable} from '../../util';
|
|
28
28
|
|
|
29
|
+
// TODO identify which tests need to run on wasm and move them into their own file
|
|
29
30
|
const runtimes = ['duckdb', 'duckdb_wasm'];
|
|
30
31
|
|
|
31
32
|
const [_describe, databases] = describeIfDatabaseAvailable(runtimes);
|
|
@@ -33,17 +34,12 @@ const allDucks = new RuntimeList(databases);
|
|
|
33
34
|
|
|
34
35
|
describe.each(allDucks.runtimeList)('duckdb:%s', (dbName, runtime) => {
|
|
35
36
|
it('can open tables with wildcards', async () => {
|
|
36
|
-
|
|
37
|
-
.
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
}
|
|
43
|
-
`
|
|
44
|
-
)
|
|
45
|
-
.run();
|
|
46
|
-
expect(result.data.path(0, 'carrier').value).toEqual('AA');
|
|
37
|
+
await expect(`
|
|
38
|
+
run: duckdb.table('test/data/duckdb/flights/part.*.parquet') -> {
|
|
39
|
+
top: 1
|
|
40
|
+
group_by: carrier;
|
|
41
|
+
}
|
|
42
|
+
`).malloyResultMatches(runtime, {carrier: 'AA'});
|
|
47
43
|
});
|
|
48
44
|
|
|
49
45
|
it('accepts all schema numbers', async () => {
|
|
@@ -60,36 +56,32 @@ describe.each(allDucks.runtimeList)('duckdb:%s', (dbName, runtime) => {
|
|
|
60
56
|
];
|
|
61
57
|
const allFields = allInts.map(intType => `a${intType.toLowerCase()}`);
|
|
62
58
|
const query = `
|
|
63
|
-
|
|
59
|
+
run: ${dbName}.sql("""
|
|
64
60
|
SELECT
|
|
65
61
|
${allInts
|
|
66
62
|
.map(intType => `1::${intType} as a${intType.toLowerCase()}`)
|
|
67
63
|
.join(',\n')}
|
|
68
|
-
"""
|
|
69
|
-
query: from_sql(allInts) -> {
|
|
64
|
+
""") -> {
|
|
70
65
|
aggregate:
|
|
71
66
|
${allFields
|
|
72
67
|
.map(fieldType => `sum_${fieldType} is sum(${fieldType})`)
|
|
73
68
|
.join('\n')}
|
|
74
69
|
}
|
|
75
70
|
`;
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
71
|
+
await expect(query).malloyResultMatches(
|
|
72
|
+
runtime,
|
|
73
|
+
allInts.reduce<Record<string, number>>((building, ent) => {
|
|
74
|
+
building[`sum_a${ent.toLowerCase()}`] = 1;
|
|
75
|
+
return building;
|
|
76
|
+
}, {})
|
|
77
|
+
);
|
|
80
78
|
});
|
|
81
79
|
|
|
82
80
|
it('can open json files', async () => {
|
|
83
|
-
|
|
84
|
-
.
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
select: *
|
|
88
|
-
}
|
|
89
|
-
`
|
|
90
|
-
)
|
|
91
|
-
.run();
|
|
92
|
-
expect(result.data.path(0, 'foo').value).toEqual('bar');
|
|
81
|
+
await expect(`
|
|
82
|
+
run: duckdb.table('test/data/duckdb/test.json') -> {
|
|
83
|
+
select: *
|
|
84
|
+
}`).malloyResultMatches(runtime, {foo: 'bar'});
|
|
93
85
|
});
|
|
94
86
|
|
|
95
87
|
it('supports timezones', async () => {
|
|
@@ -101,10 +93,9 @@ describe.each(allDucks.runtimeList)('duckdb:%s', (dbName, runtime) => {
|
|
|
101
93
|
});
|
|
102
94
|
|
|
103
95
|
it('supports varchars with parameters', async () => {
|
|
104
|
-
await expect(
|
|
105
|
-
"run: duckdb.sql(\"SELECT 'a'::VARCHAR as abc, 'a3'::VARCHAR(3) as abc3\")"
|
|
106
|
-
|
|
107
|
-
);
|
|
96
|
+
await expect(
|
|
97
|
+
"run: duckdb.sql(\"SELECT 'a'::VARCHAR as abc, 'a3'::VARCHAR(3) as abc3\")"
|
|
98
|
+
).malloyResultMatches(runtime, {abc: 'a', abc3: 'a3'});
|
|
108
99
|
});
|
|
109
100
|
|
|
110
101
|
describe('time', () => {
|
|
@@ -118,17 +109,14 @@ describe.each(allDucks.runtimeList)('duckdb:%s', (dbName, runtime) => {
|
|
|
118
109
|
second: 0,
|
|
119
110
|
zone,
|
|
120
111
|
});
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
}`,
|
|
130
|
-
{mex_220: zone_2020.toJSDate()}
|
|
131
|
-
);
|
|
112
|
+
test('can cast TIMESTAMPTZ to timestamp', async () => {
|
|
113
|
+
await expect(
|
|
114
|
+
`run: duckdb.sql("""
|
|
115
|
+
SELECT TIMESTAMPTZ '2020-02-20 00:00:00 ${zone}' as t_tstz
|
|
116
|
+
""") -> {
|
|
117
|
+
select: mex_220 is t_tstz::timestamp
|
|
118
|
+
}`
|
|
119
|
+
).malloyResultMatches(runtime, {mex_220: zone_2020.toJSDate()});
|
|
132
120
|
});
|
|
133
121
|
});
|
|
134
122
|
});
|
|
@@ -23,7 +23,9 @@
|
|
|
23
23
|
|
|
24
24
|
/* eslint-disable no-console */
|
|
25
25
|
|
|
26
|
+
import {ok} from 'assert';
|
|
26
27
|
import {RuntimeList} from '../../runtimes';
|
|
28
|
+
import {Runtime} from '@malloydata/malloy';
|
|
27
29
|
import {describeIfDatabaseAvailable} from '../../util';
|
|
28
30
|
import '../../util/db-jest-matchers';
|
|
29
31
|
import {DateTime} from 'luxon';
|
|
@@ -56,96 +58,67 @@ describe('Postgres tests', () => {
|
|
|
56
58
|
await runtimeList.closeAll();
|
|
57
59
|
});
|
|
58
60
|
|
|
59
|
-
it('
|
|
60
|
-
|
|
61
|
-
.
|
|
62
|
-
|
|
63
|
-
sql: one is {select:"""
|
|
64
|
-
SELECT 1 as n
|
|
65
|
-
"""}
|
|
66
|
-
|
|
67
|
-
query: from_sql(one) -> { select: n }
|
|
68
|
-
`
|
|
69
|
-
)
|
|
70
|
-
.run();
|
|
71
|
-
expect(result.data.value[0]['n']).toBe(1);
|
|
61
|
+
it('run an sql query', async () => {
|
|
62
|
+
await expect(
|
|
63
|
+
'run: postgres.sql("SELECT 1 as n") -> { select: n }'
|
|
64
|
+
).malloyResultMatches(runtime, {n: 1});
|
|
72
65
|
});
|
|
73
66
|
|
|
74
|
-
it('
|
|
75
|
-
|
|
76
|
-
.
|
|
77
|
-
|
|
78
|
-
sql: one is {select:"""
|
|
79
|
-
SELECT 1 as "upperLower"
|
|
80
|
-
"""}
|
|
81
|
-
|
|
82
|
-
query: from_sql(one) -> { select: upperLower }
|
|
83
|
-
`
|
|
84
|
-
)
|
|
85
|
-
.run();
|
|
86
|
-
expect(result.data.value[0]['upperLower']).toBe(1);
|
|
67
|
+
it('mixed case col names are properly quoted so they retain case in results', async () => {
|
|
68
|
+
await expect(`
|
|
69
|
+
run: postgres.sql('SELECT 1 as "upperLower"') -> { select: upperLower }
|
|
70
|
+
`).malloyResultMatches(runtime, {upperLower: 1});
|
|
87
71
|
});
|
|
88
72
|
|
|
89
|
-
it('
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
query: from_sql(one) -> {
|
|
98
|
-
select:
|
|
99
|
-
select
|
|
100
|
-
create is select + 1
|
|
101
|
-
}
|
|
102
|
-
`
|
|
103
|
-
)
|
|
104
|
-
.run();
|
|
105
|
-
expect(result.data.value[0]['select']).toBe(1);
|
|
106
|
-
expect(result.data.value[0]['create']).toBe(2);
|
|
73
|
+
it('fields which are sql keywords are quoted', async () => {
|
|
74
|
+
await expect(`
|
|
75
|
+
run: postgres.sql('SELECT 1 as "select"') -> {
|
|
76
|
+
select:
|
|
77
|
+
select
|
|
78
|
+
create is select + 1
|
|
79
|
+
}
|
|
80
|
+
`).malloyResultMatches(runtime, {select: 1, create: 2});
|
|
107
81
|
});
|
|
108
82
|
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
83
|
+
async function oneExists(rt: Runtime, tn: string): Promise<boolean> {
|
|
84
|
+
try {
|
|
85
|
+
const lookForOne = await rt
|
|
86
|
+
.loadQuery(`run: postgres.sql('SELECT one FROM ${tn}')`)
|
|
87
|
+
.run();
|
|
88
|
+
const one = lookForOne.data.path(0, 'one').value;
|
|
89
|
+
return one === 1;
|
|
90
|
+
} catch (e) {
|
|
91
|
+
return false;
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
it('will quote to properly access mixed case table name', async () => {
|
|
96
|
+
if (await oneExists(runtime, 'public."UpperTablePublic"')) {
|
|
97
|
+
await expect(`
|
|
98
|
+
run: postgres.table('public.UpperTablePublic') -> { select: one }
|
|
99
|
+
`).malloyResultMatches(runtime, {one: 1});
|
|
100
|
+
}
|
|
119
101
|
});
|
|
120
102
|
|
|
121
|
-
it
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
)
|
|
128
|
-
.run();
|
|
129
|
-
expect(result.data.value[0]['one']).toBe(1);
|
|
103
|
+
it('quote to properly access mixes case schema name', async () => {
|
|
104
|
+
if (await oneExists(runtime, '"UpperSchema"."UpperSchemaUpperTable"')) {
|
|
105
|
+
await expect(`
|
|
106
|
+
run: postgres.table('UpperSchema.UpperSchemaUpperTable') -> { select: one }
|
|
107
|
+
`).malloyResultMatches(runtime, {one: 1});
|
|
108
|
+
}
|
|
130
109
|
});
|
|
131
110
|
|
|
132
111
|
it('passes unsupported data', async () => {
|
|
133
112
|
const result = await runtime
|
|
134
|
-
.loadQuery(
|
|
135
|
-
`
|
|
136
|
-
sql: badType is { select: """SELECT int4range(10, 20) as ranger""" }
|
|
137
|
-
query: from_sql(badType)->{ select: *}
|
|
138
|
-
`
|
|
139
|
-
)
|
|
113
|
+
.loadQuery('run: postgres.sql("SELECT int4range(10, 20) as ranger")')
|
|
140
114
|
.run();
|
|
141
115
|
expect(result.data.value[0]['ranger']).toBeDefined();
|
|
142
116
|
});
|
|
143
117
|
|
|
144
118
|
it('supports varchars with parameters', async () => {
|
|
145
|
-
await expect(
|
|
146
|
-
"run: postgres.sql(\"SELECT 'a'::VARCHAR as abc, 'a3'::VARCHAR(3) as abc3\")"
|
|
147
|
-
|
|
148
|
-
);
|
|
119
|
+
await expect(
|
|
120
|
+
"run: postgres.sql(\"SELECT 'a'::VARCHAR as abc, 'a3'::VARCHAR(3) as abc3\")"
|
|
121
|
+
).malloyResultMatches(runtime, {abc: 'a', abc3: 'a3'});
|
|
149
122
|
});
|
|
150
123
|
|
|
151
124
|
describe('time', () => {
|
|
@@ -159,17 +132,14 @@ describe('Postgres tests', () => {
|
|
|
159
132
|
second: 0,
|
|
160
133
|
zone,
|
|
161
134
|
});
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
}`,
|
|
171
|
-
{mex_220: zone_2020.toJSDate()}
|
|
172
|
-
);
|
|
135
|
+
test('can cast TIMESTAMPTZ to timestamp', async () => {
|
|
136
|
+
await expect(
|
|
137
|
+
`run: duckdb.sql("""
|
|
138
|
+
SELECT TIMESTAMPTZ '2020-02-20 00:00:00 ${zone}' as t_tstz
|
|
139
|
+
""") -> {
|
|
140
|
+
select: mex_220 is t_tstz::timestamp
|
|
141
|
+
}`
|
|
142
|
+
).malloyResultMatches(runtime, {mex_220: zone_2020.toJSDate()});
|
|
173
143
|
});
|
|
174
144
|
});
|
|
175
145
|
});
|