@malloydata/malloy 0.0.434 → 0.0.436
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/build_targets.d.ts +5 -6
- package/dist/api/foundation/build_targets.js +74 -6
- package/dist/api/foundation/compile.d.ts +0 -1
- package/dist/api/foundation/compile.js +0 -3
- package/dist/api/foundation/core.d.ts +16 -4
- package/dist/api/foundation/core.js +23 -23
- package/dist/api/foundation/index.d.ts +1 -1
- package/dist/api/foundation/runtime.d.ts +21 -3
- package/dist/api/foundation/runtime.js +45 -54
- package/dist/api/foundation/types.d.ts +43 -4
- package/dist/connection/dbmeta.d.ts +29 -0
- package/dist/connection/dbmeta.js +46 -0
- package/dist/connection/index.d.ts +1 -0
- package/dist/connection/index.js +1 -0
- package/dist/dialect/databricks/databricks.d.ts +11 -12
- package/dist/dialect/databricks/databricks.js +24 -36
- package/dist/dialect/dialect.d.ts +70 -171
- package/dist/dialect/dialect.js +123 -92
- package/dist/dialect/dialect_map.js +2 -0
- package/dist/dialect/duckdb/duckdb.d.ts +2 -2
- package/dist/dialect/duckdb/duckdb.js +1 -1
- package/dist/dialect/index.d.ts +3 -2
- package/dist/dialect/index.js +4 -2
- package/dist/dialect/mysql/mysql.d.ts +11 -12
- package/dist/dialect/mysql/mysql.js +20 -32
- package/dist/dialect/pg_impl.d.ts +9 -10
- package/dist/dialect/pg_impl.js +27 -73
- package/dist/dialect/postgres/postgres.d.ts +4 -4
- package/dist/dialect/postgres/postgres.js +4 -12
- package/dist/dialect/snowflake/snowflake.d.ts +11 -13
- package/dist/dialect/snowflake/snowflake.js +30 -82
- package/dist/dialect/sqlserver/dialect_functions.d.ts +2 -0
- package/dist/dialect/sqlserver/dialect_functions.js +42 -0
- package/dist/dialect/sqlserver/function_overrides.d.ts +2 -0
- package/dist/dialect/sqlserver/function_overrides.js +55 -0
- package/dist/dialect/sqlserver/index.d.ts +1 -0
- package/dist/dialect/sqlserver/index.js +22 -0
- package/dist/dialect/sqlserver/sqlserver.d.ts +101 -0
- package/dist/dialect/sqlserver/sqlserver.js +537 -0
- package/dist/dialect/sqlserver/windows_zones.d.ts +1 -0
- package/dist/dialect/sqlserver/windows_zones.js +613 -0
- package/dist/dialect/standardsql/standardsql.d.ts +11 -16
- package/dist/dialect/standardsql/standardsql.js +22 -73
- package/dist/dialect/trino/trino.d.ts +16 -18
- package/dist/dialect/trino/trino.js +127 -181
- package/dist/index.d.ts +2 -2
- package/dist/index.js +6 -4
- package/dist/lang/ast/expressions/boolean-value.d.ts +24 -0
- package/dist/lang/ast/expressions/boolean-value.js +41 -0
- package/dist/lang/ast/expressions/case.js +7 -0
- package/dist/lang/ast/expressions/expr-alternation-tree.js +11 -0
- package/dist/lang/ast/expressions/expr-cast.js +5 -0
- package/dist/lang/ast/expressions/expr-coalesce.js +4 -0
- package/dist/lang/ast/expressions/expr-func.js +13 -2
- package/dist/lang/ast/expressions/expr-minus.js +5 -2
- package/dist/lang/ast/expressions/expr-null.js +4 -0
- package/dist/lang/ast/expressions/pick-when.js +7 -0
- package/dist/lang/ast/field-space/index-field-space.js +13 -1
- package/dist/lang/ast/field-space/query-spaces.js +11 -0
- package/dist/lang/ast/query-builders/reduce-builder.js +1 -1
- package/dist/lang/ast/query-items/field-declaration.d.ts +5 -0
- package/dist/lang/ast/query-items/field-declaration.js +22 -16
- package/dist/lang/ast/query-items/field-references.d.ts +6 -0
- package/dist/lang/ast/query-items/field-references.js +23 -0
- package/dist/lang/ast/query-properties/filters.js +3 -1
- package/dist/lang/ast/source-elements/named-source.js +0 -2
- package/dist/lang/ast/source-elements/sql-source.js +0 -3
- package/dist/lang/ast/source-elements/table-source.js +1 -2
- package/dist/lang/ast/statements/import-statement.js +17 -0
- package/dist/lang/ast/types/expression-def.js +17 -2
- package/dist/lang/ast/types/malloy-element.d.ts +24 -8
- package/dist/lang/ast/types/malloy-element.js +63 -44
- package/dist/lang/ast/view-elements/reference-view.js +4 -0
- package/dist/lang/ast/view-elements/refine-utils.js +1 -0
- package/dist/lang/composite-source-utils.js +10 -17
- package/dist/lang/parse-log.d.ts +12 -0
- package/dist/lang/parse-log.js +2 -0
- package/dist/lang/parse-malloy.d.ts +17 -2
- package/dist/lang/parse-malloy.js +48 -14
- package/dist/lang/parse-tree-walkers/find-external-references.d.ts +3 -3
- package/dist/lang/parse-tree-walkers/find-external-references.js +17 -16
- package/dist/lang/test/test-translator.d.ts +11 -1
- package/dist/lang/test/test-translator.js +51 -3
- package/dist/model/expression_compiler.js +9 -8
- package/dist/model/filter_compilers.js +13 -12
- package/dist/model/given_binding.d.ts +8 -0
- package/dist/model/given_binding.js +13 -0
- package/dist/model/index.d.ts +1 -1
- package/dist/model/index.js +2 -1
- package/dist/model/query_query.d.ts +3 -1
- package/dist/model/query_query.js +157 -139
- package/dist/model/source_def_utils.d.ts +7 -1
- package/dist/model/source_def_utils.js +20 -0
- package/dist/model/sql_compiled.d.ts +19 -20
- package/dist/model/sql_compiled.js +29 -17
- package/dist/test/index.js +16 -8
- package/dist/test/matchers.d.ts +1 -1
- package/dist/test/matchers.js +1 -1
- package/dist/test/test-models.js +6 -13
- package/dist/version.d.ts +1 -1
- package/dist/version.js +1 -1
- package/femto-config.motly +13 -1
- package/package.json +7 -5
|
@@ -12,7 +12,7 @@ const error_factory_1 = require("../error-factory");
|
|
|
12
12
|
const dialect_1 = require("../../../dialect");
|
|
13
13
|
class TableSource extends source_1.Source {
|
|
14
14
|
getSourceDef() {
|
|
15
|
-
var _a, _b
|
|
15
|
+
var _a, _b;
|
|
16
16
|
const info = this.getTableInfo();
|
|
17
17
|
if (info === undefined) {
|
|
18
18
|
return error_factory_1.ErrorFactory.structDef;
|
|
@@ -36,7 +36,6 @@ class TableSource extends source_1.Source {
|
|
|
36
36
|
let msg = `Schema read failure for table '${tablePath}' for connection '${connectionName}'`;
|
|
37
37
|
if (tableDefEntry) {
|
|
38
38
|
if (tableDefEntry.status === 'present') {
|
|
39
|
-
(_c = this.document()) === null || _c === void 0 ? void 0 : _c.checkExperimentalDialect(this, tableDefEntry.value.dialect);
|
|
40
39
|
tableDefEntry.value.location = this.location;
|
|
41
40
|
tableDefEntry.value.fields.forEach(field => {
|
|
42
41
|
field.location = this.location;
|
|
@@ -152,14 +152,31 @@ class ImportStatement extends malloy_element_1.ListOf {
|
|
|
152
152
|
}
|
|
153
153
|
const importMe = { ...sourceEntry };
|
|
154
154
|
importMe.as = dstName;
|
|
155
|
+
const dialect = (0, source_def_utils_1.dialectOfEntry)(importMe, importedModel);
|
|
156
|
+
if (dialect !== undefined) {
|
|
157
|
+
trans.checkExperimentalDialect(dialect, this.location);
|
|
158
|
+
}
|
|
155
159
|
doc.setEntry(dstName, { entry: importMe, exported: false });
|
|
156
160
|
// Every source the walk touched, routes included — not just the
|
|
157
161
|
// persistent ones. A `#@ -persist` wrapper that adds a join is
|
|
158
162
|
// not a table, but it can be the only way to reach the tables
|
|
159
163
|
// under it, and this model has to be able to take the same route.
|
|
164
|
+
//
|
|
165
|
+
// The persistent ones are also what a builder will build from
|
|
166
|
+
// this model, so the document checks at the end of compile that
|
|
167
|
+
// this model can supply every given their SQL reads.
|
|
160
168
|
if ((0, malloy_types_1.isSourceDef)(importMe) && (0, malloy_types_1.isPersistableSourceDef)(importMe)) {
|
|
161
169
|
for (const found of (0, persist_utils_1.walkPersistentDependencies)([importMe], importedModel)) {
|
|
162
170
|
neededSourceIDs.add(found.sourceID);
|
|
171
|
+
if (!found.persistent)
|
|
172
|
+
continue;
|
|
173
|
+
const def = (0, source_def_utils_1.resolveSourceID)(importedModel, found.sourceID);
|
|
174
|
+
if (def && !doc.importedPersistSources.has(found.sourceID)) {
|
|
175
|
+
doc.importedPersistSources.set(found.sourceID, {
|
|
176
|
+
source: def,
|
|
177
|
+
at: this.location,
|
|
178
|
+
});
|
|
179
|
+
}
|
|
163
180
|
}
|
|
164
181
|
}
|
|
165
182
|
}
|
|
@@ -49,6 +49,7 @@ const granular_result_1 = require("./granular-result");
|
|
|
49
49
|
const malloy_element_1 = require("./malloy-element");
|
|
50
50
|
const binary_operators_1 = require("./binary_operators");
|
|
51
51
|
const malloy_filter_1 = require("@malloydata/malloy-filter");
|
|
52
|
+
const boolean_value_1 = require("../expressions/boolean-value");
|
|
52
53
|
class TypeMismatch extends Error {
|
|
53
54
|
}
|
|
54
55
|
/**
|
|
@@ -341,11 +342,21 @@ function regexEqual(left, right) {
|
|
|
341
342
|
}
|
|
342
343
|
return undefined;
|
|
343
344
|
}
|
|
345
|
+
/**
|
|
346
|
+
* A boolean can only be compared, with anything, on a dialect with boolean
|
|
347
|
+
* values. Every comparison operator, applied plainly, as a partial, or
|
|
348
|
+
* through an alternation, arrives here through equality() or compare().
|
|
349
|
+
*/
|
|
350
|
+
function booleanCompare(fs, left, lhs, op, right, rhs) {
|
|
351
|
+
const [side, val] = lhs.type === 'boolean' ? [left, lhs] : [right, rhs];
|
|
352
|
+
return (0, boolean_value_1.booleanValueError)(fs, val, `compared with ${op}`, side, boolean_value_1.COMPARE_HINT);
|
|
353
|
+
}
|
|
344
354
|
function equality(fs, left, op, right) {
|
|
345
355
|
const lhs = left.getExpression(fs);
|
|
346
356
|
const rhs = right.getExpression(fs);
|
|
347
357
|
const node = (0, binary_operators_1.getExprNode)(op);
|
|
348
|
-
const err = errorCascade('boolean', lhs, rhs)
|
|
358
|
+
const err = errorCascade('boolean', lhs, rhs) ||
|
|
359
|
+
booleanCompare(fs, left, lhs, op, right, rhs);
|
|
349
360
|
if (err)
|
|
350
361
|
return err;
|
|
351
362
|
// Unsupported types can be compare with null
|
|
@@ -365,6 +376,9 @@ function equality(fs, left, op, right) {
|
|
|
365
376
|
if (op !== '~' && op !== '!~') {
|
|
366
377
|
return right.loggedErrorExpr('filter-expression-error', `Cannot use the '${op}' operator with a filter expression`);
|
|
367
378
|
}
|
|
379
|
+
const noMatch = (0, boolean_value_1.booleanValueError)(fs, lhs, 'matched with a filter expression', left, 'test the condition itself');
|
|
380
|
+
if (noMatch)
|
|
381
|
+
return noMatch;
|
|
368
382
|
if ((0, malloy_filter_1.isFilterable)(lhs.type)) {
|
|
369
383
|
let actualFilter = rhs.value;
|
|
370
384
|
while (actualFilter.node === '()') {
|
|
@@ -419,7 +433,8 @@ function equality(fs, left, op, right) {
|
|
|
419
433
|
function compare(fs, left, op, right) {
|
|
420
434
|
const lhs = left.getExpression(fs);
|
|
421
435
|
const rhs = right.getExpression(fs);
|
|
422
|
-
const err = errorCascade('boolean', lhs, rhs)
|
|
436
|
+
const err = errorCascade('boolean', lhs, rhs) ||
|
|
437
|
+
booleanCompare(fs, left, lhs, op, right, rhs);
|
|
423
438
|
if (err)
|
|
424
439
|
return err;
|
|
425
440
|
const noCompare = unsupportError(left, lhs, right, rhs);
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { AnnotationsDef, DocumentLocation, DocumentReference, Given, ModelDef, ModelAnnotationEntry, ModelID, NamedModelObject, Query, SourceID, SourceRegistryValue } from '../../../model/malloy_types';
|
|
1
|
+
import type { AnnotationsDef, DocumentLocation, DocumentReference, Given, ModelDef, ModelAnnotationEntry, ModelID, NamedModelObject, PersistableSourceDef, Query, SourceID, SourceRegistryValue } from '../../../model/malloy_types';
|
|
2
2
|
import { Tag } from '@malloydata/malloy-tag';
|
|
3
3
|
import type { LogMessageOptions, MessageLogger, MessageParameterType, MessageCode } from '../../parse-log';
|
|
4
4
|
import type { MalloyTranslation } from '../../parse-malloy';
|
|
@@ -116,6 +116,13 @@ export declare class Document extends MalloyElement implements NameSpace {
|
|
|
116
116
|
documentModel: Map<string, ModelEntry>;
|
|
117
117
|
documentSrcRegistry: Record<SourceID, SourceRegistryValue>;
|
|
118
118
|
documentGivens: Map<string, Given>;
|
|
119
|
+
/** Every persistent source an `import` brought within reach of this model's
|
|
120
|
+
* builder, with the import that did so. Checked at end of compile: the
|
|
121
|
+
* model has to be able to supply every given their SQL reads. */
|
|
122
|
+
importedPersistSources: Map<string, {
|
|
123
|
+
source: PersistableSourceDef;
|
|
124
|
+
at: DocumentLocation;
|
|
125
|
+
}>;
|
|
119
126
|
/** {@link ModelAnnotationEntry} of every imported/extended model in the
|
|
120
127
|
* closure, keyed by ModelID, accumulated as imports/extends are processed;
|
|
121
128
|
* copied into the result `modelAnnotations` in {@link modelDef}, which then
|
|
@@ -146,19 +153,28 @@ export declare class Document extends MalloyElement implements NameSpace {
|
|
|
146
153
|
initModelDef(extendingModelDef: ModelDef | undefined): void;
|
|
147
154
|
compile(): DocumentCompileResult;
|
|
148
155
|
private checkGivenAliasCollisions;
|
|
156
|
+
/**
|
|
157
|
+
* The givens in `usage` this model can neither supply nor default: not
|
|
158
|
+
* surfaced in the namespace and declared without a default. The set checked
|
|
159
|
+
* is each id plus its precomputed default-chain closure (`Given.givenUsage`),
|
|
160
|
+
* which is already transitive, so there is no recursion here.
|
|
161
|
+
*/
|
|
162
|
+
private unsatisfiedGivens;
|
|
149
163
|
private checkQueryGivenSatisfiability;
|
|
164
|
+
/**
|
|
165
|
+
* A persisted source is built from whatever model a builder is handed, so a
|
|
166
|
+
* model that imports one has to be able to supply every given its SQL reads;
|
|
167
|
+
* an import that cannot is a source no build or query here could ever use.
|
|
168
|
+
* Checked at end of compile rather than at the import, since the given may
|
|
169
|
+
* be imported by a later statement. A locally declared source cannot fail
|
|
170
|
+
* this: `$NAME` only resolves to a given in scope.
|
|
171
|
+
*/
|
|
172
|
+
private checkPersistSourceGivenSatisfiability;
|
|
150
173
|
hasAnnotation(): boolean;
|
|
151
174
|
currentModelAnnotation(): AnnotationsDef | undefined;
|
|
152
175
|
modelDef(): ModelDef;
|
|
153
176
|
getEntry(str: string): ModelEntry | undefined;
|
|
154
177
|
setEntry(str: string, ent: ModelEntry): void;
|
|
155
|
-
/**
|
|
156
|
-
* Return an error message if this dialect is the first reference to this particular
|
|
157
|
-
* dialect, and the dialect is marked as experimental, and we are not running tests.
|
|
158
|
-
* @param dialect The dialect name
|
|
159
|
-
* @returns The error message or undefined
|
|
160
|
-
*/
|
|
161
|
-
checkExperimentalDialect(me: MalloyElement, dialect: string): void;
|
|
162
178
|
private readonly dialectNameSpaces;
|
|
163
179
|
getDialectNamespace(dialectName: string): NameSpace | undefined;
|
|
164
180
|
}
|
|
@@ -9,6 +9,7 @@ exports.isDocStatement = isDocStatement;
|
|
|
9
9
|
exports.isDocStatementOrDocStatementList = isDocStatementOrDocStatementList;
|
|
10
10
|
const dialect_1 = require("../../../dialect");
|
|
11
11
|
const malloy_types_1 = require("../../../model/malloy_types");
|
|
12
|
+
const sql_compiled_1 = require("../../../model/sql_compiled");
|
|
12
13
|
const utils_1 = require("../../../model/utils");
|
|
13
14
|
const malloy_tag_1 = require("@malloydata/malloy-tag");
|
|
14
15
|
const parse_log_1 = require("../../parse-log");
|
|
@@ -436,6 +437,10 @@ class Document extends MalloyElement {
|
|
|
436
437
|
this.documentModel = new Map();
|
|
437
438
|
this.documentSrcRegistry = {};
|
|
438
439
|
this.documentGivens = new Map();
|
|
440
|
+
/** Every persistent source an `import` brought within reach of this model's
|
|
441
|
+
* builder, with the import that did so. Checked at end of compile: the
|
|
442
|
+
* model has to be able to supply every given their SQL reads. */
|
|
443
|
+
this.importedPersistSources = new Map();
|
|
439
444
|
/** {@link ModelAnnotationEntry} of every imported/extended model in the
|
|
440
445
|
* closure, keyed by ModelID, accumulated as imports/extends are processed;
|
|
441
446
|
* copied into the result `modelAnnotations` in {@link modelDef}, which then
|
|
@@ -512,6 +517,7 @@ class Document extends MalloyElement {
|
|
|
512
517
|
if (needs === undefined) {
|
|
513
518
|
this.checkGivenAliasCollisions();
|
|
514
519
|
this.checkQueryGivenSatisfiability();
|
|
520
|
+
this.checkPersistSourceGivenSatisfiability();
|
|
515
521
|
}
|
|
516
522
|
const ret = {
|
|
517
523
|
modelDef: {
|
|
@@ -553,40 +559,46 @@ class Document extends MalloyElement {
|
|
|
553
559
|
`with a default-chain reference: \`given: NEW_NAME :: T is $${sourceName}\`.`);
|
|
554
560
|
}
|
|
555
561
|
}
|
|
562
|
+
/**
|
|
563
|
+
* The givens in `usage` this model can neither supply nor default: not
|
|
564
|
+
* surfaced in the namespace and declared without a default. The set checked
|
|
565
|
+
* is each id plus its precomputed default-chain closure (`Given.givenUsage`),
|
|
566
|
+
* which is already transitive, so there is no recursion here.
|
|
567
|
+
*/
|
|
568
|
+
unsatisfiedGivens(usage) {
|
|
569
|
+
var _a, _b;
|
|
570
|
+
const namespaceGivens = new Set();
|
|
571
|
+
for (const m of this.documentModel.values()) {
|
|
572
|
+
if (m.entry.type === 'given')
|
|
573
|
+
namespaceGivens.add(m.entry.id);
|
|
574
|
+
}
|
|
575
|
+
const allIds = new Set();
|
|
576
|
+
for (const g of usage) {
|
|
577
|
+
allIds.add(g.id);
|
|
578
|
+
const decl = this.documentGivens.get(g.id);
|
|
579
|
+
for (const t of (_a = decl === null || decl === void 0 ? void 0 : decl.givenUsage) !== null && _a !== void 0 ? _a : [])
|
|
580
|
+
allIds.add(t.id);
|
|
581
|
+
}
|
|
582
|
+
const unsatisfied = [];
|
|
583
|
+
for (const id of allIds) {
|
|
584
|
+
if (namespaceGivens.has(id))
|
|
585
|
+
continue;
|
|
586
|
+
if (((_b = this.documentGivens.get(id)) === null || _b === void 0 ? void 0 : _b.default) !== undefined)
|
|
587
|
+
continue;
|
|
588
|
+
unsatisfied.push(id);
|
|
589
|
+
}
|
|
590
|
+
return unsatisfied;
|
|
591
|
+
}
|
|
556
592
|
checkQueryGivenSatisfiability() {
|
|
557
593
|
// Always runs at end-of-compile, not gated on imports — a notebook cell
|
|
558
594
|
// that calls `extendModel` with a prior modelDef inherits that model's
|
|
559
595
|
// queries and givens, and a query inherited from cell N can become
|
|
560
596
|
// unsatisfiable in cell N+1 if the satisfying given is removed (or
|
|
561
597
|
// never re-supplied). Cheap when there's nothing to check.
|
|
562
|
-
const namespaceGivens = new Set();
|
|
563
|
-
for (const m of this.documentModel.values()) {
|
|
564
|
-
if (m.entry.type === 'given')
|
|
565
|
-
namespaceGivens.add(m.entry.id);
|
|
566
|
-
}
|
|
567
598
|
const checkOne = (q, label) => {
|
|
568
599
|
var _a;
|
|
569
|
-
const
|
|
570
|
-
|
|
571
|
-
return;
|
|
572
|
-
// Build the full set of ids the query transitively needs: each id
|
|
573
|
-
// in Q.givenUsage, plus each id's precomputed default-chain closure
|
|
574
|
-
// (Given.givenUsage). Since the closure is already transitive, no
|
|
575
|
-
// recursion at check time.
|
|
576
|
-
const allIds = new Set();
|
|
577
|
-
for (const g of usage) {
|
|
578
|
-
allIds.add(g.id);
|
|
579
|
-
const decl = this.documentGivens.get(g.id);
|
|
580
|
-
for (const t of (_a = decl === null || decl === void 0 ? void 0 : decl.givenUsage) !== null && _a !== void 0 ? _a : [])
|
|
581
|
-
allIds.add(t.id);
|
|
582
|
-
}
|
|
583
|
-
for (const id of allIds) {
|
|
584
|
-
if (namespaceGivens.has(id))
|
|
585
|
-
continue;
|
|
586
|
-
const decl = this.documentGivens.get(id);
|
|
587
|
-
if ((decl === null || decl === void 0 ? void 0 : decl.default) !== undefined)
|
|
588
|
-
continue;
|
|
589
|
-
this.logError('unsatisfied-given-in-query', unsatisfiedGivenMessage(label, decl, id), { at: q.location });
|
|
600
|
+
for (const id of this.unsatisfiedGivens((_a = q.givenUsage) !== null && _a !== void 0 ? _a : [])) {
|
|
601
|
+
this.logError('unsatisfied-given-in-query', unsatisfiedGivenMessage(label, this.documentGivens.get(id), id), { at: q.location });
|
|
590
602
|
}
|
|
591
603
|
};
|
|
592
604
|
// Named queries in the namespace (locally defined OR imported).
|
|
@@ -599,6 +611,31 @@ class Document extends MalloyElement {
|
|
|
599
611
|
checkOne(q, 'run: statement');
|
|
600
612
|
}
|
|
601
613
|
}
|
|
614
|
+
/**
|
|
615
|
+
* A persisted source is built from whatever model a builder is handed, so a
|
|
616
|
+
* model that imports one has to be able to supply every given its SQL reads;
|
|
617
|
+
* an import that cannot is a source no build or query here could ever use.
|
|
618
|
+
* Checked at end of compile rather than at the import, since the given may
|
|
619
|
+
* be imported by a later statement. A locally declared source cannot fail
|
|
620
|
+
* this: `$NAME` only resolves to a given in scope.
|
|
621
|
+
*/
|
|
622
|
+
checkPersistSourceGivenSatisfiability() {
|
|
623
|
+
var _a;
|
|
624
|
+
// One import routinely reaches several sources that share a table — a
|
|
625
|
+
// persisted source and its extensions — so each given is reported once
|
|
626
|
+
// per import, against the first source that reads it.
|
|
627
|
+
const reported = new Map();
|
|
628
|
+
for (const { source, at } of this.importedPersistSources.values()) {
|
|
629
|
+
const seen = (_a = reported.get(at)) !== null && _a !== void 0 ? _a : new Set();
|
|
630
|
+
reported.set(at, seen);
|
|
631
|
+
for (const id of this.unsatisfiedGivens((0, sql_compiled_1.givenUsageOfRelation)(source))) {
|
|
632
|
+
if (seen.has(id))
|
|
633
|
+
continue;
|
|
634
|
+
seen.add(id);
|
|
635
|
+
this.logError('unsatisfied-given-in-persist-source', unsatisfiedGivenMessage(`Imported persisted source \`${(0, malloy_types_1.activeName)(source)}\``, this.documentGivens.get(id), id), { at });
|
|
636
|
+
}
|
|
637
|
+
}
|
|
638
|
+
}
|
|
602
639
|
hasAnnotation() {
|
|
603
640
|
return ((this.annotations.notes && this.annotations.notes.length > 0) ||
|
|
604
641
|
this.annotations.inherits !== undefined);
|
|
@@ -656,9 +693,6 @@ class Document extends MalloyElement {
|
|
|
656
693
|
if (this.globalNameSpace.getEntry(str) !== undefined) {
|
|
657
694
|
this.logError('name-conflict-with-global', `Cannot redefine '${str}', which is in global namespace`);
|
|
658
695
|
}
|
|
659
|
-
if ((0, malloy_types_1.isSourceDef)(ent.entry)) {
|
|
660
|
-
this.checkExperimentalDialect(this, ent.entry.dialect);
|
|
661
|
-
}
|
|
662
696
|
// Track if the model was modified after initialization
|
|
663
697
|
if (this.didInitModel) {
|
|
664
698
|
this.modelWasModified = true;
|
|
@@ -675,21 +709,6 @@ class Document extends MalloyElement {
|
|
|
675
709
|
};
|
|
676
710
|
}
|
|
677
711
|
}
|
|
678
|
-
/**
|
|
679
|
-
* Return an error message if this dialect is the first reference to this particular
|
|
680
|
-
* dialect, and the dialect is marked as experimental, and we are not running tests.
|
|
681
|
-
* @param dialect The dialect name
|
|
682
|
-
* @returns The error message or undefined
|
|
683
|
-
*/
|
|
684
|
-
checkExperimentalDialect(me, dialect) {
|
|
685
|
-
const t = this.translator();
|
|
686
|
-
if (t &&
|
|
687
|
-
t.firstReferenceToDialect(dialect) &&
|
|
688
|
-
(0, dialect_1.getDialect)(dialect).experimental &&
|
|
689
|
-
!t.experimentalDialectEnabled(dialect)) {
|
|
690
|
-
me.logError('experimental-dialect-not-enabled', { dialect });
|
|
691
|
-
}
|
|
692
|
-
}
|
|
693
712
|
getDialectNamespace(dialectName) {
|
|
694
713
|
if (this.dialectNameSpaces.has(dialectName)) {
|
|
695
714
|
return this.dialectNameSpaces.get(dialectName);
|
|
@@ -8,6 +8,7 @@ exports.ReferenceView = void 0;
|
|
|
8
8
|
const composite_source_utils_1 = require("../../composite-source-utils");
|
|
9
9
|
const malloy_types_1 = require("../../../model/malloy_types");
|
|
10
10
|
const error_factory_1 = require("../error-factory");
|
|
11
|
+
const boolean_value_1 = require("../expressions/boolean-value");
|
|
11
12
|
const drill_1 = require("../query-properties/drill");
|
|
12
13
|
const space_field_1 = require("../types/space-field");
|
|
13
14
|
const refine_utils_1 = require("./refine-utils");
|
|
@@ -55,6 +56,9 @@ class ReferenceView extends view_1.View {
|
|
|
55
56
|
throw new Error('Expected field to have definition');
|
|
56
57
|
}
|
|
57
58
|
if ((0, malloy_types_1.isAtomic)(fieldDef)) {
|
|
59
|
+
if ((0, boolean_value_1.booleanValueError)(fs, fieldDef, 'as a result column', this.reference, (0, boolean_value_1.resultColumnHint)(this.reference.refString))) {
|
|
60
|
+
return oops();
|
|
61
|
+
}
|
|
58
62
|
const name = this.reference.nameString;
|
|
59
63
|
const outputStruct = {
|
|
60
64
|
...(0, malloy_types_1.sourceBase)(fs.structDef()),
|
|
@@ -33,6 +33,7 @@ function refine(logTo, refineTo, refineFrom) {
|
|
|
33
33
|
if (from.orderBy !== undefined && !from.defaultOrderBy) {
|
|
34
34
|
if (to.orderBy === undefined || to.defaultOrderBy) {
|
|
35
35
|
to.orderBy = from.orderBy;
|
|
36
|
+
delete to.defaultOrderBy;
|
|
36
37
|
}
|
|
37
38
|
else {
|
|
38
39
|
logTo.logError('ordering-overridden-in-refinement', 'refinement cannot override existing ordering');
|
|
@@ -28,6 +28,7 @@ exports.pathBegins = pathBegins;
|
|
|
28
28
|
exports.hasCompositesAnywhere = hasCompositesAnywhere;
|
|
29
29
|
exports.logCompositeError = logCompositeError;
|
|
30
30
|
const malloy_filter_1 = require("@malloydata/malloy-filter");
|
|
31
|
+
const sql_compiled_1 = require("../model/sql_compiled");
|
|
31
32
|
const malloy_types_1 = require("../model/malloy_types");
|
|
32
33
|
const utils_1 = require("./utils");
|
|
33
34
|
const utils_2 = require("../model/utils");
|
|
@@ -1214,9 +1215,12 @@ function computeQueryGivenUsage(pipeline) {
|
|
|
1214
1215
|
return result;
|
|
1215
1216
|
}
|
|
1216
1217
|
// Returns every given a SourceDef references — its own filterList plus any
|
|
1217
|
-
// givens hiding inside an embedded Query.
|
|
1218
|
-
//
|
|
1219
|
-
//
|
|
1218
|
+
// givens hiding inside an embedded Query. For a persistable source the
|
|
1219
|
+
// embedded part is `givenUsageOfRelation` (model/sql_compiled.ts), which is
|
|
1220
|
+
// also what persistence keys a table on; the filterList is added here because
|
|
1221
|
+
// a query over the source applies it, but a table built from the source does
|
|
1222
|
+
// not. When a future SourceDef variant grows an embedded Query, teach that
|
|
1223
|
+
// helper or this one.
|
|
1220
1224
|
//
|
|
1221
1225
|
// Composite sources: composite-source support across the codebase is
|
|
1222
1226
|
// partial; the givens work isn't chasing it. The `composite` arm here is
|
|
@@ -1226,23 +1230,12 @@ function computeQueryGivenUsage(pipeline) {
|
|
|
1226
1230
|
// reaches here (e.g. composites in join positions if/when those work),
|
|
1227
1231
|
// the conservative union over all branches is the safe fallback.
|
|
1228
1232
|
function givenUsageOfSource(sd) {
|
|
1229
|
-
var _a
|
|
1233
|
+
var _a;
|
|
1230
1234
|
const fromFilters = ((_a = sd.filterList) !== null && _a !== void 0 ? _a : []).flatMap(f => (0, malloy_types_1.givenUsageFrom)(f.refSummary));
|
|
1231
1235
|
switch (sd.type) {
|
|
1232
1236
|
case 'query_source':
|
|
1233
|
-
|
|
1234
|
-
|
|
1235
|
-
const fromSegments = ((_c = sd.selectSegments) !== null && _c !== void 0 ? _c : []).flatMap(seg => {
|
|
1236
|
-
var _a;
|
|
1237
|
-
if ((0, malloy_types_1.isSegmentSQL)(seg))
|
|
1238
|
-
return [];
|
|
1239
|
-
if ((0, malloy_types_1.isSegmentSource)(seg))
|
|
1240
|
-
return givenUsageOfSource(seg);
|
|
1241
|
-
// Remaining variant of SQLPhraseSegment is `Query`
|
|
1242
|
-
return (_a = seg.givenUsage) !== null && _a !== void 0 ? _a : [];
|
|
1243
|
-
});
|
|
1244
|
-
return [...fromFilters, ...fromSegments];
|
|
1245
|
-
}
|
|
1237
|
+
case 'sql_select':
|
|
1238
|
+
return [...fromFilters, ...(0, sql_compiled_1.givenUsageOfRelation)(sd)];
|
|
1246
1239
|
case 'composite':
|
|
1247
1240
|
return [
|
|
1248
1241
|
...fromFilters,
|
package/dist/lang/parse-log.d.ts
CHANGED
|
@@ -64,6 +64,17 @@ type MessageParameterTypes = {
|
|
|
64
64
|
'nesting-unsupported': {
|
|
65
65
|
dialect: string;
|
|
66
66
|
};
|
|
67
|
+
'boolean-value-unsupported': {
|
|
68
|
+
dialect: string;
|
|
69
|
+
where: string;
|
|
70
|
+
hint: string;
|
|
71
|
+
};
|
|
72
|
+
'wildcard-omits-boolean': {
|
|
73
|
+
dialect: string;
|
|
74
|
+
wildcard: string;
|
|
75
|
+
name: string;
|
|
76
|
+
hint: string;
|
|
77
|
+
};
|
|
67
78
|
'nested-multi-stage-unsupported': {
|
|
68
79
|
dialect: string;
|
|
69
80
|
};
|
|
@@ -297,6 +308,7 @@ type MessageParameterTypes = {
|
|
|
297
308
|
'export-name-not-exportable': string;
|
|
298
309
|
'duplicate-export-name': string;
|
|
299
310
|
'unsatisfied-given-in-query': string;
|
|
311
|
+
'unsatisfied-given-in-persist-source': string;
|
|
300
312
|
'given-alias-collision': string;
|
|
301
313
|
'failed-to-compute-output-schema': string;
|
|
302
314
|
'invalid-timeframe-for-time-offset': string;
|
package/dist/lang/parse-log.js
CHANGED
|
@@ -56,6 +56,8 @@ exports.MESSAGE_FORMATTERS = {
|
|
|
56
56
|
}),
|
|
57
57
|
'experimental-dialect-not-enabled': e => `Requires compiler flag '##! experimental.dialect.${e.dialect}'`,
|
|
58
58
|
'nesting-unsupported': e => `'${e.dialect}' does not support nested queries`,
|
|
59
|
+
'boolean-value-unsupported': e => `'${e.dialect}' does not support a boolean ${e.where}; ${e.hint}`,
|
|
60
|
+
'wildcard-omits-boolean': e => `'${e.name}' is left out of '${e.wildcard}': '${e.dialect}' does not support a boolean as a result column; ${e.hint}`,
|
|
59
61
|
'nested-multi-stage-unsupported': e => `'${e.dialect}' does not support a multi-stage pipeline ('->') in a nested query`,
|
|
60
62
|
'nested-projection-limit-unsupported': e => `'${e.dialect}' does not support 'limit:' on a nested 'select:'`,
|
|
61
63
|
'pick-missing-else': "pick incomplete, missing 'else'",
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import type { DocumentLocation, DocumentPosition, DocumentReference, ImportLocation, ModelDef, SourceDef, SQLSourceDef, DependencyTree, DocumentRange, VirtualMap } from '../model/malloy_types';
|
|
2
2
|
import type { LogMessage, LogMessageOptions, MessageCode, MessageParameterType } from './parse-log';
|
|
3
3
|
import { BaseMessageLogger } from './parse-log';
|
|
4
|
+
import type { FindReferencesData } from './parse-tree-walkers/find-external-references';
|
|
4
5
|
import type { ZoneData } from './zone';
|
|
5
6
|
import { Zone } from './zone';
|
|
6
7
|
import { ReferenceList } from './reference-list';
|
|
@@ -52,7 +53,7 @@ declare class ParseStep implements TranslationStep {
|
|
|
52
53
|
}
|
|
53
54
|
declare class ImportsAndTablesStep implements TranslationStep {
|
|
54
55
|
readonly parseStep: ParseStep;
|
|
55
|
-
|
|
56
|
+
parseReferences: FindReferencesData | undefined;
|
|
56
57
|
constructor(parseStep: ParseStep);
|
|
57
58
|
step(that: MalloyTranslation): DataRequestResponse | ParseResponse;
|
|
58
59
|
}
|
|
@@ -61,6 +62,16 @@ declare class ASTStep implements TranslationStep {
|
|
|
61
62
|
response?: ASTResponse;
|
|
62
63
|
private walked;
|
|
63
64
|
constructor(importStep: ImportsAndTablesStep);
|
|
65
|
+
/**
|
|
66
|
+
* A file acknowledges an experimental dialect with
|
|
67
|
+
* `##! experimental.dialect.NAME` wherever it names a connection on that
|
|
68
|
+
* dialect. Every connection name in this file is already resolved to a
|
|
69
|
+
* dialect by the time the AST exists, and the file's compiler flags are
|
|
70
|
+
* known only now, so this is the first moment the check can run. The
|
|
71
|
+
* other place a file takes on a dialect is `import`, checked by
|
|
72
|
+
* `ImportStatement`.
|
|
73
|
+
*/
|
|
74
|
+
private checkExperimentalDialects;
|
|
64
75
|
step(that: MalloyTranslation): ASTResponse;
|
|
65
76
|
}
|
|
66
77
|
declare class MetadataStep implements TranslationStep {
|
|
@@ -166,8 +177,12 @@ export declare abstract class MalloyTranslation {
|
|
|
166
177
|
rangeFromContext(pcx: ParserRuleContext): DocumentRange;
|
|
167
178
|
private dialectAlreadyChecked;
|
|
168
179
|
firstReferenceToDialect(dialect: string): boolean;
|
|
169
|
-
allDialectsEnabled: boolean;
|
|
170
180
|
experimentalDialectEnabled(dialect: string): boolean;
|
|
181
|
+
/**
|
|
182
|
+
* Log, once per dialect per file, that this file uses an experimental
|
|
183
|
+
* dialect without acknowledging it.
|
|
184
|
+
*/
|
|
185
|
+
checkExperimentalDialect(dialect: string, at: DocumentLocation): void;
|
|
171
186
|
}
|
|
172
187
|
export declare class MalloyChildTranslator extends MalloyTranslation {
|
|
173
188
|
readonly root: MalloyTranslator;
|
|
@@ -38,6 +38,7 @@ var __importStar = (this && this.__importStar) || (function () {
|
|
|
38
38
|
})();
|
|
39
39
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
40
40
|
exports.MalloyTranslator = exports.MalloyChildTranslator = exports.MalloyTranslation = void 0;
|
|
41
|
+
const malloy_types_1 = require("../model/malloy_types");
|
|
41
42
|
const utils_1 = require("../model/utils");
|
|
42
43
|
const annotation_utils_1 = require("../model/annotation_utils");
|
|
43
44
|
const ast = __importStar(require("./ast"));
|
|
@@ -132,14 +133,13 @@ class ImportsAndTablesStep {
|
|
|
132
133
|
// Register connection dialects and imports immediately. Table
|
|
133
134
|
// references are deferred until dialects are resolved, because
|
|
134
135
|
// validating a table path requires knowing its dialect's grammar.
|
|
135
|
-
for (const connName
|
|
136
|
+
for (const [connName, need] of this.parseReferences.connectionDialects) {
|
|
136
137
|
that.root.connectionDialectZone.reference(connName, {
|
|
137
138
|
url: that.sourceURL,
|
|
138
|
-
range:
|
|
139
|
+
range: need.firstReference,
|
|
139
140
|
});
|
|
140
141
|
}
|
|
141
|
-
for (const relativeRef
|
|
142
|
-
const firstRef = this.parseReferences.urls[relativeRef];
|
|
142
|
+
for (const [relativeRef, firstRef] of this.parseReferences.urls) {
|
|
143
143
|
try {
|
|
144
144
|
const ref = decodeURI(new URL(relativeRef, that.importBaseURL || that.sourceURL).toString());
|
|
145
145
|
that.addChild(ref);
|
|
@@ -171,8 +171,7 @@ class ImportsAndTablesStep {
|
|
|
171
171
|
// Invalid paths are dropped here and re-reported by the AST step.
|
|
172
172
|
// tableRequests feeds the missing-table loop below.
|
|
173
173
|
const tableRequests = {};
|
|
174
|
-
for (const
|
|
175
|
-
const info = this.parseReferences.tables[rawKey];
|
|
174
|
+
for (const info of this.parseReferences.tables.values()) {
|
|
176
175
|
const dialectName = that.root.connectionDialectZone.get(info.connectionName);
|
|
177
176
|
if (dialectName === undefined)
|
|
178
177
|
continue;
|
|
@@ -204,7 +203,9 @@ class ImportsAndTablesStep {
|
|
|
204
203
|
}
|
|
205
204
|
const missingDialects = that.root.connectionDialectZone.getUndefined();
|
|
206
205
|
if (missingDialects) {
|
|
207
|
-
|
|
206
|
+
// Keyed by connection name, user text: a plain `{}` would turn a
|
|
207
|
+
// connection named `__proto__` into a prototype and drop the request.
|
|
208
|
+
const connectionDialects = (0, malloy_types_1.mkSafeRecord)();
|
|
208
209
|
for (const connName of missingDialects) {
|
|
209
210
|
connectionDialects[connName] = { connectionName: connName };
|
|
210
211
|
}
|
|
@@ -237,6 +238,31 @@ class ASTStep {
|
|
|
237
238
|
this.importStep = importStep;
|
|
238
239
|
this.walked = false;
|
|
239
240
|
}
|
|
241
|
+
/**
|
|
242
|
+
* A file acknowledges an experimental dialect with
|
|
243
|
+
* `##! experimental.dialect.NAME` wherever it names a connection on that
|
|
244
|
+
* dialect. Every connection name in this file is already resolved to a
|
|
245
|
+
* dialect by the time the AST exists, and the file's compiler flags are
|
|
246
|
+
* known only now, so this is the first moment the check can run. The
|
|
247
|
+
* other place a file takes on a dialect is `import`, checked by
|
|
248
|
+
* `ImportStatement`.
|
|
249
|
+
*/
|
|
250
|
+
checkExperimentalDialects(that) {
|
|
251
|
+
var _a;
|
|
252
|
+
const connections = (_a = this.importStep.parseReferences) === null || _a === void 0 ? void 0 : _a.connectionDialects;
|
|
253
|
+
if (!connections) {
|
|
254
|
+
return;
|
|
255
|
+
}
|
|
256
|
+
for (const [connName, need] of connections) {
|
|
257
|
+
const dialect = that.root.connectionDialectZone.get(connName);
|
|
258
|
+
if (dialect !== undefined) {
|
|
259
|
+
that.checkExperimentalDialect(dialect, {
|
|
260
|
+
url: that.sourceURL,
|
|
261
|
+
range: need.firstReference,
|
|
262
|
+
});
|
|
263
|
+
}
|
|
264
|
+
}
|
|
265
|
+
}
|
|
240
266
|
step(that) {
|
|
241
267
|
const stepTimer = new timing_1.Timer('ast_step');
|
|
242
268
|
if (this.response) {
|
|
@@ -262,6 +288,7 @@ class ASTStep {
|
|
|
262
288
|
const { ast: newAST, compilerFlagSrc, timingInfo } = secondPass.run();
|
|
263
289
|
stepTimer.contribute([timingInfo]);
|
|
264
290
|
that.compilerFlagSrc = compilerFlagSrc;
|
|
291
|
+
this.checkExperimentalDialects(that);
|
|
265
292
|
if (newAST.elementType === 'unimplemented') {
|
|
266
293
|
newAST.logError('untranslated-parse-node', 'INTERNAL COMPILER ERROR: Untranslated parse node');
|
|
267
294
|
}
|
|
@@ -429,9 +456,9 @@ class TranslateStep {
|
|
|
429
456
|
// seeding (e.g. TestTranslator's compilerFlags option) survive.
|
|
430
457
|
if (extendingModel && !this.importedAnnotations) {
|
|
431
458
|
const parseCompilerFlagsTimer = new timing_1.Timer('parse_compiler_flags');
|
|
432
|
-
//
|
|
433
|
-
//
|
|
434
|
-
//
|
|
459
|
+
// `##!` is a pragma: it governs the file it sits in and does not cross
|
|
460
|
+
// `import`. An extension is a continuation of the base's text, so the
|
|
461
|
+
// base's flags are in force here.
|
|
435
462
|
that.compilerFlagSrc.push(...new annotation_1.Annotations((0, annotation_utils_1.getModelAnnotations)(extendingModel)).texts('!'));
|
|
436
463
|
stepTimer.contribute([parseCompilerFlagsTimer.stop()]);
|
|
437
464
|
this.importedAnnotations = true;
|
|
@@ -515,7 +542,6 @@ class MalloyTranslation {
|
|
|
515
542
|
Not sure where the logic for this needs to go, this is just my first guess
|
|
516
543
|
*/
|
|
517
544
|
this.dialectAlreadyChecked = {};
|
|
518
|
-
this.allDialectsEnabled = false;
|
|
519
545
|
this.childTranslators = new Map();
|
|
520
546
|
this.modelDef = (0, utils_1.mkModelDef)(sourceURL, (0, utils_1.mkModelID)(sourceURL));
|
|
521
547
|
/**
|
|
@@ -753,13 +779,21 @@ class MalloyTranslation {
|
|
|
753
779
|
return true;
|
|
754
780
|
}
|
|
755
781
|
experimentalDialectEnabled(dialect) {
|
|
756
|
-
if (this.allDialectsEnabled) {
|
|
757
|
-
return true;
|
|
758
|
-
}
|
|
759
782
|
const experimental = this.getCompilerFlags().tag('experimental');
|
|
760
783
|
return (experimental !== undefined &&
|
|
761
784
|
(experimental.bare() || experimental.has('dialect', dialect)));
|
|
762
785
|
}
|
|
786
|
+
/**
|
|
787
|
+
* Log, once per dialect per file, that this file uses an experimental
|
|
788
|
+
* dialect without acknowledging it.
|
|
789
|
+
*/
|
|
790
|
+
checkExperimentalDialect(dialect, at) {
|
|
791
|
+
if (this.firstReferenceToDialect(dialect) &&
|
|
792
|
+
(0, dialect_1.getDialect)(dialect).isExperimental() &&
|
|
793
|
+
!this.experimentalDialectEnabled(dialect)) {
|
|
794
|
+
this.root.logError('experimental-dialect-not-enabled', { dialect }, { at });
|
|
795
|
+
}
|
|
796
|
+
}
|
|
763
797
|
}
|
|
764
798
|
exports.MalloyTranslation = MalloyTranslation;
|
|
765
799
|
class MalloyChildTranslator extends MalloyTranslation {
|
|
@@ -2,13 +2,13 @@ import type { CommonTokenStream } from 'antlr4ts';
|
|
|
2
2
|
import type { ParseTree } from 'antlr4ts/tree';
|
|
3
3
|
import type { DocumentRange } from '../../model/malloy_types';
|
|
4
4
|
import type { MalloyTranslation } from '../parse-malloy';
|
|
5
|
-
type NeedImports =
|
|
6
|
-
type NeedTables =
|
|
5
|
+
type NeedImports = Map<string, DocumentRange>;
|
|
6
|
+
type NeedTables = Map<string, {
|
|
7
7
|
connectionName: string;
|
|
8
8
|
tablePath: string;
|
|
9
9
|
firstReference: DocumentRange;
|
|
10
10
|
}>;
|
|
11
|
-
type NeedConnectionDialects =
|
|
11
|
+
type NeedConnectionDialects = Map<string, {
|
|
12
12
|
firstReference: DocumentRange;
|
|
13
13
|
}>;
|
|
14
14
|
export declare function constructTableKey(connectionName: string, tablePath: string): string;
|