@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.
Files changed (103) hide show
  1. package/dist/api/foundation/build_targets.d.ts +5 -6
  2. package/dist/api/foundation/build_targets.js +74 -6
  3. package/dist/api/foundation/compile.d.ts +0 -1
  4. package/dist/api/foundation/compile.js +0 -3
  5. package/dist/api/foundation/core.d.ts +16 -4
  6. package/dist/api/foundation/core.js +23 -23
  7. package/dist/api/foundation/index.d.ts +1 -1
  8. package/dist/api/foundation/runtime.d.ts +21 -3
  9. package/dist/api/foundation/runtime.js +45 -54
  10. package/dist/api/foundation/types.d.ts +43 -4
  11. package/dist/connection/dbmeta.d.ts +29 -0
  12. package/dist/connection/dbmeta.js +46 -0
  13. package/dist/connection/index.d.ts +1 -0
  14. package/dist/connection/index.js +1 -0
  15. package/dist/dialect/databricks/databricks.d.ts +11 -12
  16. package/dist/dialect/databricks/databricks.js +24 -36
  17. package/dist/dialect/dialect.d.ts +70 -171
  18. package/dist/dialect/dialect.js +123 -92
  19. package/dist/dialect/dialect_map.js +2 -0
  20. package/dist/dialect/duckdb/duckdb.d.ts +2 -2
  21. package/dist/dialect/duckdb/duckdb.js +1 -1
  22. package/dist/dialect/index.d.ts +3 -2
  23. package/dist/dialect/index.js +4 -2
  24. package/dist/dialect/mysql/mysql.d.ts +11 -12
  25. package/dist/dialect/mysql/mysql.js +20 -32
  26. package/dist/dialect/pg_impl.d.ts +9 -10
  27. package/dist/dialect/pg_impl.js +27 -73
  28. package/dist/dialect/postgres/postgres.d.ts +4 -4
  29. package/dist/dialect/postgres/postgres.js +4 -12
  30. package/dist/dialect/snowflake/snowflake.d.ts +11 -13
  31. package/dist/dialect/snowflake/snowflake.js +30 -82
  32. package/dist/dialect/sqlserver/dialect_functions.d.ts +2 -0
  33. package/dist/dialect/sqlserver/dialect_functions.js +42 -0
  34. package/dist/dialect/sqlserver/function_overrides.d.ts +2 -0
  35. package/dist/dialect/sqlserver/function_overrides.js +55 -0
  36. package/dist/dialect/sqlserver/index.d.ts +1 -0
  37. package/dist/dialect/sqlserver/index.js +22 -0
  38. package/dist/dialect/sqlserver/sqlserver.d.ts +101 -0
  39. package/dist/dialect/sqlserver/sqlserver.js +537 -0
  40. package/dist/dialect/sqlserver/windows_zones.d.ts +1 -0
  41. package/dist/dialect/sqlserver/windows_zones.js +613 -0
  42. package/dist/dialect/standardsql/standardsql.d.ts +11 -16
  43. package/dist/dialect/standardsql/standardsql.js +22 -73
  44. package/dist/dialect/trino/trino.d.ts +16 -18
  45. package/dist/dialect/trino/trino.js +127 -181
  46. package/dist/index.d.ts +2 -2
  47. package/dist/index.js +6 -4
  48. package/dist/lang/ast/expressions/boolean-value.d.ts +24 -0
  49. package/dist/lang/ast/expressions/boolean-value.js +41 -0
  50. package/dist/lang/ast/expressions/case.js +7 -0
  51. package/dist/lang/ast/expressions/expr-alternation-tree.js +11 -0
  52. package/dist/lang/ast/expressions/expr-cast.js +5 -0
  53. package/dist/lang/ast/expressions/expr-coalesce.js +4 -0
  54. package/dist/lang/ast/expressions/expr-func.js +13 -2
  55. package/dist/lang/ast/expressions/expr-minus.js +5 -2
  56. package/dist/lang/ast/expressions/expr-null.js +4 -0
  57. package/dist/lang/ast/expressions/pick-when.js +7 -0
  58. package/dist/lang/ast/field-space/index-field-space.js +13 -1
  59. package/dist/lang/ast/field-space/query-spaces.js +11 -0
  60. package/dist/lang/ast/query-builders/reduce-builder.js +1 -1
  61. package/dist/lang/ast/query-items/field-declaration.d.ts +5 -0
  62. package/dist/lang/ast/query-items/field-declaration.js +22 -16
  63. package/dist/lang/ast/query-items/field-references.d.ts +6 -0
  64. package/dist/lang/ast/query-items/field-references.js +23 -0
  65. package/dist/lang/ast/query-properties/filters.js +3 -1
  66. package/dist/lang/ast/source-elements/named-source.js +0 -2
  67. package/dist/lang/ast/source-elements/sql-source.js +0 -3
  68. package/dist/lang/ast/source-elements/table-source.js +1 -2
  69. package/dist/lang/ast/statements/import-statement.js +17 -0
  70. package/dist/lang/ast/types/expression-def.js +17 -2
  71. package/dist/lang/ast/types/malloy-element.d.ts +24 -8
  72. package/dist/lang/ast/types/malloy-element.js +63 -44
  73. package/dist/lang/ast/view-elements/reference-view.js +4 -0
  74. package/dist/lang/ast/view-elements/refine-utils.js +1 -0
  75. package/dist/lang/composite-source-utils.js +10 -17
  76. package/dist/lang/parse-log.d.ts +12 -0
  77. package/dist/lang/parse-log.js +2 -0
  78. package/dist/lang/parse-malloy.d.ts +17 -2
  79. package/dist/lang/parse-malloy.js +48 -14
  80. package/dist/lang/parse-tree-walkers/find-external-references.d.ts +3 -3
  81. package/dist/lang/parse-tree-walkers/find-external-references.js +17 -16
  82. package/dist/lang/test/test-translator.d.ts +11 -1
  83. package/dist/lang/test/test-translator.js +51 -3
  84. package/dist/model/expression_compiler.js +9 -8
  85. package/dist/model/filter_compilers.js +13 -12
  86. package/dist/model/given_binding.d.ts +8 -0
  87. package/dist/model/given_binding.js +13 -0
  88. package/dist/model/index.d.ts +1 -1
  89. package/dist/model/index.js +2 -1
  90. package/dist/model/query_query.d.ts +3 -1
  91. package/dist/model/query_query.js +157 -139
  92. package/dist/model/source_def_utils.d.ts +7 -1
  93. package/dist/model/source_def_utils.js +20 -0
  94. package/dist/model/sql_compiled.d.ts +19 -20
  95. package/dist/model/sql_compiled.js +29 -17
  96. package/dist/test/index.js +16 -8
  97. package/dist/test/matchers.d.ts +1 -1
  98. package/dist/test/matchers.js +1 -1
  99. package/dist/test/test-models.js +6 -13
  100. package/dist/version.d.ts +1 -1
  101. package/dist/version.js +1 -1
  102. package/femto-config.motly +13 -1
  103. package/package.json +7 -5
@@ -78,9 +78,6 @@ class SQLSource extends source_1.Source {
78
78
  compileSQL: sql,
79
79
  };
80
80
  }
81
- else if (lookup.status === 'present') {
82
- doc.checkExperimentalDialect(this, lookup.value.dialect);
83
- }
84
81
  }
85
82
  getSourceDef() {
86
83
  var _a;
@@ -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, _c;
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 usage = q.givenUsage;
570
- if (!usage || usage.length === 0)
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. The single point of knowledge for
1218
- // "where can a Query embed in a SourceDef" — when a future SourceDef variant
1219
- // grows an embedded Query, teach this helper.
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, _b, _c;
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
- return [...fromFilters, ...((_b = sd.query.givenUsage) !== null && _b !== void 0 ? _b : [])];
1234
- case 'sql_select': {
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,
@@ -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;
@@ -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
- private parseReferences;
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 in this.parseReferences.connectionDialects) {
136
+ for (const [connName, need] of this.parseReferences.connectionDialects) {
136
137
  that.root.connectionDialectZone.reference(connName, {
137
138
  url: that.sourceURL,
138
- range: this.parseReferences.connectionDialects[connName].firstReference,
139
+ range: need.firstReference,
139
140
  });
140
141
  }
141
- for (const relativeRef in this.parseReferences.urls) {
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 rawKey in this.parseReferences.tables) {
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
- const connectionDialects = {};
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
- // Compiler flags from the extending base's `##` annotations. NOTE: `##!`
433
- // flag semantics are still to be settled; this keeps the existing
434
- // behavior (flags from the base model) green and is not the final design.
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 = Record<string, DocumentRange>;
6
- type NeedTables = Record<string, {
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 = Record<string, {
11
+ type NeedConnectionDialects = Map<string, {
12
12
  firstReference: DocumentRange;
13
13
  }>;
14
14
  export declare function constructTableKey(connectionName: string, tablePath: string): string;