@malloydata/malloy 0.0.215 → 0.0.216-dev241118202522
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/dialect/functions/util.d.ts +8 -8
- package/dist/dialect/functions/util.js +15 -7
- package/dist/lang/ast/ast-utils.js +2 -0
- package/dist/lang/ast/expressions/expr-aggregate-function.js +1 -0
- package/dist/lang/ast/expressions/expr-compare.js +7 -14
- package/dist/lang/ast/expressions/expr-count-distinct.js +1 -0
- package/dist/lang/ast/expressions/expr-count.js +3 -0
- package/dist/lang/ast/expressions/expr-func.js +3 -0
- package/dist/lang/ast/expressions/expr-id-reference.js +10 -1
- package/dist/lang/ast/expressions/expr-now.js +2 -0
- package/dist/lang/ast/expressions/expr-ungroup.js +1 -0
- package/dist/lang/ast/field-space/dynamic-space.d.ts +1 -3
- package/dist/lang/ast/field-space/dynamic-space.js +11 -15
- package/dist/lang/ast/field-space/query-spaces.d.ts +19 -0
- package/dist/lang/ast/field-space/query-spaces.js +104 -12
- package/dist/lang/ast/field-space/reference-field.js +7 -1
- package/dist/lang/ast/field-space/refined-space.d.ts +1 -0
- package/dist/lang/ast/field-space/refined-space.js +9 -6
- package/dist/lang/ast/field-space/struct-space-field-base.js +4 -0
- package/dist/lang/ast/index.d.ts +1 -0
- package/dist/lang/ast/index.js +1 -0
- package/dist/lang/ast/query-builders/index-builder.js +1 -1
- package/dist/lang/ast/query-builders/reduce-builder.d.ts +2 -1
- package/dist/lang/ast/query-builders/reduce-builder.js +9 -5
- package/dist/lang/ast/query-elements/query-base.js +16 -3
- package/dist/lang/ast/query-items/field-declaration.js +3 -0
- package/dist/lang/ast/query-properties/filters.d.ts +3 -0
- package/dist/lang/ast/query-properties/filters.js +42 -26
- package/dist/lang/ast/query-properties/nest.d.ts +1 -0
- package/dist/lang/ast/query-properties/nest.js +5 -1
- package/dist/lang/ast/source-elements/composite-source.d.ts +16 -0
- package/dist/lang/ast/source-elements/composite-source.js +75 -0
- package/dist/lang/ast/source-elements/source.d.ts +2 -2
- package/dist/lang/ast/source-properties/join.js +2 -0
- package/dist/lang/ast/source-query-elements/sq-compose.d.ts +13 -0
- package/dist/lang/ast/source-query-elements/sq-compose.js +48 -0
- package/dist/lang/ast/typedesc-utils.js +7 -1
- package/dist/lang/ast/types/expr-value.js +4 -0
- package/dist/lang/ast/types/expression-def.js +4 -5
- package/dist/lang/ast/types/query-builder.d.ts +2 -1
- package/dist/lang/ast/types/space-field.js +14 -1
- package/dist/lang/ast/types/space-param.js +3 -0
- package/dist/lang/ast/view-elements/reference-view.js +1 -0
- package/dist/lang/ast/view-elements/refine-utils.js +2 -0
- package/dist/lang/lib/Malloy/MalloyLexer.d.ts +113 -112
- package/dist/lang/lib/Malloy/MalloyLexer.js +1149 -1143
- package/dist/lang/lib/Malloy/MalloyParser.d.ts +126 -112
- package/dist/lang/lib/Malloy/MalloyParser.js +1380 -1282
- package/dist/lang/lib/Malloy/MalloyParserListener.d.ts +13 -0
- package/dist/lang/lib/Malloy/MalloyParserVisitor.d.ts +8 -0
- package/dist/lang/malloy-to-ast.d.ts +1 -0
- package/dist/lang/malloy-to-ast.js +5 -0
- package/dist/lang/parse-log.d.ts +10 -1
- package/dist/lang/parse-log.js +7 -0
- package/dist/lang/test/composite-field-usage.spec.d.ts +1 -0
- package/dist/lang/test/composite-field-usage.spec.js +155 -0
- package/dist/lang/test/parse-expects.d.ts +2 -1
- package/dist/lang/test/parse-expects.js +36 -0
- package/dist/lang/test/source.spec.js +9 -0
- package/dist/lang/test/test-translator.d.ts +3 -1
- package/dist/lang/test/test-translator.js +15 -4
- package/dist/lang/utils.d.ts +1 -0
- package/dist/lang/utils.js +5 -1
- package/dist/model/composite_source_utils.d.ts +65 -0
- package/dist/model/composite_source_utils.js +279 -0
- package/dist/model/malloy_query.d.ts +3 -3
- package/dist/model/malloy_query.js +13 -6
- package/dist/model/malloy_types.d.ts +19 -2
- package/dist/model/malloy_types.js +15 -3
- package/dist/version.d.ts +1 -1
- package/dist/version.js +1 -1
- package/package.json +1 -1
|
@@ -78,6 +78,7 @@ import { JoinWithContext } from "./MalloyParser";
|
|
|
78
78
|
import { JoinOnContext } from "./MalloyParser";
|
|
79
79
|
import { SQIDContext } from "./MalloyParser";
|
|
80
80
|
import { SQParensContext } from "./MalloyParser";
|
|
81
|
+
import { SQComposeContext } from "./MalloyParser";
|
|
81
82
|
import { SQRefinedQueryContext } from "./MalloyParser";
|
|
82
83
|
import { SQArrowContext } from "./MalloyParser";
|
|
83
84
|
import { SQExtendedSourceContext } from "./MalloyParser";
|
|
@@ -1177,6 +1178,18 @@ export interface MalloyParserListener extends ParseTreeListener {
|
|
|
1177
1178
|
* @param ctx the parse tree
|
|
1178
1179
|
*/
|
|
1179
1180
|
exitSQParens?: (ctx: SQParensContext) => void;
|
|
1181
|
+
/**
|
|
1182
|
+
* Enter a parse tree produced by the `SQCompose`
|
|
1183
|
+
* labeled alternative in `MalloyParser.sqExpr`.
|
|
1184
|
+
* @param ctx the parse tree
|
|
1185
|
+
*/
|
|
1186
|
+
enterSQCompose?: (ctx: SQComposeContext) => void;
|
|
1187
|
+
/**
|
|
1188
|
+
* Exit a parse tree produced by the `SQCompose`
|
|
1189
|
+
* labeled alternative in `MalloyParser.sqExpr`.
|
|
1190
|
+
* @param ctx the parse tree
|
|
1191
|
+
*/
|
|
1192
|
+
exitSQCompose?: (ctx: SQComposeContext) => void;
|
|
1180
1193
|
/**
|
|
1181
1194
|
* Enter a parse tree produced by the `SQRefinedQuery`
|
|
1182
1195
|
* labeled alternative in `MalloyParser.sqExpr`.
|
|
@@ -78,6 +78,7 @@ import { JoinWithContext } from "./MalloyParser";
|
|
|
78
78
|
import { JoinOnContext } from "./MalloyParser";
|
|
79
79
|
import { SQIDContext } from "./MalloyParser";
|
|
80
80
|
import { SQParensContext } from "./MalloyParser";
|
|
81
|
+
import { SQComposeContext } from "./MalloyParser";
|
|
81
82
|
import { SQRefinedQueryContext } from "./MalloyParser";
|
|
82
83
|
import { SQArrowContext } from "./MalloyParser";
|
|
83
84
|
import { SQExtendedSourceContext } from "./MalloyParser";
|
|
@@ -785,6 +786,13 @@ export interface MalloyParserVisitor<Result> extends ParseTreeVisitor<Result> {
|
|
|
785
786
|
* @return the visitor result
|
|
786
787
|
*/
|
|
787
788
|
visitSQParens?: (ctx: SQParensContext) => Result;
|
|
789
|
+
/**
|
|
790
|
+
* Visit a parse tree produced by the `SQCompose`
|
|
791
|
+
* labeled alternative in `MalloyParser.sqExpr`.
|
|
792
|
+
* @param ctx the parse tree
|
|
793
|
+
* @return the visitor result
|
|
794
|
+
*/
|
|
795
|
+
visitSQCompose?: (ctx: SQComposeContext) => Result;
|
|
788
796
|
/**
|
|
789
797
|
* Visit a parse tree produced by the `SQRefinedQuery`
|
|
790
798
|
* labeled alternative in `MalloyParser.sqExpr`.
|
|
@@ -212,6 +212,7 @@ export declare class MalloyToAST extends AbstractParseTreeVisitor<ast.MalloyElem
|
|
|
212
212
|
protected getSqExpr(cx: parse.SqExprContext): ast.SourceQueryElement;
|
|
213
213
|
visitSQExtendedSource(pcx: parse.SQExtendedSourceContext): ast.SQExtend;
|
|
214
214
|
visitSQParens(pcx: parse.SQParensContext): ast.SourceQueryElement;
|
|
215
|
+
visitSQCompose(pcx: parse.SQComposeContext): ast.SQCompose;
|
|
215
216
|
visitSQArrow(pcx: parse.SQArrowContext): ast.SQArrow;
|
|
216
217
|
getVExpr(pcx: ParserRuleContext): ast.View;
|
|
217
218
|
visitSegField(pcx: parse.SegFieldContext): ast.ReferenceView;
|
|
@@ -1280,6 +1280,11 @@ class MalloyToAST extends AbstractParseTreeVisitor_1.AbstractParseTreeVisitor {
|
|
|
1280
1280
|
const sqExpr = this.getSqExpr(pcx.sqExpr());
|
|
1281
1281
|
return this.astAt(sqExpr, pcx);
|
|
1282
1282
|
}
|
|
1283
|
+
visitSQCompose(pcx) {
|
|
1284
|
+
const sources = pcx.sqExpr().map(sqExpr => this.getSqExpr(sqExpr));
|
|
1285
|
+
this.inExperiment('composite_sources', pcx);
|
|
1286
|
+
return this.astAt(new ast.SQCompose(sources), pcx);
|
|
1287
|
+
}
|
|
1283
1288
|
visitSQArrow(pcx) {
|
|
1284
1289
|
const applyTo = this.getSqExpr(pcx.sqExpr());
|
|
1285
1290
|
const headCx = pcx.segExpr();
|
package/dist/lang/parse-log.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { DocumentLocation, ExpressionValueType } from '../model/malloy_types';
|
|
1
|
+
import { CompositeFieldUsage, DocumentLocation, ExpressionValueType } from '../model/malloy_types';
|
|
2
2
|
import { EventStream } from '../runtime_types';
|
|
3
3
|
export type LogSeverity = 'error' | 'warn' | 'debug';
|
|
4
4
|
/**
|
|
@@ -156,6 +156,15 @@ type MessageParameterTypes = {
|
|
|
156
156
|
'field-list-edit-not-found': string;
|
|
157
157
|
'unexpected-element-type': string;
|
|
158
158
|
'field-not-found': string;
|
|
159
|
+
'invalid-composite-source-input': string;
|
|
160
|
+
'invalid-composite-field-usage': {
|
|
161
|
+
newUsage: CompositeFieldUsage;
|
|
162
|
+
allUsage: CompositeFieldUsage;
|
|
163
|
+
};
|
|
164
|
+
'empty-composite-source': string;
|
|
165
|
+
'unnecessary-composite-source': string;
|
|
166
|
+
'composite-source-atomic-fields-only': string;
|
|
167
|
+
'composite-source-connection-mismatch': string;
|
|
159
168
|
'invalid-property-access-in-field-reference': string;
|
|
160
169
|
'parameter-default-does-not-match-declared-type': string;
|
|
161
170
|
'parameter-null-default-without-declared-type': string;
|
package/dist/lang/parse-log.js
CHANGED
|
@@ -23,6 +23,7 @@
|
|
|
23
23
|
*/
|
|
24
24
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
25
25
|
exports.makeLogMessage = exports.MESSAGE_FORMATTERS = exports.BaseMessageLogger = void 0;
|
|
26
|
+
const composite_source_utils_1 = require("../model/composite_source_utils");
|
|
26
27
|
class BaseMessageLogger {
|
|
27
28
|
constructor(eventStream) {
|
|
28
29
|
this.eventStream = eventStream;
|
|
@@ -94,6 +95,12 @@ exports.MESSAGE_FORMATTERS = {
|
|
|
94
95
|
'case-else-type-does-not-match': e => `Case else type ${e.elseType} does not match return type ${e.returnType}`,
|
|
95
96
|
'case-when-must-be-boolean': e => `Case when expression must be boolean, not ${e.whenType}`,
|
|
96
97
|
'case-when-type-does-not-match': e => `Case when type ${e.whenType} does not match value type ${e.valueType}`,
|
|
98
|
+
'invalid-composite-field-usage': e => {
|
|
99
|
+
const formattedNewCompositeUsage = (0, composite_source_utils_1.formatCompositeFieldUsages)(e.newUsage);
|
|
100
|
+
const formattedAllCompositeUsage = (0, composite_source_utils_1.formatCompositeFieldUsages)(e.allUsage);
|
|
101
|
+
const pluralUse = (0, composite_source_utils_1.compositeFieldUsageIsPlural)(e.newUsage) ? 's' : '';
|
|
102
|
+
return `This operation uses composite field${pluralUse} ${formattedNewCompositeUsage}, resulting in invalid usage of the composite source, as there is no composite input source which defines all of ${formattedAllCompositeUsage}`;
|
|
103
|
+
},
|
|
97
104
|
};
|
|
98
105
|
function makeLogMessage(code, parameters, options) {
|
|
99
106
|
var _a, _b, _c, _d, _e;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import './parse-expects';
|
|
@@ -0,0 +1,155 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/*
|
|
3
|
+
* Copyright (c) Meta Platforms, Inc. and affiliates.
|
|
4
|
+
*
|
|
5
|
+
* This source code is licensed under the MIT license found in the
|
|
6
|
+
* LICENSE file in the root directory of this source tree.
|
|
7
|
+
*/
|
|
8
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
9
|
+
const test_translator_1 = require("./test-translator");
|
|
10
|
+
require("./parse-expects");
|
|
11
|
+
const composite_source_utils_1 = require("../../model/composite_source_utils");
|
|
12
|
+
function addPathToCompositeUsage(path, compositeUsage) {
|
|
13
|
+
var _a;
|
|
14
|
+
if (path.length === 0)
|
|
15
|
+
throw new Error('empty path');
|
|
16
|
+
if (path.length === 1) {
|
|
17
|
+
return {
|
|
18
|
+
fields: [...compositeUsage.fields, path[0]],
|
|
19
|
+
joinedUsage: compositeUsage.joinedUsage,
|
|
20
|
+
};
|
|
21
|
+
}
|
|
22
|
+
else {
|
|
23
|
+
return {
|
|
24
|
+
fields: compositeUsage.fields,
|
|
25
|
+
joinedUsage: {
|
|
26
|
+
...compositeUsage.joinedUsage,
|
|
27
|
+
[path[0]]: addPathToCompositeUsage(path.slice(1), (_a = compositeUsage.joinedUsage[path[0]]) !== null && _a !== void 0 ? _a : (0, composite_source_utils_1.emptyCompositeFieldUsage)()),
|
|
28
|
+
},
|
|
29
|
+
};
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
function paths(paths) {
|
|
33
|
+
let cu = (0, composite_source_utils_1.emptyCompositeFieldUsage)();
|
|
34
|
+
for (const path of paths) {
|
|
35
|
+
cu = addPathToCompositeUsage(path, cu);
|
|
36
|
+
}
|
|
37
|
+
return cu;
|
|
38
|
+
}
|
|
39
|
+
describe('composite sources', () => {
|
|
40
|
+
describe('composite field usage', () => {
|
|
41
|
+
const m = (0, test_translator_1.model) `
|
|
42
|
+
##! experimental.composite_sources
|
|
43
|
+
source: x is compose(ab, ab) extend {
|
|
44
|
+
dimension: aif is ai + af
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
source: y is compose(ab, ab) extend {
|
|
48
|
+
join_one: x on 1 = 1
|
|
49
|
+
}
|
|
50
|
+
`;
|
|
51
|
+
beforeAll(() => {
|
|
52
|
+
m.translator.translate();
|
|
53
|
+
});
|
|
54
|
+
test('looked up value', () => {
|
|
55
|
+
const mexpr = (0, test_translator_1.makeExprFunc)(m.translator.modelDef, 'y');
|
|
56
|
+
expect(mexpr `ai`).hasCompositeUsage(paths([['ai']]));
|
|
57
|
+
});
|
|
58
|
+
test('multiple values', () => {
|
|
59
|
+
const mexpr = (0, test_translator_1.makeExprFunc)(m.translator.modelDef, 'y');
|
|
60
|
+
expect(mexpr `ai + af`).hasCompositeUsage(paths([['ai'], ['af']]));
|
|
61
|
+
});
|
|
62
|
+
test('value plus constant', () => {
|
|
63
|
+
const mexpr = (0, test_translator_1.makeExprFunc)(m.translator.modelDef, 'y');
|
|
64
|
+
expect(mexpr `ai + 1`).hasCompositeUsage(paths([['ai']]));
|
|
65
|
+
});
|
|
66
|
+
test('join usage', () => {
|
|
67
|
+
const mexpr = (0, test_translator_1.makeExprFunc)(m.translator.modelDef, 'y');
|
|
68
|
+
expect(mexpr `x.ai + 1`).hasCompositeUsage(paths([['x', 'ai']]));
|
|
69
|
+
});
|
|
70
|
+
test('join usage complex', () => {
|
|
71
|
+
const mexpr = (0, test_translator_1.makeExprFunc)(m.translator.modelDef, 'y');
|
|
72
|
+
expect(mexpr `x.aif`).hasCompositeUsage(paths([
|
|
73
|
+
['x', 'ai'],
|
|
74
|
+
['x', 'af'],
|
|
75
|
+
]));
|
|
76
|
+
});
|
|
77
|
+
});
|
|
78
|
+
describe('composite source resolution and validation', () => {
|
|
79
|
+
test('compose fails on group_by that is relevant', () => {
|
|
80
|
+
expect(`
|
|
81
|
+
##! experimental.composite_sources
|
|
82
|
+
run: compose(
|
|
83
|
+
a extend { dimension: one is 1, two is 2 },
|
|
84
|
+
a extend { dimension: one is 1, three is 3 }
|
|
85
|
+
) -> {
|
|
86
|
+
group_by: one
|
|
87
|
+
group_by: three
|
|
88
|
+
group_by: ${'two'}
|
|
89
|
+
}
|
|
90
|
+
`).toLog((0, test_translator_1.errorMessage)('This operation uses composite field `two`, resulting in invalid usage of the composite source, as there is no composite input source which defines all of `one`, `three`, `two`'));
|
|
91
|
+
});
|
|
92
|
+
test('compose fails on filter that is relevant', () => {
|
|
93
|
+
expect(`
|
|
94
|
+
##! experimental.composite_sources
|
|
95
|
+
run: compose(
|
|
96
|
+
a extend { dimension: one is 1, two is 2 },
|
|
97
|
+
a extend { dimension: one is 1, three is 3 }
|
|
98
|
+
) -> {
|
|
99
|
+
group_by: one
|
|
100
|
+
group_by: three
|
|
101
|
+
where: ${'two = 2'}
|
|
102
|
+
}
|
|
103
|
+
`).toLog((0, test_translator_1.errorMessage)('This operation uses composite field `two`, resulting in invalid usage of the composite source, as there is no composite input source which defines all of `one`, `three`, `two`'));
|
|
104
|
+
});
|
|
105
|
+
test('compose fails in case where second field has overlap with first', () => {
|
|
106
|
+
expect(`
|
|
107
|
+
##! experimental.composite_sources
|
|
108
|
+
run: compose(
|
|
109
|
+
a extend { dimension: one is 1 },
|
|
110
|
+
a extend { dimension: two is 3 }
|
|
111
|
+
) -> {
|
|
112
|
+
group_by: one
|
|
113
|
+
group_by: ${'three is one + two'}
|
|
114
|
+
}
|
|
115
|
+
`).toLog((0, test_translator_1.errorMessage)('This operation uses composite field `two`, resulting in invalid usage of the composite source, as there is no composite input source which defines all of `one`, `two`'));
|
|
116
|
+
});
|
|
117
|
+
test('compose resolution succeeds nested', () => {
|
|
118
|
+
expect(`
|
|
119
|
+
##! experimental.composite_sources
|
|
120
|
+
run: compose(
|
|
121
|
+
compose(
|
|
122
|
+
a extend { dimension: one is 1, two is 2 },
|
|
123
|
+
a extend { dimension: one is 1, three is 3 }
|
|
124
|
+
),
|
|
125
|
+
a extend { dimension: one is 1, two is 2, three is 3 }
|
|
126
|
+
) -> {
|
|
127
|
+
group_by: one, two ${'three'}
|
|
128
|
+
}
|
|
129
|
+
`).toTranslate();
|
|
130
|
+
});
|
|
131
|
+
test('good composite field usage works', () => expect(`
|
|
132
|
+
##! experimental.composite_sources
|
|
133
|
+
run: compose(a, a extend { dimension: one is 1 }) -> { group_by: one }
|
|
134
|
+
`).toTranslate());
|
|
135
|
+
test.skip('index on composite fails', () => expect(`
|
|
136
|
+
##! experimental.composite_sources
|
|
137
|
+
run: compose(a extend { dimension: two is 2 }, a extend { dimension: one is 1 }) -> { index: * }
|
|
138
|
+
`).toLog((0, test_translator_1.errorMessage)('Cannot index on a composite source')));
|
|
139
|
+
test('raw run of composite source fails', () => expect(`
|
|
140
|
+
##! experimental.composite_sources
|
|
141
|
+
run: compose(a extend { dimension: two is 2 }, a extend { dimension: one is 1 })
|
|
142
|
+
`).toLog((0, test_translator_1.errorMessage)('Cannot run this object as a query')));
|
|
143
|
+
test('composite source with parameter', () => {
|
|
144
|
+
expect(`
|
|
145
|
+
##! experimental { composite_sources parameters }
|
|
146
|
+
source: foo(param is 1) is compose(
|
|
147
|
+
a extend { dimension: x is param },
|
|
148
|
+
a extend { dimension: y is param + 1 }
|
|
149
|
+
)
|
|
150
|
+
run: foo(param is 2) -> { group_by: y }
|
|
151
|
+
`).toTranslate();
|
|
152
|
+
});
|
|
153
|
+
});
|
|
154
|
+
});
|
|
155
|
+
//# sourceMappingURL=composite-field-usage.spec.js.map
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { DocumentLocation } from '../../model';
|
|
1
|
+
import { CompositeFieldUsage, DocumentLocation } from '../../model';
|
|
2
2
|
import { LogSeverity } from '../parse-log';
|
|
3
3
|
type MessageProblemSpec = {
|
|
4
4
|
severity: LogSeverity;
|
|
@@ -45,6 +45,7 @@ declare global {
|
|
|
45
45
|
* Warnings are ignored, so need to be checked seperately
|
|
46
46
|
*/
|
|
47
47
|
compilesTo(exprString: string): R;
|
|
48
|
+
hasCompositeUsage(compositeUsage: CompositeFieldUsage): R;
|
|
48
49
|
}
|
|
49
50
|
}
|
|
50
51
|
}
|
|
@@ -276,6 +276,42 @@ expect.extend({
|
|
|
276
276
|
const msg = pass ? `Matched: ${rcvExpr}` : this.utils.diff(expr, rcvExpr);
|
|
277
277
|
return { pass, message: () => `${msg}` };
|
|
278
278
|
},
|
|
279
|
+
hasCompositeUsage: function (tx, compositeFieldUsage) {
|
|
280
|
+
let bx;
|
|
281
|
+
if (typeof tx === 'string') {
|
|
282
|
+
bx = new test_translator_1.BetaExpression(tx);
|
|
283
|
+
}
|
|
284
|
+
else {
|
|
285
|
+
const x = xlator(tx);
|
|
286
|
+
if (x instanceof test_translator_1.BetaExpression) {
|
|
287
|
+
bx = x;
|
|
288
|
+
}
|
|
289
|
+
else {
|
|
290
|
+
return {
|
|
291
|
+
pass: false,
|
|
292
|
+
message: () => 'Must pass expr`EXPRESSION` to expect(EXPRSSION).compilesTo()',
|
|
293
|
+
};
|
|
294
|
+
}
|
|
295
|
+
}
|
|
296
|
+
bx.compile();
|
|
297
|
+
// Only report errors, callers will need to test for warnings
|
|
298
|
+
if (bx.logger.hasErrors()) {
|
|
299
|
+
return {
|
|
300
|
+
message: () => `Translation problems:\n${bx.prettyErrors()}`,
|
|
301
|
+
pass: false,
|
|
302
|
+
};
|
|
303
|
+
}
|
|
304
|
+
const badRefs = checkForNeededs(bx);
|
|
305
|
+
if (!badRefs.pass) {
|
|
306
|
+
return badRefs;
|
|
307
|
+
}
|
|
308
|
+
const actual = bx.generated().compositeFieldUsage;
|
|
309
|
+
const pass = this.equals(actual, compositeFieldUsage);
|
|
310
|
+
const msg = pass
|
|
311
|
+
? `Matched: ${actual}`
|
|
312
|
+
: this.utils.diff(compositeFieldUsage, actual);
|
|
313
|
+
return { pass, message: () => `${msg}` };
|
|
314
|
+
},
|
|
279
315
|
});
|
|
280
316
|
function problemSpecSummary(s) {
|
|
281
317
|
return `${s.severity} '${'message' in s ? s.message : s.code}' ${s.data !== undefined ? (0, test_translator_1.pretty)(s.data) : ''}`;
|
|
@@ -316,5 +316,14 @@ describe('source:', () => {
|
|
|
316
316
|
const ad = (0, test_translator_1.getFieldDef)(hasDate, 'ad');
|
|
317
317
|
expect(ad).toMatchObject({ name: 'ad', type: 'date' });
|
|
318
318
|
});
|
|
319
|
+
describe('composite sources', () => {
|
|
320
|
+
test('basic composite source', () => {
|
|
321
|
+
expect(`
|
|
322
|
+
##! experimental.composite_sources
|
|
323
|
+
source: x is compose(a, a extend { dimension: foo is 1 })
|
|
324
|
+
run: x -> { select: foo }
|
|
325
|
+
`).toTranslate();
|
|
326
|
+
});
|
|
327
|
+
});
|
|
319
328
|
});
|
|
320
329
|
//# sourceMappingURL=source.spec.js.map
|
|
@@ -45,8 +45,9 @@ export declare class TestTranslator extends MalloyTranslator {
|
|
|
45
45
|
getQuery(queryName: string | number): Query | undefined;
|
|
46
46
|
}
|
|
47
47
|
export declare class BetaExpression extends TestTranslator {
|
|
48
|
+
readonly sourceName: string;
|
|
48
49
|
private compiled?;
|
|
49
|
-
constructor(src: string);
|
|
50
|
+
constructor(src: string, model?: ModelDef, sourceName?: string);
|
|
50
51
|
private testFS;
|
|
51
52
|
compile(): void;
|
|
52
53
|
generated(): ExprValue;
|
|
@@ -72,6 +73,7 @@ export declare function makeModelFunc(options: {
|
|
|
72
73
|
prefix?: string;
|
|
73
74
|
wrap?: (code: string) => string;
|
|
74
75
|
}): (unmarked: TemplateStringsArray, ...marked: string[]) => HasTranslator<TestTranslator>;
|
|
76
|
+
export declare function makeExprFunc(model: ModelDef, sourceName: string): (unmarked: TemplateStringsArray, ...marked: string[]) => HasTranslator<TestTranslator>;
|
|
75
77
|
export declare function markSource(unmarked: TemplateStringsArray, ...marked: string[]): MarkedSource;
|
|
76
78
|
export declare function getSelectOneStruct(sqlBlock: SQLSentence): SQLSourceDef;
|
|
77
79
|
export declare function error<T extends MessageCode>(code: T, data?: MessageParameterType<T>): {
|
|
@@ -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.warningMessage = exports.errorMessage = exports.warning = exports.error = 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;
|
|
26
|
+
exports.warningMessage = exports.errorMessage = exports.warning = exports.error = exports.getSelectOneStruct = exports.markSource = exports.makeExprFunc = 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");
|
|
@@ -386,11 +386,12 @@ class TestTranslator extends parse_malloy_1.MalloyTranslator {
|
|
|
386
386
|
exports.TestTranslator = TestTranslator;
|
|
387
387
|
TestTranslator.inspectCompile = false;
|
|
388
388
|
class BetaExpression extends TestTranslator {
|
|
389
|
-
constructor(src) {
|
|
390
|
-
super(src, null, null, 'debugExpr');
|
|
389
|
+
constructor(src, model, sourceName = 'ab') {
|
|
390
|
+
super(src, null, null, 'debugExpr', model);
|
|
391
|
+
this.sourceName = sourceName;
|
|
391
392
|
}
|
|
392
393
|
testFS() {
|
|
393
|
-
const aStruct = this.internalModel.contents[
|
|
394
|
+
const aStruct = this.internalModel.contents[this.sourceName];
|
|
394
395
|
if ((0, malloy_types_1.isSourceDef)(aStruct)) {
|
|
395
396
|
const tstFS = new static_space_1.StaticSourceSpace(aStruct);
|
|
396
397
|
return tstFS;
|
|
@@ -498,6 +499,16 @@ function makeModelFunc(options) {
|
|
|
498
499
|
};
|
|
499
500
|
}
|
|
500
501
|
exports.makeModelFunc = makeModelFunc;
|
|
502
|
+
function makeExprFunc(model, sourceName) {
|
|
503
|
+
return function expr(unmarked, ...marked) {
|
|
504
|
+
const ms = markSource(unmarked, ...marked);
|
|
505
|
+
return {
|
|
506
|
+
...ms,
|
|
507
|
+
translator: new BetaExpression(ms.code, model, sourceName),
|
|
508
|
+
};
|
|
509
|
+
};
|
|
510
|
+
}
|
|
511
|
+
exports.makeExprFunc = makeExprFunc;
|
|
501
512
|
function markSource(unmarked, ...marked) {
|
|
502
513
|
let code = '';
|
|
503
514
|
const locations = [];
|
package/dist/lang/utils.d.ts
CHANGED
package/dist/lang/utils.js
CHANGED
|
@@ -22,7 +22,7 @@
|
|
|
22
22
|
* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
|
23
23
|
*/
|
|
24
24
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
25
|
-
exports.locationContainsPosition = void 0;
|
|
25
|
+
exports.isNotUndefined = exports.locationContainsPosition = void 0;
|
|
26
26
|
function locationContainsPosition(location, position) {
|
|
27
27
|
return (location.range.start.line <= position.line &&
|
|
28
28
|
location.range.end.line >= position.line &&
|
|
@@ -32,4 +32,8 @@ function locationContainsPosition(location, position) {
|
|
|
32
32
|
position.character <= location.range.end.character));
|
|
33
33
|
}
|
|
34
34
|
exports.locationContainsPosition = locationContainsPosition;
|
|
35
|
+
function isNotUndefined(value) {
|
|
36
|
+
return value !== undefined;
|
|
37
|
+
}
|
|
38
|
+
exports.isNotUndefined = isNotUndefined;
|
|
35
39
|
//# sourceMappingURL=utils.js.map
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
import { CompositeFieldUsage, SourceDef } from './malloy_types';
|
|
2
|
+
type CompositeError = {
|
|
3
|
+
code: 'not_a_composite_source';
|
|
4
|
+
data: {
|
|
5
|
+
path: string[];
|
|
6
|
+
};
|
|
7
|
+
} | {
|
|
8
|
+
code: 'composite_source_not_defined';
|
|
9
|
+
data: {
|
|
10
|
+
path: string[];
|
|
11
|
+
};
|
|
12
|
+
} | {
|
|
13
|
+
code: 'composite_source_not_a_join';
|
|
14
|
+
data: {
|
|
15
|
+
path: string[];
|
|
16
|
+
};
|
|
17
|
+
} | {
|
|
18
|
+
code: 'composite_source_is_not_joinable';
|
|
19
|
+
data: {
|
|
20
|
+
path: string[];
|
|
21
|
+
};
|
|
22
|
+
} | {
|
|
23
|
+
code: 'no_suitable_composite_source_input';
|
|
24
|
+
data: {
|
|
25
|
+
path: string[];
|
|
26
|
+
fields: string[];
|
|
27
|
+
};
|
|
28
|
+
};
|
|
29
|
+
type SingleNarrowedCompositeFieldResolution = {
|
|
30
|
+
source: SourceDef;
|
|
31
|
+
nested?: SingleNarrowedCompositeFieldResolution | undefined;
|
|
32
|
+
}[];
|
|
33
|
+
interface NarrowedCompositeFieldResolution {
|
|
34
|
+
source: SingleNarrowedCompositeFieldResolution | undefined;
|
|
35
|
+
joined: {
|
|
36
|
+
[name: string]: NarrowedCompositeFieldResolution;
|
|
37
|
+
};
|
|
38
|
+
}
|
|
39
|
+
export declare function emptyNarrowedCompositeFieldResolution(): NarrowedCompositeFieldResolution;
|
|
40
|
+
export declare function narrowCompositeFieldResolution(source: SourceDef, compositeFieldUsage: CompositeFieldUsage, narrowedCompositeFieldResolution: NarrowedCompositeFieldResolution): {
|
|
41
|
+
narrowedCompositeFieldResolution: NarrowedCompositeFieldResolution;
|
|
42
|
+
error: undefined;
|
|
43
|
+
} | {
|
|
44
|
+
error: CompositeError;
|
|
45
|
+
narrowedCompositeFieldResolution: undefined;
|
|
46
|
+
};
|
|
47
|
+
export declare function resolveCompositeSources(source: SourceDef, compositeFieldUsage: CompositeFieldUsage): {
|
|
48
|
+
sourceDef: SourceDef;
|
|
49
|
+
error: undefined;
|
|
50
|
+
} | {
|
|
51
|
+
error: CompositeError;
|
|
52
|
+
sourceDef: undefined;
|
|
53
|
+
};
|
|
54
|
+
export declare function compositeFieldUsagePaths(compositeFieldUsage: CompositeFieldUsage): string[][];
|
|
55
|
+
export declare function formatCompositeFieldUsages(compositeFieldUsage: CompositeFieldUsage): string;
|
|
56
|
+
export declare function isEmptyCompositeFieldUsage(compositeFieldUsage: CompositeFieldUsage): boolean;
|
|
57
|
+
export declare function compositeFieldUsageIsPlural(compositeFieldUsage: CompositeFieldUsage): boolean;
|
|
58
|
+
export declare function formatCompositeFieldUsage(compositeFieldUsage: string[]): string;
|
|
59
|
+
export declare function unique<T>(values: T[]): T[];
|
|
60
|
+
export declare function mergeCompositeFieldUsage(...usages: (CompositeFieldUsage | undefined)[]): CompositeFieldUsage;
|
|
61
|
+
export declare function emptyCompositeFieldUsage(): CompositeFieldUsage;
|
|
62
|
+
export declare function compositeFieldUsageDifference(a: CompositeFieldUsage, b: CompositeFieldUsage): CompositeFieldUsage;
|
|
63
|
+
export declare function joinedCompositeFieldUsage(joinPath: string[], compositeFieldUsage: CompositeFieldUsage): CompositeFieldUsage;
|
|
64
|
+
export declare function compositeFieldUsageJoinPaths(compositeFieldUsage: CompositeFieldUsage): string[][];
|
|
65
|
+
export {};
|