@malloydata/malloy 0.0.435 → 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 (76) 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/core.d.ts +16 -4
  4. package/dist/api/foundation/core.js +23 -23
  5. package/dist/api/foundation/index.d.ts +1 -1
  6. package/dist/api/foundation/runtime.d.ts +21 -2
  7. package/dist/api/foundation/runtime.js +44 -31
  8. package/dist/api/foundation/types.d.ts +43 -3
  9. package/dist/connection/dbmeta.d.ts +29 -0
  10. package/dist/connection/dbmeta.js +46 -0
  11. package/dist/connection/index.d.ts +1 -0
  12. package/dist/connection/index.js +1 -0
  13. package/dist/dialect/databricks/databricks.d.ts +11 -12
  14. package/dist/dialect/databricks/databricks.js +24 -36
  15. package/dist/dialect/dialect.d.ts +56 -170
  16. package/dist/dialect/dialect.js +89 -90
  17. package/dist/dialect/duckdb/duckdb.d.ts +2 -2
  18. package/dist/dialect/duckdb/duckdb.js +1 -1
  19. package/dist/dialect/index.d.ts +1 -1
  20. package/dist/dialect/index.js +2 -2
  21. package/dist/dialect/mysql/mysql.d.ts +11 -12
  22. package/dist/dialect/mysql/mysql.js +20 -32
  23. package/dist/dialect/pg_impl.d.ts +9 -10
  24. package/dist/dialect/pg_impl.js +27 -73
  25. package/dist/dialect/postgres/postgres.d.ts +4 -4
  26. package/dist/dialect/postgres/postgres.js +4 -12
  27. package/dist/dialect/snowflake/snowflake.d.ts +11 -13
  28. package/dist/dialect/snowflake/snowflake.js +30 -82
  29. package/dist/dialect/sqlserver/sqlserver.d.ts +12 -13
  30. package/dist/dialect/sqlserver/sqlserver.js +46 -47
  31. package/dist/dialect/standardsql/standardsql.d.ts +11 -16
  32. package/dist/dialect/standardsql/standardsql.js +22 -73
  33. package/dist/dialect/trino/trino.d.ts +16 -18
  34. package/dist/dialect/trino/trino.js +127 -181
  35. package/dist/index.d.ts +2 -2
  36. package/dist/index.js +2 -2
  37. package/dist/lang/ast/expressions/boolean-value.d.ts +24 -0
  38. package/dist/lang/ast/expressions/boolean-value.js +41 -0
  39. package/dist/lang/ast/expressions/case.js +7 -0
  40. package/dist/lang/ast/expressions/expr-alternation-tree.js +11 -0
  41. package/dist/lang/ast/expressions/expr-cast.js +5 -0
  42. package/dist/lang/ast/expressions/expr-coalesce.js +4 -0
  43. package/dist/lang/ast/expressions/expr-func.js +13 -2
  44. package/dist/lang/ast/expressions/expr-minus.js +5 -2
  45. package/dist/lang/ast/expressions/expr-null.js +4 -0
  46. package/dist/lang/ast/expressions/pick-when.js +7 -0
  47. package/dist/lang/ast/field-space/index-field-space.js +13 -1
  48. package/dist/lang/ast/field-space/query-spaces.js +11 -0
  49. package/dist/lang/ast/query-builders/reduce-builder.js +1 -1
  50. package/dist/lang/ast/query-items/field-declaration.d.ts +5 -0
  51. package/dist/lang/ast/query-items/field-declaration.js +22 -16
  52. package/dist/lang/ast/query-items/field-references.d.ts +6 -0
  53. package/dist/lang/ast/query-items/field-references.js +23 -0
  54. package/dist/lang/ast/query-properties/filters.js +3 -1
  55. package/dist/lang/ast/statements/import-statement.js +13 -0
  56. package/dist/lang/ast/types/expression-def.js +17 -2
  57. package/dist/lang/ast/types/malloy-element.d.ts +24 -1
  58. package/dist/lang/ast/types/malloy-element.js +63 -26
  59. package/dist/lang/ast/view-elements/reference-view.js +4 -0
  60. package/dist/lang/ast/view-elements/refine-utils.js +1 -0
  61. package/dist/lang/composite-source-utils.js +10 -17
  62. package/dist/lang/parse-log.d.ts +12 -0
  63. package/dist/lang/parse-log.js +2 -0
  64. package/dist/lang/test/test-translator.d.ts +2 -1
  65. package/dist/lang/test/test-translator.js +37 -3
  66. package/dist/model/expression_compiler.js +8 -7
  67. package/dist/model/filter_compilers.js +13 -12
  68. package/dist/model/given_binding.d.ts +8 -0
  69. package/dist/model/given_binding.js +13 -0
  70. package/dist/model/index.d.ts +1 -1
  71. package/dist/model/index.js +2 -1
  72. package/dist/model/sql_compiled.d.ts +19 -20
  73. package/dist/model/sql_compiled.js +29 -17
  74. package/dist/version.d.ts +1 -1
  75. package/dist/version.js +1 -1
  76. package/package.json +4 -4
@@ -38,7 +38,6 @@ var __importStar = (this && this.__importStar) || (function () {
38
38
  })();
39
39
  Object.defineProperty(exports, "__esModule", { value: true });
40
40
  exports.ExprMinus = void 0;
41
- const ast_utils_1 = require("../ast-utils");
42
41
  const TDU = __importStar(require("../typedesc-utils"));
43
42
  const expression_def_1 = require("../types/expression-def");
44
43
  class ExprMinus extends expression_def_1.ExpressionDef {
@@ -50,6 +49,10 @@ class ExprMinus extends expression_def_1.ExpressionDef {
50
49
  }
51
50
  computeExpression(fs) {
52
51
  const expr = this.expr.getExpression(fs);
52
+ // An error is already logged; it stays an error so nothing else logs it
53
+ if (expr.type === 'error') {
54
+ return expr;
55
+ }
53
56
  if (TDU.typeIn(expr, this.legalChildTypes)) {
54
57
  return {
55
58
  ...expr,
@@ -57,7 +60,7 @@ class ExprMinus extends expression_def_1.ExpressionDef {
57
60
  value: { node: 'unary-', e: expr.value },
58
61
  };
59
62
  }
60
- return (0, ast_utils_1.errorFor)('negate requires number');
63
+ return this.expr.loggedErrorExpr('expression-type-error', `'${this.elementType}' Can't use type ${TDU.inspect(expr)}`);
61
64
  }
62
65
  }
63
66
  exports.ExprMinus = ExprMinus;
@@ -7,11 +7,15 @@ Object.defineProperty(exports, "__esModule", { value: true });
7
7
  exports.ExprIsNull = exports.PartialIsNull = exports.ExprNULL = void 0;
8
8
  const expr_value_1 = require("../types/expr-value");
9
9
  const expression_def_1 = require("../types/expression-def");
10
+ const boolean_value_1 = require("./boolean-value");
10
11
  function doIsNull(fs, op, expr) {
11
12
  // Build a new value rather than retyping the operand's in place: what
12
13
  // getExpression hands back is shared, and the operand is usually asked for
13
14
  // its value again after this.
14
15
  const nullCmp = expr.getExpression(fs);
16
+ const noTest = (0, boolean_value_1.booleanValueError)(fs, nullCmp, 'in a null test', expr, boolean_value_1.CONDITION_HINT);
17
+ if (noTest)
18
+ return noTest;
15
19
  return (0, expr_value_1.computedExprValue)({
16
20
  dataType: { type: 'boolean' },
17
21
  value: {
@@ -42,6 +42,7 @@ const TDU = __importStar(require("../typedesc-utils"));
42
42
  const expr_value_1 = require("../types/expr-value");
43
43
  const expression_def_1 = require("../types/expression-def");
44
44
  const malloy_element_1 = require("../types/malloy-element");
45
+ const boolean_value_1 = require("./boolean-value");
45
46
  function typeCoalesce(ev1, ev2) {
46
47
  if (ev1 === undefined || ev1.type === 'null' || ev1.type === 'error') {
47
48
  return ev2;
@@ -131,6 +132,9 @@ class Pick extends expression_def_1.ExpressionDef {
131
132
  if (definedReturnType.type === 'filter expression') {
132
133
  return this.loggedErrorExpr('filter-expression-error', 'Pick statments cannot have filter expression values');
133
134
  }
135
+ const noPick = (0, boolean_value_1.booleanValueError)(fs, definedReturnType, 'as the result of a pick', this, boolean_value_1.CHOICE_HINT);
136
+ if (noPick)
137
+ return noPick;
134
138
  caseValue.kids.caseElse = defaultVal.value;
135
139
  return (0, expr_value_1.computedExprValue)({
136
140
  dataType: definedReturnType,
@@ -193,6 +197,9 @@ class Pick extends expression_def_1.ExpressionDef {
193
197
  if (definedReturnType.type === 'filter expression') {
194
198
  return this.loggedErrorExpr('filter-expression-error', 'Pick statments cannot have filter expression values');
195
199
  }
200
+ const noPick = (0, boolean_value_1.booleanValueError)(fs, definedReturnType, 'as the result of a pick', this, boolean_value_1.CHOICE_HINT);
201
+ if (noPick)
202
+ return noPick;
196
203
  pick.kids.caseElse = defVal.value;
197
204
  return (0, expr_value_1.computedExprValue)({
198
205
  dataType: definedReturnType,
@@ -8,6 +8,7 @@ exports.IndexFieldSpace = 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 field_references_1 = require("../query-items/field-references");
12
13
  const space_field_1 = require("../types/space-field");
13
14
  const query_spaces_1 = require("./query-spaces");
@@ -98,10 +99,21 @@ class IndexFieldSpace extends query_spaces_1.QueryOperationSpace {
98
99
  }
99
100
  else {
100
101
  const eTypeDesc = entry.typeDesc();
102
+ if (
101
103
  // Don't index arrays and records
102
- if (malloy_types_1.TD.isBasicAtomic(eTypeDesc) &&
104
+ malloy_types_1.TD.isBasicAtomic(eTypeDesc) &&
103
105
  (0, malloy_types_1.expressionIsScalar)(eTypeDesc.expressionType) &&
104
106
  (dialect === undefined || !dialect.ignoreInProject(name))) {
107
+ if (eTypeDesc.type === 'boolean' && (dialect === null || dialect === void 0 ? void 0 : dialect.booleanType) === 'none') {
108
+ const path = [...joinPath, name].join('.');
109
+ wild.logWarning('wildcard-omits-boolean', {
110
+ dialect: dialect.name,
111
+ wildcard: wild.refString,
112
+ name: path,
113
+ hint: (0, boolean_value_1.resultColumnHint)(path),
114
+ });
115
+ continue;
116
+ }
105
117
  expandEntries.push({ name: indexName, entry });
106
118
  this.expandedWild.set(indexName, {
107
119
  path: joinPath.concat(name),
@@ -48,6 +48,7 @@ const static_space_1 = require("./static-space");
48
48
  const query_input_space_1 = require("./query-input-space");
49
49
  const composite_source_utils_1 = require("../../composite-source-utils");
50
50
  const error_factory_1 = require("../error-factory");
51
+ const boolean_value_1 = require("../expressions/boolean-value");
51
52
  const reference_field_1 = require("./reference-field");
52
53
  const refine_from_space_field_1 = require("./refine-from-space-field");
53
54
  /**
@@ -158,6 +159,16 @@ class QueryOperationSpace extends refined_space_1.RefinedSpace {
158
159
  if (model.TD.isAtomic(eType) &&
159
160
  model.expressionIsScalar(eType.expressionType) &&
160
161
  (dialect === undefined || !dialect.ignoreInProject(name))) {
162
+ if (eType.type === 'boolean' && (dialect === null || dialect === void 0 ? void 0 : dialect.booleanType) === 'none') {
163
+ const path = joinPath.concat(name).join('.');
164
+ wild.logWarning('wildcard-omits-boolean', {
165
+ dialect: dialect.name,
166
+ wildcard: wild.refString,
167
+ name: path,
168
+ hint: (0, boolean_value_1.resultColumnHint)(path),
169
+ });
170
+ continue;
171
+ }
161
172
  expandEntries.push({ name, entry });
162
173
  this.expandedWild.set(name, {
163
174
  path: joinPath.concat(name),
@@ -55,7 +55,7 @@ class QuerySegmentBuilder {
55
55
  }
56
56
  refineFrom(from, to) {
57
57
  if (from && from.type !== 'index' && from.type !== 'raw') {
58
- if (!this.limit && from.orderBy && !from.defaultOrderBy) {
58
+ if (!this.order && from.orderBy && !from.defaultOrderBy) {
59
59
  to.orderBy = from.orderBy;
60
60
  }
61
61
  if (!this.limit && from.limit) {
@@ -19,24 +19,29 @@ export declare abstract class AtomicFieldDeclaration extends MalloyElement imple
19
19
  fieldDef(fs: FieldSpace, exprName: string): FieldDef;
20
20
  abstract typecheckExprValue(expr: ExprValue): void;
21
21
  executesInOutputSpace(): boolean;
22
+ outputsValue(): boolean;
22
23
  queryFieldDef(exprFS: FieldSpace, exprName: string): AtomicFieldDef;
23
24
  makeEntry(fs: DynamicSpace): void;
24
25
  }
25
26
  export declare class CalculateFieldDeclaration extends AtomicFieldDeclaration {
26
27
  elementType: string;
28
+ outputsValue(): boolean;
27
29
  typecheckExprValue(expr: ExprValue): void;
28
30
  executesInOutputSpace(): boolean;
29
31
  }
30
32
  export declare class AggregateFieldDeclaration extends AtomicFieldDeclaration {
31
33
  elementType: string;
34
+ outputsValue(): boolean;
32
35
  typecheckExprValue(expr: ExprValue): void;
33
36
  }
34
37
  export declare class GroupByFieldDeclaration extends AtomicFieldDeclaration {
35
38
  elementType: string;
39
+ outputsValue(): boolean;
36
40
  typecheckExprValue(expr: ExprValue): void;
37
41
  }
38
42
  export declare class ProjectFieldDeclaration extends AtomicFieldDeclaration {
39
43
  elementType: string;
44
+ outputsValue(): boolean;
40
45
  typecheckExprValue(expr: ExprValue): void;
41
46
  }
42
47
  export declare class DeclareFieldDeclaration extends AtomicFieldDeclaration {
@@ -45,6 +45,7 @@ const malloy_element_1 = require("../types/malloy-element");
45
45
  const typecheck_utils_1 = require("./typecheck_utils");
46
46
  const space_field_1 = require("../types/space-field");
47
47
  const def_space_1 = require("../field-space/def-space");
48
+ const boolean_value_1 = require("../expressions/boolean-value");
48
49
  class AtomicFieldDeclaration extends malloy_element_1.MalloyElement {
49
50
  constructor(expr, defineName, exprSrc) {
50
51
  super({ expr: expr });
@@ -71,6 +72,10 @@ class AtomicFieldDeclaration extends malloy_element_1.MalloyElement {
71
72
  executesInOutputSpace() {
72
73
  return false;
73
74
  }
75
+ // The declaration is a result column of a query
76
+ outputsValue() {
77
+ return false;
78
+ }
74
79
  queryFieldDef(exprFS, exprName) {
75
80
  var _a;
76
81
  let exprValue;
@@ -105,27 +110,16 @@ class AtomicFieldDeclaration extends malloy_element_1.MalloyElement {
105
110
  }
106
111
  if ((0, malloy_types_1.isAtomicFieldType)(exprValue.type) && exprValue.type !== 'error') {
107
112
  this.typecheckExprValue(exprValue);
113
+ if (this.outputsValue() &&
114
+ (0, boolean_value_1.booleanValueError)(exprFS, exprValue, 'as a result column', this.expr, (0, boolean_value_1.resultColumnHint)((_a = this.exprSrc) !== null && _a !== void 0 ? _a : '…'))) {
115
+ return { name: exprName, type: 'error' };
116
+ }
108
117
  const ret = (0, malloy_types_1.mkFieldDef)(TDU.atomicDef(exprValue), exprName);
109
118
  if (malloy_types_1.TD.isTemporal(ret) && (0, granular_result_1.isGranularResult)(exprValue)) {
110
119
  ret.timeframe = exprValue.timeframe;
111
120
  }
112
121
  ret.location = this.location;
113
- if (exprValue.type === 'boolean' &&
114
- ((_a = exprFS.dialectObj()) === null || _a === void 0 ? void 0 : _a.booleanType) === 'none') {
115
- // when generating a boolean field on a database without boolean support
116
- // map it to integers
117
- ret.e = {
118
- node: 'case',
119
- kids: {
120
- caseWhen: [exprValue.value],
121
- caseThen: [{ node: 'numberLiteral', literal: '1' }],
122
- caseElse: { node: 'numberLiteral', literal: '0' },
123
- },
124
- };
125
- }
126
- else {
127
- ret.e = exprValue.value;
128
- }
122
+ ret.e = exprValue.value;
129
123
  ret.drillExpression = this.expr.drillExpression();
130
124
  ret.refSummary = exprValue.refSummary;
131
125
  ret.ungroupings = exprValue.ungroupings;
@@ -163,6 +157,9 @@ class CalculateFieldDeclaration extends AtomicFieldDeclaration {
163
157
  super(...arguments);
164
158
  this.elementType = 'calculateFieldDeclaration';
165
159
  }
160
+ outputsValue() {
161
+ return true;
162
+ }
166
163
  typecheckExprValue(expr) {
167
164
  (0, typecheck_utils_1.typecheckCalculate)(expr, this);
168
165
  }
@@ -176,6 +173,9 @@ class AggregateFieldDeclaration extends AtomicFieldDeclaration {
176
173
  super(...arguments);
177
174
  this.elementType = 'aggregateFieldDeclaration';
178
175
  }
176
+ outputsValue() {
177
+ return true;
178
+ }
179
179
  typecheckExprValue(expr) {
180
180
  (0, typecheck_utils_1.typecheckAggregate)(expr, this);
181
181
  }
@@ -186,6 +186,9 @@ class GroupByFieldDeclaration extends AtomicFieldDeclaration {
186
186
  super(...arguments);
187
187
  this.elementType = 'groupByFieldDeclaration';
188
188
  }
189
+ outputsValue() {
190
+ return true;
191
+ }
189
192
  typecheckExprValue(expr) {
190
193
  (0, typecheck_utils_1.typecheckGroupBy)(expr, this);
191
194
  }
@@ -196,6 +199,9 @@ class ProjectFieldDeclaration extends AtomicFieldDeclaration {
196
199
  super(...arguments);
197
200
  this.elementType = 'projectFieldDeclaration';
198
201
  }
202
+ outputsValue() {
203
+ return true;
204
+ }
199
205
  typecheckExprValue(expr) {
200
206
  (0, typecheck_utils_1.typecheckProject)(expr, this);
201
207
  }
@@ -25,6 +25,7 @@ export declare abstract class FieldReference extends ListOf<FieldName> implement
25
25
  get sourceString(): string | undefined;
26
26
  get nameString(): string;
27
27
  abstract typecheck(type: TypeDesc): void;
28
+ outputsValue(): boolean;
28
29
  getField(fs: FieldSpace): LookupResult;
29
30
  }
30
31
  export declare class AcceptExceptFieldReference extends FieldReference {
@@ -61,27 +62,32 @@ export declare class DrillFieldReference extends FieldReference {
61
62
  }
62
63
  export declare class CalculateFieldReference extends FieldReference {
63
64
  elementType: string;
65
+ outputsValue(): boolean;
64
66
  protected autoRenameDeclarationCtor(): typeof CalculateFieldDeclaration;
65
67
  typecheck(type: TypeDesc): void;
66
68
  }
67
69
  export declare class IndexFieldReference extends FieldReference {
68
70
  elementType: string;
71
+ outputsValue(): boolean;
69
72
  typecheck(type: TypeDesc): void;
70
73
  static indexOutputName(path: string[]): string;
71
74
  get outputName(): string;
72
75
  }
73
76
  export declare class AggregateFieldReference extends FieldReference {
74
77
  elementType: string;
78
+ outputsValue(): boolean;
75
79
  protected autoRenameDeclarationCtor(): typeof AggregateFieldDeclaration;
76
80
  typecheck(type: TypeDesc): void;
77
81
  }
78
82
  export declare class GroupByFieldReference extends FieldReference {
79
83
  elementType: string;
84
+ outputsValue(): boolean;
80
85
  protected autoRenameDeclarationCtor(): typeof GroupByFieldDeclaration;
81
86
  typecheck(type: TypeDesc): void;
82
87
  }
83
88
  export declare class ProjectFieldReference extends FieldReference {
84
89
  elementType: string;
90
+ outputsValue(): boolean;
85
91
  protected autoRenameDeclarationCtor(): typeof ProjectFieldDeclaration;
86
92
  typecheck(type: TypeDesc): void;
87
93
  }
@@ -10,6 +10,7 @@ const definition_list_1 = require("../types/definition-list");
10
10
  const field_space_1 = require("../types/field-space");
11
11
  const malloy_element_1 = require("../types/malloy-element");
12
12
  const expr_id_reference_1 = require("../expressions/expr-id-reference");
13
+ const boolean_value_1 = require("../expressions/boolean-value");
13
14
  const field_declaration_1 = require("./field-declaration");
14
15
  const typecheck_utils_1 = require("./typecheck_utils");
15
16
  class FieldReference extends malloy_element_1.ListOf {
@@ -108,11 +109,18 @@ class FieldReference extends malloy_element_1.ListOf {
108
109
  get nameString() {
109
110
  return this.list[this.list.length - 1].refString;
110
111
  }
112
+ // The reference is a result column of a query
113
+ outputsValue() {
114
+ return false;
115
+ }
111
116
  getField(fs) {
112
117
  const result = fs.lookup(this.list);
113
118
  if (result.found) {
114
119
  const actualType = result.found.typeDesc();
115
120
  this.typecheck(actualType);
121
+ if (this.outputsValue()) {
122
+ (0, boolean_value_1.booleanValueError)(fs, actualType, 'as a result column', this, (0, boolean_value_1.resultColumnHint)(this.refString));
123
+ }
116
124
  }
117
125
  return result;
118
126
  }
@@ -210,6 +218,9 @@ class CalculateFieldReference extends FieldReference {
210
218
  super(...arguments);
211
219
  this.elementType = 'calculateFieldReference';
212
220
  }
221
+ outputsValue() {
222
+ return true;
223
+ }
213
224
  autoRenameDeclarationCtor() {
214
225
  return field_declaration_1.CalculateFieldDeclaration;
215
226
  }
@@ -223,6 +234,9 @@ class IndexFieldReference extends FieldReference {
223
234
  super(...arguments);
224
235
  this.elementType = 'indexFieldReference';
225
236
  }
237
+ outputsValue() {
238
+ return true;
239
+ }
226
240
  typecheck(type) {
227
241
  (0, typecheck_utils_1.typecheckIndex)(type, this);
228
242
  }
@@ -240,6 +254,9 @@ class AggregateFieldReference extends FieldReference {
240
254
  super(...arguments);
241
255
  this.elementType = 'aggregateFieldReference';
242
256
  }
257
+ outputsValue() {
258
+ return true;
259
+ }
243
260
  autoRenameDeclarationCtor() {
244
261
  return field_declaration_1.AggregateFieldDeclaration;
245
262
  }
@@ -253,6 +270,9 @@ class GroupByFieldReference extends FieldReference {
253
270
  super(...arguments);
254
271
  this.elementType = 'groupByFieldReference';
255
272
  }
273
+ outputsValue() {
274
+ return true;
275
+ }
256
276
  autoRenameDeclarationCtor() {
257
277
  return field_declaration_1.GroupByFieldDeclaration;
258
278
  }
@@ -266,6 +286,9 @@ class ProjectFieldReference extends FieldReference {
266
286
  super(...arguments);
267
287
  this.elementType = 'projectFieldReference';
268
288
  }
289
+ outputsValue() {
290
+ return true;
291
+ }
269
292
  autoRenameDeclarationCtor() {
270
293
  return field_declaration_1.ProjectFieldDeclaration;
271
294
  }
@@ -50,7 +50,9 @@ class FilterElement extends malloy_element_1.MalloyElement {
50
50
  filterCondition(fs) {
51
51
  const exprVal = this.expr.getExpression(fs);
52
52
  if (exprVal.type !== 'boolean') {
53
- this.expr.logError('non-boolean-filter', 'Filter expression must have boolean value');
53
+ if (exprVal.type !== 'error') {
54
+ this.expr.logError('non-boolean-filter', 'Filter expression must have boolean value');
55
+ }
54
56
  return {
55
57
  node: 'filterCondition',
56
58
  code: this.exprSrc,
@@ -161,9 +161,22 @@ class ImportStatement extends malloy_element_1.ListOf {
161
161
  // persistent ones. A `#@ -persist` wrapper that adds a join is
162
162
  // not a table, but it can be the only way to reach the tables
163
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.
164
168
  if ((0, malloy_types_1.isSourceDef)(importMe) && (0, malloy_types_1.isPersistableSourceDef)(importMe)) {
165
169
  for (const found of (0, persist_utils_1.walkPersistentDependencies)([importMe], importedModel)) {
166
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
+ }
167
180
  }
168
181
  }
169
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,7 +153,23 @@ 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;
@@ -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);
@@ -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()),