@malloydata/malloy 0.0.394 → 0.0.396

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