@malloydata/malloy 0.0.435 → 0.0.437

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 (81) 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 +10 -14
  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/expr-props.js +1 -1
  47. package/dist/lang/ast/expressions/pick-when.js +7 -0
  48. package/dist/lang/ast/expressions/time-literal.js +6 -1
  49. package/dist/lang/ast/field-space/index-field-space.js +13 -1
  50. package/dist/lang/ast/field-space/query-spaces.js +11 -0
  51. package/dist/lang/ast/query-builders/reduce-builder.js +1 -1
  52. package/dist/lang/ast/query-items/field-declaration.d.ts +5 -0
  53. package/dist/lang/ast/query-items/field-declaration.js +22 -16
  54. package/dist/lang/ast/query-items/field-references.d.ts +6 -0
  55. package/dist/lang/ast/query-items/field-references.js +23 -0
  56. package/dist/lang/ast/query-properties/drill.js +7 -1
  57. package/dist/lang/ast/query-properties/filters.js +3 -1
  58. package/dist/lang/ast/statements/import-statement.js +13 -0
  59. package/dist/lang/ast/types/expression-def.js +17 -2
  60. package/dist/lang/ast/types/malloy-element.d.ts +24 -1
  61. package/dist/lang/ast/types/malloy-element.js +63 -26
  62. package/dist/lang/ast/view-elements/reference-view.js +4 -0
  63. package/dist/lang/ast/view-elements/refine-utils.js +1 -0
  64. package/dist/lang/composite-source-utils.js +10 -17
  65. package/dist/lang/lib/Malloy/MalloyLexer.js +976 -978
  66. package/dist/lang/parse-log.d.ts +12 -0
  67. package/dist/lang/parse-log.js +2 -0
  68. package/dist/lang/test/test-translator.d.ts +2 -1
  69. package/dist/lang/test/test-translator.js +37 -3
  70. package/dist/model/expression_compiler.js +8 -7
  71. package/dist/model/filter_compilers.js +13 -12
  72. package/dist/model/given_binding.d.ts +8 -0
  73. package/dist/model/given_binding.js +13 -0
  74. package/dist/model/index.d.ts +1 -1
  75. package/dist/model/index.js +2 -1
  76. package/dist/model/query_query.js +11 -2
  77. package/dist/model/sql_compiled.d.ts +19 -20
  78. package/dist/model/sql_compiled.js +29 -17
  79. package/dist/version.d.ts +1 -1
  80. package/dist/version.js +1 -1
  81. 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: {
@@ -67,7 +67,7 @@ class ExprProps extends expression_def_1.ExpressionDef {
67
67
  for (const where of wheres) {
68
68
  const testList = where.getFilterList(fs);
69
69
  if (testList.find(cond => (0, malloy_types_1.expressionIsCalculation)(cond.expressionType))) {
70
- where.logError('aggregate-filter-expression-not-scalar', 'Cannot filter an expresion with an aggregate or analytical computation');
70
+ where.logError('aggregate-filter-expression-not-scalar', 'Cannot filter an expression with an aggregate or analytical computation');
71
71
  return expr;
72
72
  }
73
73
  filterList.push(...testList);
@@ -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,
@@ -53,7 +53,12 @@ class TimeLiteral extends expression_def_1.ExpressionDef {
53
53
  }
54
54
  }
55
55
  makeLiteral(fs, val, typ, units) {
56
- return dialect_1.Dialect.makeTimeLiteralNode(fs.dialectObj(), val, this.timeZone, units, typ);
56
+ let timeZone = this.timeZone;
57
+ if (timeZone !== undefined && !dialect_1.Dialect.isSafeTimezoneName(timeZone)) {
58
+ this.logError('invalid-timezone', { timezone: timeZone });
59
+ timeZone = undefined;
60
+ }
61
+ return dialect_1.Dialect.makeTimeLiteralNode(fs.dialectObj(), val, timeZone, units, typ);
57
62
  }
58
63
  makeValue(fs, val, dataType) {
59
64
  const value = this.makeLiteral(fs, val, dataType, this.units);
@@ -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
  }
@@ -89,6 +89,7 @@ class Drill extends filters_1.Filter {
89
89
  let previousName = viewName;
90
90
  let fieldDef = undefined;
91
91
  let compareField = undefined;
92
+ let compareUsage = undefined;
92
93
  const requiredDimensions = [];
93
94
  const pathSoFar = [viewName.name];
94
95
  for (const name of path) {
@@ -189,6 +190,9 @@ class Drill extends filters_1.Filter {
189
190
  if (theFieldDef === undefined || (0, malloy_types_1.isAtomic)(theFieldDef)) {
190
191
  fieldDef = theFieldDef;
191
192
  compareField = { node: 'field', path: field.path };
193
+ // theFieldDef's refSummary is relative to the struct it lives in;
194
+ // use the full path, as `where:` does, so joins along it activate.
195
+ compareUsage = [{ path: field.path, at: reference.location }];
192
196
  }
193
197
  else {
194
198
  name.logError('drill-field-reference-not-field', 'Final element of drill path must be a field');
@@ -268,7 +272,9 @@ class Drill extends filters_1.Filter {
268
272
  ...fieldDef,
269
273
  evalSpace: 'output',
270
274
  expressionType: (_b = fieldDef.expressionType) !== null && _b !== void 0 ? _b : 'scalar',
271
- refSummary: fieldDef.refSummary,
275
+ refSummary: compareUsage
276
+ ? (0, malloy_types_1.mkRefSummary)({ fieldUsage: compareUsage })
277
+ : fieldDef.refSummary,
272
278
  });
273
279
  filter.has({ drillField });
274
280
  const fExpression = new expr_compare_1.ExprCompare(drillField, '=', value);
@@ -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;