@malloydata/malloy 0.0.393 → 0.0.395
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/config.d.ts +2 -3
- package/dist/api/foundation/config.js +23 -11
- package/dist/api/foundation/core.d.ts +0 -4
- package/dist/api/foundation/core.js +14 -11
- package/dist/api/foundation/runtime.js +21 -1
- 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-compare.d.ts +15 -0
- package/dist/lang/ast/expressions/expr-compare.js +82 -2
- package/dist/lang/ast/source-elements/table-source.d.ts +1 -7
- package/dist/lang/ast/source-elements/table-source.js +20 -19
- package/dist/lang/ast/statements/define-given.d.ts +2 -1
- package/dist/lang/ast/statements/define-given.js +52 -1
- package/dist/lang/ast/types/malloy-element.js +2 -0
- package/dist/lang/lib/Malloy/MalloyParser.d.ts +188 -167
- package/dist/lang/lib/Malloy/MalloyParser.js +2582 -2442
- package/dist/lang/lib/Malloy/MalloyParserListener.d.ts +24 -0
- package/dist/lang/lib/Malloy/MalloyParserVisitor.d.ts +15 -0
- package/dist/lang/malloy-to-ast.d.ts +9 -2
- package/dist/lang/malloy-to-ast.js +37 -2
- package/dist/lang/parse-log.d.ts +23 -0
- package/dist/lang/parse-log.js +6 -0
- package/dist/lang/parse-malloy.js +37 -7
- 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/expr-to-str.js +3 -0
- package/dist/lang/translate-response.d.ts +1 -1
- package/dist/model/expression_compiler.js +38 -11
- package/dist/model/filter_compilers.js +1 -1
- package/dist/model/given_binding.d.ts +15 -0
- package/dist/model/given_binding.js +35 -0
- package/dist/model/inline_expr.d.ts +30 -0
- package/dist/model/inline_expr.js +184 -0
- package/dist/model/malloy_types.d.ts +19 -1
- package/dist/model/query_model_impl.js +7 -7
- package/dist/model/query_query.d.ts +1 -1
- package/dist/model/query_query.js +37 -33
- package/dist/model/sql_compiled.d.ts +2 -4
- package/dist/model/sql_compiled.js +14 -15
- 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
|
@@ -42,6 +42,7 @@ import { ExprCompareContext } from "./MalloyParser";
|
|
|
42
42
|
import { ExprWarnLikeContext } from "./MalloyParser";
|
|
43
43
|
import { ExprNullCheckContext } from "./MalloyParser";
|
|
44
44
|
import { ExprWarnInContext } from "./MalloyParser";
|
|
45
|
+
import { ExprInGivenContext } from "./MalloyParser";
|
|
45
46
|
import { ExprApplyContext } from "./MalloyParser";
|
|
46
47
|
import { ExprNotContext } from "./MalloyParser";
|
|
47
48
|
import { ExprLogicalAndContext } from "./MalloyParser";
|
|
@@ -104,6 +105,7 @@ import { DefineQueryContext } from "./MalloyParser";
|
|
|
104
105
|
import { DefineGivenStatementContext } from "./MalloyParser";
|
|
105
106
|
import { GivenDefListContext } from "./MalloyParser";
|
|
106
107
|
import { GivenDefContext } from "./MalloyParser";
|
|
108
|
+
import { GivenModifierContext } from "./MalloyParser";
|
|
107
109
|
import { GivenNameDefContext } from "./MalloyParser";
|
|
108
110
|
import { GivenTypeContext } from "./MalloyParser";
|
|
109
111
|
import { TopLevelAnonQueryDefContext } from "./MalloyParser";
|
|
@@ -792,6 +794,18 @@ export interface MalloyParserListener extends ParseTreeListener {
|
|
|
792
794
|
* @param ctx the parse tree
|
|
793
795
|
*/
|
|
794
796
|
exitExprWarnIn?: (ctx: ExprWarnInContext) => void;
|
|
797
|
+
/**
|
|
798
|
+
* Enter a parse tree produced by the `exprInGiven`
|
|
799
|
+
* labeled alternative in `MalloyParser.fieldExpr`.
|
|
800
|
+
* @param ctx the parse tree
|
|
801
|
+
*/
|
|
802
|
+
enterExprInGiven?: (ctx: ExprInGivenContext) => void;
|
|
803
|
+
/**
|
|
804
|
+
* Exit a parse tree produced by the `exprInGiven`
|
|
805
|
+
* labeled alternative in `MalloyParser.fieldExpr`.
|
|
806
|
+
* @param ctx the parse tree
|
|
807
|
+
*/
|
|
808
|
+
exitExprInGiven?: (ctx: ExprInGivenContext) => void;
|
|
795
809
|
/**
|
|
796
810
|
* Enter a parse tree produced by the `exprApply`
|
|
797
811
|
* labeled alternative in `MalloyParser.fieldExpr`.
|
|
@@ -1506,6 +1520,16 @@ export interface MalloyParserListener extends ParseTreeListener {
|
|
|
1506
1520
|
* @param ctx the parse tree
|
|
1507
1521
|
*/
|
|
1508
1522
|
exitGivenDef?: (ctx: GivenDefContext) => void;
|
|
1523
|
+
/**
|
|
1524
|
+
* Enter a parse tree produced by `MalloyParser.givenModifier`.
|
|
1525
|
+
* @param ctx the parse tree
|
|
1526
|
+
*/
|
|
1527
|
+
enterGivenModifier?: (ctx: GivenModifierContext) => void;
|
|
1528
|
+
/**
|
|
1529
|
+
* Exit a parse tree produced by `MalloyParser.givenModifier`.
|
|
1530
|
+
* @param ctx the parse tree
|
|
1531
|
+
*/
|
|
1532
|
+
exitGivenModifier?: (ctx: GivenModifierContext) => void;
|
|
1509
1533
|
/**
|
|
1510
1534
|
* Enter a parse tree produced by `MalloyParser.givenNameDef`.
|
|
1511
1535
|
* @param ctx the parse tree
|
|
@@ -42,6 +42,7 @@ import { ExprCompareContext } from "./MalloyParser";
|
|
|
42
42
|
import { ExprWarnLikeContext } from "./MalloyParser";
|
|
43
43
|
import { ExprNullCheckContext } from "./MalloyParser";
|
|
44
44
|
import { ExprWarnInContext } from "./MalloyParser";
|
|
45
|
+
import { ExprInGivenContext } from "./MalloyParser";
|
|
45
46
|
import { ExprApplyContext } from "./MalloyParser";
|
|
46
47
|
import { ExprNotContext } from "./MalloyParser";
|
|
47
48
|
import { ExprLogicalAndContext } from "./MalloyParser";
|
|
@@ -104,6 +105,7 @@ import { DefineQueryContext } from "./MalloyParser";
|
|
|
104
105
|
import { DefineGivenStatementContext } from "./MalloyParser";
|
|
105
106
|
import { GivenDefListContext } from "./MalloyParser";
|
|
106
107
|
import { GivenDefContext } from "./MalloyParser";
|
|
108
|
+
import { GivenModifierContext } from "./MalloyParser";
|
|
107
109
|
import { GivenNameDefContext } from "./MalloyParser";
|
|
108
110
|
import { GivenTypeContext } from "./MalloyParser";
|
|
109
111
|
import { TopLevelAnonQueryDefContext } from "./MalloyParser";
|
|
@@ -580,6 +582,13 @@ export interface MalloyParserVisitor<Result> extends ParseTreeVisitor<Result> {
|
|
|
580
582
|
* @return the visitor result
|
|
581
583
|
*/
|
|
582
584
|
visitExprWarnIn?: (ctx: ExprWarnInContext) => Result;
|
|
585
|
+
/**
|
|
586
|
+
* Visit a parse tree produced by the `exprInGiven`
|
|
587
|
+
* labeled alternative in `MalloyParser.fieldExpr`.
|
|
588
|
+
* @param ctx the parse tree
|
|
589
|
+
* @return the visitor result
|
|
590
|
+
*/
|
|
591
|
+
visitExprInGiven?: (ctx: ExprInGivenContext) => Result;
|
|
583
592
|
/**
|
|
584
593
|
* Visit a parse tree produced by the `exprApply`
|
|
585
594
|
* labeled alternative in `MalloyParser.fieldExpr`.
|
|
@@ -999,6 +1008,12 @@ export interface MalloyParserVisitor<Result> extends ParseTreeVisitor<Result> {
|
|
|
999
1008
|
* @return the visitor result
|
|
1000
1009
|
*/
|
|
1001
1010
|
visitGivenDef?: (ctx: GivenDefContext) => Result;
|
|
1011
|
+
/**
|
|
1012
|
+
* Visit a parse tree produced by `MalloyParser.givenModifier`.
|
|
1013
|
+
* @param ctx the parse tree
|
|
1014
|
+
* @return the visitor result
|
|
1015
|
+
*/
|
|
1016
|
+
visitGivenModifier?: (ctx: GivenModifierContext) => Result;
|
|
1002
1017
|
/**
|
|
1003
1018
|
* Visit a parse tree produced by `MalloyParser.givenNameDef`.
|
|
1004
1019
|
* @param ctx the parse tree
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { ParserRuleContext } from 'antlr4ts';
|
|
2
|
-
import type { ParseTree } from 'antlr4ts/tree';
|
|
2
|
+
import type { ParseTree, TerminalNode } from 'antlr4ts/tree';
|
|
3
3
|
import { AbstractParseTreeVisitor } from 'antlr4ts/tree/AbstractParseTreeVisitor';
|
|
4
4
|
import type { MalloyParserVisitor } from './lib/Malloy/MalloyParserVisitor';
|
|
5
5
|
import type * as parse from './lib/Malloy/MalloyParser';
|
|
@@ -72,7 +72,13 @@ export declare class MalloyToAST extends AbstractParseTreeVisitor<ast.MalloyElem
|
|
|
72
72
|
protected getFieldName(cx: HasID): ast.FieldName;
|
|
73
73
|
protected getModelEntryName(cx: HasID): ast.ModelEntryReference;
|
|
74
74
|
defaultResult(): ast.MalloyElement;
|
|
75
|
-
|
|
75
|
+
/**
|
|
76
|
+
* Attach a source location to an AST element and return it. The range
|
|
77
|
+
* is taken from the supplied parse-tree node — either a
|
|
78
|
+
* `ParserRuleContext` (entire rule's token span) or a `TerminalNode`
|
|
79
|
+
* (just that one token).
|
|
80
|
+
*/
|
|
81
|
+
protected astAt<MT extends ast.MalloyElement>(el: MT, cx: ParserRuleContext | TerminalNode): MT;
|
|
76
82
|
protected getSourceCode(cx: ParserRuleContext): string;
|
|
77
83
|
protected getFilterElement(cx: parse.FieldExprContext): ast.FilterElement;
|
|
78
84
|
protected getFieldDefs(cxList: parse.FieldDefContext[], makeFieldDef: ast.FieldDeclarationConstructor): ast.AtomicFieldDeclaration[];
|
|
@@ -275,6 +281,7 @@ export declare class MalloyToAST extends AbstractParseTreeVisitor<ast.MalloyElem
|
|
|
275
281
|
visitExprArrayLiteral(pcx: parse.ExprArrayLiteralContext): ast.ArrayLiteral;
|
|
276
282
|
visitExprWarnLike(pcx: parse.ExprWarnLikeContext): ast.ExprCompare;
|
|
277
283
|
visitExprNullCheck(pcx: parse.ExprNullCheckContext): ast.ExprIsNull;
|
|
284
|
+
visitExprInGiven(pcx: parse.ExprInGivenContext): ast.ExprInGiven;
|
|
278
285
|
visitExprWarnIn(pcx: parse.ExprWarnInContext): ast.ExprLegacyIn;
|
|
279
286
|
visitTickFilterString(pcx: parse.TickFilterStringContext): ast.ExprFilterExpression;
|
|
280
287
|
visitTripFilterString(pcx: parse.TripFilterStringContext): ast.ExprFilterExpression;
|
|
@@ -195,10 +195,19 @@ class MalloyToAST extends AbstractParseTreeVisitor_1.AbstractParseTreeVisitor {
|
|
|
195
195
|
defaultResult() {
|
|
196
196
|
return new ast.Unimplemented();
|
|
197
197
|
}
|
|
198
|
+
/**
|
|
199
|
+
* Attach a source location to an AST element and return it. The range
|
|
200
|
+
* is taken from the supplied parse-tree node — either a
|
|
201
|
+
* `ParserRuleContext` (entire rule's token span) or a `TerminalNode`
|
|
202
|
+
* (just that one token).
|
|
203
|
+
*/
|
|
198
204
|
astAt(el, cx) {
|
|
205
|
+
const range = cx instanceof antlr4ts_1.ParserRuleContext
|
|
206
|
+
? this.rangeFromContext(cx)
|
|
207
|
+
: (0, utils_1.rangeFromToken)(this.parseInfo.sourceInfo, cx.symbol);
|
|
199
208
|
el.location = {
|
|
200
209
|
url: this.parseInfo.sourceURL,
|
|
201
|
-
range
|
|
210
|
+
range,
|
|
202
211
|
};
|
|
203
212
|
return el;
|
|
204
213
|
}
|
|
@@ -317,7 +326,25 @@ class MalloyToAST extends AbstractParseTreeVisitor_1.AbstractParseTreeVisitor {
|
|
|
317
326
|
this.contextError(afterIs, 'given-no-tags-after-is', 'Annotations are not allowed between `is` and the default value of a given; place them above the declaration');
|
|
318
327
|
}
|
|
319
328
|
}
|
|
320
|
-
|
|
329
|
+
// The modifier slot accepts any identifier and rejects anything but
|
|
330
|
+
// `inline` at AST-build — this keeps `inline` from being a reserved
|
|
331
|
+
// word elsewhere in the language (fields, sources, view names, etc.
|
|
332
|
+
// can still be called `inline`). The cost: a slightly later error
|
|
333
|
+
// surface if someone misspells the modifier.
|
|
334
|
+
// Case-insensitive match: Malloy keywords are case-insensitive in
|
|
335
|
+
// the lexer, so accept `inline`, `INLINE`, `Inline`, etc.
|
|
336
|
+
const modCx = pcx.givenModifier();
|
|
337
|
+
let inline = false;
|
|
338
|
+
if (modCx) {
|
|
339
|
+
const modText = (0, parse_utils_1.getId)(modCx);
|
|
340
|
+
if (modText.toLowerCase() === 'inline') {
|
|
341
|
+
inline = true;
|
|
342
|
+
}
|
|
343
|
+
else {
|
|
344
|
+
this.contextError(modCx, 'invalid-given-modifier', { modifier: modText });
|
|
345
|
+
}
|
|
346
|
+
}
|
|
347
|
+
const decl = new ast.GivenDeclaration(name, typeDef, defVal, inline);
|
|
321
348
|
decl.extendNote({ notes: this.getNotes(pcx.tags()) });
|
|
322
349
|
return this.astAt(decl, pcx);
|
|
323
350
|
}
|
|
@@ -1766,6 +1793,14 @@ class MalloyToAST extends AbstractParseTreeVisitor_1.AbstractParseTreeVisitor {
|
|
|
1766
1793
|
const expr = pcx.fieldExpr();
|
|
1767
1794
|
return this.astAt(new ast.ExprIsNull(this.getFieldExpr(expr), pcx.NOT() ? '!=' : '='), pcx);
|
|
1768
1795
|
}
|
|
1796
|
+
visitExprInGiven(pcx) {
|
|
1797
|
+
this.inExperiment('givens', pcx);
|
|
1798
|
+
const lhs = this.getFieldExpr(pcx.fieldExpr());
|
|
1799
|
+
const isNot = !!pcx.NOT();
|
|
1800
|
+
const givenName = pcx.GIVEN_REF().text.slice(1);
|
|
1801
|
+
const givenRef = this.astAt(new ast.GivenReference(givenName), pcx.GIVEN_REF());
|
|
1802
|
+
return this.astAt(new ast.ExprInGiven(lhs, isNot, givenRef), pcx);
|
|
1803
|
+
}
|
|
1769
1804
|
visitExprWarnIn(pcx) {
|
|
1770
1805
|
const expr = this.getFieldExpr(pcx.fieldExpr());
|
|
1771
1806
|
const isNot = !!pcx.NOT();
|
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;
|
|
@@ -292,6 +293,28 @@ type MessageParameterTypes = {
|
|
|
292
293
|
'given-reference-not-implemented': string;
|
|
293
294
|
'given-not-found': string;
|
|
294
295
|
'given-no-tags-after-is': string;
|
|
296
|
+
'in-given-rhs-not-array': {
|
|
297
|
+
givenName: string;
|
|
298
|
+
actualType: string;
|
|
299
|
+
};
|
|
300
|
+
'in-given-rhs-not-basic-array': {
|
|
301
|
+
givenName: string;
|
|
302
|
+
elementType: string;
|
|
303
|
+
};
|
|
304
|
+
'in-given-type-mismatch': {
|
|
305
|
+
lhsType: string;
|
|
306
|
+
elementType: string;
|
|
307
|
+
};
|
|
308
|
+
'inline-no-default': {
|
|
309
|
+
name: string;
|
|
310
|
+
};
|
|
311
|
+
'inline-bad-operator': {
|
|
312
|
+
name: string;
|
|
313
|
+
operators: string;
|
|
314
|
+
};
|
|
315
|
+
'invalid-given-modifier': {
|
|
316
|
+
modifier: string;
|
|
317
|
+
};
|
|
295
318
|
'illegal-filter-type': string;
|
|
296
319
|
'invalid-source-from-given': string;
|
|
297
320
|
'aggregate-analytic-in-select': string;
|
package/dist/lang/parse-log.js
CHANGED
|
@@ -95,6 +95,12 @@ exports.MESSAGE_FORMATTERS = {
|
|
|
95
95
|
'case-else-type-does-not-match': e => `Case else type ${e.elseType} does not match return type ${e.returnType}`,
|
|
96
96
|
'case-when-must-be-boolean': e => `Case when expression must be boolean, not ${e.whenType}`,
|
|
97
97
|
'case-when-type-does-not-match': e => `Case when type ${e.whenType} does not match value type ${e.valueType}`,
|
|
98
|
+
'in-given-rhs-not-array': e => `\`in $${e.givenName}\` requires \`${e.givenName}\` to be an array , but it is \`${e.actualType}\``,
|
|
99
|
+
'in-given-rhs-not-basic-array': e => `\`in $${e.givenName}\` requires \`${e.givenName}\` to be an array of a basic type (string, number, etc.), but its element type is \`${e.elementType}\``,
|
|
100
|
+
'in-given-type-mismatch': e => `\`in\` left-hand side type \`${e.lhsType}\` does not match the array element type \`${e.elementType}\``,
|
|
101
|
+
'inline-no-default': e => `inline given \`${e.name}\` must have a value — there is nothing to inline without one`,
|
|
102
|
+
'inline-bad-operator': e => `inline given \`${e.name}\` uses operator(s) not allowed in inline expressions: ${e.operators}`,
|
|
103
|
+
'invalid-given-modifier': e => `Unknown modifier \`${e.modifier}\` on \`given:\` declaration; the only modifier allowed here is \`inline\``,
|
|
98
104
|
};
|
|
99
105
|
function makeLogMessage(code, parameters, options) {
|
|
100
106
|
var _a, _b, _c, _d, _e;
|
|
@@ -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");
|
|
@@ -140,12 +141,9 @@ class ImportsAndTablesStep {
|
|
|
140
141
|
}
|
|
141
142
|
if (!this.parseReferences) {
|
|
142
143
|
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
|
-
}
|
|
144
|
+
// Register connection dialects and imports immediately. Table
|
|
145
|
+
// references are deferred until dialects are resolved, because
|
|
146
|
+
// validating a table path requires knowing its dialect's grammar.
|
|
149
147
|
for (const connName in this.parseReferences.connectionDialects) {
|
|
150
148
|
that.root.connectionDialectZone.reference(connName, {
|
|
151
149
|
url: that.sourceURL,
|
|
@@ -180,17 +178,49 @@ class ImportsAndTablesStep {
|
|
|
180
178
|
return { timingInfo: parseReq.timingInfo };
|
|
181
179
|
}
|
|
182
180
|
let allMissing = {};
|
|
181
|
+
// Validate each table reference against its dialect's grammar (if
|
|
182
|
+
// the dialect is resolved) and register the canonical entry. Bad
|
|
183
|
+
// paths are silently dropped — the AST step re-validates and logs
|
|
184
|
+
// an error at the precise source range. Entries whose dialect
|
|
185
|
+
// isn't resolved yet are preserved unchanged and re-processed on a
|
|
186
|
+
// later step.
|
|
187
|
+
{
|
|
188
|
+
const refs = this.parseReferences.tables;
|
|
189
|
+
const canonical = {};
|
|
190
|
+
for (const rawKey in refs) {
|
|
191
|
+
const info = refs[rawKey];
|
|
192
|
+
let { tablePath } = info;
|
|
193
|
+
const dialectName = that.root.connectionDialectZone.get(info.connectionName);
|
|
194
|
+
if (dialectName !== undefined) {
|
|
195
|
+
const result = (0, dialect_1.getDialect)(dialectName).sqlValidateTableName(tablePath);
|
|
196
|
+
if (!result.ok)
|
|
197
|
+
continue;
|
|
198
|
+
tablePath = result.canonical;
|
|
199
|
+
}
|
|
200
|
+
const key = `${info.connectionName}:${tablePath}`;
|
|
201
|
+
canonical[key] = { ...info, tablePath };
|
|
202
|
+
that.root.schemaZone.reference(key, {
|
|
203
|
+
url: that.sourceURL,
|
|
204
|
+
range: info.firstReference,
|
|
205
|
+
});
|
|
206
|
+
}
|
|
207
|
+
this.parseReferences.tables = canonical;
|
|
208
|
+
}
|
|
183
209
|
const missingTables = that.root.schemaZone.getUndefined();
|
|
184
210
|
if (missingTables) {
|
|
185
211
|
const tables = {};
|
|
186
212
|
for (const key of missingTables) {
|
|
187
213
|
const info = this.parseReferences.tables[key];
|
|
214
|
+
if (info === undefined)
|
|
215
|
+
continue;
|
|
188
216
|
tables[key] = {
|
|
189
217
|
connectionName: info.connectionName,
|
|
190
218
|
tablePath: info.tablePath,
|
|
191
219
|
};
|
|
192
220
|
}
|
|
193
|
-
|
|
221
|
+
if (Object.keys(tables).length > 0) {
|
|
222
|
+
allMissing = { tables };
|
|
223
|
+
}
|
|
194
224
|
}
|
|
195
225
|
const missingDialects = that.root.connectionDialectZone.getUndefined();
|
|
196
226
|
if (missingDialects) {
|
|
@@ -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);
|
|
@@ -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
|
}
|
|
@@ -185,6 +185,23 @@ function compileExpr(resultSet, context, expr, state = new utils_1.GenerateState
|
|
|
185
185
|
const oneOf = expr.kids.oneOf.map(o => o.sql).join(',');
|
|
186
186
|
return `${expr.kids.e.sql} ${expr.not ? 'NOT IN' : 'IN'} (${oneOf})`;
|
|
187
187
|
}
|
|
188
|
+
case 'inGiven': {
|
|
189
|
+
const bound = resolveGivenBoundExpr(context, expr.givenRef.id, expr.givenRef.refName);
|
|
190
|
+
// null binding collapses to empty-set semantics — not the SQL
|
|
191
|
+
// `IN (NULL)` shape, which has confusing NULL-membership rules.
|
|
192
|
+
if (bound.node === 'null') {
|
|
193
|
+
return expr.not ? 'TRUE' : 'FALSE';
|
|
194
|
+
}
|
|
195
|
+
if (bound.node !== 'arrayLiteral') {
|
|
196
|
+
throw new Error(`Internal compiler error: 'inGiven' bound to '${bound.node}', expected 'arrayLiteral'. The translator should have rejected a non-array given here.`);
|
|
197
|
+
}
|
|
198
|
+
if (bound.kids.values.length === 0) {
|
|
199
|
+
return expr.not ? 'TRUE' : 'FALSE';
|
|
200
|
+
}
|
|
201
|
+
const elemSqls = bound.kids.values.map(v => exprToSQL(resultSet, context, v, state));
|
|
202
|
+
const verb = expr.not ? 'NOT IN' : 'IN';
|
|
203
|
+
return `${expr.e.sql} ${verb} (${elemSqls.join(',')})`;
|
|
204
|
+
}
|
|
188
205
|
case 'like':
|
|
189
206
|
case '!like': {
|
|
190
207
|
const likeIt = expr.node === 'like' ? 'LIKE' : 'NOT LIKE';
|
|
@@ -599,20 +616,30 @@ function generateParameterFragment(resultSet, context, expr, state) {
|
|
|
599
616
|
}
|
|
600
617
|
throw new Error(`Can't generate SQL, no value for ${expr.path}`);
|
|
601
618
|
}
|
|
602
|
-
|
|
619
|
+
/**
|
|
620
|
+
* Resolve a given to the Expr that should stand in for it at SQL emit:
|
|
621
|
+
* supplied value if the caller bound one, otherwise the declaration's
|
|
622
|
+
* default. Throws when neither is available — that case is a query the
|
|
623
|
+
* compiler can't satisfy.
|
|
624
|
+
*
|
|
625
|
+
* Shared by `generateGivenFragment` ($NAME directly in an expression)
|
|
626
|
+
* and the `'inGiven'` SQL-emit case ($ARR in `expr in $ARR`).
|
|
627
|
+
*/
|
|
628
|
+
function resolveGivenBoundExpr(context, id, refName) {
|
|
603
629
|
var _a, _b;
|
|
604
|
-
const id = expr.id;
|
|
605
630
|
const supplied = (_b = (_a = context.prepareResultOptions) === null || _a === void 0 ? void 0 : _a.resolvedGivens) === null || _b === void 0 ? void 0 : _b.get(id);
|
|
606
|
-
if (supplied !== undefined)
|
|
607
|
-
return
|
|
608
|
-
}
|
|
609
|
-
// The default may itself be a `$OTHER`-bearing expression — recursive
|
|
610
|
-
// compile handles default chains.
|
|
631
|
+
if (supplied !== undefined)
|
|
632
|
+
return supplied;
|
|
611
633
|
const decl = context.getModel().givens[id];
|
|
612
|
-
if ((decl === null || decl === void 0 ? void 0 : decl.default) !== undefined)
|
|
613
|
-
return
|
|
614
|
-
|
|
615
|
-
|
|
634
|
+
if ((decl === null || decl === void 0 ? void 0 : decl.default) !== undefined)
|
|
635
|
+
return decl.default;
|
|
636
|
+
throw new Error(unsatisfiedGivenMessage(refName));
|
|
637
|
+
}
|
|
638
|
+
function generateGivenFragment(resultSet, context, expr, state) {
|
|
639
|
+
// The bound expr may itself be a `$OTHER`-bearing expression; recursive
|
|
640
|
+
// compile handles default chains.
|
|
641
|
+
const bound = resolveGivenBoundExpr(context, expr.id, expr.refName);
|
|
642
|
+
return exprToSQL(resultSet, context, bound, state);
|
|
616
643
|
}
|
|
617
644
|
function unsatisfiedGivenMessage(refName) {
|
|
618
645
|
return (`Given '${refName}' has no value and no default. ` +
|
|
@@ -129,7 +129,7 @@ exports.FilterCompilers = {
|
|
|
129
129
|
// For some databases checking NULL combined with a boolean check
|
|
130
130
|
// is faster than a COALESCE, for now, just detect if the expression
|
|
131
131
|
// is just a column reference, and if so, don't use COALECSE.
|
|
132
|
-
const quoteChar = d.
|
|
132
|
+
const quoteChar = d.sqlQuoteIdentifier('select')[0];
|
|
133
133
|
const isColumn = x.match(`^[()${quoteChar}\\w.]+$`);
|
|
134
134
|
if (isColumn) {
|
|
135
135
|
if (bc.operator === 'true') {
|
|
@@ -1,2 +1,17 @@
|
|
|
1
1
|
import type { Expr, GivenID, GivenValue, ModelDef } from './malloy_types';
|
|
2
2
|
export declare function resolveSuppliedGivens(supplied: Record<string, GivenValue> | undefined, modelDef: ModelDef | undefined): Map<GivenID, Expr>;
|
|
3
|
+
/**
|
|
4
|
+
* Evaluate every `inline` given's default to a literal Expr and add it
|
|
5
|
+
* to the bound map. Mutates and returns `bound` for convenience.
|
|
6
|
+
*
|
|
7
|
+
* Givens already in `bound` (caller supplied a value) are left alone —
|
|
8
|
+
* the caller's value wins over the inline default. Inline givens with
|
|
9
|
+
* no default are skipped here; the translator already logged the
|
|
10
|
+
* `inline-no-default` error at declaration time.
|
|
11
|
+
*
|
|
12
|
+
* Iteration follows `modelDef.contents` insertion order, which (by
|
|
13
|
+
* Malloy's no-forward-refs rule) is also topological order: if inline
|
|
14
|
+
* A's default references inline B, B's declaration came first and is
|
|
15
|
+
* already in the map by the time A runs.
|
|
16
|
+
*/
|
|
17
|
+
export declare function evaluateInlineGivens(bound: Map<GivenID, Expr>, modelDef: ModelDef | undefined): Map<GivenID, Expr>;
|
|
@@ -5,8 +5,10 @@
|
|
|
5
5
|
*/
|
|
6
6
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
7
7
|
exports.resolveSuppliedGivens = resolveSuppliedGivens;
|
|
8
|
+
exports.evaluateInlineGivens = evaluateInlineGivens;
|
|
8
9
|
const luxon_1 = require("luxon");
|
|
9
10
|
const closest_match_1 = require("../util/closest_match");
|
|
11
|
+
const inline_expr_1 = require("./inline_expr");
|
|
10
12
|
const malloy_types_1 = require("./malloy_types");
|
|
11
13
|
function resolveSuppliedGivens(supplied, modelDef) {
|
|
12
14
|
var _a;
|
|
@@ -43,6 +45,39 @@ function resolveSuppliedGivens(supplied, modelDef) {
|
|
|
43
45
|
}
|
|
44
46
|
return out;
|
|
45
47
|
}
|
|
48
|
+
/**
|
|
49
|
+
* Evaluate every `inline` given's default to a literal Expr and add it
|
|
50
|
+
* to the bound map. Mutates and returns `bound` for convenience.
|
|
51
|
+
*
|
|
52
|
+
* Givens already in `bound` (caller supplied a value) are left alone —
|
|
53
|
+
* the caller's value wins over the inline default. Inline givens with
|
|
54
|
+
* no default are skipped here; the translator already logged the
|
|
55
|
+
* `inline-no-default` error at declaration time.
|
|
56
|
+
*
|
|
57
|
+
* Iteration follows `modelDef.contents` insertion order, which (by
|
|
58
|
+
* Malloy's no-forward-refs rule) is also topological order: if inline
|
|
59
|
+
* A's default references inline B, B's declaration came first and is
|
|
60
|
+
* already in the map by the time A runs.
|
|
61
|
+
*/
|
|
62
|
+
function evaluateInlineGivens(bound, modelDef) {
|
|
63
|
+
var _a;
|
|
64
|
+
if (!modelDef)
|
|
65
|
+
return bound;
|
|
66
|
+
const givens = (_a = modelDef.givens) !== null && _a !== void 0 ? _a : {};
|
|
67
|
+
for (const [, entry] of Object.entries(modelDef.contents)) {
|
|
68
|
+
if (entry.type !== 'given')
|
|
69
|
+
continue;
|
|
70
|
+
if (bound.has(entry.id))
|
|
71
|
+
continue;
|
|
72
|
+
const decl = givens[entry.id];
|
|
73
|
+
if (!(decl === null || decl === void 0 ? void 0 : decl.inline))
|
|
74
|
+
continue;
|
|
75
|
+
if (decl.default === undefined)
|
|
76
|
+
continue;
|
|
77
|
+
bound.set(entry.id, (0, inline_expr_1.inlineExpr)(decl.default, bound));
|
|
78
|
+
}
|
|
79
|
+
return bound;
|
|
80
|
+
}
|
|
46
81
|
function valueToExpr(path, type, value) {
|
|
47
82
|
var _a, _b;
|
|
48
83
|
if (value === null) {
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import type { Expr, GivenID } from './malloy_types';
|
|
2
|
+
/**
|
|
3
|
+
* Operator node types allowed inside an `inline` given's default. Single
|
|
4
|
+
* source of truth — both the translator-side validator
|
|
5
|
+
* (`GivenDeclaration.execute`) and the bind-time evaluator below read
|
|
6
|
+
* this set when deciding whether an expression is reducible to a
|
|
7
|
+
* literal at bind time.
|
|
8
|
+
*
|
|
9
|
+
* Add a new operator here AND a corresponding `case` to `inlineExpr`'s
|
|
10
|
+
* switch in the same commit; the two are intentionally co-located so
|
|
11
|
+
* they can't drift apart.
|
|
12
|
+
*/
|
|
13
|
+
export declare const INLINE_OPS: ReadonlySet<string>;
|
|
14
|
+
/**
|
|
15
|
+
* Leaf-shaped node types that are valid inside an inline expression but
|
|
16
|
+
* aren't "operators." Literals are returned as-is by the evaluator;
|
|
17
|
+
* `given` nodes resolve through the bound values map.
|
|
18
|
+
*/
|
|
19
|
+
export declare const INLINE_LEAVES: ReadonlySet<string>;
|
|
20
|
+
/**
|
|
21
|
+
* Bind-time evaluator for `inline` given defaults. Walks the Expr tree,
|
|
22
|
+
* recursing on bound values for given-refs, and returns a literal Expr
|
|
23
|
+
* (string/number/boolean/null/arrayLiteral).
|
|
24
|
+
*
|
|
25
|
+
* Throws on any node outside `INLINE_OPS ∪ INLINE_LEAVES`. The
|
|
26
|
+
* translator's pre-flight check should have rejected such defaults
|
|
27
|
+
* already, so a throw here flags a compiler bug rather than a caller
|
|
28
|
+
* error.
|
|
29
|
+
*/
|
|
30
|
+
export declare function inlineExpr(e: Expr, bound: Map<GivenID, Expr>): Expr;
|