@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
@@ -64,6 +64,17 @@ type MessageParameterTypes = {
64
64
  'nesting-unsupported': {
65
65
  dialect: string;
66
66
  };
67
+ 'boolean-value-unsupported': {
68
+ dialect: string;
69
+ where: string;
70
+ hint: string;
71
+ };
72
+ 'wildcard-omits-boolean': {
73
+ dialect: string;
74
+ wildcard: string;
75
+ name: string;
76
+ hint: string;
77
+ };
67
78
  'nested-multi-stage-unsupported': {
68
79
  dialect: string;
69
80
  };
@@ -297,6 +308,7 @@ type MessageParameterTypes = {
297
308
  'export-name-not-exportable': string;
298
309
  'duplicate-export-name': string;
299
310
  'unsatisfied-given-in-query': string;
311
+ 'unsatisfied-given-in-persist-source': string;
300
312
  'given-alias-collision': string;
301
313
  'failed-to-compute-output-schema': string;
302
314
  'invalid-timeframe-for-time-offset': string;
@@ -56,6 +56,8 @@ exports.MESSAGE_FORMATTERS = {
56
56
  }),
57
57
  'experimental-dialect-not-enabled': e => `Requires compiler flag '##! experimental.dialect.${e.dialect}'`,
58
58
  'nesting-unsupported': e => `'${e.dialect}' does not support nested queries`,
59
+ 'boolean-value-unsupported': e => `'${e.dialect}' does not support a boolean ${e.where}; ${e.hint}`,
60
+ 'wildcard-omits-boolean': e => `'${e.name}' is left out of '${e.wildcard}': '${e.dialect}' does not support a boolean as a result column; ${e.hint}`,
59
61
  'nested-multi-stage-unsupported': e => `'${e.dialect}' does not support a multi-stage pipeline ('->') in a nested query`,
60
62
  'nested-projection-limit-unsupported': e => `'${e.dialect}' does not support 'limit:' on a nested 'select:'`,
61
63
  'pick-missing-else': "pick incomplete, missing 'else'",
@@ -29,6 +29,7 @@ export declare const aTableDef: TableSourceDef;
29
29
  export declare const db2TableDef: TableSourceDef;
30
30
  export declare const bqTableDef: SourceDef;
31
31
  export declare const pgTableDef: SourceDef;
32
+ export declare const mssqlTableDef: SourceDef;
32
33
  /**
33
34
  * A TestTranlator never actually talks to connection, instead uses
34
35
  * some mocked schema definitions.
@@ -69,7 +70,7 @@ export interface TestTranslatorOptions {
69
70
  }
70
71
  /**
71
72
  * Every connection name a test mentions resolves to a dialect, as it would
72
- * through a host's connection lookup: the four listed below to theirs, and
73
+ * through a host's connection lookup: the five listed below to theirs, and
73
74
  * any other name to `TEST_DIALECT`.
74
75
  */
75
76
  declare class TestConnectionDialectZone extends Zone<string> {
@@ -5,7 +5,7 @@
5
5
  * SPDX-License-Identifier: MIT
6
6
  */
7
7
  Object.defineProperty(exports, "__esModule", { value: true });
8
- exports.BetaExpression = exports.TestTranslator = exports.TestChildTranslator = exports.mockSchema = exports.pgTableDef = exports.bqTableDef = exports.db2TableDef = exports.aTableDef = exports.TEST_DIALECT = void 0;
8
+ exports.BetaExpression = exports.TestTranslator = exports.TestChildTranslator = exports.mockSchema = exports.mssqlTableDef = exports.pgTableDef = exports.bqTableDef = exports.db2TableDef = exports.aTableDef = exports.TEST_DIALECT = void 0;
9
9
  exports.pretty = pretty;
10
10
  exports.humanify = humanify;
11
11
  exports.getExplore = getExplore;
@@ -194,6 +194,16 @@ exports.pgTableDef = {
194
194
  connection: '_pg_',
195
195
  fields: baseFields,
196
196
  };
197
+ // SQL Server has no boolean values (booleanType 'none'), so no column of
198
+ // its table is a boolean
199
+ exports.mssqlTableDef = {
200
+ type: 'table',
201
+ name: 'aTable',
202
+ dialect: 'sqlserver',
203
+ tablePath: 'aTable',
204
+ connection: '_mssql_',
205
+ fields: baseFields.filter(f => f.name !== 'abool'),
206
+ };
197
207
  /**
198
208
  * A TestTranlator never actually talks to connection, instead uses
199
209
  * some mocked schema definitions.
@@ -203,6 +213,7 @@ exports.mockSchema = [
203
213
  exports.db2TableDef,
204
214
  exports.bqTableDef,
205
215
  exports.pgTableDef,
216
+ exports.mssqlTableDef,
206
217
  {
207
218
  type: 'table',
208
219
  name: 'carriers',
@@ -339,7 +350,7 @@ exports.TestChildTranslator = TestChildTranslator;
339
350
  const testURI = 'internal://test/langtests/root.malloy';
340
351
  /**
341
352
  * Every connection name a test mentions resolves to a dialect, as it would
342
- * through a host's connection lookup: the four listed below to theirs, and
353
+ * through a host's connection lookup: the five listed below to theirs, and
343
354
  * any other name to `TEST_DIALECT`.
344
355
  */
345
356
  class TestConnectionDialectZone extends zone_1.Zone {
@@ -357,7 +368,7 @@ class TestTranslator extends parse_malloy_1.MalloyTranslator {
357
368
  this.testSrc = testSrc;
358
369
  this.connectionDialectZone = new TestConnectionDialectZone();
359
370
  /*
360
- * There are four connections:
371
+ * There are five connections:
361
372
  * _db_ - duckdb dialect, with the following tables ...
362
373
  * aTable, malloytest.carriers, malloytest.flights, malloytest.airports
363
374
  * _db2_ - duckdb dialect, with one table, aTable. A second connection
@@ -366,6 +377,8 @@ class TestTranslator extends parse_malloy_1.MalloyTranslator {
366
377
  * aTable
367
378
  * _pg_ - postgres dialect, with one table
368
379
  * aTable
380
+ * _mssql_ - sqlserver dialect, which has no boolean values, with one
381
+ * table, aTable
369
382
  *
370
383
  * The "aTable" table is a mocked table with one column of each type.
371
384
  * The _bq_ version does not have the timestamptz column, and when
@@ -394,6 +407,7 @@ class TestTranslator extends parse_malloy_1.MalloyTranslator {
394
407
  _db2_: { type: 'connection', name: '_db2_' },
395
408
  _bq_: { type: 'connection', name: '_bq_' },
396
409
  _pg_: { type: 'connection', name: '_pg_' },
410
+ _mssql_: { type: 'connection', name: '_mssql_' },
397
411
  a: { ...exports.aTableDef, primaryKey: 'astr', name: 'a' },
398
412
  b: { ...exports.aTableDef, primaryKey: 'astr', name: 'b' },
399
413
  bq_a: { ...exports.bqTableDef, primaryKey: 'astr', name: 'bq_a' },
@@ -451,6 +465,7 @@ class TestTranslator extends parse_malloy_1.MalloyTranslator {
451
465
  this.connectionDialectZone.define('_db2_', exports.TEST_DIALECT);
452
466
  this.connectionDialectZone.define('_bq_', 'standardsql');
453
467
  this.connectionDialectZone.define('_pg_', 'postgres');
468
+ this.connectionDialectZone.define('_mssql_', 'sqlserver');
454
469
  for (const flag of (_e = options.compilerFlags) !== null && _e !== void 0 ? _e : []) {
455
470
  this.compilerFlagSrc.push(`##! ${flag}\n`);
456
471
  }
@@ -658,10 +673,29 @@ function makeModelFunc(options) {
658
673
  const { prefix, wrap, ...ttOptions } = options;
659
674
  return {
660
675
  ...ms,
676
+ // A marked location is where the mark sits in the whole source, after
677
+ // the prefix. A wrap could put the code anywhere, so marks stay as
678
+ // they are.
679
+ locations: wrap ? ms.locations : ms.locations.map(l => after(prefix, l)),
661
680
  translator: new TestTranslator((prefix !== null && prefix !== void 0 ? prefix : '') + (wrap ? wrap(ms.code) : ms.code), ttOptions),
662
681
  };
663
682
  };
664
683
  }
684
+ function after(prefix, l) {
685
+ if (!prefix)
686
+ return l;
687
+ const lines = prefix.split('\n');
688
+ const dLine = lines.length - 1;
689
+ const dChar = lines[dLine].length;
690
+ const shift = (p) => ({
691
+ line: p.line + dLine,
692
+ character: p.line === 0 ? p.character + dChar : p.character,
693
+ });
694
+ return {
695
+ ...l,
696
+ range: { start: shift(l.range.start), end: shift(l.range.end) },
697
+ };
698
+ }
665
699
  function makeExprFunc(model, sourceName = 'ab', options = {}) {
666
700
  return function expr(unmarked, ...marked) {
667
701
  const ms = markSource(unmarked, ...marked);
@@ -109,7 +109,7 @@ function compileExpr(resultSet, context, expr, state = new utils_1.GenerateState
109
109
  return expr;
110
110
  }
111
111
  const sql = (() => {
112
- var _a;
112
+ var _a, _b;
113
113
  switch (expr.node) {
114
114
  case 'field':
115
115
  return generateFieldFragment(resultSet, context, expr, state);
@@ -176,7 +176,7 @@ function compileExpr(resultSet, context, expr, state = new utils_1.GenerateState
176
176
  // Malloy inequality comparisons always return a boolean
177
177
  case '!=': {
178
178
  const notEqual = `${expr.kids.left.sql}!=${expr.kids.right.sql}`;
179
- return `COALESCE(${notEqual},true)`;
179
+ return context.dialect.sqlNullIsTrue(notEqual);
180
180
  }
181
181
  case 'and':
182
182
  case 'or':
@@ -192,13 +192,13 @@ function compileExpr(resultSet, context, expr, state = new utils_1.GenerateState
192
192
  // null binding collapses to empty-set semantics — not the SQL
193
193
  // `IN (NULL)` shape, which has confusing NULL-membership rules.
194
194
  if (bound.node === 'null') {
195
- return expr.not ? 'TRUE' : 'FALSE';
195
+ return context.dialect.sqlBoolean(expr.not);
196
196
  }
197
197
  if (bound.node !== 'arrayLiteral') {
198
198
  throw new Error(`Internal compiler error: 'inGiven' bound to '${bound.node}', expected 'arrayLiteral'. The translator should have rejected a non-array given here.`);
199
199
  }
200
200
  if (bound.kids.values.length === 0) {
201
- return expr.not ? 'TRUE' : 'FALSE';
201
+ return context.dialect.sqlBoolean(expr.not);
202
202
  }
203
203
  const elemSqls = bound.kids.values.map(v => exprToSQL(resultSet, context, v, state));
204
204
  const verb = expr.not ? 'NOT IN' : 'IN';
@@ -210,13 +210,14 @@ function compileExpr(resultSet, context, expr, state = new utils_1.GenerateState
210
210
  const compare = expr.kids.right.node === 'stringLiteral'
211
211
  ? context.dialect.sqlLike(likeIt, (_a = expr.kids.left.sql) !== null && _a !== void 0 ? _a : '', expr.kids.right.literal)
212
212
  : `${expr.kids.left.sql} ${likeIt} ${expr.kids.right.sql}`;
213
- return expr.node === 'like' ? compare : `COALESCE(${compare},true)`;
213
+ return expr.node === 'like'
214
+ ? compare
215
+ : context.dialect.sqlNullIsTrue(compare);
214
216
  }
215
217
  case '()':
216
218
  return `(${expr.e.sql})`;
217
219
  case 'not':
218
- // Malloy not operator always returns a boolean
219
- return `COALESCE(NOT ${expr.e.sql},TRUE)`;
220
+ return context.dialect.sqlNot((_b = expr.e.sql) !== null && _b !== void 0 ? _b : '');
220
221
  case 'unary-':
221
222
  return `-${expr.e.sql}`;
222
223
  case 'is-null':
@@ -34,13 +34,13 @@ function unlike(disLiked, x) {
34
34
  // The `none` filter ("always false") compiles to a constant, independent of
35
35
  // column type or value; `not none` is "always true". Shared by the top-level
36
36
  // guard and every per-type compiler so the constant lives in one place.
37
- function noneToSQL(not) {
38
- return not ? 'true' : 'false';
37
+ function noneToSQL(not, d) {
38
+ return d.sqlBoolean(not === true);
39
39
  }
40
40
  exports.FilterCompilers = {
41
41
  compile(t, c, x, d, qi = {}) {
42
42
  if (c === null) {
43
- return 'true';
43
+ return d.sqlBoolean(true);
44
44
  }
45
45
  // `none` is the always-false filter (companion to the empty/`null` filter,
46
46
  // which is always-true above). It compiles to a constant independent of
@@ -48,7 +48,7 @@ exports.FilterCompilers = {
48
48
  // also the primary path for boolean, whose compiler has no switch and
49
49
  // would otherwise route `none` through the null/false truth-table.
50
50
  if (c.operator === 'none') {
51
- return noneToSQL(c.not);
51
+ return noneToSQL(c.not, d);
52
52
  }
53
53
  if (t === 'string' && (0, malloy_filter_1.isStringFilter)(c)) {
54
54
  return exports.FilterCompilers.stringCompile(c, x, d);
@@ -102,7 +102,7 @@ exports.FilterCompilers = {
102
102
  case 'null':
103
103
  return nc.not ? `${x} IS NOT NULL` : `${x} IS NULL`;
104
104
  case 'none':
105
- return noneToSQL(nc.not);
105
+ return noneToSQL(nc.not, d);
106
106
  case '()': {
107
107
  const wrapped = '(' + exports.FilterCompilers.numberCompile(nc.expr, x, d) + ')';
108
108
  return nc.not ? `NOT ${wrapped}` : wrapped;
@@ -135,7 +135,7 @@ exports.FilterCompilers = {
135
135
  // Normally intercepted by compile()'s top-level guard; handled here too so
136
136
  // booleanCompile is self-safe (this if-chain has no default, and `none`
137
137
  // must not fall through to the null/false truth-table below).
138
- return noneToSQL(bc.not);
138
+ return noneToSQL(bc.not, d);
139
139
  }
140
140
  else if (bc.operator === '=true') {
141
141
  return bc.not ? `NOT ${px}` : x;
@@ -172,7 +172,7 @@ exports.FilterCompilers = {
172
172
  case 'null':
173
173
  return sc.not ? `${x} IS NOT NULL` : `${x} IS NULL`;
174
174
  case 'none':
175
- return noneToSQL(sc.not);
175
+ return noneToSQL(sc.not, d);
176
176
  case 'empty':
177
177
  return sc.not ? `COALESCE(${x},'') != ''` : `COALESCE(${x},'') = ''`;
178
178
  case '=': {
@@ -267,7 +267,7 @@ exports.FilterCompilers = {
267
267
  }
268
268
  }
269
269
  if ((includeEmpty && excludeEmpty) || (includeNull && excludeNull)) {
270
- return 'false';
270
+ return d.sqlBoolean(false);
271
271
  }
272
272
  let includeSQL = '';
273
273
  if (includes.length > 0 || includeNull || includeEmpty) {
@@ -298,7 +298,7 @@ exports.FilterCompilers = {
298
298
  ? `(${includeSQL}) AND (${excludeSQL})`
299
299
  : includeSQL;
300
300
  }
301
- return excludeSQL !== '' ? excludeSQL : 'true';
301
+ return excludeSQL !== '' ? excludeSQL : d.sqlBoolean(true);
302
302
  }
303
303
  }
304
304
  },
@@ -331,7 +331,7 @@ class TemporalFilterCompiler {
331
331
  if (this.timetype !== 'date') {
332
332
  return timeSQL;
333
333
  }
334
- return this.d.sqlCast({}, {
334
+ const toDate = {
335
335
  node: 'cast',
336
336
  e: {
337
337
  node: 'genericSQLExpr',
@@ -342,7 +342,8 @@ class TemporalFilterCompiler {
342
342
  srcType: { type: 'timestamp' },
343
343
  dstType: { type: 'date' },
344
344
  safe: false,
345
- });
345
+ };
346
+ return this.d.sqlTimeCast(this.qi, toDate);
346
347
  }
347
348
  compile(tc) {
348
349
  const x = this.expr;
@@ -421,7 +422,7 @@ class TemporalFilterCompiler {
421
422
  case 'null':
422
423
  return tc.not ? `${x} IS NOT NULL` : `${x} IS NULL`;
423
424
  case 'none':
424
- return noneToSQL(tc.not);
425
+ return noneToSQL(tc.not, this.d);
425
426
  case '()': {
426
427
  const wrapped = '(' + this.compile(tc.expr) + ')';
427
428
  return tc.not ? `NOT ${wrapped}` : wrapped;
@@ -27,3 +27,11 @@ export declare function resolveSuppliedGivens(supplied: Record<string, GivenValu
27
27
  * `resolveGiven`), which needs no ordering.
28
28
  */
29
29
  export declare function evaluateInlineGivens(bound: Map<GivenID, Expr>, modelDef: ModelDef | undefined): Map<GivenID, Expr>;
30
+ /**
31
+ * Binds the givens a compile runs under: the supplied values, then each
32
+ * `inline` given's default folded against them. Queries and persistence builds
33
+ * both bind here, so the same values always compile to the same SQL and a
34
+ * persisted table's key is the key a query recomputes. Undefined when nothing
35
+ * binds, which the compiler reads as "no givens".
36
+ */
37
+ export declare function bindGivens(supplied: Record<string, GivenValue> | undefined, modelDef: ModelDef | undefined): Map<GivenID, Expr> | undefined;
@@ -7,6 +7,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
7
7
  exports.lookupGivenValue = lookupGivenValue;
8
8
  exports.resolveSuppliedGivens = resolveSuppliedGivens;
9
9
  exports.evaluateInlineGivens = evaluateInlineGivens;
10
+ exports.bindGivens = bindGivens;
10
11
  const luxon_1 = require("luxon");
11
12
  const closest_match_1 = require("../util/closest_match");
12
13
  const inline_expr_1 = require("./inline_expr");
@@ -106,6 +107,18 @@ function evaluateInlineGivens(bound, modelDef) {
106
107
  }
107
108
  return bound;
108
109
  }
110
+ /**
111
+ * Binds the givens a compile runs under: the supplied values, then each
112
+ * `inline` given's default folded against them. Queries and persistence builds
113
+ * both bind here, so the same values always compile to the same SQL and a
114
+ * persisted table's key is the key a query recomputes. Undefined when nothing
115
+ * binds, which the compiler reads as "no givens".
116
+ */
117
+ function bindGivens(supplied, modelDef) {
118
+ const bound = resolveSuppliedGivens(supplied, modelDef);
119
+ evaluateInlineGivens(bound, modelDef);
120
+ return bound.size > 0 ? bound : undefined;
121
+ }
109
122
  function valueToExpr(path, type, value) {
110
123
  var _a, _b;
111
124
  if (value === null) {
@@ -7,7 +7,7 @@ export { getResultStructDefForQuery, getResultStructDefForView, } from './query_
7
7
  export { indent, composeSQLExpr, makeDigest, mkModelDef, mkModelID, pathToKey, typeDefToString, } from './utils';
8
8
  export { getModelAnnotations } from './annotation_utils';
9
9
  export { constantExprToSQL } from './constant_expression_compiler';
10
- export { getCompiledSQL, buildIdOptions } from './sql_compiled';
10
+ export { getCompiledSQL, buildIdOptions, givenUsageOfRelation, } from './sql_compiled';
11
11
  export type { BuildIdOptions } from './sql_compiled';
12
12
  export { MalloyCompileError } from './malloy_compile_error';
13
13
  export { mkSourceID, mkBuildID, mkQuerySourceDef, mkSQLSourceDef, mkTableSourceDef, resolveSourceID, resolveSourceRef, sourceNamespaceReference, registerSource, hasSourceRegistryEntry, } from './source_def_utils';
@@ -18,7 +18,7 @@ var __exportStar = (this && this.__exportStar) || function(m, exports) {
18
18
  for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
19
19
  };
20
20
  Object.defineProperty(exports, "__esModule", { value: true });
21
- exports.hasSourceRegistryEntry = exports.registerSource = exports.sourceNamespaceReference = exports.resolveSourceRef = exports.resolveSourceID = exports.mkTableSourceDef = exports.mkSQLSourceDef = exports.mkQuerySourceDef = exports.mkBuildID = exports.mkSourceID = exports.MalloyCompileError = exports.buildIdOptions = exports.getCompiledSQL = exports.constantExprToSQL = exports.getModelAnnotations = exports.typeDefToString = exports.pathToKey = exports.mkModelID = exports.mkModelDef = exports.makeDigest = exports.composeSQLExpr = exports.indent = exports.getResultStructDefForView = exports.getResultStructDefForQuery = exports.QueryModel = exports.QueryQuery = exports.QueryStruct = exports.QueryField = void 0;
21
+ exports.hasSourceRegistryEntry = exports.registerSource = exports.sourceNamespaceReference = exports.resolveSourceRef = exports.resolveSourceID = exports.mkTableSourceDef = exports.mkSQLSourceDef = exports.mkQuerySourceDef = exports.mkBuildID = exports.mkSourceID = exports.MalloyCompileError = exports.givenUsageOfRelation = exports.buildIdOptions = exports.getCompiledSQL = exports.constantExprToSQL = exports.getModelAnnotations = exports.typeDefToString = exports.pathToKey = exports.mkModelID = exports.mkModelDef = exports.makeDigest = exports.composeSQLExpr = exports.indent = exports.getResultStructDefForView = exports.getResultStructDefForQuery = exports.QueryModel = exports.QueryQuery = exports.QueryStruct = exports.QueryField = void 0;
22
22
  __exportStar(require("./malloy_types"), exports);
23
23
  const query_node_1 = require("./query_node");
24
24
  Object.defineProperty(exports, "QueryField", { enumerable: true, get: function () { return query_node_1.QueryField; } });
@@ -52,6 +52,7 @@ Object.defineProperty(exports, "constantExprToSQL", { enumerable: true, get: fun
52
52
  var sql_compiled_1 = require("./sql_compiled");
53
53
  Object.defineProperty(exports, "getCompiledSQL", { enumerable: true, get: function () { return sql_compiled_1.getCompiledSQL; } });
54
54
  Object.defineProperty(exports, "buildIdOptions", { enumerable: true, get: function () { return sql_compiled_1.buildIdOptions; } });
55
+ Object.defineProperty(exports, "givenUsageOfRelation", { enumerable: true, get: function () { return sql_compiled_1.givenUsageOfRelation; } });
55
56
  var malloy_compile_error_1 = require("./malloy_compile_error");
56
57
  Object.defineProperty(exports, "MalloyCompileError", { enumerable: true, get: function () { return malloy_compile_error_1.MalloyCompileError; } });
57
58
  var source_def_utils_1 = require("./source_def_utils");
@@ -906,11 +906,20 @@ class QueryQuery extends query_node_1.QueryField {
906
906
  }
907
907
  // What a join contributes to the SELECT of a filtered join's subquery: every
908
908
  // column named outside the subquery, and the generated __distinct_key when
909
- // the join needed one.
909
+ // the join needed one. A field is packed under the column it reads, which
910
+ // is how references outside the subquery name it; a `rename:` leaves that
911
+ // different from the name the query knows the field by.
910
912
  joinProjectionFieldList(ji) {
911
913
  const packed = this.packedColumnsByJoin().get(ji.alias);
912
914
  const dialect = ji.queryStruct.dialect;
913
- const fieldList = (0, utils_1.getDialectFieldList)(ji.queryStruct.structDef, dialect).filter(d => packed === null || packed === void 0 ? void 0 : packed.has(d.rawName));
915
+ const fieldList = ji.queryStruct.structDef.fields
916
+ .filter(malloy_types_1.fieldIsIntrinsic)
917
+ .filter(f => packed === null || packed === void 0 ? void 0 : packed.has((0, malloy_types_1.activeName)(f)))
918
+ .map(f => ({
919
+ typeDef: f,
920
+ sqlExpression: dialect.sqlQuoteIdentifier(f.name),
921
+ rawName: f.name,
922
+ }));
914
923
  if (ji.makeUniqueKey) {
915
924
  fieldList.push({
916
925
  typeDef: { type: 'string' },
@@ -1,4 +1,4 @@
1
- import type { PrepareResultOptions, SQLSourceDef, SQLPhraseSegment, PersistableSourceDef, Query, VirtualMap } from './malloy_types';
1
+ import type { Expr, GivenID, GivenUsage, PrepareResultOptions, SQLSourceDef, SQLPhraseSegment, PersistableSourceDef, Query, VirtualMap } from './malloy_types';
2
2
  /**
3
3
  * Callback type for compiling a Query to SQL.
4
4
  * opts is optional - omit for "full SQL" (BuildID computation),
@@ -19,31 +19,23 @@ export declare function getCompiledSQL(src: SQLSourceDef, opts: PrepareResultOpt
19
19
  /** Expand SQL text and interpolations for schema discovery or execution. */
20
20
  export declare function expandSQLSegments(segments: SQLPhraseSegment[], opts: PrepareResultOptions, compileQuery: CompileQueryCallback): string;
21
21
  /**
22
- * The options a BuildID's SQL is compiled under — everything the builder and
23
- * the compiler must both know, and nothing else.
22
+ * What a BuildID's SQL is generated under: everything that changes what the
23
+ * SQL computes and that the builder and the compiler can both know.
24
24
  *
25
- * Narrow on purpose. A BuildID must not depend on what has already been built
26
- * or build order would change the key, so this type cannot express a manifest;
25
+ * No manifest. A BuildID must not depend on what has already been built, or
26
+ * build order would change the key, so this type cannot carry one;
27
27
  * `mkBuildTargets` takes it for the same reason.
28
+ *
29
+ * `virtualMap` decides which table a virtual source reads. `resolvedGivens`
30
+ * is the value of every given the SQL reads, supplied or defaulted; both sides
31
+ * produce it through `bindGivens`, so equal values give the same key and
32
+ * different values give different keys.
28
33
  */
29
34
  export interface BuildIdOptions {
30
35
  virtualMap?: VirtualMap;
36
+ resolvedGivens?: Map<GivenID, Expr>;
31
37
  }
32
- /**
33
- * Project compile options down to what a BuildID is computed under.
34
- *
35
- * `virtualMap` decides which table a virtual source reads, so it changes the
36
- * SQL and has to be in the key; the builder supplies its own.
37
- *
38
- * `resolvedGivens` is deliberately absent even though it also changes the SQL.
39
- * The builder has no way to produce one — `Runtime.getBuildTargets` takes no
40
- * givens and `PersistSource.getSQL` never resolves them — so including it makes
41
- * the two sides disagree: the compiler folds `$IS_ADMIN` to a literal while the
42
- * builder re-derives it from the declaration and emits `'admin'='admin'`. Both
43
- * sides therefore compile givens the same way, by not resolving them. The
44
- * consequence — a source whose SQL depends on a supplied given cannot be
45
- * persisted, and says so badly — is #3041.
46
- */
38
+ /** Project compile options down to what a BuildID is computed under. */
47
39
  export declare function buildIdOptions(opts: PrepareResultOptions): BuildIdOptions;
48
40
  /**
49
41
  * Ask the manifest what table backs a persistable source.
@@ -57,6 +49,13 @@ export declare function buildIdOptions(opts: PrepareResultOptions): BuildIdOptio
57
49
  * undefined when the caller should emit the source's own SQL.
58
50
  */
59
51
  export declare function persistedTableFor(source: PersistableSourceDef, opts: PrepareResultOptions, compileQuery: CompileQueryCallback): string | undefined;
52
+ /**
53
+ * The givens a persisted source's SQL reads — the relation, not the source.
54
+ * A `where:` added by `extend` is applied by the query that reads the table,
55
+ * so it is not here, and a given used only there does not split the table by
56
+ * value. Mirrors `getSourceSQL`: what that compiles, this reports on.
57
+ */
58
+ export declare function givenUsageOfRelation(source: PersistableSourceDef): GivenUsage;
60
59
  /**
61
60
  * Get the SQL for a PersistableSourceDef.
62
61
  *
@@ -8,6 +8,7 @@ exports.getCompiledSQL = getCompiledSQL;
8
8
  exports.expandSQLSegments = expandSQLSegments;
9
9
  exports.buildIdOptions = buildIdOptions;
10
10
  exports.persistedTableFor = persistedTableFor;
11
+ exports.givenUsageOfRelation = givenUsageOfRelation;
11
12
  exports.getSourceSQL = getSourceSQL;
12
13
  const malloy_types_1 = require("./malloy_types");
13
14
  const source_def_utils_1 = require("./source_def_utils");
@@ -50,23 +51,9 @@ function expandSegment(segment, opts, compileQuery) {
50
51
  // Query segment
51
52
  return expandQuery(segment, opts, compileQuery);
52
53
  }
53
- /**
54
- * Project compile options down to what a BuildID is computed under.
55
- *
56
- * `virtualMap` decides which table a virtual source reads, so it changes the
57
- * SQL and has to be in the key; the builder supplies its own.
58
- *
59
- * `resolvedGivens` is deliberately absent even though it also changes the SQL.
60
- * The builder has no way to produce one — `Runtime.getBuildTargets` takes no
61
- * givens and `PersistSource.getSQL` never resolves them — so including it makes
62
- * the two sides disagree: the compiler folds `$IS_ADMIN` to a literal while the
63
- * builder re-derives it from the declaration and emits `'admin'='admin'`. Both
64
- * sides therefore compile givens the same way, by not resolving them. The
65
- * consequence — a source whose SQL depends on a supplied given cannot be
66
- * persisted, and says so badly — is #3041.
67
- */
54
+ /** Project compile options down to what a BuildID is computed under. */
68
55
  function buildIdOptions(opts) {
69
- return { virtualMap: opts.virtualMap };
56
+ return { virtualMap: opts.virtualMap, resolvedGivens: opts.resolvedGivens };
70
57
  }
71
58
  /**
72
59
  * Ask the manifest what table backs a persistable source.
@@ -100,12 +87,37 @@ function persistedTableFor(source, opts, compileQuery) {
100
87
  const named = source.sourceID
101
88
  ? `Persisted source '${source.sourceID}'`
102
89
  : 'Persisted source';
90
+ const perValue = givenUsageOfRelation(source).length > 0
91
+ ? ' This source reads givens, and no table was built for the given ' +
92
+ 'values this query supplies.'
93
+ : '';
103
94
  const base = `${named} not found in manifest (buildId: ${buildId}); ` +
104
- 'strict manifest mode forbids fallback to live compilation.';
95
+ 'strict manifest mode forbids fallback to live compilation.' +
96
+ perValue;
105
97
  throw new malloy_compile_error_1.MalloyCompileError(buildManifest.loadError ? `${base}\n ${buildManifest.loadError}` : base, 'runtime-manifest-strict-miss', source.location);
106
98
  }
107
99
  return undefined;
108
100
  }
101
+ /**
102
+ * The givens a persisted source's SQL reads — the relation, not the source.
103
+ * A `where:` added by `extend` is applied by the query that reads the table,
104
+ * so it is not here, and a given used only there does not split the table by
105
+ * value. Mirrors `getSourceSQL`: what that compiles, this reports on.
106
+ */
107
+ function givenUsageOfRelation(source) {
108
+ var _a, _b;
109
+ if (source.type === 'query_source') {
110
+ return (_a = source.query.givenUsage) !== null && _a !== void 0 ? _a : [];
111
+ }
112
+ return ((_b = source.selectSegments) !== null && _b !== void 0 ? _b : []).flatMap(segment => {
113
+ var _a;
114
+ if ((0, malloy_types_1.isSegmentSQL)(segment))
115
+ return [];
116
+ if ((0, malloy_types_1.isSegmentSource)(segment))
117
+ return givenUsageOfRelation(segment);
118
+ return (_a = segment.givenUsage) !== null && _a !== void 0 ? _a : [];
119
+ });
120
+ }
109
121
  /**
110
122
  * Expand a PersistableSourceDef as a `%{ }` segment.
111
123
  * Always returns a subquery form: (SELECT * FROM table) or (inline SQL)
package/dist/version.d.ts CHANGED
@@ -1 +1 @@
1
- export declare const MALLOY_VERSION = "0.0.435";
1
+ export declare const MALLOY_VERSION = "0.0.437";
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.435';
5
+ exports.MALLOY_VERSION = '0.0.437';
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.435",
3
+ "version": "0.0.437",
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.435",
55
- "@malloydata/malloy-interfaces": "0.0.435",
56
- "@malloydata/malloy-tag": "0.0.435",
54
+ "@malloydata/malloy-filter": "0.0.437",
55
+ "@malloydata/malloy-interfaces": "0.0.437",
56
+ "@malloydata/malloy-tag": "0.0.437",
57
57
  "@noble/hashes": "^1.8.0",
58
58
  "antlr4ts": "^0.5.0-alpha.4",
59
59
  "assert": "^2.0.0",