@malloydata/malloy 0.0.240-dev250311202829 → 0.0.240-dev250311214430
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/dist/api/core.d.ts +1 -0
- package/dist/api/core.js +2 -1
- package/dist/lang/malloy-parse-info.d.ts +2 -10
- package/dist/lang/malloy-to-ast.d.ts +1 -0
- package/dist/lang/malloy-to-ast.js +9 -6
- package/dist/lang/malloy-to-stable-query.d.ts +76 -0
- package/dist/lang/malloy-to-stable-query.js +660 -0
- package/dist/lang/parse-log.d.ts +1 -0
- package/dist/lang/parse-malloy.d.ts +2 -9
- package/dist/lang/parse-malloy.js +7 -112
- package/dist/lang/parse-tree-walkers/model-annotation-walker.js +1 -1
- package/dist/lang/run-malloy-parser.d.ts +3 -0
- package/dist/lang/run-malloy-parser.js +53 -0
- package/dist/lang/syntax-errors/malloy-parser-error-listener.d.ts +4 -3
- package/dist/lang/syntax-errors/malloy-parser-error-listener.js +8 -4
- package/dist/lang/utils.d.ts +27 -1
- package/dist/lang/utils.js +78 -1
- package/package.json +3 -3
- package/dist/api/asynchronous.spec.d.ts +0 -1
- package/dist/api/asynchronous.spec.js +0 -240
- package/dist/api/sessioned.spec.d.ts +0 -1
- package/dist/api/sessioned.spec.js +0 -476
- package/dist/api/stateless.spec.d.ts +0 -1
- package/dist/api/stateless.spec.js +0 -400
- package/dist/lang/test/annotation.spec.d.ts +0 -14
- package/dist/lang/test/annotation.spec.js +0 -809
- package/dist/lang/test/composite-field-usage.spec.d.ts +0 -1
- package/dist/lang/test/composite-field-usage.spec.js +0 -261
- package/dist/lang/test/document-help-context-walker.spec.d.ts +0 -1
- package/dist/lang/test/document-help-context-walker.spec.js +0 -55
- package/dist/lang/test/document-symbol-walker.spec.d.ts +0 -1
- package/dist/lang/test/document-symbol-walker.spec.js +0 -209
- package/dist/lang/test/expressions.spec.d.ts +0 -1
- package/dist/lang/test/expressions.spec.js +0 -1181
- package/dist/lang/test/field-symbols.spec.d.ts +0 -1
- package/dist/lang/test/field-symbols.spec.js +0 -194
- package/dist/lang/test/find-table-path-walker.spec.d.ts +0 -1
- package/dist/lang/test/find-table-path-walker.spec.js +0 -50
- package/dist/lang/test/imports.spec.d.ts +0 -1
- package/dist/lang/test/imports.spec.js +0 -283
- package/dist/lang/test/lenses.spec.d.ts +0 -1
- package/dist/lang/test/lenses.spec.js +0 -384
- package/dist/lang/test/literals.spec.d.ts +0 -1
- package/dist/lang/test/literals.spec.js +0 -267
- package/dist/lang/test/locations.spec.d.ts +0 -1
- package/dist/lang/test/locations.spec.js +0 -679
- package/dist/lang/test/model-annotation-walker.spec.d.ts +0 -1
- package/dist/lang/test/model-annotation-walker.spec.js +0 -46
- package/dist/lang/test/model_serialization.spec.d.ts +0 -5
- package/dist/lang/test/model_serialization.spec.js +0 -116
- package/dist/lang/test/parameters.spec.d.ts +0 -1
- package/dist/lang/test/parameters.spec.js +0 -619
- package/dist/lang/test/parse.spec.d.ts +0 -1
- package/dist/lang/test/parse.spec.js +0 -893
- package/dist/lang/test/pretranslate.spec.d.ts +0 -1
- package/dist/lang/test/pretranslate.spec.js +0 -80
- package/dist/lang/test/query.spec.d.ts +0 -1
- package/dist/lang/test/query.spec.js +0 -1149
- package/dist/lang/test/source.spec.d.ts +0 -1
- package/dist/lang/test/source.spec.js +0 -570
- package/dist/lang/test/sql-block.spec.d.ts +0 -1
- package/dist/lang/test/sql-block.spec.js +0 -145
- package/dist/lang/test/syntax-errors.spec.d.ts +0 -1
- package/dist/lang/test/syntax-errors.spec.js +0 -142
- package/dist/model/utils.spec.d.ts +0 -1
- package/dist/model/utils.spec.js +0 -38
|
@@ -1,893 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
/* eslint-disable no-console */
|
|
3
|
-
/*
|
|
4
|
-
* Copyright 2023 Google LLC
|
|
5
|
-
*
|
|
6
|
-
* Permission is hereby granted, free of charge, to any person obtaining
|
|
7
|
-
* a copy of this software and associated documentation files
|
|
8
|
-
* (the "Software"), to deal in the Software without restriction,
|
|
9
|
-
* including without limitation the rights to use, copy, modify, merge,
|
|
10
|
-
* publish, distribute, sublicense, and/or sell copies of the Software,
|
|
11
|
-
* and to permit persons to whom the Software is furnished to do so,
|
|
12
|
-
* subject to the following conditions:
|
|
13
|
-
*
|
|
14
|
-
* The above copyright notice and this permission notice shall be
|
|
15
|
-
* included in all copies or substantial portions of the Software.
|
|
16
|
-
*
|
|
17
|
-
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
|
18
|
-
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
|
19
|
-
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
|
20
|
-
* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
|
|
21
|
-
* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
|
|
22
|
-
* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
|
|
23
|
-
* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
|
24
|
-
*/
|
|
25
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
26
|
-
const test_translator_1 = require("./test-translator");
|
|
27
|
-
require("./parse-expects");
|
|
28
|
-
const sql_block_1 = require("../../model/sql_block");
|
|
29
|
-
describe('model statements', () => {
|
|
30
|
-
describe('table method', () => {
|
|
31
|
-
test('table method works', () => {
|
|
32
|
-
expect("source: testA is _db_.table('aTable')").toTranslate();
|
|
33
|
-
});
|
|
34
|
-
test('table method works with quoted connection name', () => {
|
|
35
|
-
expect("source: testA is `_db_`.table('aTable')").toTranslate();
|
|
36
|
-
});
|
|
37
|
-
test('table method fails when connection name is wrong', () => {
|
|
38
|
-
expect("source: testA is bad_conn.table('aTable')").not.toTranslate();
|
|
39
|
-
});
|
|
40
|
-
test('table method fails when connection is not a connection', () => {
|
|
41
|
-
const m = (0, test_translator_1.model) `source: a1 is a; source: testA is a1.table('aTable')`;
|
|
42
|
-
// This is a somewhat weird behavior from the translator. During the
|
|
43
|
-
// first phase of translation, we fetch "external references" (tables and
|
|
44
|
-
// imports). At that point we don't know that there will be an error
|
|
45
|
-
// "a1 is not a connection," so we attempt to fetch `aTable` from connection
|
|
46
|
-
// `a1` even though a1 is not a connection. The response from the application
|
|
47
|
-
// is basically ignored in the end, as we still eventually get the error
|
|
48
|
-
// that a1 is not a connection (it's a source).
|
|
49
|
-
m.translator.update({
|
|
50
|
-
errors: { tables: { 'a1:aTable': 'invalid connection' } },
|
|
51
|
-
});
|
|
52
|
-
expect(m).toLog((0, test_translator_1.errorMessage)('a1 is not a connection'));
|
|
53
|
-
});
|
|
54
|
-
test('cannot refine table', () => {
|
|
55
|
-
expect((0, test_translator_1.markSource) `run: \`_db_\`.table('aTable') + { limit: 10 }`).toLog((0, test_translator_1.errorMessage)('Cannot add view refinements to a source'));
|
|
56
|
-
});
|
|
57
|
-
test('table function is deprecated', () => {
|
|
58
|
-
expect(`##! m4warnings=warn
|
|
59
|
-
source: testA is table('_db_:aTable')
|
|
60
|
-
`).toLog((0, test_translator_1.warningMessage)("`table('connection_name:table_path')` is deprecated; use `connection_name.table('table_path')`"));
|
|
61
|
-
});
|
|
62
|
-
test('cannot run table', () => {
|
|
63
|
-
expect((0, test_translator_1.markSource) `run: \`_db_\`.table('aTable')`).toLog((0, test_translator_1.errorMessage)('This source cannot be used as a query'));
|
|
64
|
-
});
|
|
65
|
-
});
|
|
66
|
-
test('errors on redefinition of query', () => {
|
|
67
|
-
expect('query: q1 is a -> { select: * }, q1 is a -> { select: * }').toLog((0, test_translator_1.errorMessage)("'q1' is already defined, cannot redefine"));
|
|
68
|
-
});
|
|
69
|
-
test('##! experimental enables all experiments', () => {
|
|
70
|
-
expect('##! experimental\n;;[ "x" ]').toTranslate();
|
|
71
|
-
});
|
|
72
|
-
test('experimental explicit enable', () => {
|
|
73
|
-
expect('##! experimental { compilerTestExperimentParse compilerTestExperimentTranslate }\n;;[ "x" ]').toTranslate();
|
|
74
|
-
});
|
|
75
|
-
test('experiments do not bleed into one another', () => {
|
|
76
|
-
expect('##! experimental { this_experiment_does_not_exist }\n;;[ "x" ]').toLog((0, test_translator_1.error)('experiment-not-enabled', {
|
|
77
|
-
experimentId: 'compilerTestExperimentParse',
|
|
78
|
-
}));
|
|
79
|
-
});
|
|
80
|
-
test('experiment failures in parse are flagged', () => {
|
|
81
|
-
expect(';;[ "x" ]').toLog((0, test_translator_1.error)('experiment-not-enabled', {
|
|
82
|
-
experimentId: 'compilerTestExperimentParse',
|
|
83
|
-
}));
|
|
84
|
-
});
|
|
85
|
-
test('experiment failures in second pass are flagged', () => {
|
|
86
|
-
expect('##! experimental.compilerTestExperimentParse\n;;[ "x" ]').toLog((0, test_translator_1.error)('experiment-not-enabled', {
|
|
87
|
-
experimentId: 'compilerTestExperimentTranslate',
|
|
88
|
-
}));
|
|
89
|
-
});
|
|
90
|
-
});
|
|
91
|
-
describe('error handling', () => {
|
|
92
|
-
test('no close brace', () => {
|
|
93
|
-
expect('run: a -> { group_by: ai').toLog((0, test_translator_1.errorMessage)("Missing '}' at '<EOF>'"));
|
|
94
|
-
});
|
|
95
|
-
test('field and query with same name does not overflow', () => {
|
|
96
|
-
expect(`
|
|
97
|
-
source: flights is _db_.table('malloytest.flights') extend {
|
|
98
|
-
view: carrier is { group_by: carrier }
|
|
99
|
-
}
|
|
100
|
-
`).toLog((0, test_translator_1.errorMessage)("Cannot redefine 'carrier'"));
|
|
101
|
-
});
|
|
102
|
-
test('redefine source', () => {
|
|
103
|
-
expect((0, test_translator_1.markSource) `
|
|
104
|
-
source: airports is _db_.table('malloytest.airports') extend {
|
|
105
|
-
primary_key: code
|
|
106
|
-
}
|
|
107
|
-
source: airports is _db_.table('malloytest.airports') extend {
|
|
108
|
-
primary_key: code
|
|
109
|
-
}
|
|
110
|
-
`).toLog((0, test_translator_1.errorMessage)("Cannot redefine 'airports'"));
|
|
111
|
-
});
|
|
112
|
-
test('query from undefined source', () => {
|
|
113
|
-
expect((0, test_translator_1.markSource) `run: ${'x'}->{ select: y }`).toLog((0, test_translator_1.errorMessage)("Reference to undefined object 'x'"));
|
|
114
|
-
});
|
|
115
|
-
test('query with expression from undefined source', () => {
|
|
116
|
-
// Regression check: Once upon a time this died with an exception even
|
|
117
|
-
// when "query: x->{ group_by: y}" (above) generated the correct error.
|
|
118
|
-
expect((0, test_translator_1.markSource) `run: ${'x'}->{ select: y is z / 2 }`).toLog((0, test_translator_1.errorMessage)("Reference to undefined object 'x'"));
|
|
119
|
-
});
|
|
120
|
-
test('join reference before definition', () => {
|
|
121
|
-
expect((0, test_translator_1.markSource) `
|
|
122
|
-
source: newAB is a extend { join_one: newB is ${'bb'} on astring }
|
|
123
|
-
source: newB is b
|
|
124
|
-
`).toLog((0, test_translator_1.errorMessage)("Reference to undefined object 'bb'"));
|
|
125
|
-
});
|
|
126
|
-
test('non-rename rename', () => {
|
|
127
|
-
expect('source: na is a extend { rename: astr is astr }').toLog((0, test_translator_1.errorMessage)("Can't rename field to itself"));
|
|
128
|
-
});
|
|
129
|
-
test('reference to field in its definition', () => {
|
|
130
|
-
expect('source: na is a extend { dimension: ustr is upper(ustr) } ').toLog((0, test_translator_1.errorMessage)("Circular reference to 'ustr' in definition"));
|
|
131
|
-
});
|
|
132
|
-
test('empty model', () => {
|
|
133
|
-
expect('').toTranslate();
|
|
134
|
-
});
|
|
135
|
-
test('one line model ', () => {
|
|
136
|
-
expect('\n').toTranslate();
|
|
137
|
-
});
|
|
138
|
-
test('query without fields', () => {
|
|
139
|
-
expect('run: a -> { top: 5 }').toLog((0, test_translator_1.error)('ambiguous-view-type'));
|
|
140
|
-
});
|
|
141
|
-
test('refine cannot change query type', () => {
|
|
142
|
-
expect('run: ab -> aturtle + { select: astr }').toLog((0, test_translator_1.errorMessage)(/Not legal in grouping query/));
|
|
143
|
-
});
|
|
144
|
-
test('undefined field ref in query', () => {
|
|
145
|
-
expect('run: ab -> { aggregate: xyzzy }').toLog((0, test_translator_1.errorMessage)("'xyzzy' is not defined"));
|
|
146
|
-
});
|
|
147
|
-
test('query on source with errors', () => {
|
|
148
|
-
expect((0, test_translator_1.markSource) `
|
|
149
|
-
source: na is a extend { join_one: ${'n'} on astr }
|
|
150
|
-
`).toLog((0, test_translator_1.errorMessage)("Reference to undefined object 'n'"));
|
|
151
|
-
});
|
|
152
|
-
test('detect duplicate output field names', () => {
|
|
153
|
-
expect((0, test_translator_1.markSource) `run: ab -> { group_by: astr, ${'astr'} }`).toLog((0, test_translator_1.errorMessage)("Output already has a field named 'astr'"));
|
|
154
|
-
});
|
|
155
|
-
test('detect join tail overlap existing ref', () => {
|
|
156
|
-
expect((0, test_translator_1.markSource) `run: ab -> { group_by: astr, ${'b.astr'} }`).toLog((0, test_translator_1.errorMessage)("Output already has a field named 'astr'"));
|
|
157
|
-
});
|
|
158
|
-
test('undefined in expression with regex compare', () => {
|
|
159
|
-
expect(`
|
|
160
|
-
source: c is a extend {
|
|
161
|
-
dimension: d is meaning_of_life ~ r'(forty two|fifty four)'
|
|
162
|
-
}
|
|
163
|
-
`).toLog((0, test_translator_1.errorMessage)("'meaning_of_life' is not defined"));
|
|
164
|
-
});
|
|
165
|
-
test('detect output collision on join references', () => {
|
|
166
|
-
expect(`
|
|
167
|
-
run: ab -> {
|
|
168
|
-
group_by: astr, b.astr
|
|
169
|
-
}
|
|
170
|
-
`).toLog((0, test_translator_1.errorMessage)("Output already has a field named 'astr'"));
|
|
171
|
-
});
|
|
172
|
-
test('rejoin a query is renamed', () => {
|
|
173
|
-
expect(`
|
|
174
|
-
source: querySrc is _db_.table('malloytest.flights')->{
|
|
175
|
-
group_by: origin
|
|
176
|
-
nest: nested is { group_by: destination }
|
|
177
|
-
}
|
|
178
|
-
|
|
179
|
-
source: refineQuerySrc is querySrc extend {
|
|
180
|
-
join_one: rejoin is querySrc on 7=8
|
|
181
|
-
view: broken is {
|
|
182
|
-
group_by: rejoin.nested.destination
|
|
183
|
-
}
|
|
184
|
-
}
|
|
185
|
-
`).toTranslate();
|
|
186
|
-
});
|
|
187
|
-
test('popping out of embedding when not embedded', () => {
|
|
188
|
-
expect('}%').toLog((0, test_translator_1.errorMessage)(/extraneous input '}%' expecting/));
|
|
189
|
-
});
|
|
190
|
-
test('bad sql in sql block', () => {
|
|
191
|
-
const badSelect = (0, test_translator_1.model) `source: someName is _db_.sql(")")`;
|
|
192
|
-
const badTrans = badSelect.translator;
|
|
193
|
-
expect(badSelect).toParse();
|
|
194
|
-
const needSchema = badTrans.translate();
|
|
195
|
-
expect(needSchema.compileSQL).toBeDefined();
|
|
196
|
-
if (needSchema.compileSQL) {
|
|
197
|
-
const key = (0, sql_block_1.sqlKey)(needSchema.compileSQL.connection, needSchema.compileSQL.selectStr);
|
|
198
|
-
badTrans.update({
|
|
199
|
-
errors: {
|
|
200
|
-
compileSQL: {
|
|
201
|
-
[key]: 'Nobody ZZZZ',
|
|
202
|
-
},
|
|
203
|
-
},
|
|
204
|
-
});
|
|
205
|
-
}
|
|
206
|
-
expect(badTrans).toLog((0, test_translator_1.errorMessage)('Invalid SQL, Nobody ZZZZ'));
|
|
207
|
-
});
|
|
208
|
-
});
|
|
209
|
-
describe('translate imports', () => {
|
|
210
|
-
test('import at', () => {
|
|
211
|
-
const source = (0, test_translator_1.markSource) `import ${'"myfile.malloy"'}`;
|
|
212
|
-
const m = new test_translator_1.TestTranslator(source.code);
|
|
213
|
-
const resp1 = m.translate();
|
|
214
|
-
expect(resp1.final).toBeUndefined();
|
|
215
|
-
m.update({
|
|
216
|
-
urls: {
|
|
217
|
-
'internal://test/langtests/myfile.malloy': 'query: ab -> { select: * }',
|
|
218
|
-
},
|
|
219
|
-
});
|
|
220
|
-
const resp2 = m.translate();
|
|
221
|
-
expect(resp2.final).toBeDefined();
|
|
222
|
-
const im = m.importAt({ line: 0, character: 10 });
|
|
223
|
-
expect(im === null || im === void 0 ? void 0 : im.importURL).toEqual('internal://test/langtests/myfile.malloy');
|
|
224
|
-
expect(im === null || im === void 0 ? void 0 : im.location.url).toEqual('internal://test/langtests/root.malloy');
|
|
225
|
-
expect(im === null || im === void 0 ? void 0 : im.location).toMatchObject(source.locations[0]);
|
|
226
|
-
});
|
|
227
|
-
});
|
|
228
|
-
describe('translation need error locations', () => {
|
|
229
|
-
test('import error location', () => {
|
|
230
|
-
const source = (0, test_translator_1.model) `import ${'"badfile"'}`;
|
|
231
|
-
const m = source.translator;
|
|
232
|
-
const result = m.translate();
|
|
233
|
-
m.update({
|
|
234
|
-
errors: { urls: { [(result.urls || [])[0]]: 'Bad file!' } },
|
|
235
|
-
});
|
|
236
|
-
expect(source).toLog((0, test_translator_1.errorMessage)(/Bad file!/));
|
|
237
|
-
});
|
|
238
|
-
test('sql struct error location', () => {
|
|
239
|
-
const source = (0, test_translator_1.markSource) `
|
|
240
|
-
source: bad_sql is _db_.sql(${'"""BAD_SQL"""'})
|
|
241
|
-
run: bad_sql -> { select: * }
|
|
242
|
-
`;
|
|
243
|
-
const m = new test_translator_1.TestTranslator(source.code);
|
|
244
|
-
expect(m).toParse();
|
|
245
|
-
const req = m.translate().compileSQL;
|
|
246
|
-
expect(req).toBeDefined();
|
|
247
|
-
if (req) {
|
|
248
|
-
const key = (0, sql_block_1.sqlKey)(req.connection, req.selectStr);
|
|
249
|
-
m.update({ errors: { compileSQL: { [key]: 'Bad SQL!' } } });
|
|
250
|
-
}
|
|
251
|
-
expect(m).not.toTranslate();
|
|
252
|
-
const errList = m.problems();
|
|
253
|
-
expect(errList[0].at).isLocationIn(source.locations[0], source.code);
|
|
254
|
-
});
|
|
255
|
-
test('table struct error location', () => {
|
|
256
|
-
const source = (0, test_translator_1.model) `
|
|
257
|
-
source: bad_explore is ${"_db_.table('malloydata-org.bad.table')"}
|
|
258
|
-
`;
|
|
259
|
-
const m = source.translator;
|
|
260
|
-
const result = m.translate();
|
|
261
|
-
m.update({
|
|
262
|
-
errors: {
|
|
263
|
-
tables: { [Object.keys(result.tables || {})[0]]: 'Bad table!' },
|
|
264
|
-
},
|
|
265
|
-
});
|
|
266
|
-
expect(m).toLog((0, test_translator_1.errorMessage)(/Bad table!/));
|
|
267
|
-
});
|
|
268
|
-
});
|
|
269
|
-
describe('error cascading', () => {
|
|
270
|
-
test('errors can appear in multiple top level objects', () => {
|
|
271
|
-
expect((0, test_translator_1.markSource) `
|
|
272
|
-
source: a1 is a extend { dimension: ${'x is count()'} }
|
|
273
|
-
source: a2 is a extend { dimension: ${'x is count()'} }
|
|
274
|
-
`).toLog((0, test_translator_1.errorMessage)('Cannot use an aggregate field in a dimension declaration, did you mean to use a measure declaration instead?'), (0, test_translator_1.errorMessage)('Cannot use an aggregate field in a dimension declaration, did you mean to use a measure declaration instead?'));
|
|
275
|
-
});
|
|
276
|
-
const typedScalars = {
|
|
277
|
-
'err': 'error',
|
|
278
|
-
'@2003 ~ @2003 for err hours': 'boolean',
|
|
279
|
-
'err.hour': 'timestamp',
|
|
280
|
-
'err.month': 'date',
|
|
281
|
-
'day_of_week(err)': 'number',
|
|
282
|
-
'err::string': 'string',
|
|
283
|
-
'-err': 'number',
|
|
284
|
-
'err * 1': 'number',
|
|
285
|
-
'1 * err': 'number',
|
|
286
|
-
'err / 1': 'number',
|
|
287
|
-
'1 / err': 'number',
|
|
288
|
-
'err % 1': 'number',
|
|
289
|
-
'1 % err': 'number',
|
|
290
|
-
'err + 1': 'number',
|
|
291
|
-
'1 + err': 'number',
|
|
292
|
-
'err - 1': 'number',
|
|
293
|
-
'1 - err': 'number',
|
|
294
|
-
'@2003 ? err for 1 minute': 'boolean',
|
|
295
|
-
'@2003 ? @2003 for err minutes': 'boolean',
|
|
296
|
-
'3 ? > err & > 3': 'boolean',
|
|
297
|
-
'3 ? > 3 & > err': 'boolean',
|
|
298
|
-
'3 ? > err | > 3': 'boolean',
|
|
299
|
-
'3 ? > 3 | > err': 'boolean',
|
|
300
|
-
'err ? > 3': 'boolean',
|
|
301
|
-
'3 ? > err': 'boolean',
|
|
302
|
-
'1 > err': 'boolean',
|
|
303
|
-
'err > 1': 'boolean',
|
|
304
|
-
'1 >= err': 'boolean',
|
|
305
|
-
'err >= 1': 'boolean',
|
|
306
|
-
'1 < err': 'boolean',
|
|
307
|
-
'err < 1': 'boolean',
|
|
308
|
-
'1 <= err': 'boolean',
|
|
309
|
-
'err <= 1': 'boolean',
|
|
310
|
-
'1 = err': 'boolean',
|
|
311
|
-
'err = 1': 'boolean',
|
|
312
|
-
'1 != err': 'boolean',
|
|
313
|
-
'err != 1': 'boolean',
|
|
314
|
-
'1 ~ err': 'boolean',
|
|
315
|
-
'err ~ 1': 'boolean',
|
|
316
|
-
'1 !~ err': 'boolean',
|
|
317
|
-
'err !~ 1': 'boolean',
|
|
318
|
-
'not err': 'boolean',
|
|
319
|
-
'err and true': 'boolean',
|
|
320
|
-
'true and err': 'boolean',
|
|
321
|
-
'err or true': 'boolean',
|
|
322
|
-
'true or err': 'boolean',
|
|
323
|
-
'err ?? 1': 'number',
|
|
324
|
-
'1 ?? err': 'number',
|
|
325
|
-
'cast(err as number)': 'number',
|
|
326
|
-
'(err)': 'error',
|
|
327
|
-
'length(err)': 'number',
|
|
328
|
-
'pick err when true else false': 'boolean',
|
|
329
|
-
'pick true when err else false': 'boolean',
|
|
330
|
-
'pick true when true else err': 'boolean',
|
|
331
|
-
'days(err to @2003)': 'number',
|
|
332
|
-
'days(@2003 to err)': 'number',
|
|
333
|
-
};
|
|
334
|
-
const scalars = Object.keys(typedScalars);
|
|
335
|
-
const aggregates = ['measure_err { where: true }'];
|
|
336
|
-
const ungroupedAggregates = ['all(measure_err)'];
|
|
337
|
-
test('dependent errors do not cascade', () => {
|
|
338
|
-
expect(`
|
|
339
|
-
source: a1 is a extend {
|
|
340
|
-
join_one: b with astr
|
|
341
|
-
dimension:
|
|
342
|
-
${'err is nothing'}
|
|
343
|
-
${scalars.map((d, i) => `e${i} is ${d}`).join('\n ')}
|
|
344
|
-
measure:
|
|
345
|
-
measure_err is count(foo),
|
|
346
|
-
${[...aggregates, ...ungroupedAggregates]
|
|
347
|
-
.map((m, i) => `e${i + scalars.length} is ${m}`)
|
|
348
|
-
.join('\n ')}
|
|
349
|
-
}
|
|
350
|
-
`).toLog((0, test_translator_1.errorMessage)("'nothing' is not defined"), (0, test_translator_1.errorMessage)("'foo' is not defined"));
|
|
351
|
-
});
|
|
352
|
-
test('error type inference is good', () => {
|
|
353
|
-
for (const scalar of scalars) {
|
|
354
|
-
const source = `
|
|
355
|
-
source: a1 is a extend {
|
|
356
|
-
dimension:
|
|
357
|
-
${'err is nothing'}
|
|
358
|
-
dim is length(${scalar}, 1)
|
|
359
|
-
}
|
|
360
|
-
`;
|
|
361
|
-
expect(source).toLog((0, test_translator_1.errorMessage)("'nothing' is not defined"), (0, test_translator_1.errorMessage)(`No matching overload for function length(${typedScalars[scalar]}, number)`));
|
|
362
|
-
}
|
|
363
|
-
});
|
|
364
|
-
test('eval space of errors is preserved', () => {
|
|
365
|
-
expect(`
|
|
366
|
-
source: a1 is a extend {
|
|
367
|
-
join_one: b with astr
|
|
368
|
-
}
|
|
369
|
-
run: a1 -> {
|
|
370
|
-
group_by:
|
|
371
|
-
${'err is nothing'}
|
|
372
|
-
aggregate:
|
|
373
|
-
measure_err is count(foo)
|
|
374
|
-
calculate:
|
|
375
|
-
${scalars
|
|
376
|
-
.map((d, i) => `e${i} is lag(${d})`)
|
|
377
|
-
.join('\n ')}
|
|
378
|
-
${aggregates
|
|
379
|
-
.map((m, i) => `e${i + scalars.length} is lag(${m})`)
|
|
380
|
-
.join('\n ')}
|
|
381
|
-
}
|
|
382
|
-
`).toLog((0, test_translator_1.errorMessage)("'nothing' is not defined"), (0, test_translator_1.errorMessage)("'foo' is not defined"));
|
|
383
|
-
});
|
|
384
|
-
});
|
|
385
|
-
describe('pipeline comprehension', () => {
|
|
386
|
-
test('second query gets namespace from first', () => {
|
|
387
|
-
expect(`
|
|
388
|
-
source: aq is a extend {
|
|
389
|
-
view: t1 is {
|
|
390
|
-
group_by: t1int is ai, t1str is astr
|
|
391
|
-
} -> {
|
|
392
|
-
select: t1str, t1int
|
|
393
|
-
}
|
|
394
|
-
}
|
|
395
|
-
`).toTranslate();
|
|
396
|
-
});
|
|
397
|
-
test("second query doesn't have access to original fields", () => {
|
|
398
|
-
expect((0, test_translator_1.model) `
|
|
399
|
-
source: aq is a extend {
|
|
400
|
-
view: t1 is {
|
|
401
|
-
group_by: t1int is ai, t1str is astr
|
|
402
|
-
} -> {
|
|
403
|
-
select: ${'ai'}
|
|
404
|
-
}
|
|
405
|
-
}
|
|
406
|
-
`).toLog((0, test_translator_1.errorMessage)("'ai' is not defined"));
|
|
407
|
-
});
|
|
408
|
-
test('new query can append ops to existing query', () => {
|
|
409
|
-
expect(`
|
|
410
|
-
source: aq is a extend {
|
|
411
|
-
view: t0 is {
|
|
412
|
-
group_by: t1int is ai, t1str is astr
|
|
413
|
-
}
|
|
414
|
-
view: t1 is t0 -> {
|
|
415
|
-
select: t1str, t1int
|
|
416
|
-
}
|
|
417
|
-
}
|
|
418
|
-
`).toTranslate();
|
|
419
|
-
});
|
|
420
|
-
test('new query can refine and append to exisiting query', () => {
|
|
421
|
-
expect(`
|
|
422
|
-
source: aq is _db_.table('aTable') extend {
|
|
423
|
-
view: by_region is {
|
|
424
|
-
group_by: region is astr
|
|
425
|
-
nest: dateNest is { group_by: ad }
|
|
426
|
-
}
|
|
427
|
-
-> { select: dateNest.ad }
|
|
428
|
-
}
|
|
429
|
-
`).toTranslate();
|
|
430
|
-
});
|
|
431
|
-
test('reference to a query can include a refinement', () => {
|
|
432
|
-
expect(`
|
|
433
|
-
run: ab -> {
|
|
434
|
-
group_by: ai
|
|
435
|
-
nest: aturtle + { limit: 1 }
|
|
436
|
-
}
|
|
437
|
-
`).toTranslate();
|
|
438
|
-
});
|
|
439
|
-
test('Querying an sourcebased on a query', () => {
|
|
440
|
-
expect(`
|
|
441
|
-
query: q is a -> { group_by: astr; aggregate: strsum is ai.sum() }
|
|
442
|
-
source: aq is a extend {
|
|
443
|
-
join_one: aq is q on astr = aq.astr
|
|
444
|
-
}
|
|
445
|
-
run: aq -> { select: * }
|
|
446
|
-
`).toTranslate();
|
|
447
|
-
});
|
|
448
|
-
test('new query appends to existing query', () => {
|
|
449
|
-
const src = `
|
|
450
|
-
query: s1 is _db_.table('malloytest.flights') -> {
|
|
451
|
-
group_by: origin, destination
|
|
452
|
-
}
|
|
453
|
-
query: s2 is s1 -> {
|
|
454
|
-
select: *, extra is 'extra'
|
|
455
|
-
}
|
|
456
|
-
`;
|
|
457
|
-
const m = new test_translator_1.TestTranslator(src);
|
|
458
|
-
expect(m).toTranslate();
|
|
459
|
-
const s2 = m.getQuery('s2');
|
|
460
|
-
expect(s2 === null || s2 === void 0 ? void 0 : s2.pipeline.length).toBe(2);
|
|
461
|
-
});
|
|
462
|
-
});
|
|
463
|
-
describe('raw function call with type specified', () => {
|
|
464
|
-
test('timestamp_seconds', () => {
|
|
465
|
-
expect('timestamp_seconds!timestamp(0)').toReturnType('timestamp');
|
|
466
|
-
});
|
|
467
|
-
});
|
|
468
|
-
describe('sql expressions', () => {
|
|
469
|
-
test('reference to sql expression in unextended source', () => {
|
|
470
|
-
const m = (0, test_translator_1.model) `
|
|
471
|
-
source: na is bigquery.sql("""SELECT 1 as one""")
|
|
472
|
-
`;
|
|
473
|
-
expect(m).toParse();
|
|
474
|
-
const compileSql = m.translator.translate().compileSQL;
|
|
475
|
-
expect(compileSql).toBeDefined();
|
|
476
|
-
if (compileSql) {
|
|
477
|
-
m.translator.update({
|
|
478
|
-
compileSQL: (0, test_translator_1.getSelectOneStruct)(compileSql),
|
|
479
|
-
});
|
|
480
|
-
expect(m).toTranslate();
|
|
481
|
-
}
|
|
482
|
-
});
|
|
483
|
-
test('sql expression legal with single quote', () => {
|
|
484
|
-
const m = (0, test_translator_1.model) `
|
|
485
|
-
source: na is bigquery.sql('SELECT 1 as one')
|
|
486
|
-
`;
|
|
487
|
-
expect(m).toParse();
|
|
488
|
-
const compileSql = m.translator.translate().compileSQL;
|
|
489
|
-
expect(compileSql).toBeDefined();
|
|
490
|
-
if (compileSql) {
|
|
491
|
-
m.translator.update({
|
|
492
|
-
compileSQL: (0, test_translator_1.getSelectOneStruct)(compileSql),
|
|
493
|
-
});
|
|
494
|
-
expect(m).toTranslate();
|
|
495
|
-
}
|
|
496
|
-
});
|
|
497
|
-
test('sql expression legal with double quote', () => {
|
|
498
|
-
const m = (0, test_translator_1.model) `
|
|
499
|
-
source: na is bigquery.sql("SELECT 1 as one")
|
|
500
|
-
`;
|
|
501
|
-
expect(m).toParse();
|
|
502
|
-
const compileSql = m.translator.translate().compileSQL;
|
|
503
|
-
expect(compileSql).toBeDefined();
|
|
504
|
-
if (compileSql) {
|
|
505
|
-
m.translator.update({
|
|
506
|
-
compileSQL: (0, test_translator_1.getSelectOneStruct)(compileSql),
|
|
507
|
-
});
|
|
508
|
-
expect(m).toTranslate();
|
|
509
|
-
}
|
|
510
|
-
});
|
|
511
|
-
test('reference to sql expression in extended source', () => {
|
|
512
|
-
const m = (0, test_translator_1.model) `
|
|
513
|
-
source: na is bigquery.sql("""SELECT 1 as one""") extend {
|
|
514
|
-
dimension: two is one + one
|
|
515
|
-
}
|
|
516
|
-
`;
|
|
517
|
-
const compileSql = m.translator.translate().compileSQL;
|
|
518
|
-
expect(compileSql).toBeDefined();
|
|
519
|
-
if (compileSql) {
|
|
520
|
-
m.translator.update({
|
|
521
|
-
compileSQL: (0, test_translator_1.getSelectOneStruct)(compileSql),
|
|
522
|
-
});
|
|
523
|
-
expect(m).toTranslate();
|
|
524
|
-
}
|
|
525
|
-
});
|
|
526
|
-
test('reference to sql expression in named query', () => {
|
|
527
|
-
const m = new test_translator_1.TestTranslator(`
|
|
528
|
-
query: nq is bigquery.sql("""SELECT 1 as one""") -> { select: * }
|
|
529
|
-
`);
|
|
530
|
-
expect(m).toParse();
|
|
531
|
-
const compileSql = m.translate().compileSQL;
|
|
532
|
-
expect(compileSql).toBeDefined();
|
|
533
|
-
if (compileSql) {
|
|
534
|
-
m.update({
|
|
535
|
-
compileSQL: (0, test_translator_1.getSelectOneStruct)(compileSql),
|
|
536
|
-
});
|
|
537
|
-
expect(m).toTranslate();
|
|
538
|
-
}
|
|
539
|
-
});
|
|
540
|
-
test('reference to sql expression in unnamed query', () => {
|
|
541
|
-
const m = new test_translator_1.TestTranslator(`
|
|
542
|
-
run: bigquery.sql("""SELECT 1 as one""") -> { select: * }
|
|
543
|
-
`);
|
|
544
|
-
expect(m).toParse();
|
|
545
|
-
const compileSql = m.translate().compileSQL;
|
|
546
|
-
expect(compileSql).toBeDefined();
|
|
547
|
-
if (compileSql) {
|
|
548
|
-
m.update({
|
|
549
|
-
compileSQL: (0, test_translator_1.getSelectOneStruct)(compileSql),
|
|
550
|
-
});
|
|
551
|
-
expect(m).toTranslate();
|
|
552
|
-
}
|
|
553
|
-
});
|
|
554
|
-
test('cannot refine sql query', () => {
|
|
555
|
-
const m = new test_translator_1.TestTranslator(`
|
|
556
|
-
run: duckdb.sql("SELECT 1 as one") + { limit: 10 }
|
|
557
|
-
`);
|
|
558
|
-
expect(m).toParse();
|
|
559
|
-
const compileSql = m.translate().compileSQL;
|
|
560
|
-
expect(compileSql).toBeDefined();
|
|
561
|
-
if (compileSql) {
|
|
562
|
-
m.update({
|
|
563
|
-
compileSQL: (0, test_translator_1.getSelectOneStruct)(compileSql),
|
|
564
|
-
});
|
|
565
|
-
expect(m).toLog((0, test_translator_1.errorMessage)('Cannot add view refinements to a source'));
|
|
566
|
-
}
|
|
567
|
-
});
|
|
568
|
-
test('reference to sql expression in join', () => {
|
|
569
|
-
const m = (0, test_translator_1.model) `
|
|
570
|
-
source: quux is a extend {
|
|
571
|
-
join_one: xyzzy is
|
|
572
|
-
duckdb.sql("""SELECT 1 as one""")
|
|
573
|
-
on ai = xyzzy.one
|
|
574
|
-
}
|
|
575
|
-
`;
|
|
576
|
-
const compileSql = m.translator.translate().compileSQL;
|
|
577
|
-
expect(compileSql).toBeDefined();
|
|
578
|
-
if (compileSql) {
|
|
579
|
-
m.translator.update({
|
|
580
|
-
compileSQL: (0, test_translator_1.getSelectOneStruct)(compileSql),
|
|
581
|
-
});
|
|
582
|
-
expect(m).toTranslate();
|
|
583
|
-
}
|
|
584
|
-
});
|
|
585
|
-
test('reference to sql expression in run', () => {
|
|
586
|
-
const m = new test_translator_1.TestTranslator(`
|
|
587
|
-
run: bigquery.sql("""select 1 as one""")
|
|
588
|
-
`);
|
|
589
|
-
expect(m).toParse();
|
|
590
|
-
const compileSql = m.translate().compileSQL;
|
|
591
|
-
expect(compileSql).toBeDefined();
|
|
592
|
-
if (compileSql) {
|
|
593
|
-
m.update({
|
|
594
|
-
compileSQL: (0, test_translator_1.getSelectOneStruct)(compileSql),
|
|
595
|
-
});
|
|
596
|
-
expect(m).toTranslate();
|
|
597
|
-
}
|
|
598
|
-
});
|
|
599
|
-
test('reference to sql expression in query def', () => {
|
|
600
|
-
const m = new test_translator_1.TestTranslator(`
|
|
601
|
-
query: q is bigquery.sql("""select 1 as one""")
|
|
602
|
-
`);
|
|
603
|
-
expect(m).toParse();
|
|
604
|
-
const compileSql = m.translate().compileSQL;
|
|
605
|
-
expect(compileSql).toBeDefined();
|
|
606
|
-
if (compileSql) {
|
|
607
|
-
m.update({
|
|
608
|
-
compileSQL: (0, test_translator_1.getSelectOneStruct)(compileSql),
|
|
609
|
-
});
|
|
610
|
-
expect(m).toTranslate();
|
|
611
|
-
}
|
|
612
|
-
});
|
|
613
|
-
test('reference to sql expression in anonymous query', () => {
|
|
614
|
-
const m = new test_translator_1.TestTranslator(`
|
|
615
|
-
run: bigquery.sql("""select 1 as one""")
|
|
616
|
-
`);
|
|
617
|
-
expect(m).toParse();
|
|
618
|
-
const compileSql = m.translate().compileSQL;
|
|
619
|
-
expect(compileSql).toBeDefined();
|
|
620
|
-
if (compileSql) {
|
|
621
|
-
m.update({
|
|
622
|
-
compileSQL: (0, test_translator_1.getSelectOneStruct)(compileSql),
|
|
623
|
-
});
|
|
624
|
-
expect(m).toTranslate();
|
|
625
|
-
}
|
|
626
|
-
});
|
|
627
|
-
test('cannot refine a SQL query saved as a query', () => {
|
|
628
|
-
const m = new test_translator_1.TestTranslator(`
|
|
629
|
-
query: q1 is bigquery.sql("""select 1 as one""")
|
|
630
|
-
run: q1 + { where: 1 = 1 }
|
|
631
|
-
`);
|
|
632
|
-
expect(m).toParse();
|
|
633
|
-
const compileSql = m.translate().compileSQL;
|
|
634
|
-
expect(compileSql).toBeDefined();
|
|
635
|
-
if (compileSql) {
|
|
636
|
-
m.update({
|
|
637
|
-
compileSQL: (0, test_translator_1.getSelectOneStruct)(compileSql),
|
|
638
|
-
});
|
|
639
|
-
expect(m).toLog((0, test_translator_1.errorMessage)('A raw query cannot be refined'));
|
|
640
|
-
}
|
|
641
|
-
});
|
|
642
|
-
test('cannot refine a SQL query directly', () => {
|
|
643
|
-
const m = new test_translator_1.TestTranslator(`
|
|
644
|
-
run: bigquery.sql("""select 1 as one""") + { where: 1 = 1 }
|
|
645
|
-
`);
|
|
646
|
-
expect(m).toParse();
|
|
647
|
-
const compileSql = m.translate().compileSQL;
|
|
648
|
-
expect(compileSql).toBeDefined();
|
|
649
|
-
if (compileSql) {
|
|
650
|
-
m.update({
|
|
651
|
-
compileSQL: (0, test_translator_1.getSelectOneStruct)(compileSql),
|
|
652
|
-
});
|
|
653
|
-
expect(m).toLog((0, test_translator_1.errorMessage)('Cannot add view refinements to a source'));
|
|
654
|
-
}
|
|
655
|
-
});
|
|
656
|
-
});
|
|
657
|
-
describe('extend and refine', () => {
|
|
658
|
-
describe('extend and refine, new syntax', () => {
|
|
659
|
-
test('query name with query refinements', () => {
|
|
660
|
-
expect(`
|
|
661
|
-
query: q is a -> { group_by: ai }
|
|
662
|
-
run: q + { group_by: ai2 is ai }
|
|
663
|
-
`).toTranslate();
|
|
664
|
-
});
|
|
665
|
-
test('query name with source refinements', () => {
|
|
666
|
-
expect(`
|
|
667
|
-
query: q is a -> { group_by: ai }
|
|
668
|
-
source: s is q extend { dimension: ai_2 is ai + ai }
|
|
669
|
-
`).toTranslate();
|
|
670
|
-
});
|
|
671
|
-
test('source name with query refinements', () => {
|
|
672
|
-
expect('run: a + { group_by: ai }').toLog((0, test_translator_1.errorMessage)("Cannot add view refinements to 'a' because it is a source"));
|
|
673
|
-
});
|
|
674
|
-
test('source name with source refinements', () => {
|
|
675
|
-
expect('source: s is a extend { dimension: ai_2 is ai + ai }').toTranslate();
|
|
676
|
-
});
|
|
677
|
-
test('query with source refinements', () => {
|
|
678
|
-
expect('source: s is a -> { group_by: ai } extend { dimension: ai_2 is ai + ai }').toTranslate();
|
|
679
|
-
});
|
|
680
|
-
test('query with extension then new stage', () => {
|
|
681
|
-
expect('run: a extend { dimension: x is 1 } -> { group_by: x }').toTranslate();
|
|
682
|
-
});
|
|
683
|
-
});
|
|
684
|
-
describe('extend and refine, old syntax', () => {
|
|
685
|
-
test('query name with query refinements', () => {
|
|
686
|
-
expect(`
|
|
687
|
-
query: q is a -> { group_by: ai }
|
|
688
|
-
run: q + { group_by: ai2 is ai }
|
|
689
|
-
`).toTranslate();
|
|
690
|
-
});
|
|
691
|
-
test('query name with source refinements', () => {
|
|
692
|
-
expect(`
|
|
693
|
-
query: q is a -> { group_by: ai }
|
|
694
|
-
source: s is q extend { dimension: ai_2 is ai + ai }
|
|
695
|
-
`).toTranslate();
|
|
696
|
-
});
|
|
697
|
-
test('source name with query refinements', () => {
|
|
698
|
-
expect('run: a + { group_by: one }').toLog((0, test_translator_1.errorMessage)("Cannot add view refinements to 'a' because it is a source"));
|
|
699
|
-
});
|
|
700
|
-
test('source name with source refinements', () => {
|
|
701
|
-
expect('source: s is a extend { dimension: ai_2 is ai + ai }').toTranslate();
|
|
702
|
-
});
|
|
703
|
-
describe('query name with ambiguous refinements', () => {
|
|
704
|
-
test('adding segment to ambuguously refined query', () => {
|
|
705
|
-
expect(`
|
|
706
|
-
##! -m4warnings
|
|
707
|
-
query: q is a -> { group_by: ai }
|
|
708
|
-
run: q + { join_one: b on 1 = 1 } -> { select: b.* }
|
|
709
|
-
`).toLog((0, test_translator_1.errorMessage)("No such field as 'b'"));
|
|
710
|
-
expect(`
|
|
711
|
-
##! -m4warnings
|
|
712
|
-
query: q is a -> { group_by: ai }
|
|
713
|
-
run: q + { where: 1 = 1 } -> { select: * }
|
|
714
|
-
`).toTranslate();
|
|
715
|
-
expect(`
|
|
716
|
-
##! -m4warnings
|
|
717
|
-
query: q is a -> { group_by: ai }
|
|
718
|
-
run: q + { declare: three is 3 } -> { select: * }
|
|
719
|
-
`).toTranslate();
|
|
720
|
-
});
|
|
721
|
-
test('automatically recognize this as a query refinement without new stage', () => {
|
|
722
|
-
expect(`
|
|
723
|
-
##! -m4warnings
|
|
724
|
-
query: q is a -> { group_by: ai }
|
|
725
|
-
run: q + { join_one: b on 1 = 1 }
|
|
726
|
-
`).toTranslate();
|
|
727
|
-
expect(`
|
|
728
|
-
##! -m4warnings
|
|
729
|
-
query: q is a -> { group_by: ai }
|
|
730
|
-
run: q + { where: 1 = 1 }
|
|
731
|
-
`).toTranslate();
|
|
732
|
-
expect(`
|
|
733
|
-
##! -m4warnings
|
|
734
|
-
query: q is a -> { group_by: ai }
|
|
735
|
-
run: q + { declare: three is 3 }
|
|
736
|
-
`).toTranslate();
|
|
737
|
-
});
|
|
738
|
-
test('can extend a query into a source', () => {
|
|
739
|
-
expect(`
|
|
740
|
-
query: q is a -> { group_by: ai }
|
|
741
|
-
source: s is q extend { join_one: b on b.ai = ai }
|
|
742
|
-
`).toTranslate();
|
|
743
|
-
});
|
|
744
|
-
test('can also add an arrow to clarify that it is a query', () => {
|
|
745
|
-
// Of course, number 1 and 3 are actually useless because you're defining
|
|
746
|
-
// something and then not using it...
|
|
747
|
-
expect(`
|
|
748
|
-
##! -m4warnings
|
|
749
|
-
query: q is a -> { group_by: ai }
|
|
750
|
-
run: q + { join_one: b on b.ai = ai } -> { select: * }
|
|
751
|
-
`).toTranslate();
|
|
752
|
-
expect(`
|
|
753
|
-
##! -m4warnings
|
|
754
|
-
query: q is a -> { group_by: ai }
|
|
755
|
-
run: q + { where: 1 = 1 } -> { select: * }
|
|
756
|
-
`).toTranslate();
|
|
757
|
-
expect(`
|
|
758
|
-
##! -m4warnings
|
|
759
|
-
query: q is a -> { group_by: ai }
|
|
760
|
-
run: q + { declare: three is 3 } -> { select: * }
|
|
761
|
-
`).toTranslate();
|
|
762
|
-
// Alternatively, fix 1 and 3 by actually using the declared thing
|
|
763
|
-
expect(`
|
|
764
|
-
##! -m4warnings
|
|
765
|
-
query: q is a -> { group_by: ai }
|
|
766
|
-
run: q + { join_one: b on b.ai = ai; group_by: ai2 is b.ai }
|
|
767
|
-
`).toTranslate();
|
|
768
|
-
expect(`
|
|
769
|
-
##! -m4warnings
|
|
770
|
-
query: q is a -> { group_by: ai }
|
|
771
|
-
run: q + { extend: {dimension: three is 3} group_by: three }
|
|
772
|
-
`).toTranslate();
|
|
773
|
-
});
|
|
774
|
-
});
|
|
775
|
-
});
|
|
776
|
-
describe('extend and refine fallout', () => {
|
|
777
|
-
test('syntactically valid to run a source, but still illegal', () => {
|
|
778
|
-
expect('run: a').toLog((0, test_translator_1.errorMessage)("Illegal reference to 'a', query expected"));
|
|
779
|
-
});
|
|
780
|
-
test('syntactically valid to refine a source, but illegal', () => {
|
|
781
|
-
expect('run: a + { group_by: ai } ').toLog((0, test_translator_1.errorMessage)("Cannot add view refinements to 'a' because it is a source"));
|
|
782
|
-
});
|
|
783
|
-
});
|
|
784
|
-
describe('turtles', () => {
|
|
785
|
-
test('explicit refine in turtle works', () => {
|
|
786
|
-
expect(`source: c is a extend {
|
|
787
|
-
view: x is { select: * }
|
|
788
|
-
view: y is x + { limit: 1 }
|
|
789
|
-
}`).toTranslate();
|
|
790
|
-
});
|
|
791
|
-
test('implicit refine in turtle works', () => {
|
|
792
|
-
expect(`source: c is a extend {
|
|
793
|
-
view: x is { select: * }
|
|
794
|
-
view: y is x + { limit: 1 }
|
|
795
|
-
}`).toTranslate();
|
|
796
|
-
});
|
|
797
|
-
});
|
|
798
|
-
describe('nests', () => {
|
|
799
|
-
test('explicit refine in nest', () => {
|
|
800
|
-
expect(`source: c is a extend {
|
|
801
|
-
view: x is { select: * }
|
|
802
|
-
}
|
|
803
|
-
|
|
804
|
-
run: c -> {
|
|
805
|
-
nest: x + { limit: 1 }
|
|
806
|
-
}`).toTranslate();
|
|
807
|
-
});
|
|
808
|
-
test('refine in nest', () => {
|
|
809
|
-
expect(`source: c is a extend {
|
|
810
|
-
view: x is { select: * }
|
|
811
|
-
}
|
|
812
|
-
|
|
813
|
-
run: c -> {
|
|
814
|
-
nest: x + { limit: 1 }
|
|
815
|
-
}`).toTranslate();
|
|
816
|
-
});
|
|
817
|
-
});
|
|
818
|
-
});
|
|
819
|
-
describe('miscellaneous m4 warnings', () => {
|
|
820
|
-
test('project is deprecated', () => {
|
|
821
|
-
expect(`
|
|
822
|
-
##! m4warnings=warn
|
|
823
|
-
query: q is a -> { project: * }
|
|
824
|
-
`).toLog((0, test_translator_1.warningMessage)('project: keyword is deprecated, use select:'));
|
|
825
|
-
});
|
|
826
|
-
test('query leading arrow', () => {
|
|
827
|
-
expect(`
|
|
828
|
-
##! m4warnings=warn
|
|
829
|
-
query: x is a -> { select: * }
|
|
830
|
-
run: x
|
|
831
|
-
`).toTranslate();
|
|
832
|
-
expect(`
|
|
833
|
-
##! m4warnings=warn
|
|
834
|
-
query: x is a -> { select: * }
|
|
835
|
-
run: x -> { select: * }
|
|
836
|
-
`).toTranslate();
|
|
837
|
-
});
|
|
838
|
-
});
|
|
839
|
-
describe('m3/m4 source query sentences', () => {
|
|
840
|
-
const srcExtend = '{accept:ai}';
|
|
841
|
-
const qryRefine = '{limit:1}';
|
|
842
|
-
const query = '{select:*}';
|
|
843
|
-
// todo MTOY write test to make sure arrow has correct precedence vs +
|
|
844
|
-
// also maybe arrow vs extend
|
|
845
|
-
test('M4 should error on these sq expressions', () => {
|
|
846
|
-
expect(`source: s is a + ${qryRefine}`).toLog((0, test_translator_1.errorMessage)("Cannot add view refinements to 'a' because it is a source"));
|
|
847
|
-
expect(`query: q_m4_err is a + ${qryRefine}`).toLog((0, test_translator_1.errorMessage)("Cannot add view refinements to 'a' because it is a source"));
|
|
848
|
-
});
|
|
849
|
-
test('legal sqexpressions', () => {
|
|
850
|
-
// some things that are m4 warnings are commented out
|
|
851
|
-
expect(`
|
|
852
|
-
source: s is a
|
|
853
|
-
query: q is s -> ${query}
|
|
854
|
-
|
|
855
|
-
source: s0 is a;
|
|
856
|
-
// source: s0_extbare is s ${srcExtend};
|
|
857
|
-
// source: s0_extplus is s + ${srcExtend};
|
|
858
|
-
source: s0_ext is s extend ${srcExtend};
|
|
859
|
-
source: qs is q;
|
|
860
|
-
source: qs0 is q extend ${srcExtend};
|
|
861
|
-
source: qs1 is q + ${qryRefine};
|
|
862
|
-
source: s1_m4 is q + ${qryRefine};
|
|
863
|
-
source: s2_m4 is q + ${qryRefine} -> ${query} extend ${srcExtend};
|
|
864
|
-
source: s3 is s extend ${srcExtend};
|
|
865
|
-
source: s4 is q extend ${srcExtend};
|
|
866
|
-
// source: s5 is from(s -> ${query})
|
|
867
|
-
|
|
868
|
-
query: q0 is q;
|
|
869
|
-
// query: q0_refbare is q ${qryRefine};
|
|
870
|
-
query: q0_refplus is q + ${qryRefine};
|
|
871
|
-
// query: q1_bare is ab -> aturtle ${qryRefine};
|
|
872
|
-
query: q1_plus is ab -> aturtle + ${qryRefine};
|
|
873
|
-
query: q2 is s -> ${query} extend ${srcExtend} -> ${query};
|
|
874
|
-
|
|
875
|
-
`).toTranslate();
|
|
876
|
-
});
|
|
877
|
-
});
|
|
878
|
-
describe('sql_functions', () => {
|
|
879
|
-
test('can aggregate sql function', () => {
|
|
880
|
-
expect((0, test_translator_1.markSource) `
|
|
881
|
-
##! experimental { sql_functions }
|
|
882
|
-
source: s is a extend {
|
|
883
|
-
measure: c is count()
|
|
884
|
-
dimension: sql_expr is sql_number("LENGTH(\${TABLE}.astr)")
|
|
885
|
-
}
|
|
886
|
-
run: s -> {
|
|
887
|
-
aggregate: v is sql_expr.sum()
|
|
888
|
-
}
|
|
889
|
-
`).toTranslate();
|
|
890
|
-
});
|
|
891
|
-
});
|
|
892
|
-
test('non breaking space in source', () => expect('source:\u00a0z\u00a0is\u00a0a').toParse());
|
|
893
|
-
//# sourceMappingURL=parse.spec.js.map
|