@malloydata/malloy 0.0.394 → 0.0.396
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/foundation/compile.d.ts +7 -6
- package/dist/api/foundation/compile.js +22 -6
- package/dist/api/foundation/config.d.ts +2 -3
- package/dist/api/foundation/config.js +23 -11
- package/dist/api/foundation/core.js +1 -1
- package/dist/api/foundation/runtime.d.ts +85 -5
- package/dist/api/foundation/runtime.js +204 -14
- package/dist/api/foundation/types.d.ts +2 -0
- package/dist/api/util.js +4 -0
- package/dist/connection/base_connection.js +6 -0
- package/dist/connection/validate_table_path.d.ts +10 -0
- package/dist/connection/validate_table_path.js +56 -0
- package/dist/dialect/databricks/databricks.d.ts +4 -4
- package/dist/dialect/databricks/databricks.js +17 -22
- package/dist/dialect/dialect.d.ts +100 -4
- package/dist/dialect/dialect.js +145 -1
- package/dist/dialect/duckdb/duckdb.d.ts +2 -3
- package/dist/dialect/duckdb/duckdb.js +12 -14
- package/dist/dialect/duckdb/table-path-parser.d.ts +2 -0
- package/dist/dialect/duckdb/table-path-parser.js +57 -0
- package/dist/dialect/index.d.ts +2 -0
- package/dist/dialect/index.js +4 -1
- package/dist/dialect/mysql/mysql.d.ts +4 -4
- package/dist/dialect/mysql/mysql.js +25 -20
- package/dist/dialect/pg_impl.d.ts +3 -1
- package/dist/dialect/pg_impl.js +6 -3
- package/dist/dialect/postgres/postgres.d.ts +1 -3
- package/dist/dialect/postgres/postgres.js +8 -16
- package/dist/dialect/snowflake/snowflake.d.ts +4 -4
- package/dist/dialect/snowflake/snowflake.js +11 -27
- package/dist/dialect/standardsql/standardsql.d.ts +6 -4
- package/dist/dialect/standardsql/standardsql.js +36 -15
- package/dist/dialect/table-path.d.ts +54 -0
- package/dist/dialect/table-path.js +144 -0
- package/dist/dialect/trino/trino.d.ts +0 -3
- package/dist/dialect/trino/trino.js +7 -20
- package/dist/index.d.ts +2 -2
- package/dist/index.js +4 -2
- package/dist/lang/ast/expressions/expr-func.js +30 -11
- package/dist/lang/ast/expressions/expr-given.js +1 -0
- package/dist/lang/ast/field-space/reference-field.js +1 -1
- package/dist/lang/ast/source-elements/sql-source.js +4 -0
- package/dist/lang/ast/source-elements/table-source.d.ts +1 -7
- package/dist/lang/ast/source-elements/table-source.js +24 -19
- package/dist/lang/ast/statements/define-given.d.ts +1 -0
- package/dist/lang/ast/statements/define-given.js +7 -0
- package/dist/lang/ast/statements/import-statement.js +4 -0
- package/dist/lang/ast/types/annotation-elements.d.ts +1 -0
- package/dist/lang/ast/types/annotation-elements.js +10 -3
- package/dist/lang/ast/types/malloy-element.d.ts +1 -0
- package/dist/lang/ast/types/malloy-element.js +4 -0
- package/dist/lang/malloy-to-ast.d.ts +2 -1
- package/dist/lang/malloy-to-ast.js +11 -1
- package/dist/lang/parse-log.d.ts +2 -0
- package/dist/lang/parse-log.js +4 -0
- package/dist/lang/parse-malloy.d.ts +4 -1
- package/dist/lang/parse-malloy.js +63 -11
- package/dist/lang/parse-tree-walkers/find-external-references.d.ts +2 -15
- package/dist/lang/parse-tree-walkers/find-external-references.js +6 -23
- package/dist/lang/test/test-translator.d.ts +19 -5
- package/dist/lang/test/test-translator.js +15 -12
- package/dist/lang/translate-response.d.ts +1 -1
- package/dist/lang/zone.d.ts +2 -0
- package/dist/lang/zone.js +10 -0
- package/dist/model/constant_expression_compiler.js +14 -5
- package/dist/model/expression_compiler.js +19 -17
- package/dist/model/field_instance.js +7 -3
- package/dist/model/filter_compilers.js +1 -1
- package/dist/model/given_binding.js +26 -21
- package/dist/model/index.d.ts +1 -0
- package/dist/model/index.js +3 -1
- package/dist/model/malloy_compile_error.d.ts +13 -0
- package/dist/model/malloy_compile_error.js +23 -0
- package/dist/model/malloy_types.d.ts +2 -0
- package/dist/model/query_model_impl.js +9 -8
- package/dist/model/query_node.d.ts +5 -5
- package/dist/model/query_node.js +21 -16
- package/dist/model/query_query.js +60 -44
- package/dist/model/sql_compiled.d.ts +2 -4
- package/dist/model/sql_compiled.js +20 -18
- package/dist/test/test-models.js +2 -2
- package/dist/version.d.ts +1 -1
- package/dist/version.js +1 -1
- package/package.json +4 -4
|
@@ -92,10 +92,11 @@ const LEGAL_FILTER_TYPES = 'string, number, boolean, date, timestamp, timestampt
|
|
|
92
92
|
* AST from an ANTLR parse tree.
|
|
93
93
|
*/
|
|
94
94
|
class MalloyToAST extends AbstractParseTreeVisitor_1.AbstractParseTreeVisitor {
|
|
95
|
-
constructor(parseInfo, msgLog, compilerFlagSrc) {
|
|
95
|
+
constructor(parseInfo, msgLog, compilerFlagSrc, restrictedMode = false) {
|
|
96
96
|
super();
|
|
97
97
|
this.parseInfo = parseInfo;
|
|
98
98
|
this.msgLog = msgLog;
|
|
99
|
+
this.restrictedMode = restrictedMode;
|
|
99
100
|
this.timer = new timing_1.Timer('generate_ast');
|
|
100
101
|
const parseCompilerFlagsTimer = new timing_1.Timer('parse_compiler_flags');
|
|
101
102
|
this.compilerFlagSrc = [...DEFAULT_COMPILER_FLAGS, ...compilerFlagSrc];
|
|
@@ -1498,6 +1499,15 @@ class MalloyToAST extends AbstractParseTreeVisitor_1.AbstractParseTreeVisitor {
|
|
|
1498
1499
|
}
|
|
1499
1500
|
updateCompilerFlags(tags) {
|
|
1500
1501
|
const parseCompilerFlagsTimer = new timing_1.Timer('parse_compiler_flags');
|
|
1502
|
+
if (this.restrictedMode) {
|
|
1503
|
+
// `##!` lines in restricted text never become active flags. The
|
|
1504
|
+
// user-facing rejection is logged at execute time (see
|
|
1505
|
+
// ModelAnnotation.execute) so it doesn't short-circuit ASTStep,
|
|
1506
|
+
// which lets every other restricted-mode violation in the same
|
|
1507
|
+
// compile log its own diagnostic.
|
|
1508
|
+
this.timer.contribute([parseCompilerFlagsTimer.stop()]);
|
|
1509
|
+
return;
|
|
1510
|
+
}
|
|
1501
1511
|
const newLines = tags.getCompilerFlagLines();
|
|
1502
1512
|
if (newLines.length > 0) {
|
|
1503
1513
|
const oldLength = this.compilerFlagSrc.length;
|
package/dist/lang/parse-log.d.ts
CHANGED
|
@@ -275,6 +275,7 @@ type MessageParameterTypes = {
|
|
|
275
275
|
'parameter-name-conflict': string;
|
|
276
276
|
'parameter-shadowing-field': string;
|
|
277
277
|
'invalid-import-url': string;
|
|
278
|
+
'invalid-table-path': string;
|
|
278
279
|
'no-translator-for-import': string;
|
|
279
280
|
'name-conflict-on-selective-import': string;
|
|
280
281
|
'selective-import-not-found': string;
|
|
@@ -429,6 +430,7 @@ type MessageParameterTypes = {
|
|
|
429
430
|
'missing-required-group-by': string;
|
|
430
431
|
'invalid-partition-composite': string;
|
|
431
432
|
'integer-literal-out-of-range': string;
|
|
433
|
+
'restricted-construct-forbidden': string;
|
|
432
434
|
};
|
|
433
435
|
export declare const MESSAGE_FORMATTERS: PartialErrorCodeMessageMap;
|
|
434
436
|
export type MessageCode = keyof MessageParameterTypes;
|
package/dist/lang/parse-log.js
CHANGED
|
@@ -101,6 +101,10 @@ exports.MESSAGE_FORMATTERS = {
|
|
|
101
101
|
'inline-no-default': e => `inline given \`${e.name}\` must have a value — there is nothing to inline without one`,
|
|
102
102
|
'inline-bad-operator': e => `inline given \`${e.name}\` uses operator(s) not allowed in inline expressions: ${e.operators}`,
|
|
103
103
|
'invalid-given-modifier': e => `Unknown modifier \`${e.modifier}\` on \`given:\` declaration; the only modifier allowed here is \`inline\``,
|
|
104
|
+
'restricted-construct-forbidden': e => ({
|
|
105
|
+
message: e,
|
|
106
|
+
tag: 'restricted-mode',
|
|
107
|
+
}),
|
|
104
108
|
};
|
|
105
109
|
function makeLogMessage(code, parameters, options) {
|
|
106
110
|
var _a, _b, _c, _d, _e;
|
|
@@ -193,6 +193,7 @@ export declare class MalloyChildTranslator extends MalloyTranslation {
|
|
|
193
193
|
*/
|
|
194
194
|
export declare class MalloyTranslator extends MalloyTranslation {
|
|
195
195
|
private readonly eventStream;
|
|
196
|
+
readonly restrictedMode: boolean;
|
|
196
197
|
schemaZone: Zone<SourceDef>;
|
|
197
198
|
importZone: Zone<string>;
|
|
198
199
|
pretranslatedModels: Map<string, ModelDef>;
|
|
@@ -200,8 +201,10 @@ export declare class MalloyTranslator extends MalloyTranslation {
|
|
|
200
201
|
connectionDialectZone: Zone<string>;
|
|
201
202
|
logger: BaseMessageLogger;
|
|
202
203
|
readonly root: MalloyTranslator;
|
|
203
|
-
constructor(rootURL: string, importURL?: string | null, preload?: ParseUpdate | null, eventStream?: EventStream | null);
|
|
204
|
+
constructor(rootURL: string, importURL?: string | null, preload?: ParseUpdate | null, eventStream?: EventStream | null, restrictedMode?: boolean);
|
|
204
205
|
update(dd: ParseUpdate): void;
|
|
206
|
+
private lockZonesIfRestricted;
|
|
207
|
+
translate(extendingModel?: ModelDef): TranslateResponse;
|
|
205
208
|
logError<T extends MessageCode>(code: T, parameters: MessageParameterType<T>, options?: Omit<LogMessageOptions, 'severity'>): T;
|
|
206
209
|
}
|
|
207
210
|
interface ErrorData {
|
|
@@ -61,6 +61,7 @@ const ast = __importStar(require("./ast"));
|
|
|
61
61
|
const malloy_to_ast_1 = require("./malloy-to-ast");
|
|
62
62
|
const parse_log_1 = require("./parse-log");
|
|
63
63
|
const find_external_references_1 = require("./parse-tree-walkers/find-external-references");
|
|
64
|
+
const dialect_1 = require("../dialect");
|
|
64
65
|
const zone_1 = require("./zone");
|
|
65
66
|
const document_symbol_walker_1 = require("./parse-tree-walkers/document-symbol-walker");
|
|
66
67
|
const document_completion_walker_1 = require("./parse-tree-walkers/document-completion-walker");
|
|
@@ -138,14 +139,17 @@ class ImportsAndTablesStep {
|
|
|
138
139
|
if (parseReq.parse === undefined) {
|
|
139
140
|
return parseReq;
|
|
140
141
|
}
|
|
142
|
+
// Every reference this step would register — connection dialects,
|
|
143
|
+
// imports, table schemas — is for a construct that's forbidden in
|
|
144
|
+
// restricted code.
|
|
145
|
+
if (that.root.restrictedMode) {
|
|
146
|
+
return { timingInfo: parseReq.timingInfo };
|
|
147
|
+
}
|
|
141
148
|
if (!this.parseReferences) {
|
|
142
149
|
this.parseReferences = (0, find_external_references_1.findReferences)(that, parseReq.parse.tokenStream, parseReq.parse.root);
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
range: this.parseReferences.tables[ref].firstReference,
|
|
147
|
-
});
|
|
148
|
-
}
|
|
150
|
+
// Register connection dialects and imports immediately. Table
|
|
151
|
+
// references are deferred until dialects are resolved, because
|
|
152
|
+
// validating a table path requires knowing its dialect's grammar.
|
|
149
153
|
for (const connName in this.parseReferences.connectionDialects) {
|
|
150
154
|
that.root.connectionDialectZone.reference(connName, {
|
|
151
155
|
url: that.sourceURL,
|
|
@@ -180,17 +184,49 @@ class ImportsAndTablesStep {
|
|
|
180
184
|
return { timingInfo: parseReq.timingInfo };
|
|
181
185
|
}
|
|
182
186
|
let allMissing = {};
|
|
187
|
+
// Validate each table reference against its dialect's grammar (if
|
|
188
|
+
// the dialect is resolved) and register the canonical entry. Bad
|
|
189
|
+
// paths are silently dropped — the AST step re-validates and logs
|
|
190
|
+
// an error at the precise source range. Entries whose dialect
|
|
191
|
+
// isn't resolved yet are preserved unchanged and re-processed on a
|
|
192
|
+
// later step.
|
|
193
|
+
{
|
|
194
|
+
const refs = this.parseReferences.tables;
|
|
195
|
+
const canonical = {};
|
|
196
|
+
for (const rawKey in refs) {
|
|
197
|
+
const info = refs[rawKey];
|
|
198
|
+
let { tablePath } = info;
|
|
199
|
+
const dialectName = that.root.connectionDialectZone.get(info.connectionName);
|
|
200
|
+
if (dialectName !== undefined) {
|
|
201
|
+
const result = (0, dialect_1.getDialect)(dialectName).sqlValidateTableName(tablePath);
|
|
202
|
+
if (!result.ok)
|
|
203
|
+
continue;
|
|
204
|
+
tablePath = result.canonical;
|
|
205
|
+
}
|
|
206
|
+
const key = `${info.connectionName}:${tablePath}`;
|
|
207
|
+
canonical[key] = { ...info, tablePath };
|
|
208
|
+
that.root.schemaZone.reference(key, {
|
|
209
|
+
url: that.sourceURL,
|
|
210
|
+
range: info.firstReference,
|
|
211
|
+
});
|
|
212
|
+
}
|
|
213
|
+
this.parseReferences.tables = canonical;
|
|
214
|
+
}
|
|
183
215
|
const missingTables = that.root.schemaZone.getUndefined();
|
|
184
216
|
if (missingTables) {
|
|
185
217
|
const tables = {};
|
|
186
218
|
for (const key of missingTables) {
|
|
187
219
|
const info = this.parseReferences.tables[key];
|
|
220
|
+
if (info === undefined)
|
|
221
|
+
continue;
|
|
188
222
|
tables[key] = {
|
|
189
223
|
connectionName: info.connectionName,
|
|
190
224
|
tablePath: info.tablePath,
|
|
191
225
|
};
|
|
192
226
|
}
|
|
193
|
-
|
|
227
|
+
if (Object.keys(tables).length > 0) {
|
|
228
|
+
allMissing = { tables };
|
|
229
|
+
}
|
|
194
230
|
}
|
|
195
231
|
const missingDialects = that.root.connectionDialectZone.getUndefined();
|
|
196
232
|
if (missingDialects) {
|
|
@@ -248,7 +284,7 @@ class ASTStep {
|
|
|
248
284
|
throw new Error('TRANSLATOR INTERNAL ERROR: Translator parse response had no errors, but also no parser');
|
|
249
285
|
}
|
|
250
286
|
stepTimer.incorporate(parseResponse.timingInfo);
|
|
251
|
-
const secondPass = new malloy_to_ast_1.MalloyToAST(parse, that.root.logger, that.compilerFlagSrc);
|
|
287
|
+
const secondPass = new malloy_to_ast_1.MalloyToAST(parse, that.root.logger, that.compilerFlagSrc, that.root.restrictedMode);
|
|
252
288
|
const { ast: newAST, compilerFlagSrc, timingInfo } = secondPass.run();
|
|
253
289
|
stepTimer.contribute([timingInfo]);
|
|
254
290
|
that.compilerFlagSrc = compilerFlagSrc;
|
|
@@ -436,10 +472,13 @@ class TranslateStep {
|
|
|
436
472
|
modelWasModified: false,
|
|
437
473
|
};
|
|
438
474
|
}
|
|
439
|
-
//
|
|
475
|
+
// Layer the extending model's compiler flags on top of whatever was
|
|
476
|
+
// there already. In production the array starts empty so push vs.
|
|
477
|
+
// overwrite produce the same result; the push lets constructor-time
|
|
478
|
+
// seeding (e.g. TestTranslator's compilerFlags option) survive.
|
|
440
479
|
if (extendingModel && !this.importedAnnotations) {
|
|
441
480
|
const parseCompilerFlagsTimer = new timing_1.Timer('parse_compiler_flags');
|
|
442
|
-
that.compilerFlagSrc
|
|
481
|
+
that.compilerFlagSrc.push(...(0, annotation_1.annotationToTaglines)(extendingModel.annotation, /^##! /));
|
|
443
482
|
stepTimer.contribute([parseCompilerFlagsTimer.stop()]);
|
|
444
483
|
this.importedAnnotations = true;
|
|
445
484
|
}
|
|
@@ -791,9 +830,10 @@ exports.MalloyChildTranslator = MalloyChildTranslator;
|
|
|
791
830
|
* no need to call again, the translation is finished or error'd.
|
|
792
831
|
*/
|
|
793
832
|
class MalloyTranslator extends MalloyTranslation {
|
|
794
|
-
constructor(rootURL, importURL = null, preload = null, eventStream = null) {
|
|
833
|
+
constructor(rootURL, importURL = null, preload = null, eventStream = null, restrictedMode = false) {
|
|
795
834
|
super(rootURL, importURL);
|
|
796
835
|
this.eventStream = eventStream;
|
|
836
|
+
this.restrictedMode = restrictedMode;
|
|
797
837
|
this.schemaZone = new zone_1.Zone();
|
|
798
838
|
this.importZone = new zone_1.Zone();
|
|
799
839
|
this.pretranslatedModels = new Map();
|
|
@@ -815,6 +855,18 @@ class MalloyTranslator extends MalloyTranslation {
|
|
|
815
855
|
this.pretranslatedModels.set(url, dd.translations[url]);
|
|
816
856
|
}
|
|
817
857
|
}
|
|
858
|
+
lockZonesIfRestricted() {
|
|
859
|
+
if (!this.restrictedMode)
|
|
860
|
+
return;
|
|
861
|
+
this.schemaZone.lock();
|
|
862
|
+
this.importZone.lock();
|
|
863
|
+
this.sqlQueryZone.lock();
|
|
864
|
+
this.connectionDialectZone.lock();
|
|
865
|
+
}
|
|
866
|
+
translate(extendingModel) {
|
|
867
|
+
this.lockZonesIfRestricted();
|
|
868
|
+
return super.translate(extendingModel);
|
|
869
|
+
}
|
|
818
870
|
logError(code, parameters, options) {
|
|
819
871
|
this.logger.log((0, parse_log_1.makeLogMessage)(code, parameters, { severity: 'error', ...options }));
|
|
820
872
|
return code;
|
|
@@ -4,27 +4,14 @@ import type { DocumentRange } from '../../model/malloy_types';
|
|
|
4
4
|
import type { MalloyTranslation } from '../parse-malloy';
|
|
5
5
|
type NeedImports = Record<string, DocumentRange>;
|
|
6
6
|
type NeedTables = Record<string, {
|
|
7
|
-
connectionName: string
|
|
7
|
+
connectionName: string;
|
|
8
8
|
tablePath: string;
|
|
9
9
|
firstReference: DocumentRange;
|
|
10
10
|
}>;
|
|
11
11
|
type NeedConnectionDialects = Record<string, {
|
|
12
12
|
firstReference: DocumentRange;
|
|
13
13
|
}>;
|
|
14
|
-
export declare function constructTableKey(connectionName: string
|
|
15
|
-
/**
|
|
16
|
-
* This function parses an old-style `tableURI` into a connection name and
|
|
17
|
-
* table path. The name includes `deprecated` because it should only be used
|
|
18
|
-
* in the (deprecated) old-style `table('conn:tab')` syntax. Any use of this
|
|
19
|
-
* anywhere else is bad.
|
|
20
|
-
* @param tableURI The sting that is passed into the `table('conn:tab')` syntax.
|
|
21
|
-
* @returns A connection name and table path.
|
|
22
|
-
* @deprecated
|
|
23
|
-
*/
|
|
24
|
-
export declare function deprecatedParseTableURI(tableURI: string): {
|
|
25
|
-
connectionName?: string;
|
|
26
|
-
tablePath: string;
|
|
27
|
-
};
|
|
14
|
+
export declare function constructTableKey(connectionName: string, tablePath: string): string;
|
|
28
15
|
export interface FindReferencesData {
|
|
29
16
|
tables: NeedTables;
|
|
30
17
|
urls: NeedImports;
|
|
@@ -23,7 +23,6 @@
|
|
|
23
23
|
*/
|
|
24
24
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
25
25
|
exports.constructTableKey = constructTableKey;
|
|
26
|
-
exports.deprecatedParseTableURI = deprecatedParseTableURI;
|
|
27
26
|
exports.findReferences = findReferences;
|
|
28
27
|
const ParseTreeWalker_1 = require("antlr4ts/tree/ParseTreeWalker");
|
|
29
28
|
const parse_utils_1 = require("../parse-utils");
|
|
@@ -70,6 +69,11 @@ class FindExternalReferences {
|
|
|
70
69
|
const tablePath = getPlainString(pcx.tablePath());
|
|
71
70
|
const reference = this.trans.rangeFromContext(pcx);
|
|
72
71
|
this.registerTableReference(connId, tablePath, reference);
|
|
72
|
+
// Register a need for the connection's dialect so the validator in
|
|
73
|
+
// ImportsAndTablesStep can run against it.
|
|
74
|
+
if (!this.needConnectionDialects[connId]) {
|
|
75
|
+
this.needConnectionDialects[connId] = { firstReference: reference };
|
|
76
|
+
}
|
|
73
77
|
}
|
|
74
78
|
enterVirtualSource(pcx) {
|
|
75
79
|
const connId = (0, parse_utils_1.getId)(pcx.connectionId());
|
|
@@ -87,28 +91,7 @@ class FindExternalReferences {
|
|
|
87
91
|
}
|
|
88
92
|
}
|
|
89
93
|
function constructTableKey(connectionName, tablePath) {
|
|
90
|
-
return connectionName
|
|
91
|
-
? tablePath
|
|
92
|
-
: `${connectionName}:${tablePath}`;
|
|
93
|
-
}
|
|
94
|
-
/**
|
|
95
|
-
* This function parses an old-style `tableURI` into a connection name and
|
|
96
|
-
* table path. The name includes `deprecated` because it should only be used
|
|
97
|
-
* in the (deprecated) old-style `table('conn:tab')` syntax. Any use of this
|
|
98
|
-
* anywhere else is bad.
|
|
99
|
-
* @param tableURI The sting that is passed into the `table('conn:tab')` syntax.
|
|
100
|
-
* @returns A connection name and table path.
|
|
101
|
-
* @deprecated
|
|
102
|
-
*/
|
|
103
|
-
function deprecatedParseTableURI(tableURI) {
|
|
104
|
-
const parts = tableURI.match(/^([^:]*):(.*)$/);
|
|
105
|
-
if (parts) {
|
|
106
|
-
const [, firstPart, secondPart] = parts;
|
|
107
|
-
return { connectionName: firstPart, tablePath: secondPart };
|
|
108
|
-
}
|
|
109
|
-
else {
|
|
110
|
-
return { tablePath: tableURI };
|
|
111
|
-
}
|
|
94
|
+
return `${connectionName}:${tablePath}`;
|
|
112
95
|
}
|
|
113
96
|
function findReferences(trans, tokens, parseTree) {
|
|
114
97
|
const finder = new FindExternalReferences(trans, tokens);
|
|
@@ -42,12 +42,27 @@ export declare class TestChildTranslator extends MalloyChildTranslator {
|
|
|
42
42
|
translate(): TranslateResponse;
|
|
43
43
|
addChild(url: string): void;
|
|
44
44
|
}
|
|
45
|
+
export interface TestTranslatorOptions {
|
|
46
|
+
rootRule?: string;
|
|
47
|
+
importBaseURL?: string | null;
|
|
48
|
+
eventStream?: EventStream | null;
|
|
49
|
+
internalModel?: ModelDef;
|
|
50
|
+
restrictedMode?: boolean;
|
|
51
|
+
/**
|
|
52
|
+
* Each entry is a compiler-flag tag fragment — the content that
|
|
53
|
+
* would follow `##! ` in a Malloy source annotation. Rendered to
|
|
54
|
+
* `##! <flag>\n` and pushed onto compilerFlagSrc before any
|
|
55
|
+
* TranslateStep seeding, so the flags are active during AST-build
|
|
56
|
+
* inExperiment() checks.
|
|
57
|
+
*/
|
|
58
|
+
compilerFlags?: string[];
|
|
59
|
+
}
|
|
45
60
|
export declare class TestTranslator extends MalloyTranslator {
|
|
46
61
|
readonly testSrc: string;
|
|
47
62
|
allDialectsEnabled: boolean;
|
|
48
63
|
testRoot?: TestRoot;
|
|
49
64
|
internalModel: ModelDef;
|
|
50
|
-
constructor(testSrc: string,
|
|
65
|
+
constructor(testSrc: string, options?: TestTranslatorOptions);
|
|
51
66
|
translate(): TranslateResponse;
|
|
52
67
|
addChild(url: string): void;
|
|
53
68
|
ast(): MalloyElement | undefined;
|
|
@@ -64,7 +79,7 @@ export declare class TestTranslator extends MalloyTranslator {
|
|
|
64
79
|
export declare class BetaExpression extends TestTranslator {
|
|
65
80
|
readonly sourceName: string;
|
|
66
81
|
private compiled?;
|
|
67
|
-
constructor(src: string, model?: ModelDef, sourceName?: string);
|
|
82
|
+
constructor(src: string, model?: ModelDef, sourceName?: string, options?: Omit<TestTranslatorOptions, 'rootRule' | 'internalModel'>);
|
|
68
83
|
private testFS;
|
|
69
84
|
compile(): void;
|
|
70
85
|
generated(): ExprValue;
|
|
@@ -85,12 +100,11 @@ interface HasTranslator<TT extends TestTranslator> extends MarkedSource {
|
|
|
85
100
|
}
|
|
86
101
|
export declare function expr(unmarked: TemplateStringsArray, ...marked: string[]): HasTranslator<BetaExpression>;
|
|
87
102
|
export declare function model(unmarked: TemplateStringsArray, ...marked: string[]): HasTranslator<TestTranslator>;
|
|
88
|
-
export declare function makeModelFunc(options: {
|
|
89
|
-
model?: ModelDef;
|
|
103
|
+
export declare function makeModelFunc(options: TestTranslatorOptions & {
|
|
90
104
|
prefix?: string;
|
|
91
105
|
wrap?: (code: string) => string;
|
|
92
106
|
}): (unmarked: TemplateStringsArray, ...marked: string[]) => HasTranslator<TestTranslator>;
|
|
93
|
-
export declare function makeExprFunc(model
|
|
107
|
+
export declare function makeExprFunc(model?: ModelDef, sourceName?: string, options?: Omit<TestTranslatorOptions, 'rootRule' | 'internalModel'>): (unmarked: TemplateStringsArray, ...marked: string[]) => HasTranslator<TestTranslator>;
|
|
94
108
|
export declare function markSource(unmarked: TemplateStringsArray, ...marked: string[]): MarkedSource;
|
|
95
109
|
export declare function getSelectOneStruct(sqlBlock: SQLSourceRequest): {
|
|
96
110
|
[key: string]: SQLSourceDef;
|
|
@@ -330,8 +330,9 @@ class TestChildTranslator extends parse_malloy_1.MalloyChildTranslator {
|
|
|
330
330
|
exports.TestChildTranslator = TestChildTranslator;
|
|
331
331
|
const testURI = 'internal://test/langtests/root.malloy';
|
|
332
332
|
class TestTranslator extends parse_malloy_1.MalloyTranslator {
|
|
333
|
-
constructor(testSrc,
|
|
334
|
-
|
|
333
|
+
constructor(testSrc, options = {}) {
|
|
334
|
+
var _a, _b, _c, _d, _e;
|
|
335
|
+
super(testURI, (_a = options.importBaseURL) !== null && _a !== void 0 ? _a : null, null, (_b = options.eventStream) !== null && _b !== void 0 ? _b : null, (_c = options.restrictedMode) !== null && _c !== void 0 ? _c : false);
|
|
335
336
|
this.testSrc = testSrc;
|
|
336
337
|
this.allDialectsEnabled = true;
|
|
337
338
|
/*
|
|
@@ -415,16 +416,19 @@ class TestTranslator extends parse_malloy_1.MalloyTranslator {
|
|
|
415
416
|
},
|
|
416
417
|
},
|
|
417
418
|
};
|
|
418
|
-
this.grammarRule = rootRule;
|
|
419
|
+
this.grammarRule = (_d = options.rootRule) !== null && _d !== void 0 ? _d : 'malloyDocument';
|
|
419
420
|
this.importZone.define(testURI, testSrc);
|
|
420
|
-
if (internalModel !== undefined) {
|
|
421
|
-
this.internalModel = internalModel;
|
|
421
|
+
if (options.internalModel !== undefined) {
|
|
422
|
+
this.internalModel = options.internalModel;
|
|
422
423
|
}
|
|
423
424
|
for (const actualSchema of exports.mockSchema) {
|
|
424
425
|
this.schemaZone.define(`${actualSchema.connection}:${actualSchema.tablePath}`, actualSchema);
|
|
425
426
|
}
|
|
426
427
|
this.connectionDialectZone.define('_db_', exports.TEST_DIALECT);
|
|
427
428
|
this.connectionDialectZone.define('_bq_', 'standardsql');
|
|
429
|
+
for (const flag of (_e = options.compilerFlags) !== null && _e !== void 0 ? _e : []) {
|
|
430
|
+
this.compilerFlagSrc.push(`##! ${flag}\n`);
|
|
431
|
+
}
|
|
428
432
|
}
|
|
429
433
|
translate() {
|
|
430
434
|
return super.translate(this.internalModel);
|
|
@@ -532,8 +536,8 @@ class TestTranslator extends parse_malloy_1.MalloyTranslator {
|
|
|
532
536
|
exports.TestTranslator = TestTranslator;
|
|
533
537
|
TestTranslator.inspectCompile = false;
|
|
534
538
|
class BetaExpression extends TestTranslator {
|
|
535
|
-
constructor(src, model, sourceName = 'ab') {
|
|
536
|
-
super(src,
|
|
539
|
+
constructor(src, model, sourceName = 'ab', options = {}) {
|
|
540
|
+
super(src, { ...options, rootRule: 'debugExpr', internalModel: model });
|
|
537
541
|
this.sourceName = sourceName;
|
|
538
542
|
}
|
|
539
543
|
testFS() {
|
|
@@ -627,21 +631,20 @@ function model(unmarked, ...marked) {
|
|
|
627
631
|
}
|
|
628
632
|
function makeModelFunc(options) {
|
|
629
633
|
return function model(unmarked, ...marked) {
|
|
630
|
-
var _a;
|
|
631
634
|
const ms = markSource(unmarked, ...marked);
|
|
635
|
+
const { prefix, wrap, ...ttOptions } = options;
|
|
632
636
|
return {
|
|
633
637
|
...ms,
|
|
634
|
-
translator: new TestTranslator((
|
|
635
|
-
(options.wrap ? options.wrap(ms.code) : ms.code), null, null, undefined, options === null || options === void 0 ? void 0 : options.model),
|
|
638
|
+
translator: new TestTranslator((prefix !== null && prefix !== void 0 ? prefix : '') + (wrap ? wrap(ms.code) : ms.code), ttOptions),
|
|
636
639
|
};
|
|
637
640
|
};
|
|
638
641
|
}
|
|
639
|
-
function makeExprFunc(model, sourceName) {
|
|
642
|
+
function makeExprFunc(model, sourceName = 'ab', options = {}) {
|
|
640
643
|
return function expr(unmarked, ...marked) {
|
|
641
644
|
const ms = markSource(unmarked, ...marked);
|
|
642
645
|
return {
|
|
643
646
|
...ms,
|
|
644
|
-
translator: new BetaExpression(ms.code, model, sourceName),
|
|
647
|
+
translator: new BetaExpression(ms.code, model, sourceName, options),
|
|
645
648
|
};
|
|
646
649
|
};
|
|
647
650
|
}
|
|
@@ -22,7 +22,7 @@ export interface ProblemResponse {
|
|
|
22
22
|
export type FatalResponse = FinalResponse & ProblemResponse;
|
|
23
23
|
export interface NeedSchemaData {
|
|
24
24
|
tables: Record<string, {
|
|
25
|
-
connectionName: string
|
|
25
|
+
connectionName: string;
|
|
26
26
|
tablePath: string;
|
|
27
27
|
}>;
|
|
28
28
|
}
|
package/dist/lang/zone.d.ts
CHANGED
|
@@ -28,7 +28,9 @@ export type ZoneEntry<T> = EntryPresent<T> | ReferenceEntry | EntryErrored;
|
|
|
28
28
|
export declare class Zone<TValue> {
|
|
29
29
|
zone: Map<string, ZoneEntry<TValue>>;
|
|
30
30
|
location: Record<string, DocumentLocation>;
|
|
31
|
+
private locked;
|
|
31
32
|
constructor();
|
|
33
|
+
lock(): void;
|
|
32
34
|
get(str: string): TValue | undefined;
|
|
33
35
|
getEntry(str: string): ZoneEntry<TValue>;
|
|
34
36
|
/**
|
package/dist/lang/zone.js
CHANGED
|
@@ -33,8 +33,12 @@ exports.Zone = void 0;
|
|
|
33
33
|
class Zone {
|
|
34
34
|
constructor() {
|
|
35
35
|
this.location = {};
|
|
36
|
+
this.locked = false;
|
|
36
37
|
this.zone = new Map();
|
|
37
38
|
}
|
|
39
|
+
lock() {
|
|
40
|
+
this.locked = true;
|
|
41
|
+
}
|
|
38
42
|
get(str) {
|
|
39
43
|
const zst = this.zone.get(str);
|
|
40
44
|
if ((zst === null || zst === void 0 ? void 0 : zst.status) === 'present') {
|
|
@@ -57,6 +61,8 @@ class Zone {
|
|
|
57
61
|
* @param val
|
|
58
62
|
*/
|
|
59
63
|
define(str, val) {
|
|
64
|
+
if (this.locked)
|
|
65
|
+
return;
|
|
60
66
|
this.zone.set(str, { status: 'present', value: val });
|
|
61
67
|
}
|
|
62
68
|
/**
|
|
@@ -65,6 +71,8 @@ class Zone {
|
|
|
65
71
|
* @param loc The location of the reference
|
|
66
72
|
*/
|
|
67
73
|
reference(str, loc) {
|
|
74
|
+
if (this.locked)
|
|
75
|
+
return;
|
|
68
76
|
const zst = this.zone.get(str);
|
|
69
77
|
if ((zst === null || zst === void 0 ? void 0 : zst.status) === undefined) {
|
|
70
78
|
this.zone.set(str, { status: 'reference', firstReference: loc });
|
|
@@ -89,6 +97,8 @@ class Zone {
|
|
|
89
97
|
* @param errorData Pass on errors encountered during fetch
|
|
90
98
|
*/
|
|
91
99
|
updateFrom(updateData, errorData) {
|
|
100
|
+
if (this.locked)
|
|
101
|
+
return;
|
|
92
102
|
if (updateData) {
|
|
93
103
|
for (const [updateKey, updateVal] of Object.entries(updateData)) {
|
|
94
104
|
if (updateVal !== undefined) {
|
|
@@ -14,11 +14,20 @@ const field_instance_1 = require("./field_instance");
|
|
|
14
14
|
* Used to distinguish expected errors from unexpected ones.
|
|
15
15
|
*/
|
|
16
16
|
class ConstantExpressionError extends Error {
|
|
17
|
-
constructor(message) {
|
|
17
|
+
constructor(message, at) {
|
|
18
18
|
super(message);
|
|
19
|
+
this.at = at;
|
|
19
20
|
this.name = 'ConstantExpressionError';
|
|
20
21
|
}
|
|
21
22
|
}
|
|
23
|
+
function locSuffix(at) {
|
|
24
|
+
if (!at)
|
|
25
|
+
return '';
|
|
26
|
+
// Display 1-based line/column; range positions are 0-based.
|
|
27
|
+
const line = at.range.start.line + 1;
|
|
28
|
+
const col = at.range.start.character + 1;
|
|
29
|
+
return ` at ${at.url}:${line}:${col}`;
|
|
30
|
+
}
|
|
22
31
|
/**
|
|
23
32
|
* Minimal FieldInstanceResultRoot for constant expressions.
|
|
24
33
|
* This serves as both the result set and its own root, providing
|
|
@@ -106,11 +115,11 @@ class ConstantQueryStruct extends query_node_1.QueryStruct {
|
|
|
106
115
|
/**
|
|
107
116
|
* These methods should not be called for constant expressions
|
|
108
117
|
*/
|
|
109
|
-
getFieldByName(path) {
|
|
110
|
-
throw new ConstantExpressionError(`Illegal reference to '${path.join('.')}' in constant expressions
|
|
118
|
+
getFieldByName(path, at) {
|
|
119
|
+
throw new ConstantExpressionError(`Illegal reference to '${path.join('.')}' in constant expressions${locSuffix(at)}`, at);
|
|
111
120
|
}
|
|
112
|
-
getStructByName(path) {
|
|
113
|
-
throw new ConstantExpressionError(`Illegal reference to '${path.join('.')}' in constant expressions
|
|
121
|
+
getStructByName(path, at) {
|
|
122
|
+
throw new ConstantExpressionError(`Illegal reference to '${path.join('.')}' in constant expressions${locSuffix(at)}`, at);
|
|
114
123
|
}
|
|
115
124
|
getSQLIdentifier() {
|
|
116
125
|
throw new ConstantExpressionError('Constant expressions do not need SQL identifiers');
|
|
@@ -29,6 +29,7 @@ const field_instance_1 = require("./field_instance");
|
|
|
29
29
|
const filter_compilers_1 = require("./filter_compilers");
|
|
30
30
|
const utils_1 = require("./utils");
|
|
31
31
|
const query_node_1 = require("./query_node");
|
|
32
|
+
const malloy_compile_error_1 = require("./malloy_compile_error");
|
|
32
33
|
const NUMERIC_DECIMAL_PRECISION = 9;
|
|
33
34
|
function sqlSumDistinct(dialect, sqlExp, sqlDistintKey) {
|
|
34
35
|
const precision = 9;
|
|
@@ -186,7 +187,7 @@ function compileExpr(resultSet, context, expr, state = new utils_1.GenerateState
|
|
|
186
187
|
return `${expr.kids.e.sql} ${expr.not ? 'NOT IN' : 'IN'} (${oneOf})`;
|
|
187
188
|
}
|
|
188
189
|
case 'inGiven': {
|
|
189
|
-
const bound = resolveGivenBoundExpr(context, expr.givenRef
|
|
190
|
+
const bound = resolveGivenBoundExpr(context, expr.givenRef);
|
|
190
191
|
// null binding collapses to empty-set semantics — not the SQL
|
|
191
192
|
// `IN (NULL)` shape, which has confusing NULL-membership rules.
|
|
192
193
|
if (bound.node === 'null') {
|
|
@@ -280,27 +281,27 @@ function generateAppliedFilter(context, filterMatchExpr, qi) {
|
|
|
280
281
|
filterExpr = argument.value;
|
|
281
282
|
}
|
|
282
283
|
else {
|
|
283
|
-
throw new
|
|
284
|
+
throw new malloy_compile_error_1.MalloyCompileError(`Parameter '${name}' has no value, but a filter expression is required here.`, 'compiler-filter-parameter-no-value', filterExpr.at);
|
|
284
285
|
}
|
|
285
286
|
}
|
|
286
287
|
if (filterExpr.node === 'given') {
|
|
287
|
-
const
|
|
288
|
-
const supplied = (_c = (_b = context.prepareResultOptions) === null || _b === void 0 ? void 0 : _b.resolvedGivens) === null || _c === void 0 ? void 0 : _c.get(id);
|
|
288
|
+
const supplied = (_c = (_b = context.prepareResultOptions) === null || _b === void 0 ? void 0 : _b.resolvedGivens) === null || _c === void 0 ? void 0 : _c.get(filterExpr.id);
|
|
289
289
|
if (supplied !== undefined) {
|
|
290
290
|
filterExpr = supplied;
|
|
291
291
|
}
|
|
292
292
|
else {
|
|
293
|
-
const decl = context.getModel().givens[id];
|
|
293
|
+
const decl = context.getModel().givens[filterExpr.id];
|
|
294
294
|
if ((decl === null || decl === void 0 ? void 0 : decl.default) !== undefined) {
|
|
295
295
|
filterExpr = decl.default;
|
|
296
296
|
}
|
|
297
297
|
else {
|
|
298
|
-
throw new
|
|
298
|
+
throw new malloy_compile_error_1.MalloyCompileError(unsatisfiedGivenMessage(filterExpr.refName), 'compiler-given-no-value', filterExpr.at);
|
|
299
299
|
}
|
|
300
300
|
}
|
|
301
301
|
}
|
|
302
302
|
if (filterExpr.node !== 'filterLiteral') {
|
|
303
|
-
throw new
|
|
303
|
+
throw new malloy_compile_error_1.MalloyCompileError("Filter context requires a filter-expression literal (e.g., `f'...'`) " +
|
|
304
|
+
`or a parameter that resolves to one; got node '${filterExpr.node}'.`, 'compiler-filter-not-literal', undefined);
|
|
304
305
|
}
|
|
305
306
|
const filterSrc = filterExpr.filterSrc;
|
|
306
307
|
let fParse;
|
|
@@ -320,10 +321,11 @@ function generateAppliedFilter(context, filterMatchExpr, qi) {
|
|
|
320
321
|
fParse = malloy_filter_1.TemporalFilterExpression.parse(filterSrc);
|
|
321
322
|
break;
|
|
322
323
|
default:
|
|
323
|
-
throw new
|
|
324
|
+
throw new malloy_compile_error_1.MalloyCompileError(`Filter type '${filterMatchExpr.dataType}' is not supported. ` +
|
|
325
|
+
'Supported filter types: string, number, boolean, date, timestamp, timestamptz.', 'compiler-filter-type-unsupported', undefined);
|
|
324
326
|
}
|
|
325
327
|
if (fParse.log.length > 0) {
|
|
326
|
-
throw new
|
|
328
|
+
throw new malloy_compile_error_1.MalloyCompileError(`Filter expression parse error: ${fParse.log[0]}.`, 'compiler-filter-parse-error', undefined);
|
|
327
329
|
}
|
|
328
330
|
return filter_compilers_1.FilterCompilers.compile(filterMatchExpr.dataType, fParse.parsed, filterMatchExpr.kids.expr.sql || '', context.dialect, qi);
|
|
329
331
|
}
|
|
@@ -581,7 +583,7 @@ function generateFunctionCallExpression(resultSet, context, frag, state) {
|
|
|
581
583
|
}
|
|
582
584
|
function generateFieldFragment(resultSet, context, expr, state) {
|
|
583
585
|
// find the structDef and return the path to the field...
|
|
584
|
-
const fieldRef = context.getFieldByName(expr.path);
|
|
586
|
+
const fieldRef = context.getFieldByName(expr.path, expr.at);
|
|
585
587
|
if ((0, malloy_types_1.hasExpression)(fieldRef.fieldDef)) {
|
|
586
588
|
const ret = exprToSQL(resultSet, fieldRef.parent, fieldRef.fieldDef.e, state);
|
|
587
589
|
return `(${ret})`;
|
|
@@ -614,7 +616,7 @@ function generateParameterFragment(resultSet, context, expr, state) {
|
|
|
614
616
|
if (argument === null || argument === void 0 ? void 0 : argument.value) {
|
|
615
617
|
return exprToSQL(resultSet, context, argument.value, state);
|
|
616
618
|
}
|
|
617
|
-
throw new
|
|
619
|
+
throw new malloy_compile_error_1.MalloyCompileError(`Parameter '${expr.path.join('.')}' has no value supplied.`, 'compiler-parameter-no-value', expr.at);
|
|
618
620
|
}
|
|
619
621
|
/**
|
|
620
622
|
* Resolve a given to the Expr that should stand in for it at SQL emit:
|
|
@@ -625,20 +627,20 @@ function generateParameterFragment(resultSet, context, expr, state) {
|
|
|
625
627
|
* Shared by `generateGivenFragment` ($NAME directly in an expression)
|
|
626
628
|
* and the `'inGiven'` SQL-emit case ($ARR in `expr in $ARR`).
|
|
627
629
|
*/
|
|
628
|
-
function resolveGivenBoundExpr(context,
|
|
630
|
+
function resolveGivenBoundExpr(context, ref) {
|
|
629
631
|
var _a, _b;
|
|
630
|
-
const supplied = (_b = (_a = context.prepareResultOptions) === null || _a === void 0 ? void 0 : _a.resolvedGivens) === null || _b === void 0 ? void 0 : _b.get(id);
|
|
632
|
+
const supplied = (_b = (_a = context.prepareResultOptions) === null || _a === void 0 ? void 0 : _a.resolvedGivens) === null || _b === void 0 ? void 0 : _b.get(ref.id);
|
|
631
633
|
if (supplied !== undefined)
|
|
632
634
|
return supplied;
|
|
633
|
-
const decl = context.getModel().givens[id];
|
|
635
|
+
const decl = context.getModel().givens[ref.id];
|
|
634
636
|
if ((decl === null || decl === void 0 ? void 0 : decl.default) !== undefined)
|
|
635
637
|
return decl.default;
|
|
636
|
-
throw new
|
|
638
|
+
throw new malloy_compile_error_1.MalloyCompileError(unsatisfiedGivenMessage(ref.refName), 'compiler-given-no-value', ref.at);
|
|
637
639
|
}
|
|
638
640
|
function generateGivenFragment(resultSet, context, expr, state) {
|
|
639
641
|
// The bound expr may itself be a `$OTHER`-bearing expression; recursive
|
|
640
642
|
// compile handles default chains.
|
|
641
|
-
const bound = resolveGivenBoundExpr(context, expr
|
|
643
|
+
const bound = resolveGivenBoundExpr(context, expr);
|
|
642
644
|
return exprToSQL(resultSet, context, bound, state);
|
|
643
645
|
}
|
|
644
646
|
function unsatisfiedGivenMessage(refName) {
|
|
@@ -663,7 +665,7 @@ function generateDimFragment(resultSet, context, expr, state) {
|
|
|
663
665
|
}
|
|
664
666
|
function generateUngroupedFragment(resultSet, context, expr, state) {
|
|
665
667
|
if (state.totalGroupSet !== -1) {
|
|
666
|
-
throw new
|
|
668
|
+
throw new malloy_compile_error_1.MalloyCompileError("Cannot nest 'all()' or 'exclude()' inside another 'all()' calculation.", 'compiler-nested-ungroup', undefined);
|
|
667
669
|
}
|
|
668
670
|
let totalGroupSet;
|
|
669
671
|
let ungroupSet;
|