@malloydata/malloy 0.0.395 → 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 (44) 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/runtime.d.ts +85 -5
  4. package/dist/api/foundation/runtime.js +183 -13
  5. package/dist/api/foundation/types.d.ts +2 -0
  6. package/dist/lang/ast/expressions/expr-func.js +30 -11
  7. package/dist/lang/ast/expressions/expr-given.js +1 -0
  8. package/dist/lang/ast/field-space/reference-field.js +1 -1
  9. package/dist/lang/ast/source-elements/sql-source.js +4 -0
  10. package/dist/lang/ast/source-elements/table-source.js +4 -0
  11. package/dist/lang/ast/statements/define-given.d.ts +1 -0
  12. package/dist/lang/ast/statements/define-given.js +7 -0
  13. package/dist/lang/ast/statements/import-statement.js +4 -0
  14. package/dist/lang/ast/types/annotation-elements.d.ts +1 -0
  15. package/dist/lang/ast/types/annotation-elements.js +10 -3
  16. package/dist/lang/ast/types/malloy-element.d.ts +1 -0
  17. package/dist/lang/ast/types/malloy-element.js +4 -0
  18. package/dist/lang/malloy-to-ast.d.ts +2 -1
  19. package/dist/lang/malloy-to-ast.js +11 -1
  20. package/dist/lang/parse-log.d.ts +1 -0
  21. package/dist/lang/parse-log.js +4 -0
  22. package/dist/lang/parse-malloy.d.ts +4 -1
  23. package/dist/lang/parse-malloy.js +26 -4
  24. package/dist/lang/test/test-translator.d.ts +19 -5
  25. package/dist/lang/test/test-translator.js +15 -12
  26. package/dist/lang/zone.d.ts +2 -0
  27. package/dist/lang/zone.js +10 -0
  28. package/dist/model/constant_expression_compiler.js +14 -5
  29. package/dist/model/expression_compiler.js +19 -17
  30. package/dist/model/field_instance.js +7 -3
  31. package/dist/model/given_binding.js +26 -21
  32. package/dist/model/index.d.ts +1 -0
  33. package/dist/model/index.js +3 -1
  34. package/dist/model/malloy_compile_error.d.ts +13 -0
  35. package/dist/model/malloy_compile_error.js +23 -0
  36. package/dist/model/malloy_types.d.ts +2 -0
  37. package/dist/model/query_model_impl.js +2 -1
  38. package/dist/model/query_node.d.ts +5 -5
  39. package/dist/model/query_node.js +21 -16
  40. package/dist/model/query_query.js +23 -11
  41. package/dist/model/sql_compiled.js +6 -3
  42. package/dist/version.d.ts +1 -1
  43. package/dist/version.js +1 -1
  44. package/package.json +4 -4
@@ -10,6 +10,7 @@ const luxon_1 = require("luxon");
10
10
  const closest_match_1 = require("../util/closest_match");
11
11
  const inline_expr_1 = require("./inline_expr");
12
12
  const malloy_types_1 = require("./malloy_types");
13
+ const malloy_compile_error_1 = require("./malloy_compile_error");
13
14
  function resolveSuppliedGivens(supplied, modelDef) {
14
15
  var _a;
15
16
  const out = new Map();
@@ -20,9 +21,9 @@ function resolveSuppliedGivens(supplied, modelDef) {
20
21
  // prototype pollution from e.g. an Object.create(...)-derived input.
21
22
  for (const [name, value] of Object.entries(supplied)) {
22
23
  if (value === undefined) {
23
- throw new Error(`givens.${name}: explicit undefined is not a valid value. ` +
24
+ throw new malloy_compile_error_1.MalloyCompileError(`givens.${name}: explicit undefined is not a valid value. ` +
24
25
  'Omit the key to defer to declaration default or a lower supply ' +
25
- 'layer; use null for an explicit null value.');
26
+ 'layer; use null for an explicit null value.', 'runtime-given-undefined', undefined);
26
27
  }
27
28
  const entry = modelDef.contents[name];
28
29
  if (!entry || entry.type !== 'given') {
@@ -33,7 +34,8 @@ function resolveSuppliedGivens(supplied, modelDef) {
33
34
  }
34
35
  const suggestion = (0, closest_match_1.closestMatch)(name, surfaceNames);
35
36
  const hint = suggestion ? ` (did you mean '${suggestion}'?)` : '';
36
- throw new Error(`givens: unknown given '${name}'${hint}. Model surfaces [${surfaceNames.join(', ')}]`);
37
+ throw new malloy_compile_error_1.MalloyCompileError(`givens: unknown given '${name}'${hint}. ` +
38
+ `Model surfaces [${surfaceNames.join(', ')}]`, 'runtime-given-unknown', undefined);
37
39
  }
38
40
  const decl = givens[entry.id];
39
41
  if (!decl) {
@@ -83,10 +85,13 @@ function valueToExpr(path, type, value) {
83
85
  if (value === null) {
84
86
  return { node: 'null' };
85
87
  }
88
+ function bad(msg, code = 'runtime-given-bad-value') {
89
+ throw new malloy_compile_error_1.MalloyCompileError(`givens.${path}: ${msg}`, code, undefined);
90
+ }
86
91
  switch (type.type) {
87
92
  case 'string': {
88
93
  if (typeof value !== 'string') {
89
- throw new TypeError(`givens.${path}: expected string, got ${describeJs(value)}`);
94
+ bad(`expected string, got ${describeJs(value)}`);
90
95
  }
91
96
  return { node: 'stringLiteral', literal: value };
92
97
  }
@@ -94,7 +99,7 @@ function valueToExpr(path, type, value) {
94
99
  let lit;
95
100
  if (typeof value === 'number') {
96
101
  if (!Number.isFinite(value)) {
97
- throw new TypeError(`givens.${path}: number must be finite, got ${value}`);
102
+ bad(`number must be finite, got ${value}`);
98
103
  }
99
104
  lit = String(value);
100
105
  }
@@ -103,27 +108,27 @@ function valueToExpr(path, type, value) {
103
108
  }
104
109
  else if (typeof value === 'string') {
105
110
  if (!/^-?(\d+(\.\d+)?|\.\d+)([eE][+-]?\d+)?$/.test(value)) {
106
- throw new TypeError(`givens.${path}: number-as-string must be numeric, got '${value}'`);
111
+ bad(`number-as-string must be numeric, got '${value}'`);
107
112
  }
108
113
  lit = value;
109
114
  }
110
115
  else {
111
- throw new TypeError(`givens.${path}: expected number | bigint | string, got ${describeJs(value)}`);
116
+ bad(`expected number | bigint | string, got ${describeJs(value)}`);
112
117
  }
113
118
  return { node: 'numberLiteral', literal: lit };
114
119
  }
115
120
  case 'boolean': {
116
121
  if (typeof value !== 'boolean') {
117
- throw new TypeError(`givens.${path}: expected boolean, got ${describeJs(value)}`);
122
+ bad(`expected boolean, got ${describeJs(value)}`);
118
123
  }
119
124
  return { node: value ? 'true' : 'false' };
120
125
  }
121
126
  case 'date': {
122
127
  if (typeof value !== 'string') {
123
- throw new TypeError(`givens.${path}: expected ISO date string 'YYYY-MM-DD', got ${describeJs(value)}`);
128
+ bad(`expected ISO date string 'YYYY-MM-DD', got ${describeJs(value)}`);
124
129
  }
125
130
  if (!/^\d{4}-\d{2}-\d{2}$/.test(value)) {
126
- throw new TypeError(`givens.${path}: date must match 'YYYY-MM-DD', got '${value}'`);
131
+ bad(`date must match 'YYYY-MM-DD', got '${value}'`);
127
132
  }
128
133
  return { node: 'dateLiteral', literal: value, typeDef: { type: 'date' } };
129
134
  }
@@ -133,17 +138,17 @@ function valueToExpr(path, type, value) {
133
138
  // strings (those want 'timestamptz'). Parse the rest with Luxon and
134
139
  // emit canonical "YYYY-MM-DD HH:MM:SS.sss" for the dialect.
135
140
  if (typeof value !== 'string') {
136
- throw new TypeError(`givens.${path}: expected ISO timestamp string (no offset), got ${describeJs(value)}`);
141
+ bad(`expected ISO timestamp string (no offset), got ${describeJs(value)}`);
137
142
  }
138
143
  if (/Z$|[+-]\d{2}:?\d{2}$/.test(value)) {
139
- throw new TypeError(`givens.${path}: 'timestamp' is naive — use 'timestamptz' for offset/zoned values, got '${value}'`);
144
+ bad(`'timestamp' is naive — use 'timestamptz' for offset/zoned values, got '${value}'`);
140
145
  }
141
146
  // ISO uses T-separator; SQL form uses space. Accept both.
142
147
  let dt = luxon_1.DateTime.fromISO(value, { zone: 'utc' });
143
148
  if (!dt.isValid)
144
149
  dt = luxon_1.DateTime.fromSQL(value, { zone: 'utc' });
145
150
  if (!dt.isValid) {
146
- throw new TypeError(`givens.${path}: invalid timestamp value '${value}': ${(_a = dt.invalidReason) !== null && _a !== void 0 ? _a : 'unknown'}`);
151
+ bad(`invalid timestamp value '${value}': ${(_a = dt.invalidReason) !== null && _a !== void 0 ? _a : 'unknown'}`);
147
152
  }
148
153
  return {
149
154
  node: 'timestampLiteral',
@@ -162,10 +167,10 @@ function valueToExpr(path, type, value) {
162
167
  dt = dt.toUTC();
163
168
  }
164
169
  else {
165
- throw new TypeError(`givens.${path}: expected JS Date or ISO timestamptz string, got ${describeJs(value)}`);
170
+ bad(`expected JS Date or ISO timestamptz string, got ${describeJs(value)}`);
166
171
  }
167
172
  if (!dt.isValid) {
168
- throw new TypeError(`givens.${path}: invalid timestamptz value '${value}': ${(_b = dt.invalidReason) !== null && _b !== void 0 ? _b : 'unknown'}`);
173
+ bad(`invalid timestamptz value '${value}': ${(_b = dt.invalidReason) !== null && _b !== void 0 ? _b : 'unknown'}`);
169
174
  }
170
175
  return {
171
176
  node: 'timestamptzLiteral',
@@ -176,13 +181,13 @@ function valueToExpr(path, type, value) {
176
181
  }
177
182
  case 'filter expression': {
178
183
  if (typeof value !== 'string') {
179
- throw new TypeError(`givens.${path}: filter<T> givens require a JS string of Malloy filter source, got ${describeJs(value)}`);
184
+ bad(`filter<T> givens require a JS string of Malloy filter source, got ${describeJs(value)}`);
180
185
  }
181
186
  return { node: 'filterLiteral', filterSrc: value };
182
187
  }
183
188
  case 'array': {
184
189
  if (!Array.isArray(value)) {
185
- throw new TypeError(`givens.${path}: expected array, got ${describeJs(value)}`);
190
+ bad(`expected array, got ${describeJs(value)}`);
186
191
  }
187
192
  // RepeatedRecord (array of records) carries `record_element` as its
188
193
  // element type and the record's schema in `fields`. Each element is
@@ -198,19 +203,19 @@ function valueToExpr(path, type, value) {
198
203
  if (typeof value !== 'object' ||
199
204
  Array.isArray(value) ||
200
205
  value instanceof Date) {
201
- throw new TypeError(`givens.${path}: expected object, got ${describeJs(value)}`);
206
+ bad(`expected object, got ${describeJs(value)}`);
202
207
  }
203
208
  const obj = value;
204
209
  const declared = new Set(type.fields.map(f => f.name));
205
210
  for (const k of Object.keys(obj)) {
206
211
  if (!declared.has(k)) {
207
- throw new TypeError(`givens.${path}.${k}: unexpected key (not in record type [${[...declared].join(', ')}])`);
212
+ throw new malloy_compile_error_1.MalloyCompileError(`givens.${path}.${k}: unexpected key (not in record type [${[...declared].join(', ')}])`, 'runtime-given-record-extra-key', undefined);
208
213
  }
209
214
  }
210
215
  const kids = (0, malloy_types_1.mkSafeRecord)();
211
216
  for (const field of type.fields) {
212
217
  if (!(field.name in obj)) {
213
- throw new TypeError(`givens.${path}.${field.name}: missing required key`);
218
+ throw new malloy_compile_error_1.MalloyCompileError(`givens.${path}.${field.name}: missing required key`, 'runtime-given-record-missing-key', undefined);
214
219
  }
215
220
  kids[field.name] = valueToExpr(`${path}.${field.name}`, malloy_types_1.TD.atomicDef(field), obj[field.name]);
216
221
  }
@@ -219,7 +224,7 @@ function valueToExpr(path, type, value) {
219
224
  case 'json':
220
225
  case 'sql native':
221
226
  case 'error':
222
- throw new Error(`givens.${path}: type '${type.type}' is not bindable`);
227
+ throw new malloy_compile_error_1.MalloyCompileError(`givens.${path}: type '${type.type}' is not bindable as a given value.`, 'runtime-given-type-not-bindable', undefined);
223
228
  default: {
224
229
  // Exhaustiveness: future GivenTypeDef additions will trip this.
225
230
  const _x = type;
@@ -7,4 +7,5 @@ export { getResultStructDefForQuery, getResultStructDefForView, } from './query_
7
7
  export { indent, composeSQLExpr, makeDigest, mkModelDef, pathToKey, typeDefToString, } from './utils';
8
8
  export { constantExprToSQL } from './constant_expression_compiler';
9
9
  export { getCompiledSQL } from './sql_compiled';
10
+ export { MalloyCompileError } from './malloy_compile_error';
10
11
  export { mkSourceID, mkBuildID, mkQuerySourceDef, mkSQLSourceDef, mkTableSourceDef, resolveSourceID, registerSource, hasSourceRegistryEntry, } from './source_def_utils';
@@ -36,7 +36,7 @@ var __exportStar = (this && this.__exportStar) || function(m, exports) {
36
36
  for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
37
37
  };
38
38
  Object.defineProperty(exports, "__esModule", { value: true });
39
- exports.hasSourceRegistryEntry = exports.registerSource = exports.resolveSourceID = exports.mkTableSourceDef = exports.mkSQLSourceDef = exports.mkQuerySourceDef = exports.mkBuildID = exports.mkSourceID = exports.getCompiledSQL = exports.constantExprToSQL = exports.typeDefToString = exports.pathToKey = exports.mkModelDef = exports.makeDigest = exports.composeSQLExpr = exports.indent = exports.getResultStructDefForView = exports.getResultStructDefForQuery = exports.QueryModel = exports.QueryQuery = exports.QueryStruct = exports.QueryField = void 0;
39
+ exports.hasSourceRegistryEntry = exports.registerSource = exports.resolveSourceID = exports.mkTableSourceDef = exports.mkSQLSourceDef = exports.mkQuerySourceDef = exports.mkBuildID = exports.mkSourceID = exports.MalloyCompileError = exports.getCompiledSQL = exports.constantExprToSQL = exports.typeDefToString = exports.pathToKey = exports.mkModelDef = exports.makeDigest = exports.composeSQLExpr = exports.indent = exports.getResultStructDefForView = exports.getResultStructDefForQuery = exports.QueryModel = exports.QueryQuery = exports.QueryStruct = exports.QueryField = void 0;
40
40
  __exportStar(require("./malloy_types"), exports);
41
41
  const query_node_1 = require("./query_node");
42
42
  Object.defineProperty(exports, "QueryField", { enumerable: true, get: function () { return query_node_1.QueryField; } });
@@ -66,6 +66,8 @@ var constant_expression_compiler_1 = require("./constant_expression_compiler");
66
66
  Object.defineProperty(exports, "constantExprToSQL", { enumerable: true, get: function () { return constant_expression_compiler_1.constantExprToSQL; } });
67
67
  var sql_compiled_1 = require("./sql_compiled");
68
68
  Object.defineProperty(exports, "getCompiledSQL", { enumerable: true, get: function () { return sql_compiled_1.getCompiledSQL; } });
69
+ var malloy_compile_error_1 = require("./malloy_compile_error");
70
+ Object.defineProperty(exports, "MalloyCompileError", { enumerable: true, get: function () { return malloy_compile_error_1.MalloyCompileError; } });
69
71
  var source_def_utils_1 = require("./source_def_utils");
70
72
  Object.defineProperty(exports, "mkSourceID", { enumerable: true, get: function () { return source_def_utils_1.mkSourceID; } });
71
73
  Object.defineProperty(exports, "mkBuildID", { enumerable: true, get: function () { return source_def_utils_1.mkBuildID; } });
@@ -0,0 +1,13 @@
1
+ import type { DocumentLocation } from './malloy_types';
2
+ /**
3
+ * Thrown by the SQL compiler (`src/model/`) for user-actionable errors the
4
+ * translator didn't catch. Caught at the materializer boundary and turned
5
+ * into a `LogMessage`. Singular `at` reflects the fail-fast contract — the
6
+ * first such error stops compilation. Invariant violations stay as bare
7
+ * `Error` and surface as `code: 'compiler-bug'` instead.
8
+ */
9
+ export declare class MalloyCompileError extends Error {
10
+ readonly code: string;
11
+ readonly at: DocumentLocation | undefined;
12
+ constructor(message: string, code: string, at: DocumentLocation | undefined);
13
+ }
@@ -0,0 +1,23 @@
1
+ "use strict";
2
+ /*
3
+ * Copyright Contributors to the Malloy project
4
+ * SPDX-License-Identifier: MIT
5
+ */
6
+ Object.defineProperty(exports, "__esModule", { value: true });
7
+ exports.MalloyCompileError = void 0;
8
+ /**
9
+ * Thrown by the SQL compiler (`src/model/`) for user-actionable errors the
10
+ * translator didn't catch. Caught at the materializer boundary and turned
11
+ * into a `LogMessage`. Singular `at` reflects the fail-fast contract — the
12
+ * first such error stops compilation. Invariant violations stay as bare
13
+ * `Error` and surface as `code: 'compiler-bug'` instead.
14
+ */
15
+ class MalloyCompileError extends Error {
16
+ constructor(message, code, at) {
17
+ super(message);
18
+ this.code = code;
19
+ this.at = at;
20
+ }
21
+ }
22
+ exports.MalloyCompileError = MalloyCompileError;
23
+ //# sourceMappingURL=malloy_compile_error.js.map
@@ -127,11 +127,13 @@ export interface SourceReferenceNode extends ExprLeaf {
127
127
  export interface ParameterNode extends ExprLeaf {
128
128
  node: 'parameter';
129
129
  path: string[];
130
+ at?: DocumentLocation;
130
131
  }
131
132
  export interface GivenRefNode extends ExprLeaf {
132
133
  node: 'given';
133
134
  id: GivenID;
134
135
  refName: string;
136
+ at?: DocumentLocation;
135
137
  }
136
138
  export interface NowNode extends ExprLeaf {
137
139
  node: 'now';
@@ -14,6 +14,7 @@ const stage_writer_1 = require("./stage_writer");
14
14
  const dialect_1 = require("../dialect");
15
15
  const query_node_1 = require("./query_node");
16
16
  const row_data_utils_1 = require("../api/row_data_utils");
17
+ const malloy_compile_error_1 = require("./malloy_compile_error");
17
18
  function makeQueryModel(modelDef) {
18
19
  return new QueryModelImpl(modelDef);
19
20
  }
@@ -66,7 +67,7 @@ class QueryModelImpl {
66
67
  if (s) {
67
68
  return s;
68
69
  }
69
- throw new Error(`Struct ${name} not found in model.`);
70
+ throw new malloy_compile_error_1.MalloyCompileError(`Source '${name}' is not defined in this model.`, 'compiler-undefined-source', undefined);
70
71
  }
71
72
  getStructFromRef(structRef, sourceArguments, prepareResultOptions) {
72
73
  prepareResultOptions !== null && prepareResultOptions !== void 0 ? prepareResultOptions : (prepareResultOptions = {});
@@ -1,4 +1,4 @@
1
- import type { FieldDef, BooleanFieldDef, DateFieldDef, StringFieldDef, JSONFieldDef, NumberFieldDef, ATimestampFieldDef, NativeUnsupportedFieldDef, JoinFieldDef, Argument, Given, GivenID, PrepareResultOptions, AtomicFieldDef, BasicAtomicDef, FilterCondition, RefToField, StructDef, TurtleDef, TurtleDefPlusFilters, SourceDef, Query } from './malloy_types';
1
+ import type { FieldDef, BooleanFieldDef, DateFieldDef, DocumentLocation, StringFieldDef, JSONFieldDef, NumberFieldDef, ATimestampFieldDef, NativeUnsupportedFieldDef, JoinFieldDef, Argument, Given, GivenID, PrepareResultOptions, AtomicFieldDef, BasicAtomicDef, FilterCondition, RefToField, StructDef, TurtleDef, TurtleDefPlusFilters, SourceDef, Query } from './malloy_types';
2
2
  import type { EventStream } from '../runtime_types';
3
3
  import type { Tag } from '@malloydata/malloy-tag';
4
4
  import type { Dialect } from '../dialect';
@@ -106,7 +106,7 @@ export declare class QueryStruct {
106
106
  getFullOutputName(): string;
107
107
  unnestWithNumbers(): boolean;
108
108
  getJoinableParent(): QueryStruct;
109
- addFieldToNameMap(as: string, n: QueryField): void;
109
+ addFieldToNameMap(as: string, n: QueryField, at?: DocumentLocation): void;
110
110
  /** the the primary key or throw an error. */
111
111
  getPrimaryKeyField(fieldDef: FieldDef): QueryBasicField;
112
112
  /**
@@ -126,14 +126,14 @@ export declare class QueryStruct {
126
126
  primaryKey(): QueryBasicField | undefined;
127
127
  getChildByName(name: string): QueryField | undefined;
128
128
  /** convert a path into a field reference */
129
- getFieldByName(path: string[]): QueryField;
130
- getQueryFieldByName(name: string[]): QueryField;
129
+ getFieldByName(path: string[], at?: DocumentLocation): QueryField;
130
+ getQueryFieldByName(name: string[], at?: DocumentLocation): QueryField;
131
131
  getQueryFieldReference(f: RefToField): QueryField;
132
132
  getDimensionOrMeasureByName(name: string[]): QueryField;
133
133
  /** returns a query object for the given name */
134
134
  getDimensionByName(name: string[]): QueryBasicField;
135
135
  /** returns a query object for the given name */
136
- getStructByName(name: string[]): QueryStruct;
136
+ getStructByName(name: string[], at?: DocumentLocation): QueryStruct;
137
137
  getDistinctKey(): QueryBasicField;
138
138
  applyStructFiltersToTurtleDef(turtleDef: TurtleDef | TurtleDefPlusFilters): TurtleDef;
139
139
  }
@@ -12,6 +12,7 @@ exports.isBasicAggregate = isBasicAggregate;
12
12
  exports.isBasicCalculation = isBasicCalculation;
13
13
  exports.isBasicScalar = isBasicScalar;
14
14
  const uuid_1 = require("uuid");
15
+ const malloy_compile_error_1 = require("./malloy_compile_error");
15
16
  const malloy_types_1 = require("./malloy_types");
16
17
  const annotation_1 = require("../annotation");
17
18
  const dialect_1 = require("../dialect");
@@ -245,7 +246,8 @@ class QueryStruct {
245
246
  ? this.parent.resolveParentParameterReferences(resolved1)
246
247
  : resolved1;
247
248
  if (resolved2.value === null) {
248
- throw new Error('Invalid parameter value');
249
+ throw new malloy_compile_error_1.MalloyCompileError(`Parameter '${frag.path[0]}' resolves to a null value chain; ` +
250
+ 'this parameter was not supplied.', 'compiler-parameter-no-value', undefined);
249
251
  }
250
252
  else {
251
253
  return resolved2.value;
@@ -284,10 +286,10 @@ class QueryStruct {
284
286
  if (!QueryStruct.turtleFieldMaker) {
285
287
  throw new Error('INTERNAL ERROR: QueryQuery must initialize QueryStruct nested factory method');
286
288
  }
287
- this.addFieldToNameMap(as, QueryStruct.turtleFieldMaker(field, this));
289
+ this.addFieldToNameMap(as, QueryStruct.turtleFieldMaker(field, this), field.location);
288
290
  }
289
291
  else if ((0, malloy_types_1.isAtomic)(field) || (0, malloy_types_1.isJoinedSource)(field)) {
290
- this.addFieldToNameMap(as, this.makeQueryField(field));
292
+ this.addFieldToNameMap(as, this.makeQueryField(field), field.location);
291
293
  }
292
294
  else {
293
295
  // According to the type system this should be impossible, but we have seen this happen
@@ -413,9 +415,9 @@ class QueryStruct {
413
415
  }
414
416
  return this;
415
417
  }
416
- addFieldToNameMap(as, n) {
418
+ addFieldToNameMap(as, n, at) {
417
419
  if (this.nameMap.has(as)) {
418
- throw new Error(`Redefinition of ${as}`);
420
+ throw new malloy_compile_error_1.MalloyCompileError(`Field name '${as}' is defined more than once in this scope.`, 'compiler-name-redefined', at);
419
421
  }
420
422
  this.nameMap.set(as, n);
421
423
  }
@@ -426,7 +428,9 @@ class QueryStruct {
426
428
  return pk;
427
429
  }
428
430
  else {
429
- throw new Error(`Missing primary key for ${fieldDef}`);
431
+ throw new malloy_compile_error_1.MalloyCompileError(`Source '${(0, malloy_types_1.getIdentifier)(this.structDef)}' has no primary key; ` +
432
+ `cannot compute a unique key for field '${(0, malloy_types_1.getIdentifier)(fieldDef)}'. ` +
433
+ 'Add `primary_key: <field>` to the source definition.', 'compiler-missing-primary-key', fieldDef.location);
430
434
  }
431
435
  }
432
436
  /**
@@ -528,7 +532,7 @@ class QueryStruct {
528
532
  return this.nameMap.get(name);
529
533
  }
530
534
  /** convert a path into a field reference */
531
- getFieldByName(path) {
535
+ getFieldByName(path, at) {
532
536
  let found = undefined;
533
537
  let lookIn = this;
534
538
  let notFound = path[0];
@@ -542,22 +546,23 @@ class QueryStruct {
542
546
  found instanceof QueryFieldStruct ? found.queryStruct : undefined;
543
547
  }
544
548
  if (found === undefined) {
545
- const pathErr = path.length > 1 ? ` in ${path.join('.')}` : '';
546
- throw new Error(`${notFound} not found${pathErr}`);
549
+ const pathErr = path.length > 1 ? ` in path '${path.join('.')}'` : '';
550
+ throw new malloy_compile_error_1.MalloyCompileError(`Field '${notFound}' not found${pathErr}.`, 'compiler-field-not-found', at);
547
551
  }
548
552
  return found;
549
553
  }
550
554
  // structs referenced in queries are converted to fields.
551
- getQueryFieldByName(name) {
552
- const field = this.getFieldByName(name);
555
+ getQueryFieldByName(name, at) {
556
+ const field = this.getFieldByName(name, at);
553
557
  if (field instanceof QueryFieldStruct) {
554
- throw new Error(`Cannot reference ${name.join('.')} as a scalar'`);
558
+ throw new malloy_compile_error_1.MalloyCompileError(`'${name.join('.')}' refers to a source or join, not a scalar field. ` +
559
+ 'Use `source.field` to reference fields inside it.', 'compiler-cannot-reference-as-scalar', at);
555
560
  }
556
561
  return field;
557
562
  }
558
563
  getQueryFieldReference(f) {
559
564
  const { path, annotation, drillExpression } = f;
560
- const field = this.getFieldByName(path);
565
+ const field = this.getFieldByName(path, f.at);
561
566
  if (annotation || drillExpression) {
562
567
  if (field.parent === undefined) {
563
568
  throw new Error('Inconcievable, field reference to orphaned query field');
@@ -591,15 +596,15 @@ class QueryStruct {
591
596
  throw new Error(`${name} is not an atomic scalar field? Inconceivable!`);
592
597
  }
593
598
  /** returns a query object for the given name */
594
- getStructByName(name) {
599
+ getStructByName(name, at) {
595
600
  if (name.length === 0) {
596
601
  return this;
597
602
  }
598
- const struct = this.getFieldByName(name);
603
+ const struct = this.getFieldByName(name, at);
599
604
  if (struct instanceof QueryFieldStruct) {
600
605
  return struct.queryStruct;
601
606
  }
602
- throw new Error(`Error: Path to structure not found '${name.join('.')}'`);
607
+ throw new malloy_compile_error_1.MalloyCompileError(`'${name.join('.')}' is not a source or join.`, 'compiler-struct-not-found', at);
603
608
  }
604
609
  getDistinctKey() {
605
610
  if (this.structDef.type !== 'record') {
@@ -13,6 +13,7 @@ const stage_writer_1 = require("./stage_writer");
13
13
  const field_instance_1 = require("./field_instance");
14
14
  const sql_compiled_1 = require("./sql_compiled");
15
15
  const source_def_utils_1 = require("./source_def_utils");
16
+ const malloy_compile_error_1 = require("./malloy_compile_error");
16
17
  function pathToCol(path) {
17
18
  return path.map(el => encodeURIComponent(el)).join('/');
18
19
  }
@@ -142,7 +143,8 @@ class QueryQuery extends query_node_1.QueryField {
142
143
  for (const pathSegment of ungrouping.path) {
143
144
  const nextStruct = destResult.allFields.get(pathSegment);
144
145
  if (!(nextStruct instanceof field_instance_1.FieldInstanceResult)) {
145
- throw new Error(`Ungroup path ${ungrouping.path.join('.')} segment '${pathSegment}' is not a nested query`);
146
+ throw new malloy_compile_error_1.MalloyCompileError(`Ungrouping path '${ungrouping.path.join('.')}' references ` +
147
+ `'${pathSegment}', which is not a nested view at this level.`, 'compiler-ungrouped-invalid-path', this.fieldDef.location);
146
148
  }
147
149
  destResult = nextStruct;
148
150
  }
@@ -192,7 +194,9 @@ class QueryQuery extends query_node_1.QueryField {
192
194
  const drillExpression = this.getDrillExpression(f);
193
195
  if (field instanceof QueryQuery) {
194
196
  if (this.firstSegment.type === 'project') {
195
- throw new Error(`Nested views cannot be used in select - '${field.fieldDef.name}'`);
197
+ throw new malloy_compile_error_1.MalloyCompileError(`Cannot include nested view '${field.fieldDef.name}' in a ` +
198
+ "'select:' stage. Nested views require `group_by:` or " +
199
+ '`aggregate:` to be included in output.', 'compiler-nested-view-in-select', field.fieldDef.location);
196
200
  }
197
201
  const fir = new field_instance_1.FieldInstanceResult(field.fieldDef, resultStruct);
198
202
  this.expandFields(fir);
@@ -212,7 +216,8 @@ class QueryQuery extends query_node_1.QueryField {
212
216
  }
213
217
  if ((0, query_node_1.isBasicAggregate)(field)) {
214
218
  if (this.firstSegment.type === 'project') {
215
- throw new Error(`Aggregate Fields cannot be used in select - '${field.fieldDef.name}'`);
219
+ throw new malloy_compile_error_1.MalloyCompileError(`Cannot include aggregate field '${field.fieldDef.name}' in ` +
220
+ "a 'select:' stage. Use `aggregate:` instead to compute aggregates.", 'compiler-aggregate-in-select', field.fieldDef.location);
216
221
  }
217
222
  }
218
223
  }
@@ -523,7 +528,9 @@ class QueryQuery extends query_node_1.QueryField {
523
528
  const virtualMap = (_a = qs.prepareResultOptions) === null || _a === void 0 ? void 0 : _a.virtualMap;
524
529
  const tablePath = (_b = virtualMap === null || virtualMap === void 0 ? void 0 : virtualMap.get(qs.structDef.connection)) === null || _b === void 0 ? void 0 : _b.get(qs.structDef.name);
525
530
  if (!tablePath) {
526
- throw new Error(`No virtual map entry for '${qs.structDef.name}' on connection '${qs.structDef.connection}'`);
531
+ throw new malloy_compile_error_1.MalloyCompileError(`No virtual-map entry for virtual source '${qs.structDef.name}' ` +
532
+ `on connection '${qs.structDef.connection}'. ` +
533
+ 'Add a virtual-map entry via the `virtualMap` runtime option.', 'runtime-virtual-map-missing', qs.structDef.location);
527
534
  }
528
535
  // virtualMap entries are application-supplied — assumed already
529
536
  // canonical SQL.
@@ -559,10 +566,12 @@ class QueryQuery extends query_node_1.QueryField {
559
566
  return entry.tableName;
560
567
  }
561
568
  if (buildManifest.strict) {
562
- const base = `Persist source '${qs.structDef.sourceID}' not found in manifest (buildId: ${buildId})`;
563
- throw new Error(buildManifest.loadError
569
+ const base = `Persist source '${qs.structDef.sourceID}' not found ` +
570
+ `in manifest (buildId: ${buildId}); strict manifest mode ` +
571
+ 'forbids fallback to live compilation.';
572
+ throw new malloy_compile_error_1.MalloyCompileError(buildManifest.loadError
564
573
  ? `${base}\n ${buildManifest.loadError}`
565
- : base);
574
+ : base, 'runtime-manifest-strict-miss', qs.structDef.location);
566
575
  }
567
576
  }
568
577
  }
@@ -598,7 +607,8 @@ class QueryQuery extends query_node_1.QueryField {
598
607
  if (typeof structRef === 'string') {
599
608
  const struct = this.structRefToQueryStruct(structRef);
600
609
  if (!struct) {
601
- throw new Error(`Unexpected reference to an undefined source '${structRef}'`);
610
+ throw new malloy_compile_error_1.MalloyCompileError(`Query references source '${structRef}', ` +
611
+ 'which is not defined in this model.', 'compiler-undefined-source', undefined);
602
612
  }
603
613
  sourceStruct = struct;
604
614
  }
@@ -858,7 +868,7 @@ class QueryQuery extends query_node_1.QueryField {
858
868
  }
859
869
  }
860
870
  else {
861
- throw new Error(`Unknown field in ORDER BY ${f.field}`);
871
+ throw new malloy_compile_error_1.MalloyCompileError(`ORDER BY references unknown field '${f.field}'.`, 'compiler-orderby-field-not-found', queryDef.referencedAt);
862
872
  }
863
873
  }
864
874
  else {
@@ -1210,7 +1220,9 @@ class QueryQuery extends query_node_1.QueryField {
1210
1220
  this.generateStage0Fields(this.rootResult, f, stageWriter);
1211
1221
  if (this.firstSegment.type === 'project' &&
1212
1222
  !this.parent.modelCompilerFlags().has('unsafe_complex_select_query')) {
1213
- throw new Error('PROJECT cannot be used on queries with turtles');
1223
+ throw new malloy_compile_error_1.MalloyCompileError("Cannot use 'select:' in a stage that contains nested views. " +
1224
+ 'Use `group_by:` or restructure the pipeline. ' +
1225
+ 'Set `##! unsafe_complex_select_query` to bypass at your own risk.', 'compiler-project-with-turtles', this.fieldDef.location);
1214
1226
  }
1215
1227
  const groupBy = 'GROUP BY ' + f.dimensionIndexes.join(',') + '\n';
1216
1228
  from += this.parent.dialect.sqlGroupSetTable(this.maxGroupSet) + '\n';
@@ -1611,7 +1623,7 @@ class QueryQueryIndexStage extends QueryQuery {
1611
1623
  }
1612
1624
  expandField(f) {
1613
1625
  const as = f.path.join('.');
1614
- const field = this.parent.getQueryFieldByName(f.path);
1626
+ const field = this.parent.getQueryFieldByName(f.path, f.at);
1615
1627
  return { as, field };
1616
1628
  }
1617
1629
  expandFields(resultStruct) {
@@ -8,6 +8,7 @@ exports.getCompiledSQL = getCompiledSQL;
8
8
  exports.getSourceSQL = getSourceSQL;
9
9
  const malloy_types_1 = require("./malloy_types");
10
10
  const source_def_utils_1 = require("./source_def_utils");
11
+ const malloy_compile_error_1 = require("./malloy_compile_error");
11
12
  /**
12
13
  * Compile a SQLSourceDef to its final SQL string.
13
14
  *
@@ -66,10 +67,12 @@ function expandPersistableSource(source, opts, compileQuery) {
66
67
  }
67
68
  // Not in manifest
68
69
  if (buildManifest.strict) {
69
- const base = `Persist source '${source.sourceID}' not found in manifest (buildId: ${buildId})`;
70
- throw new Error(buildManifest.loadError
70
+ const base = `Persist source '${source.sourceID}' not found in manifest ` +
71
+ `(buildId: ${buildId}); strict manifest mode forbids fallback ` +
72
+ 'to live compilation.';
73
+ throw new malloy_compile_error_1.MalloyCompileError(buildManifest.loadError
71
74
  ? `${base}\n ${buildManifest.loadError}`
72
- : base);
75
+ : base, 'runtime-manifest-strict-miss', source.location);
73
76
  }
74
77
  }
75
78
  }
package/dist/version.d.ts CHANGED
@@ -1 +1 @@
1
- export declare const MALLOY_VERSION = "0.0.395";
1
+ export declare const MALLOY_VERSION = "0.0.396";
package/dist/version.js CHANGED
@@ -2,5 +2,5 @@
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.MALLOY_VERSION = void 0;
4
4
  // generated with 'generate-version-file' script; do not edit manually
5
- exports.MALLOY_VERSION = '0.0.395';
5
+ exports.MALLOY_VERSION = '0.0.396';
6
6
  //# sourceMappingURL=version.js.map
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@malloydata/malloy",
3
- "version": "0.0.395",
3
+ "version": "0.0.396",
4
4
  "license": "MIT",
5
5
  "exports": {
6
6
  ".": "./dist/index.js",
@@ -51,9 +51,9 @@
51
51
  "generate-version-file": "VERSION=$(npm pkg get version --workspaces=false | tr -d \\\")\necho \"// generated with 'generate-version-file' script; do not edit manually\\nexport const MALLOY_VERSION = '$VERSION';\" > src/version.ts"
52
52
  },
53
53
  "dependencies": {
54
- "@malloydata/malloy-filter": "0.0.395",
55
- "@malloydata/malloy-interfaces": "0.0.395",
56
- "@malloydata/malloy-tag": "0.0.395",
54
+ "@malloydata/malloy-filter": "0.0.396",
55
+ "@malloydata/malloy-interfaces": "0.0.396",
56
+ "@malloydata/malloy-tag": "0.0.396",
57
57
  "@noble/hashes": "^1.8.0",
58
58
  "antlr4ts": "^0.5.0-alpha.4",
59
59
  "assert": "^2.0.0",