@malloydata/malloy 0.0.119-dev240123183113 → 0.0.119-dev240124170348
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/index.d.ts +2 -2
- package/dist/index.js +1 -3
- package/dist/lang/ast/error-factory.js +3 -3
- package/dist/lang/ast/expressions/expr-aggregate-function.d.ts +6 -4
- package/dist/lang/ast/expressions/expr-aggregate-function.js +24 -13
- package/dist/lang/ast/expressions/expr-count.js +1 -1
- package/dist/lang/ast/expressions/expr-func.js +4 -4
- package/dist/lang/ast/expressions/expr-id-reference.js +2 -2
- package/dist/lang/ast/field-space/dynamic-space.js +1 -1
- package/dist/lang/ast/field-space/index-field-space.d.ts +5 -3
- package/dist/lang/ast/field-space/index-field-space.js +85 -16
- package/dist/lang/ast/field-space/query-input-space.d.ts +3 -3
- package/dist/lang/ast/field-space/query-spaces.d.ts +15 -12
- package/dist/lang/ast/field-space/query-spaces.js +60 -57
- package/dist/lang/ast/field-space/reference-field.d.ts +0 -5
- package/dist/lang/ast/field-space/reference-field.js +16 -46
- package/dist/lang/ast/query-builders/index-builder.d.ts +2 -2
- package/dist/lang/ast/query-builders/partial-builder.js +28 -1
- package/dist/lang/ast/query-builders/project-builder.d.ts +8 -4
- package/dist/lang/ast/query-builders/project-builder.js +5 -6
- package/dist/lang/ast/query-builders/reduce-builder.d.ts +13 -8
- package/dist/lang/ast/query-builders/reduce-builder.js +12 -9
- package/dist/lang/ast/query-items/field-declaration.d.ts +1 -1
- package/dist/lang/ast/query-items/field-references.d.ts +5 -1
- package/dist/lang/ast/query-items/field-references.js +17 -1
- package/dist/lang/ast/query-properties/qop-desc.d.ts +2 -2
- package/dist/lang/ast/source-properties/joins.js +1 -1
- package/dist/lang/ast/types/field-space.d.ts +2 -2
- package/dist/lang/ast/types/query-builder.d.ts +2 -2
- package/dist/lang/ast/view-elements/qop-desc-view.d.ts +3 -3
- package/dist/lang/ast/view-elements/reference-view.d.ts +3 -3
- package/dist/lang/ast/view-elements/reference-view.js +1 -1
- package/dist/lang/ast/view-elements/refine-utils.js +20 -18
- package/dist/lang/ast/view-elements/view-arrow.d.ts +2 -2
- package/dist/lang/ast/view-elements/view-refine.d.ts +3 -3
- package/dist/lang/ast/view-elements/view.d.ts +4 -4
- package/dist/lang/field-utils.d.ts +3 -3
- package/dist/lang/field-utils.js +9 -8
- package/dist/lang/test/annotation.spec.js +26 -27
- package/dist/lang/test/expressions.spec.js +43 -40
- package/dist/lang/test/field-symbols.spec.js +4 -4
- package/dist/lang/test/locations.spec.js +13 -6
- package/dist/lang/test/query.spec.js +44 -56
- package/dist/lang/test/test-translator.d.ts +3 -2
- package/dist/lang/test/test-translator.js +30 -24
- package/dist/malloy.js +1 -1
- package/dist/model/malloy_query.d.ts +14 -36
- package/dist/model/malloy_query.js +145 -312
- package/dist/model/malloy_types.d.ts +18 -23
- package/dist/model/malloy_types.js +2 -15
- package/dist/tags.js +19 -4
- package/package.json +1 -1
|
@@ -408,7 +408,7 @@ describe('query operation annotations', () => {
|
|
|
408
408
|
const foundYou = m.getQuery('findme');
|
|
409
409
|
expect(foundYou).toBeDefined();
|
|
410
410
|
if (foundYou) {
|
|
411
|
-
const note_a = (0, test_translator_1.
|
|
411
|
+
const note_a = (0, test_translator_1.getQueryFieldDef)(foundYou.pipeline[0], 'note_a');
|
|
412
412
|
expect(note_a === null || note_a === void 0 ? void 0 : note_a.annotation).matchesAnnotation(defaultTags);
|
|
413
413
|
}
|
|
414
414
|
});
|
|
@@ -434,7 +434,7 @@ describe('query operation annotations', () => {
|
|
|
434
434
|
const foundYou = m.getQuery(0);
|
|
435
435
|
expect(foundYou).toBeDefined();
|
|
436
436
|
if (foundYou) {
|
|
437
|
-
const note_a = (0, test_translator_1.
|
|
437
|
+
const note_a = (0, test_translator_1.getQueryFieldDef)(foundYou.pipeline[0], 'note_a');
|
|
438
438
|
expect(note_a === null || note_a === void 0 ? void 0 : note_a.annotation).matchesAnnotation({
|
|
439
439
|
inherits: defaultTags,
|
|
440
440
|
blockNotes: ['# note1\n'],
|
|
@@ -459,7 +459,7 @@ describe('query operation annotations', () => {
|
|
|
459
459
|
const foundYou = m.getQuery('findme');
|
|
460
460
|
expect(foundYou).toBeDefined();
|
|
461
461
|
if (foundYou) {
|
|
462
|
-
const note_a = (0, test_translator_1.
|
|
462
|
+
const note_a = (0, test_translator_1.getQueryFieldDef)(foundYou.pipeline[0], 'note_a');
|
|
463
463
|
expect(note_a === null || note_a === void 0 ? void 0 : note_a.annotation).matchesAnnotation(defaultTags);
|
|
464
464
|
}
|
|
465
465
|
});
|
|
@@ -481,7 +481,7 @@ describe('query operation annotations', () => {
|
|
|
481
481
|
const foundYou = m.getQuery('findme');
|
|
482
482
|
expect(foundYou).toBeDefined();
|
|
483
483
|
if (foundYou) {
|
|
484
|
-
const note_a = (0, test_translator_1.
|
|
484
|
+
const note_a = (0, test_translator_1.getQueryFieldDef)(foundYou.pipeline[0], 'note_a');
|
|
485
485
|
expect(note_a === null || note_a === void 0 ? void 0 : note_a.annotation).matchesAnnotation(defaultTags);
|
|
486
486
|
}
|
|
487
487
|
});
|
|
@@ -508,7 +508,7 @@ describe('query operation annotations', () => {
|
|
|
508
508
|
const foundYou = m.getQuery('findme');
|
|
509
509
|
expect(foundYou).toBeDefined();
|
|
510
510
|
if (foundYou) {
|
|
511
|
-
const note_a = (0, test_translator_1.
|
|
511
|
+
const note_a = (0, test_translator_1.getQueryFieldDef)(foundYou.pipeline[0], 'note_a');
|
|
512
512
|
expect(note_a === null || note_a === void 0 ? void 0 : note_a.annotation).matchesAnnotation({
|
|
513
513
|
blockNotes: ['# note1\n'],
|
|
514
514
|
inherits: defaultTags,
|
|
@@ -533,7 +533,7 @@ describe('query operation annotations', () => {
|
|
|
533
533
|
const foundYou = m.getQuery('findme');
|
|
534
534
|
expect(foundYou).toBeDefined();
|
|
535
535
|
if (foundYou) {
|
|
536
|
-
const note_a = (0, test_translator_1.
|
|
536
|
+
const note_a = (0, test_translator_1.getQueryFieldDef)(foundYou.pipeline[0], 'note_a');
|
|
537
537
|
expect(note_a === null || note_a === void 0 ? void 0 : note_a.annotation).matchesAnnotation(defaultTags);
|
|
538
538
|
}
|
|
539
539
|
});
|
|
@@ -560,7 +560,7 @@ describe('query operation annotations', () => {
|
|
|
560
560
|
const foundYou = m.getQuery('findme');
|
|
561
561
|
expect(foundYou).toBeDefined();
|
|
562
562
|
if (foundYou) {
|
|
563
|
-
const note_a = (0, test_translator_1.
|
|
563
|
+
const note_a = (0, test_translator_1.getQueryFieldDef)(foundYou.pipeline[0], 'note_a');
|
|
564
564
|
expect(note_a === null || note_a === void 0 ? void 0 : note_a.annotation).matchesAnnotation({
|
|
565
565
|
blockNotes: ['# note1\n'],
|
|
566
566
|
inherits: defaultTags,
|
|
@@ -585,7 +585,7 @@ describe('query operation annotations', () => {
|
|
|
585
585
|
const foundYou = m.getQuery('findme');
|
|
586
586
|
expect(foundYou).toBeDefined();
|
|
587
587
|
if (foundYou) {
|
|
588
|
-
const note_a = (0, test_translator_1.
|
|
588
|
+
const note_a = (0, test_translator_1.getQueryFieldDef)(foundYou.pipeline[0], 'note_a');
|
|
589
589
|
expect(note_a === null || note_a === void 0 ? void 0 : note_a.annotation).matchesAnnotation(defaultTags);
|
|
590
590
|
}
|
|
591
591
|
});
|
|
@@ -600,7 +600,7 @@ describe('query operation annotations', () => {
|
|
|
600
600
|
const foundYou = m.getQuery(0);
|
|
601
601
|
expect(foundYou).toBeDefined();
|
|
602
602
|
if (foundYou) {
|
|
603
|
-
const note_b = (0, test_translator_1.
|
|
603
|
+
const note_b = (0, test_translator_1.getQueryFieldDef)(foundYou.pipeline[0], 'note_b');
|
|
604
604
|
expect(note_b === null || note_b === void 0 ? void 0 : note_b.annotation).matchesAnnotation({ inherits: defaultTags });
|
|
605
605
|
}
|
|
606
606
|
});
|
|
@@ -626,30 +626,29 @@ describe('query operation annotations', () => {
|
|
|
626
626
|
const foundYou = m.translator.getQuery(0);
|
|
627
627
|
expect(foundYou).toBeDefined();
|
|
628
628
|
if (foundYou) {
|
|
629
|
-
const note_b = (0, test_translator_1.
|
|
629
|
+
const note_b = (0, test_translator_1.getQueryFieldDef)(foundYou.pipeline[0], 'xdim');
|
|
630
630
|
expect(note_b === null || note_b === void 0 ? void 0 : note_b.annotation).matchesAnnotation({ inherits: defaultTags });
|
|
631
631
|
}
|
|
632
632
|
});
|
|
633
|
-
test('
|
|
634
|
-
const m = (0, test_translator_1.model) `
|
|
635
|
-
|
|
636
|
-
|
|
637
|
-
one_count is sum(pick 1 when ai = 1 else 0)
|
|
638
|
-
pct_one is one_count / count();
|
|
639
|
-
}
|
|
640
|
-
|
|
641
|
-
run: na extend {
|
|
642
|
-
join_many: na on ai = na.ai
|
|
633
|
+
test('a reference can have an annotation', () => {
|
|
634
|
+
const m = (0, test_translator_1.model) `run: a extend {
|
|
635
|
+
# noted
|
|
636
|
+
dimension: noted_str is astr
|
|
643
637
|
} -> {
|
|
644
|
-
|
|
645
|
-
|
|
646
|
-
|
|
647
|
-
na.pct_one
|
|
638
|
+
select:
|
|
639
|
+
# note
|
|
640
|
+
noted_str
|
|
648
641
|
}`;
|
|
649
642
|
expect(m).toTranslate();
|
|
650
|
-
const
|
|
651
|
-
|
|
652
|
-
|
|
643
|
+
const foundYou = m.translator.getQuery(0);
|
|
644
|
+
expect(foundYou).toBeDefined();
|
|
645
|
+
if (foundYou) {
|
|
646
|
+
const astr = (0, test_translator_1.getQueryFieldDef)(foundYou.pipeline[0], 'noted_str');
|
|
647
|
+
expect(astr === null || astr === void 0 ? void 0 : astr.annotation).matchesAnnotation({
|
|
648
|
+
inherits: { blockNotes: ['# noted\n'] },
|
|
649
|
+
notes: ['# note\n'],
|
|
650
|
+
});
|
|
651
|
+
}
|
|
653
652
|
});
|
|
654
653
|
});
|
|
655
654
|
//# sourceMappingURL=annotation.spec.js.map
|
|
@@ -539,46 +539,49 @@ describe('expressions', () => {
|
|
|
539
539
|
expect(queryDef).toBeDefined();
|
|
540
540
|
expect(queryDef === null || queryDef === void 0 ? void 0 : queryDef.type).toBe('query');
|
|
541
541
|
if (queryDef && queryDef.type === 'query') {
|
|
542
|
-
const
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
x.type
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
'
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
|
|
580
|
-
|
|
581
|
-
|
|
542
|
+
const qSeg = queryDef.pipeline[0];
|
|
543
|
+
expect(qSeg.type).toEqual('reduce');
|
|
544
|
+
if (qSeg.type === 'reduce') {
|
|
545
|
+
const x = qSeg.queryFields[0];
|
|
546
|
+
if (x.type !== 'fieldref' &&
|
|
547
|
+
(0, model_1.isFieldTypeDef)(x) &&
|
|
548
|
+
x.type === 'number' &&
|
|
549
|
+
x.e) {
|
|
550
|
+
expect(x).toMatchObject({
|
|
551
|
+
'e': [
|
|
552
|
+
{
|
|
553
|
+
'function': 'numberLiteral',
|
|
554
|
+
'literal': '1',
|
|
555
|
+
'type': 'dialect',
|
|
556
|
+
},
|
|
557
|
+
// TODO not sure why there are TWO sets of parentheses... A previous version of this test
|
|
558
|
+
// just checked that there were ANY parens, so that went under the radar. Not fixing now.
|
|
559
|
+
'+((',
|
|
560
|
+
{
|
|
561
|
+
'denominator': [
|
|
562
|
+
{
|
|
563
|
+
'function': 'numberLiteral',
|
|
564
|
+
'literal': '4',
|
|
565
|
+
'type': 'dialect',
|
|
566
|
+
},
|
|
567
|
+
],
|
|
568
|
+
'function': 'div',
|
|
569
|
+
'numerator': [
|
|
570
|
+
{
|
|
571
|
+
'function': 'numberLiteral',
|
|
572
|
+
'literal': '3',
|
|
573
|
+
'type': 'dialect',
|
|
574
|
+
},
|
|
575
|
+
],
|
|
576
|
+
'type': 'dialect',
|
|
577
|
+
},
|
|
578
|
+
'))',
|
|
579
|
+
],
|
|
580
|
+
});
|
|
581
|
+
}
|
|
582
|
+
else {
|
|
583
|
+
fail('expression with parens compiled oddly');
|
|
584
|
+
}
|
|
582
585
|
}
|
|
583
586
|
}
|
|
584
587
|
});
|
|
@@ -109,7 +109,7 @@ describe('structdef comprehension', () => {
|
|
|
109
109
|
dialect: 'standardsql',
|
|
110
110
|
structRelationship: {
|
|
111
111
|
type: 'nested',
|
|
112
|
-
|
|
112
|
+
fieldName: 'a',
|
|
113
113
|
isArray: false,
|
|
114
114
|
},
|
|
115
115
|
structSource: { type: 'nested' },
|
|
@@ -145,9 +145,9 @@ describe('structdef comprehension', () => {
|
|
|
145
145
|
type: 'one',
|
|
146
146
|
matrixOperation: 'left',
|
|
147
147
|
onExpression: [
|
|
148
|
-
{ type: 'field', path: 'aKey' },
|
|
148
|
+
{ type: 'field', path: ['aKey'] },
|
|
149
149
|
'=',
|
|
150
|
-
{ type: 'field', path: 't
|
|
150
|
+
{ type: 'field', path: ['t', 'a'] },
|
|
151
151
|
],
|
|
152
152
|
},
|
|
153
153
|
structSource: { type: 'table', tablePath: 't' },
|
|
@@ -166,7 +166,7 @@ describe('structdef comprehension', () => {
|
|
|
166
166
|
pipeline: [
|
|
167
167
|
{
|
|
168
168
|
type: 'reduce',
|
|
169
|
-
|
|
169
|
+
queryFields: [{ type: 'fieldref', path: ['a'] }],
|
|
170
170
|
},
|
|
171
171
|
],
|
|
172
172
|
};
|
|
@@ -72,8 +72,11 @@ describe('source locations', () => {
|
|
|
72
72
|
expect(m).toTranslate();
|
|
73
73
|
const na = (0, test_translator_1.getExplore)(m.modelDef, 'na');
|
|
74
74
|
const x = (0, test_translator_1.getQueryField)(na, 'x');
|
|
75
|
-
const y = (0, test_translator_1.
|
|
76
|
-
|
|
75
|
+
const y = (0, test_translator_1.getQueryFieldDef)(x.pipeline[0], 'y');
|
|
76
|
+
// mtoy todo ask chris if there should be locations
|
|
77
|
+
if (y.type !== 'fieldref') {
|
|
78
|
+
expect(y.location).toMatchObject(source.locations[0]);
|
|
79
|
+
}
|
|
77
80
|
});
|
|
78
81
|
test('location of filtered field inside a view', () => {
|
|
79
82
|
const source = (0, test_translator_1.markSource) `
|
|
@@ -87,8 +90,10 @@ describe('source locations', () => {
|
|
|
87
90
|
expect(m).toTranslate();
|
|
88
91
|
const na = (0, test_translator_1.getExplore)(m.modelDef, 'na');
|
|
89
92
|
const x = (0, test_translator_1.getQueryField)(na, 'x');
|
|
90
|
-
const z = (0, test_translator_1.
|
|
91
|
-
|
|
93
|
+
const z = (0, test_translator_1.getQueryFieldDef)(x.pipeline[0], 'z');
|
|
94
|
+
if (z.type !== 'fieldref') {
|
|
95
|
+
expect(z.location).toMatchObject(source.locations[0]);
|
|
96
|
+
}
|
|
92
97
|
});
|
|
93
98
|
test('location of field inherited from table', () => {
|
|
94
99
|
const source = (0, test_translator_1.markSource) `source: na is ${"_db_.table('aTable')"}`;
|
|
@@ -144,8 +149,10 @@ describe('source locations', () => {
|
|
|
144
149
|
const m = new test_translator_1.TestTranslator(source.code);
|
|
145
150
|
expect(m).toTranslate();
|
|
146
151
|
const q = (0, test_translator_1.getModelQuery)(m.modelDef, 'q');
|
|
147
|
-
const a = (0, test_translator_1.
|
|
148
|
-
|
|
152
|
+
const a = (0, test_translator_1.getQueryFieldDef)(q.pipeline[0], 'b');
|
|
153
|
+
if (a.type !== 'fieldref') {
|
|
154
|
+
expect(a.location).toMatchObject(source.locations[0]);
|
|
155
|
+
}
|
|
149
156
|
});
|
|
150
157
|
test('pre m4 location of named SQL block', () => {
|
|
151
158
|
const source = (0, test_translator_1.markSource) `##! -m4warnings\n${'sql: s is { select: """SELECT 1 as one""" }'}`;
|
|
@@ -25,6 +25,23 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
25
25
|
const test_translator_1 = require("./test-translator");
|
|
26
26
|
require("./parse-expects");
|
|
27
27
|
const model_1 = require("../../model");
|
|
28
|
+
function getFirstQuerySegment(q) {
|
|
29
|
+
const qSeg = q === null || q === void 0 ? void 0 : q.pipeline[0];
|
|
30
|
+
if (qSeg && (0, model_1.isQuerySegment)(qSeg)) {
|
|
31
|
+
return qSeg;
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
function getFirstSegmentFields(q) {
|
|
35
|
+
var _a;
|
|
36
|
+
const qSeg = getFirstQuerySegment(q);
|
|
37
|
+
return (_a = qSeg === null || qSeg === void 0 ? void 0 : qSeg.queryFields) !== null && _a !== void 0 ? _a : [];
|
|
38
|
+
}
|
|
39
|
+
function getFirstSegmentFieldNames(q) {
|
|
40
|
+
const qf = getFirstSegmentFields(q);
|
|
41
|
+
return qf.map(f => f.type === 'fieldref' && f.path.length === 1
|
|
42
|
+
? f.path[0]
|
|
43
|
+
: `expected simple ref, got ${JSON.stringify(f)}`);
|
|
44
|
+
}
|
|
28
45
|
describe('query:', () => {
|
|
29
46
|
describe('basic query syntax', () => {
|
|
30
47
|
test('run:anonymous query', () => expect('run: a -> { group_by: astr }').toTranslate());
|
|
@@ -93,11 +110,10 @@ describe('query:', () => {
|
|
|
93
110
|
query: nq is q + { group_by: astr }
|
|
94
111
|
`);
|
|
95
112
|
expect(x).toTranslate();
|
|
96
|
-
const q = x.getQuery('q');
|
|
113
|
+
const q = getFirstSegmentFields(x.getQuery('q'));
|
|
97
114
|
expect(q).toBeDefined();
|
|
98
115
|
if (q) {
|
|
99
|
-
|
|
100
|
-
expect(qFields.length).toBe(1);
|
|
116
|
+
expect(q.length).toBe(1);
|
|
101
117
|
}
|
|
102
118
|
});
|
|
103
119
|
test('query composition preserves original', () => {
|
|
@@ -462,13 +478,9 @@ describe('query:', () => {
|
|
|
462
478
|
calculate: l is lag(ai, 1, pi)
|
|
463
479
|
}`).translationToFailWith("Parameter 3 ('default') of lag must be literal or constant, but received output");
|
|
464
480
|
});
|
|
465
|
-
// TODO we don't handle referencing a join as a field correctly in all cases today.
|
|
466
|
-
// For now, it at least is considered type `struct` and therefore fails to parse
|
|
467
|
-
// as a function argument.
|
|
468
|
-
// We add <join_name>_id to the query, but it's not included in the output space
|
|
469
481
|
test('cannot use struct in function arg', () => {
|
|
470
|
-
expect(`run:
|
|
471
|
-
group_by: b
|
|
482
|
+
expect(`run: ab -> {
|
|
483
|
+
group_by: b.astr
|
|
472
484
|
calculate: foo is lag(b)
|
|
473
485
|
}`).translationToFailWith('No matching overload for function lag(struct)');
|
|
474
486
|
});
|
|
@@ -558,12 +570,6 @@ describe('query:', () => {
|
|
|
558
570
|
expect(`run: a -> {
|
|
559
571
|
extend: { join_one: b with astr }
|
|
560
572
|
group_by: b.ai
|
|
561
|
-
}`).toTranslate();
|
|
562
|
-
});
|
|
563
|
-
test('reference join name in group_by', () => {
|
|
564
|
-
expect(`run: a -> {
|
|
565
|
-
extend: { join_one: b with astr }
|
|
566
|
-
group_by: b
|
|
567
573
|
}`).toTranslate();
|
|
568
574
|
});
|
|
569
575
|
test('can reference select: inline join.* field in calculate', () => {
|
|
@@ -608,26 +614,22 @@ describe('query:', () => {
|
|
|
608
614
|
expect('run: a->{ select: astr, ai calculate: num is lag(ai) }').toTranslate();
|
|
609
615
|
});
|
|
610
616
|
test('aggregate reference', () => {
|
|
611
|
-
var _a;
|
|
612
617
|
const doc = (0, test_translator_1.model) `run: a->{ aggregate: ai.sum() }`;
|
|
613
618
|
expect(doc).toTranslate();
|
|
614
|
-
const q = doc.translator.getQuery(0);
|
|
619
|
+
const q = getFirstSegmentFields(doc.translator.getQuery(0));
|
|
615
620
|
expect(q).toBeDefined();
|
|
616
|
-
|
|
617
|
-
expect(ai).toMatchObject({
|
|
621
|
+
expect(q[0]).toMatchObject({
|
|
618
622
|
name: 'ai',
|
|
619
623
|
type: 'number',
|
|
620
624
|
expressionType: 'aggregate',
|
|
621
625
|
});
|
|
622
626
|
});
|
|
623
627
|
test('timeunit reference', () => {
|
|
624
|
-
var _a;
|
|
625
628
|
const doc = (0, test_translator_1.model) `run: a->{ group_by: ats.day }`;
|
|
626
629
|
expect(doc).toTranslate();
|
|
627
|
-
const q = doc.translator.getQuery(0);
|
|
630
|
+
const q = getFirstSegmentFields(doc.translator.getQuery(0));
|
|
628
631
|
expect(q).toBeDefined();
|
|
629
|
-
|
|
630
|
-
expect(ats).toMatchObject({ name: 'ats', type: 'timestamp' });
|
|
632
|
+
expect(q[0]).toMatchObject({ name: 'ats', type: 'timestamp' });
|
|
631
633
|
});
|
|
632
634
|
test('aggregate multiple', () => {
|
|
633
635
|
expect(`
|
|
@@ -646,27 +648,23 @@ describe('query:', () => {
|
|
|
646
648
|
test('expands star correctly', () => {
|
|
647
649
|
const selstar = (0, test_translator_1.model) `run: ab->{select: *}`;
|
|
648
650
|
expect(selstar).toTranslate();
|
|
649
|
-
const
|
|
650
|
-
expect(
|
|
651
|
-
const fields = query.pipeline[0].fields;
|
|
652
|
-
expect(fields.sort()).toEqual(afields);
|
|
651
|
+
const fields = getFirstSegmentFieldNames(selstar.translator.getQuery(0));
|
|
652
|
+
expect(fields).toEqual(afields);
|
|
653
653
|
});
|
|
654
654
|
test('expands join dot star correctly', () => {
|
|
655
655
|
const selstar = (0, test_translator_1.model) `run: ab->{select: b.*}`;
|
|
656
656
|
expect(selstar).toTranslate();
|
|
657
657
|
const query = selstar.translator.getQuery(0);
|
|
658
658
|
expect(query).toBeDefined();
|
|
659
|
-
const fields =
|
|
660
|
-
expect(fields
|
|
659
|
+
const fields = getFirstSegmentFields(selstar.translator.getQuery(0)).map(f => (f.type === 'fieldref' ? f.path : `wrong field type ${f.type}`));
|
|
660
|
+
expect(fields).toEqual(afields.map(f => ['b', f]));
|
|
661
661
|
});
|
|
662
662
|
test('expands star with exclusions', () => {
|
|
663
663
|
const selstar = (0, test_translator_1.model) `run: ab->{select: * { except: ai, except: aun, aweird }}`;
|
|
664
664
|
const filterdFields = afields.filter(f => f !== 'ai' && f !== 'aun' && f !== 'aweird');
|
|
665
665
|
expect(selstar).toTranslate();
|
|
666
|
-
const
|
|
667
|
-
expect(
|
|
668
|
-
const fields = query.pipeline[0].fields;
|
|
669
|
-
expect(fields.sort()).toEqual(filterdFields);
|
|
666
|
+
const fields = getFirstSegmentFieldNames(selstar.translator.getQuery(0));
|
|
667
|
+
expect(fields).toEqual(filterdFields);
|
|
670
668
|
});
|
|
671
669
|
test('star error checking', () => {
|
|
672
670
|
expect((0, test_translator_1.markSource) `run: a->{select: ${'zzz'}.*}`).translationToFailWith("No such field as 'zzz'");
|
|
@@ -686,10 +684,8 @@ describe('query:', () => {
|
|
|
686
684
|
test('regress check extend: and star', () => {
|
|
687
685
|
const m = (0, test_translator_1.model) `run: ab->{ extend: {dimension: x is 1} select: * }`;
|
|
688
686
|
expect(m).toTranslate();
|
|
689
|
-
const
|
|
690
|
-
expect(
|
|
691
|
-
const fields = q.pipeline[0].fields;
|
|
692
|
-
expect(fields.sort()).toEqual(afields.concat('x'));
|
|
687
|
+
const fields = getFirstSegmentFieldNames(m.translator.getQuery(0));
|
|
688
|
+
expect(fields).toEqual(afields.concat('x'));
|
|
693
689
|
});
|
|
694
690
|
test('project def', () => {
|
|
695
691
|
expect('run: ab->{ select: one is 1 }').toTranslate();
|
|
@@ -701,10 +697,8 @@ describe('query:', () => {
|
|
|
701
697
|
test('regress check extend: and star', () => {
|
|
702
698
|
const m = (0, test_translator_1.model) `run: ab->{ extend: {dimension: x is 1} select: * }`;
|
|
703
699
|
expect(m).toTranslate();
|
|
704
|
-
const
|
|
705
|
-
expect(
|
|
706
|
-
const fields = q.pipeline[0].fields;
|
|
707
|
-
expect(fields.sort()).toEqual(afields.concat('x'));
|
|
700
|
+
const fields = getFirstSegmentFieldNames(m.translator.getQuery(0));
|
|
701
|
+
expect(fields).toEqual(afields.concat('x'));
|
|
708
702
|
});
|
|
709
703
|
test('project def', () => {
|
|
710
704
|
expect('run:ab->{ select: one is 1 }').toTranslate();
|
|
@@ -720,13 +714,13 @@ describe('query:', () => {
|
|
|
720
714
|
expect('run:a->{index: astr}').toTranslate();
|
|
721
715
|
});
|
|
722
716
|
test('index path', () => {
|
|
723
|
-
expect('run:ab->{index:
|
|
717
|
+
expect('run:ab->{index: b.astr}').toTranslate();
|
|
724
718
|
});
|
|
725
719
|
test('index unique on path', () => {
|
|
726
|
-
expect('run:ab->{index:
|
|
720
|
+
expect('run:ab->{index: astr, b.astr}').toTranslate();
|
|
727
721
|
});
|
|
728
722
|
test('index join.*', () => {
|
|
729
|
-
expect('run:ab->{index:
|
|
723
|
+
expect('run:ab->{index: b.*}').toTranslate();
|
|
730
724
|
});
|
|
731
725
|
test('index multiple', () => {
|
|
732
726
|
const model = new test_translator_1.TestTranslator('run:a->{index: af, astr}');
|
|
@@ -736,18 +730,12 @@ describe('query:', () => {
|
|
|
736
730
|
if (q) {
|
|
737
731
|
const index = q.pipeline[0];
|
|
738
732
|
expect(index.type).toBe('index');
|
|
739
|
-
|
|
740
|
-
|
|
741
|
-
|
|
742
|
-
|
|
743
|
-
|
|
744
|
-
|
|
745
|
-
const q = model.getQuery(0);
|
|
746
|
-
expect(q).toBeDefined();
|
|
747
|
-
if (q) {
|
|
748
|
-
const index = q.pipeline[0];
|
|
749
|
-
expect(index.type).toBe('index');
|
|
750
|
-
expect(index.fields).toEqual(['*', 'astr']);
|
|
733
|
+
if (index.type === 'index') {
|
|
734
|
+
expect(index.indexFields).toEqual([
|
|
735
|
+
{ type: 'fieldref', path: ['af'] },
|
|
736
|
+
{ type: 'fieldref', path: ['astr'] },
|
|
737
|
+
]);
|
|
738
|
+
}
|
|
751
739
|
}
|
|
752
740
|
});
|
|
753
741
|
test('index by', () => {
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { DocumentLocation, FieldDef, ModelDef, NamedModelObject, PipeSegment, Query, SQLBlockSource, SQLBlockStructDef, StructDef, TurtleDef } from '../../model/malloy_types';
|
|
1
|
+
import { DocumentLocation, FieldDef, ModelDef, NamedModelObject, PipeSegment, Query, QueryFieldDef, SQLBlockSource, SQLBlockStructDef, StructDef, TurtleDef } from '../../model/malloy_types';
|
|
2
2
|
import { MalloyElement } from '../ast';
|
|
3
3
|
import { NameSpace } from '../ast/types/name-space';
|
|
4
4
|
import { ModelEntry } from '../ast/types/model-entry';
|
|
@@ -50,7 +50,8 @@ export declare class BetaExpression extends TestTranslator {
|
|
|
50
50
|
}
|
|
51
51
|
export declare function getExplore(modelDef: ModelDef, name: string): StructDef;
|
|
52
52
|
export declare function getModelQuery(modelDef: ModelDef, name: string): Query;
|
|
53
|
-
export declare function getFieldDef(
|
|
53
|
+
export declare function getFieldDef(source: StructDef, name: string): FieldDef;
|
|
54
|
+
export declare function getQueryFieldDef(query: PipeSegment, name: string): QueryFieldDef;
|
|
54
55
|
export declare function getQueryField(structDef: StructDef, name: string): TurtleDef;
|
|
55
56
|
export declare function getJoinField(structDef: StructDef, name: string): StructDef;
|
|
56
57
|
export interface MarkedSource {
|
|
@@ -23,7 +23,7 @@
|
|
|
23
23
|
* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
|
24
24
|
*/
|
|
25
25
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
26
|
-
exports.getSelectOneStruct = exports.markSource = exports.makeModelFunc = exports.model = exports.expr = exports.getJoinField = exports.getQueryField = exports.getFieldDef = exports.getModelQuery = exports.getExplore = exports.BetaExpression = exports.TestTranslator = exports.TestChildTranslator = exports.aTableDef = exports.pretty = void 0;
|
|
26
|
+
exports.getSelectOneStruct = exports.markSource = exports.makeModelFunc = exports.model = exports.expr = exports.getJoinField = exports.getQueryField = exports.getQueryFieldDef = exports.getFieldDef = exports.getModelQuery = exports.getExplore = exports.BetaExpression = exports.TestTranslator = exports.TestChildTranslator = exports.aTableDef = exports.pretty = void 0;
|
|
27
27
|
const util_1 = require("util");
|
|
28
28
|
const malloy_types_1 = require("../../model/malloy_types");
|
|
29
29
|
const ast_1 = require("../ast");
|
|
@@ -55,7 +55,7 @@ const mockSchema = {
|
|
|
55
55
|
type: 'struct',
|
|
56
56
|
name: 'astruct',
|
|
57
57
|
structSource: { type: 'nested' },
|
|
58
|
-
structRelationship: { type: 'nested', isArray: true,
|
|
58
|
+
structRelationship: { type: 'nested', isArray: true, fieldName: 'foo' },
|
|
59
59
|
fields: [{ type: 'number', name: 'column', numberType: 'integer' }],
|
|
60
60
|
dialect: 'standardsql',
|
|
61
61
|
},
|
|
@@ -242,9 +242,9 @@ class TestTranslator extends parse_malloy_1.MalloyTranslator {
|
|
|
242
242
|
type: 'one',
|
|
243
243
|
matrixOperation: 'left',
|
|
244
244
|
onExpression: [
|
|
245
|
-
{ type: 'field', path: 'astr' },
|
|
245
|
+
{ type: 'field', path: ['astr'] },
|
|
246
246
|
'=',
|
|
247
|
-
{ type: 'field', path: 'b
|
|
247
|
+
{ type: 'field', path: ['b', 'astr'] },
|
|
248
248
|
],
|
|
249
249
|
},
|
|
250
250
|
},
|
|
@@ -262,7 +262,10 @@ class TestTranslator extends parse_malloy_1.MalloyTranslator {
|
|
|
262
262
|
pipeline: [
|
|
263
263
|
{
|
|
264
264
|
type: 'reduce',
|
|
265
|
-
|
|
265
|
+
queryFields: [
|
|
266
|
+
{ type: 'fieldref', path: ['astr'] },
|
|
267
|
+
{ type: 'fieldref', path: ['acount'] },
|
|
268
|
+
],
|
|
266
269
|
},
|
|
267
270
|
],
|
|
268
271
|
},
|
|
@@ -424,30 +427,33 @@ function getModelQuery(modelDef, name) {
|
|
|
424
427
|
return modelDef.contents[name];
|
|
425
428
|
}
|
|
426
429
|
exports.getModelQuery = getModelQuery;
|
|
427
|
-
function getFieldDef(
|
|
428
|
-
|
|
429
|
-
for (const f of
|
|
430
|
-
if (
|
|
431
|
-
|
|
432
|
-
throw new Error(`Expected def for '${name}', found a ref`);
|
|
433
|
-
}
|
|
430
|
+
function getFieldDef(source, name) {
|
|
431
|
+
var _a;
|
|
432
|
+
for (const f of source.fields) {
|
|
433
|
+
if ((_a = f.as) !== null && _a !== void 0 ? _a : f.name === name) {
|
|
434
|
+
return f;
|
|
434
435
|
}
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
436
|
+
}
|
|
437
|
+
throw new Error(`Compiled source did not contain expected field '${name}'`);
|
|
438
|
+
}
|
|
439
|
+
exports.getFieldDef = getFieldDef;
|
|
440
|
+
function getQueryFieldDef(query, name) {
|
|
441
|
+
var _a;
|
|
442
|
+
if ((0, malloy_types_1.isQuerySegment)(query)) {
|
|
443
|
+
for (const f of query.queryFields) {
|
|
444
|
+
if (f.type === 'fieldref') {
|
|
445
|
+
if (name === f.path[f.path.length - 1]) {
|
|
446
|
+
return f;
|
|
447
|
+
}
|
|
448
|
+
}
|
|
449
|
+
else if ((_a = f.as) !== null && _a !== void 0 ? _a : f.name === name) {
|
|
450
|
+
return f;
|
|
438
451
|
}
|
|
439
|
-
found = f;
|
|
440
|
-
break;
|
|
441
452
|
}
|
|
442
453
|
}
|
|
443
|
-
|
|
444
|
-
throw new Error(`Compiled code did not contain expected field '${name}'`);
|
|
445
|
-
}
|
|
446
|
-
else {
|
|
447
|
-
return found;
|
|
448
|
-
}
|
|
454
|
+
throw new Error(`Compiled query did not contain expected field '${name}'`);
|
|
449
455
|
}
|
|
450
|
-
exports.
|
|
456
|
+
exports.getQueryFieldDef = getQueryFieldDef;
|
|
451
457
|
// TODO "as" is almost always a code smell ...
|
|
452
458
|
function getQueryField(structDef, name) {
|
|
453
459
|
return getFieldDef(structDef, name);
|