@malloydata/malloy 0.0.394 → 0.0.396

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (84) hide show
  1. package/dist/api/foundation/compile.d.ts +7 -6
  2. package/dist/api/foundation/compile.js +22 -6
  3. package/dist/api/foundation/config.d.ts +2 -3
  4. package/dist/api/foundation/config.js +23 -11
  5. package/dist/api/foundation/core.js +1 -1
  6. package/dist/api/foundation/runtime.d.ts +85 -5
  7. package/dist/api/foundation/runtime.js +204 -14
  8. package/dist/api/foundation/types.d.ts +2 -0
  9. package/dist/api/util.js +4 -0
  10. package/dist/connection/base_connection.js +6 -0
  11. package/dist/connection/validate_table_path.d.ts +10 -0
  12. package/dist/connection/validate_table_path.js +56 -0
  13. package/dist/dialect/databricks/databricks.d.ts +4 -4
  14. package/dist/dialect/databricks/databricks.js +17 -22
  15. package/dist/dialect/dialect.d.ts +100 -4
  16. package/dist/dialect/dialect.js +145 -1
  17. package/dist/dialect/duckdb/duckdb.d.ts +2 -3
  18. package/dist/dialect/duckdb/duckdb.js +12 -14
  19. package/dist/dialect/duckdb/table-path-parser.d.ts +2 -0
  20. package/dist/dialect/duckdb/table-path-parser.js +57 -0
  21. package/dist/dialect/index.d.ts +2 -0
  22. package/dist/dialect/index.js +4 -1
  23. package/dist/dialect/mysql/mysql.d.ts +4 -4
  24. package/dist/dialect/mysql/mysql.js +25 -20
  25. package/dist/dialect/pg_impl.d.ts +3 -1
  26. package/dist/dialect/pg_impl.js +6 -3
  27. package/dist/dialect/postgres/postgres.d.ts +1 -3
  28. package/dist/dialect/postgres/postgres.js +8 -16
  29. package/dist/dialect/snowflake/snowflake.d.ts +4 -4
  30. package/dist/dialect/snowflake/snowflake.js +11 -27
  31. package/dist/dialect/standardsql/standardsql.d.ts +6 -4
  32. package/dist/dialect/standardsql/standardsql.js +36 -15
  33. package/dist/dialect/table-path.d.ts +54 -0
  34. package/dist/dialect/table-path.js +144 -0
  35. package/dist/dialect/trino/trino.d.ts +0 -3
  36. package/dist/dialect/trino/trino.js +7 -20
  37. package/dist/index.d.ts +2 -2
  38. package/dist/index.js +4 -2
  39. package/dist/lang/ast/expressions/expr-func.js +30 -11
  40. package/dist/lang/ast/expressions/expr-given.js +1 -0
  41. package/dist/lang/ast/field-space/reference-field.js +1 -1
  42. package/dist/lang/ast/source-elements/sql-source.js +4 -0
  43. package/dist/lang/ast/source-elements/table-source.d.ts +1 -7
  44. package/dist/lang/ast/source-elements/table-source.js +24 -19
  45. package/dist/lang/ast/statements/define-given.d.ts +1 -0
  46. package/dist/lang/ast/statements/define-given.js +7 -0
  47. package/dist/lang/ast/statements/import-statement.js +4 -0
  48. package/dist/lang/ast/types/annotation-elements.d.ts +1 -0
  49. package/dist/lang/ast/types/annotation-elements.js +10 -3
  50. package/dist/lang/ast/types/malloy-element.d.ts +1 -0
  51. package/dist/lang/ast/types/malloy-element.js +4 -0
  52. package/dist/lang/malloy-to-ast.d.ts +2 -1
  53. package/dist/lang/malloy-to-ast.js +11 -1
  54. package/dist/lang/parse-log.d.ts +2 -0
  55. package/dist/lang/parse-log.js +4 -0
  56. package/dist/lang/parse-malloy.d.ts +4 -1
  57. package/dist/lang/parse-malloy.js +63 -11
  58. package/dist/lang/parse-tree-walkers/find-external-references.d.ts +2 -15
  59. package/dist/lang/parse-tree-walkers/find-external-references.js +6 -23
  60. package/dist/lang/test/test-translator.d.ts +19 -5
  61. package/dist/lang/test/test-translator.js +15 -12
  62. package/dist/lang/translate-response.d.ts +1 -1
  63. package/dist/lang/zone.d.ts +2 -0
  64. package/dist/lang/zone.js +10 -0
  65. package/dist/model/constant_expression_compiler.js +14 -5
  66. package/dist/model/expression_compiler.js +19 -17
  67. package/dist/model/field_instance.js +7 -3
  68. package/dist/model/filter_compilers.js +1 -1
  69. package/dist/model/given_binding.js +26 -21
  70. package/dist/model/index.d.ts +1 -0
  71. package/dist/model/index.js +3 -1
  72. package/dist/model/malloy_compile_error.d.ts +13 -0
  73. package/dist/model/malloy_compile_error.js +23 -0
  74. package/dist/model/malloy_types.d.ts +2 -0
  75. package/dist/model/query_model_impl.js +9 -8
  76. package/dist/model/query_node.d.ts +5 -5
  77. package/dist/model/query_node.js +21 -16
  78. package/dist/model/query_query.js +60 -44
  79. package/dist/model/sql_compiled.d.ts +2 -4
  80. package/dist/model/sql_compiled.js +20 -18
  81. package/dist/test/test-models.js +2 -2
  82. package/dist/version.d.ts +1 -1
  83. package/dist/version.js +1 -1
  84. package/package.json +4 -4
@@ -13,6 +13,7 @@ const stage_writer_1 = require("./stage_writer");
13
13
  const field_instance_1 = require("./field_instance");
14
14
  const sql_compiled_1 = require("./sql_compiled");
15
15
  const source_def_utils_1 = require("./source_def_utils");
16
+ const malloy_compile_error_1 = require("./malloy_compile_error");
16
17
  function pathToCol(path) {
17
18
  return path.map(el => encodeURIComponent(el)).join('/');
18
19
  }
@@ -142,7 +143,8 @@ class QueryQuery extends query_node_1.QueryField {
142
143
  for (const pathSegment of ungrouping.path) {
143
144
  const nextStruct = destResult.allFields.get(pathSegment);
144
145
  if (!(nextStruct instanceof field_instance_1.FieldInstanceResult)) {
145
- throw new Error(`Ungroup path ${ungrouping.path.join('.')} segment '${pathSegment}' is not a nested query`);
146
+ throw new malloy_compile_error_1.MalloyCompileError(`Ungrouping path '${ungrouping.path.join('.')}' references ` +
147
+ `'${pathSegment}', which is not a nested view at this level.`, 'compiler-ungrouped-invalid-path', this.fieldDef.location);
146
148
  }
147
149
  destResult = nextStruct;
148
150
  }
@@ -192,7 +194,9 @@ class QueryQuery extends query_node_1.QueryField {
192
194
  const drillExpression = this.getDrillExpression(f);
193
195
  if (field instanceof QueryQuery) {
194
196
  if (this.firstSegment.type === 'project') {
195
- throw new Error(`Nested views cannot be used in select - '${field.fieldDef.name}'`);
197
+ throw new malloy_compile_error_1.MalloyCompileError(`Cannot include nested view '${field.fieldDef.name}' in a ` +
198
+ "'select:' stage. Nested views require `group_by:` or " +
199
+ '`aggregate:` to be included in output.', 'compiler-nested-view-in-select', field.fieldDef.location);
196
200
  }
197
201
  const fir = new field_instance_1.FieldInstanceResult(field.fieldDef, resultStruct);
198
202
  this.expandFields(fir);
@@ -212,7 +216,8 @@ class QueryQuery extends query_node_1.QueryField {
212
216
  }
213
217
  if ((0, query_node_1.isBasicAggregate)(field)) {
214
218
  if (this.firstSegment.type === 'project') {
215
- throw new Error(`Aggregate Fields cannot be used in select - '${field.fieldDef.name}'`);
219
+ throw new malloy_compile_error_1.MalloyCompileError(`Cannot include aggregate field '${field.fieldDef.name}' in ` +
220
+ "a 'select:' stage. Use `aggregate:` instead to compute aggregates.", 'compiler-aggregate-in-select', field.fieldDef.location);
216
221
  }
217
222
  }
218
223
  }
@@ -517,14 +522,19 @@ class QueryQuery extends query_node_1.QueryField {
517
522
  var _a, _b, _c, _d;
518
523
  switch (qs.structDef.type) {
519
524
  case 'table':
520
- return this.parent.dialect.quoteTablePath(qs.structDef.tablePath);
525
+ // tablePath is canonical SQL — translator pre-validated.
526
+ return qs.structDef.tablePath;
521
527
  case 'virtual': {
522
528
  const virtualMap = (_a = qs.prepareResultOptions) === null || _a === void 0 ? void 0 : _a.virtualMap;
523
529
  const tablePath = (_b = virtualMap === null || virtualMap === void 0 ? void 0 : virtualMap.get(qs.structDef.connection)) === null || _b === void 0 ? void 0 : _b.get(qs.structDef.name);
524
530
  if (!tablePath) {
525
- throw new Error(`No virtual map entry for '${qs.structDef.name}' on connection '${qs.structDef.connection}'`);
531
+ throw new malloy_compile_error_1.MalloyCompileError(`No virtual-map entry for virtual source '${qs.structDef.name}' ` +
532
+ `on connection '${qs.structDef.connection}'. ` +
533
+ 'Add a virtual-map entry via the `virtualMap` runtime option.', 'runtime-virtual-map-missing', qs.structDef.location);
526
534
  }
527
- return this.parent.dialect.quoteTablePath(tablePath);
535
+ // virtualMap entries are application-supplied — assumed already
536
+ // canonical SQL.
537
+ return tablePath;
528
538
  }
529
539
  case 'composite':
530
540
  // TODO: throw an error here; not simple because we call into this
@@ -533,7 +543,7 @@ class QueryQuery extends query_node_1.QueryField {
533
543
  case 'finalize':
534
544
  return qs.structDef.name;
535
545
  case 'sql_select':
536
- return `(${(0, sql_compiled_1.getCompiledSQL)(qs.structDef, (_c = qs.prepareResultOptions) !== null && _c !== void 0 ? _c : {}, path => this.parent.dialect.quoteTablePath(path), (query, opts) => {
546
+ return `(${(0, sql_compiled_1.getCompiledSQL)(qs.structDef, (_c = qs.prepareResultOptions) !== null && _c !== void 0 ? _c : {}, (query, opts) => {
537
547
  // Compile query to isolated SQL (not into parent's stageWriter)
538
548
  const ret = this.compileQueryToStages(query, opts !== null && opts !== void 0 ? opts : {}, undefined, false);
539
549
  return ret.sql;
@@ -551,14 +561,17 @@ class QueryQuery extends query_node_1.QueryField {
551
561
  const buildId = (0, source_def_utils_1.mkBuildID)(connDigest, fullRet.sql);
552
562
  const entry = buildManifest.entries[buildId];
553
563
  if (entry) {
554
- // Found in manifest - use persisted table
555
- return this.parent.dialect.quoteTablePath(entry.tableName);
564
+ // Found in manifest - use persisted table.
565
+ // entry.tableName comes from the manifest, assumed canonical.
566
+ return entry.tableName;
556
567
  }
557
568
  if (buildManifest.strict) {
558
- const base = `Persist source '${qs.structDef.sourceID}' not found in manifest (buildId: ${buildId})`;
559
- throw new Error(buildManifest.loadError
569
+ const base = `Persist source '${qs.structDef.sourceID}' not found ` +
570
+ `in manifest (buildId: ${buildId}); strict manifest mode ` +
571
+ 'forbids fallback to live compilation.';
572
+ throw new malloy_compile_error_1.MalloyCompileError(buildManifest.loadError
560
573
  ? `${base}\n ${buildManifest.loadError}`
561
- : base);
574
+ : base, 'runtime-manifest-strict-miss', qs.structDef.location);
562
575
  }
563
576
  }
564
577
  }
@@ -594,7 +607,8 @@ class QueryQuery extends query_node_1.QueryField {
594
607
  if (typeof structRef === 'string') {
595
608
  const struct = this.structRefToQueryStruct(structRef);
596
609
  if (!struct) {
597
- throw new Error(`Unexpected reference to an undefined source '${structRef}'`);
610
+ throw new malloy_compile_error_1.MalloyCompileError(`Query references source '${structRef}', ` +
611
+ 'which is not defined in this model.', 'compiler-undefined-source', undefined);
598
612
  }
599
613
  sourceStruct = struct;
600
614
  }
@@ -631,7 +645,7 @@ class QueryQuery extends query_node_1.QueryField {
631
645
  }
632
646
  if (ji.makeUniqueKey) {
633
647
  const passKeys = this.generateSQLPassthroughKeys(qs);
634
- structSQL = `(SELECT ${qs.dialect.sqlGenerateUUID()} as ${qs.dialect.sqlMaybeQuoteIdentifier('__distinct_key')}, x.* ${passKeys} FROM ${structSQL} as x)`;
648
+ structSQL = `(SELECT ${qs.dialect.sqlGenerateUUID()} as ${qs.dialect.sqlQuoteIdentifier('__distinct_key')}, x.* ${passKeys} FROM ${structSQL} as x)`;
635
649
  }
636
650
  let onCondition = '';
637
651
  if (qs.parent === undefined) {
@@ -764,7 +778,7 @@ class QueryQuery extends query_node_1.QueryField {
764
778
  if ((0, malloy_types_1.isBaseTable)(qs.structDef)) {
765
779
  if (ji.makeUniqueKey) {
766
780
  const passKeys = this.generateSQLPassthroughKeys(qs);
767
- structSQL = `(SELECT ${qs.dialect.sqlGenerateUUID()} as ${qs.dialect.sqlMaybeQuoteIdentifier('__distinct_key')}, x.* ${passKeys} FROM ${structSQL} as x)`;
781
+ structSQL = `(SELECT ${qs.dialect.sqlGenerateUUID()} as ${qs.dialect.sqlQuoteIdentifier('__distinct_key')}, x.* ${passKeys} FROM ${structSQL} as x)`;
768
782
  }
769
783
  s += `FROM ${structSQL} as ${ji.alias}\n`;
770
784
  }
@@ -846,7 +860,7 @@ class QueryQuery extends query_node_1.QueryField {
846
860
  o.push(`${fi.fieldUsage.resultIndex} ${f.dir || 'ASC'}`);
847
861
  }
848
862
  else if (this.parent.dialect.orderByClause === 'output_name') {
849
- o.push(`${this.parent.dialect.sqlMaybeQuoteIdentifier(f.field)} ${f.dir || 'ASC'}`);
863
+ o.push(`${this.parent.dialect.sqlQuoteIdentifier(f.field)} ${f.dir || 'ASC'}`);
850
864
  }
851
865
  else if (this.parent.dialect.orderByClause === 'expression') {
852
866
  const fieldExpr = fi.getSQL();
@@ -854,7 +868,7 @@ class QueryQuery extends query_node_1.QueryField {
854
868
  }
855
869
  }
856
870
  else {
857
- throw new Error(`Unknown field in ORDER BY ${f.field}`);
871
+ throw new malloy_compile_error_1.MalloyCompileError(`ORDER BY references unknown field '${f.field}'.`, 'compiler-orderby-field-not-found', queryDef.referencedAt);
858
872
  }
859
873
  }
860
874
  else {
@@ -863,7 +877,7 @@ class QueryQuery extends query_node_1.QueryField {
863
877
  }
864
878
  else if (this.parent.dialect.orderByClause === 'output_name') {
865
879
  const orderingField = resultStruct.getFieldByNumber(f.field);
866
- o.push(`${this.parent.dialect.sqlMaybeQuoteIdentifier(orderingField.name)} ${f.dir || 'ASC'}`);
880
+ o.push(`${this.parent.dialect.sqlQuoteIdentifier(orderingField.name)} ${f.dir || 'ASC'}`);
867
881
  }
868
882
  else if (this.parent.dialect.orderByClause === 'expression') {
869
883
  const orderingField = resultStruct.getFieldByNumber(f.field);
@@ -887,7 +901,7 @@ class QueryQuery extends query_node_1.QueryField {
887
901
  const fields = [];
888
902
  for (const [name, field] of this.rootResult.allFields) {
889
903
  const fi = field;
890
- const sqlName = this.parent.dialect.sqlMaybeQuoteIdentifier(name);
904
+ const sqlName = this.parent.dialect.sqlQuoteIdentifier(name);
891
905
  if (fi.fieldUsage.type === 'result') {
892
906
  fields.push(` ${fi.generateExpression()} as ${sqlName}`);
893
907
  }
@@ -937,7 +951,7 @@ class QueryQuery extends query_node_1.QueryField {
937
951
  .map(o => `${o.pipelineSQL} as ${o.sqlFieldName}`)
938
952
  .join(',\n');
939
953
  const outputFields = outputPipelinedSQL.map(f => f.sqlFieldName);
940
- const allFields = Array.from(this.rootResult.allFields.keys()).map(f => this.parent.dialect.sqlMaybeQuoteIdentifier(f));
954
+ const allFields = Array.from(this.rootResult.allFields.keys()).map(f => this.parent.dialect.sqlQuoteIdentifier(f));
941
955
  const fields = allFields.filter(f => outputFields.indexOf(f) === -1);
942
956
  retSQL = `SELECT ${fields.length > 0 ? fields.join(', ') + ',' : ''} ${pipelinesSQL} FROM ${lastStageName}`;
943
957
  }
@@ -956,7 +970,7 @@ class QueryQuery extends query_node_1.QueryField {
956
970
  }
957
971
  const orderedFields = [...scalarFields, ...otherFields];
958
972
  for (const [name, fi] of orderedFields) {
959
- const outputName = this.parent.dialect.sqlMaybeQuoteIdentifier(`${name}__${resultSet.groupSet}`);
973
+ const outputName = this.parent.dialect.sqlQuoteIdentifier(`${name}__${resultSet.groupSet}`);
960
974
  if (fi instanceof field_instance_1.FieldInstanceField) {
961
975
  if (fi.fieldUsage.type === 'result') {
962
976
  const exp = fi.getSQL();
@@ -975,7 +989,7 @@ class QueryQuery extends query_node_1.QueryField {
975
989
  });
976
990
  output.sql.push(outputFieldName);
977
991
  if (fi.f.fieldDef.type === 'number') {
978
- const outputNameString = this.parent.dialect.sqlMaybeQuoteIdentifier(`${name}__${resultSet.groupSet}_string`);
992
+ const outputNameString = this.parent.dialect.sqlQuoteIdentifier(`${name}__${resultSet.groupSet}_string`);
979
993
  const outputFieldNameString = `__lateral_join_bag.${outputNameString}`;
980
994
  output.sql.push(outputFieldNameString);
981
995
  output.dimensionIndexes.push(output.fieldIndex++);
@@ -1086,7 +1100,7 @@ class QueryQuery extends query_node_1.QueryField {
1086
1100
  let r = result;
1087
1101
  while (r) {
1088
1102
  for (const name of r.fieldNames(fi => (0, query_node_1.isScalarField)(fi.f))) {
1089
- dimensions.push(this.parent.dialect.sqlMaybeQuoteIdentifier(`${name}__${r.groupSet}`));
1103
+ dimensions.push(this.parent.dialect.sqlQuoteIdentifier(`${name}__${r.groupSet}`));
1090
1104
  }
1091
1105
  r = r.parent;
1092
1106
  }
@@ -1120,7 +1134,7 @@ class QueryQuery extends query_node_1.QueryField {
1120
1134
  orderingField = result.getFieldByNumber(ordering.field);
1121
1135
  }
1122
1136
  obSQL.push(' ' +
1123
- this.parent.dialect.sqlMaybeQuoteIdentifier(`${orderingField.name}__${result.groupSet}`) +
1137
+ this.parent.dialect.sqlQuoteIdentifier(`${orderingField.name}__${result.groupSet}`) +
1124
1138
  ` ${ordering.dir || 'ASC'}`);
1125
1139
  }
1126
1140
  // partition for a row number is the parent if it exists.
@@ -1206,7 +1220,9 @@ class QueryQuery extends query_node_1.QueryField {
1206
1220
  this.generateStage0Fields(this.rootResult, f, stageWriter);
1207
1221
  if (this.firstSegment.type === 'project' &&
1208
1222
  !this.parent.modelCompilerFlags().has('unsafe_complex_select_query')) {
1209
- throw new Error('PROJECT cannot be used on queries with turtles');
1223
+ throw new malloy_compile_error_1.MalloyCompileError("Cannot use 'select:' in a stage that contains nested views. " +
1224
+ 'Use `group_by:` or restructure the pipeline. ' +
1225
+ 'Set `##! unsafe_complex_select_query` to bypass at your own risk.', 'compiler-project-with-turtles', this.fieldDef.location);
1210
1226
  }
1211
1227
  const groupBy = 'GROUP BY ' + f.dimensionIndexes.join(',') + '\n';
1212
1228
  from += this.parent.dialect.sqlGroupSetTable(this.maxGroupSet) + '\n';
@@ -1225,7 +1241,7 @@ class QueryQuery extends query_node_1.QueryField {
1225
1241
  generateDepthNFields(depth, resultSet, output, stageWriter) {
1226
1242
  const groupsToMap = [];
1227
1243
  for (const [name, fi] of resultSet.allFields) {
1228
- const sqlFieldName = this.parent.dialect.sqlMaybeQuoteIdentifier(`${name}__${resultSet.groupSet}`);
1244
+ const sqlFieldName = this.parent.dialect.sqlQuoteIdentifier(`${name}__${resultSet.groupSet}`);
1229
1245
  if (fi instanceof field_instance_1.FieldInstanceField) {
1230
1246
  if (fi.fieldUsage.type === 'result') {
1231
1247
  if ((0, query_node_1.isScalarField)(fi.f)) {
@@ -1329,15 +1345,15 @@ class QueryQuery extends query_node_1.QueryField {
1329
1345
  const outputPipelinedSQL = [];
1330
1346
  const dimensionIndexes = [];
1331
1347
  for (const [name, fi] of this.rootResult.allFields) {
1332
- const sqlName = this.parent.dialect.sqlMaybeQuoteIdentifier(name);
1348
+ const sqlName = this.parent.dialect.sqlQuoteIdentifier(name);
1333
1349
  if (fi instanceof field_instance_1.FieldInstanceField) {
1334
1350
  if (fi.fieldUsage.type === 'result') {
1335
1351
  if ((0, query_node_1.isScalarField)(fi.f)) {
1336
- fieldsSQL.push(this.parent.dialect.sqlMaybeQuoteIdentifier(`${name}__${this.rootResult.groupSet}`) + ` as ${sqlName}`);
1352
+ fieldsSQL.push(this.parent.dialect.sqlQuoteIdentifier(`${name}__${this.rootResult.groupSet}`) + ` as ${sqlName}`);
1337
1353
  dimensionIndexes.push(fieldIndex++);
1338
1354
  }
1339
1355
  else if ((0, query_node_1.isBasicCalculation)(fi.f)) {
1340
- fieldsSQL.push(this.parent.dialect.sqlAnyValueLastTurtle(this.parent.dialect.sqlMaybeQuoteIdentifier(`${name}__${this.rootResult.groupSet}`), this.rootResult.groupSet, sqlName));
1356
+ fieldsSQL.push(this.parent.dialect.sqlAnyValueLastTurtle(this.parent.dialect.sqlQuoteIdentifier(`${name}__${this.rootResult.groupSet}`), this.rootResult.groupSet, sqlName));
1341
1357
  fieldIndex++;
1342
1358
  }
1343
1359
  }
@@ -1348,7 +1364,7 @@ class QueryQuery extends query_node_1.QueryField {
1348
1364
  fieldIndex++;
1349
1365
  }
1350
1366
  else if (fi.firstSegment.type === 'project') {
1351
- fieldsSQL.push(this.parent.dialect.sqlAnyValueLastTurtle(this.parent.dialect.sqlMaybeQuoteIdentifier(`${name}__${this.rootResult.groupSet}`), this.rootResult.groupSet, sqlName));
1367
+ fieldsSQL.push(this.parent.dialect.sqlAnyValueLastTurtle(this.parent.dialect.sqlQuoteIdentifier(`${name}__${this.rootResult.groupSet}`), this.rootResult.groupSet, sqlName));
1352
1368
  fieldIndex++;
1353
1369
  }
1354
1370
  }
@@ -1375,7 +1391,7 @@ class QueryQuery extends query_node_1.QueryField {
1375
1391
  buildDialectFieldList(resultStruct) {
1376
1392
  const dialectFieldList = [];
1377
1393
  for (const [name, field] of resultStruct.allFields) {
1378
- const sqlName = this.parent.dialect.sqlMaybeQuoteIdentifier(name);
1394
+ const sqlName = this.parent.dialect.sqlQuoteIdentifier(name);
1379
1395
  //
1380
1396
  if (resultStruct.firstSegment.type === 'reduce' &&
1381
1397
  field instanceof field_instance_1.FieldInstanceResult) {
@@ -1390,7 +1406,7 @@ class QueryQuery extends query_node_1.QueryField {
1390
1406
  };
1391
1407
  dialectFieldList.push({
1392
1408
  typeDef: multiLineNest,
1393
- sqlExpression: this.parent.dialect.sqlMaybeQuoteIdentifier(`${name}__${resultStruct.groupSet}`),
1409
+ sqlExpression: this.parent.dialect.sqlQuoteIdentifier(`${name}__${resultStruct.groupSet}`),
1394
1410
  rawName: name,
1395
1411
  sqlOutputName: sqlName,
1396
1412
  });
@@ -1404,7 +1420,7 @@ class QueryQuery extends query_node_1.QueryField {
1404
1420
  };
1405
1421
  dialectFieldList.push({
1406
1422
  typeDef: oneLineNest,
1407
- sqlExpression: this.parent.dialect.sqlMaybeQuoteIdentifier(`${name}__${resultStruct.groupSet}`),
1423
+ sqlExpression: this.parent.dialect.sqlQuoteIdentifier(`${name}__${resultStruct.groupSet}`),
1408
1424
  rawName: name,
1409
1425
  sqlOutputName: sqlName,
1410
1426
  });
@@ -1415,7 +1431,7 @@ class QueryQuery extends query_node_1.QueryField {
1415
1431
  field.fieldUsage.type === 'result') {
1416
1432
  pushDialectField(dialectFieldList, {
1417
1433
  fieldDef: field.f.fieldDef,
1418
- sqlExpression: this.parent.dialect.sqlMaybeQuoteIdentifier(`${name}__${resultStruct.groupSet}`),
1434
+ sqlExpression: this.parent.dialect.sqlQuoteIdentifier(`${name}__${resultStruct.groupSet}`),
1419
1435
  rawName: name,
1420
1436
  sqlOutputName: sqlName,
1421
1437
  });
@@ -1449,10 +1465,10 @@ class QueryQuery extends query_node_1.QueryField {
1449
1465
  else {
1450
1466
  orderingField = resultStruct.getFieldByNumber(ordering.field);
1451
1467
  }
1452
- const structField = this.parent.dialect.sqlMaybeQuoteIdentifier(orderingField.name);
1468
+ const structField = this.parent.dialect.sqlQuoteIdentifier(orderingField.name);
1453
1469
  if (resultStruct.firstSegment.type === 'reduce') {
1454
1470
  compiledOrderBy.push({
1455
- field: this.parent.dialect.sqlMaybeQuoteIdentifier(`${orderingField.name}__${resultStruct.groupSet}`),
1471
+ field: this.parent.dialect.sqlQuoteIdentifier(`${orderingField.name}__${resultStruct.groupSet}`),
1456
1472
  structField,
1457
1473
  dir: ordering.dir || 'asc',
1458
1474
  });
@@ -1607,7 +1623,7 @@ class QueryQueryIndexStage extends QueryQuery {
1607
1623
  }
1608
1624
  expandField(f) {
1609
1625
  const as = f.path.join('.');
1610
- const field = this.parent.getQueryFieldByName(f.path);
1626
+ const field = this.parent.getQueryFieldByName(f.path, f.at);
1611
1627
  return { as, field };
1612
1628
  }
1613
1629
  expandFields(resultStruct) {
@@ -1636,12 +1652,12 @@ class QueryQueryIndexStage extends QueryQuery {
1636
1652
  generateSQL(stageWriter) {
1637
1653
  let measureSQL = 'COUNT(*)';
1638
1654
  const dialect = this.parent.dialect;
1639
- const fieldNameColumn = dialect.sqlMaybeQuoteIdentifier('fieldName');
1640
- const fieldPathColumn = dialect.sqlMaybeQuoteIdentifier('fieldPath');
1641
- const fieldValueColumn = dialect.sqlMaybeQuoteIdentifier('fieldValue');
1642
- const fieldTypeColumn = dialect.sqlMaybeQuoteIdentifier('fieldType');
1643
- const fieldRangeColumn = dialect.sqlMaybeQuoteIdentifier('fieldRange');
1644
- const weightColumn = dialect.sqlMaybeQuoteIdentifier('weight');
1655
+ const fieldNameColumn = dialect.sqlQuoteIdentifier('fieldName');
1656
+ const fieldPathColumn = dialect.sqlQuoteIdentifier('fieldPath');
1657
+ const fieldValueColumn = dialect.sqlQuoteIdentifier('fieldValue');
1658
+ const fieldTypeColumn = dialect.sqlQuoteIdentifier('fieldType');
1659
+ const fieldRangeColumn = dialect.sqlQuoteIdentifier('fieldRange');
1660
+ const weightColumn = dialect.sqlQuoteIdentifier('weight');
1645
1661
  const measureName = this.firstSegment.weightMeasure;
1646
1662
  if (measureName) {
1647
1663
  measureSQL = this.rootResult.getField(measureName).generateExpression();
@@ -1819,7 +1835,7 @@ class QueryQueryRaw extends QueryQuery {
1819
1835
  if (this.parent.structDef.type !== 'sql_select') {
1820
1836
  throw new Error('Invalid struct for QueryQueryRaw, currently only supports SQL');
1821
1837
  }
1822
- return stageWriter.addStage((0, sql_compiled_1.getCompiledSQL)(this.parent.structDef, (_a = this.parent.prepareResultOptions) !== null && _a !== void 0 ? _a : {}, path => this.parent.dialect.quoteTablePath(path), (query, opts) => {
1838
+ return stageWriter.addStage((0, sql_compiled_1.getCompiledSQL)(this.parent.structDef, (_a = this.parent.prepareResultOptions) !== null && _a !== void 0 ? _a : {}, (query, opts) => {
1823
1839
  // Compile query to isolated SQL (not into parent's stageWriter)
1824
1840
  const ret = this.compileQueryToStages(query, opts !== null && opts !== void 0 ? opts : {}, undefined, false);
1825
1841
  return ret.sql;
@@ -13,17 +13,15 @@ export type CompileQueryCallback = (query: Query, opts?: PrepareResultOptions) =
13
13
  *
14
14
  * @param src The SQLSourceDef to compile
15
15
  * @param opts PrepareResultOptions with buildManifest and connectionDigests
16
- * @param quoteTablePath Dialect function to safely quote a table path
17
16
  * @param compileQuery Callback to compile a Query to SQL
18
17
  */
19
- export declare function getCompiledSQL(src: SQLSourceDef, opts: PrepareResultOptions, quoteTablePath: (path: string) => string, compileQuery: CompileQueryCallback): string;
18
+ export declare function getCompiledSQL(src: SQLSourceDef, opts: PrepareResultOptions, compileQuery: CompileQueryCallback): string;
20
19
  /**
21
20
  * Get the SQL for a PersistableSourceDef.
22
21
  *
23
22
  * @param source The persistable source to compile
24
- * @param quoteTablePath Dialect function to quote table paths
25
23
  * @param compileQuery Callback to compile a Query to SQL
26
24
  * @param opts Optional - if provided with manifest, nested sources may be substituted.
27
25
  * Omit for "full SQL" (e.g., when computing BuildID).
28
26
  */
29
- export declare function getSourceSQL(source: PersistableSourceDef, quoteTablePath: (path: string) => string, compileQuery: CompileQueryCallback, opts?: PrepareResultOptions): string;
27
+ export declare function getSourceSQL(source: PersistableSourceDef, compileQuery: CompileQueryCallback, opts?: PrepareResultOptions): string;
@@ -8,6 +8,7 @@ exports.getCompiledSQL = getCompiledSQL;
8
8
  exports.getSourceSQL = getSourceSQL;
9
9
  const malloy_types_1 = require("./malloy_types");
10
10
  const source_def_utils_1 = require("./source_def_utils");
11
+ const malloy_compile_error_1 = require("./malloy_compile_error");
11
12
  /**
12
13
  * Compile a SQLSourceDef to its final SQL string.
13
14
  *
@@ -16,10 +17,9 @@ const source_def_utils_1 = require("./source_def_utils");
16
17
  *
17
18
  * @param src The SQLSourceDef to compile
18
19
  * @param opts PrepareResultOptions with buildManifest and connectionDigests
19
- * @param quoteTablePath Dialect function to safely quote a table path
20
20
  * @param compileQuery Callback to compile a Query to SQL
21
21
  */
22
- function getCompiledSQL(src, opts, quoteTablePath, compileQuery) {
22
+ function getCompiledSQL(src, opts, compileQuery) {
23
23
  // If no segments, just return the pre-computed selectStr
24
24
  if (!src.selectSegments || src.selectSegments.length === 0) {
25
25
  return src.selectStr;
@@ -27,60 +27,63 @@ function getCompiledSQL(src, opts, quoteTablePath, compileQuery) {
27
27
  // Expand each segment
28
28
  const parts = [];
29
29
  for (const segment of src.selectSegments) {
30
- parts.push(expandSegment(segment, opts, quoteTablePath, compileQuery));
30
+ parts.push(expandSegment(segment, opts, compileQuery));
31
31
  }
32
32
  return parts.join('');
33
33
  }
34
34
  /**
35
35
  * Expand a single SQLPhraseSegment to SQL.
36
36
  */
37
- function expandSegment(segment, opts, quoteTablePath, compileQuery) {
37
+ function expandSegment(segment, opts, compileQuery) {
38
38
  // Plain SQL string
39
39
  if ((0, malloy_types_1.isSegmentSQL)(segment)) {
40
40
  return segment.sql;
41
41
  }
42
42
  // PersistableSourceDef (sql_select or query_source)
43
43
  if ((0, malloy_types_1.isSegmentSource)(segment)) {
44
- return expandPersistableSource(segment, opts, quoteTablePath, compileQuery);
44
+ return expandPersistableSource(segment, opts, compileQuery);
45
45
  }
46
46
  // Query segment
47
- return expandQuery(segment, opts, quoteTablePath, compileQuery);
47
+ return expandQuery(segment, opts, compileQuery);
48
48
  }
49
49
  /**
50
50
  * Expand a PersistableSourceDef, checking manifest for pre-built table.
51
51
  * Always returns a subquery form: (SELECT * FROM table) or (inline SQL)
52
52
  */
53
- function expandPersistableSource(source, opts, quoteTablePath, compileQuery) {
53
+ function expandPersistableSource(source, opts, compileQuery) {
54
54
  const { buildManifest, connectionDigests } = opts;
55
55
  // Try manifest lookup if we have the required info (only for persistent sources)
56
56
  if (buildManifest && connectionDigests && source.persistent) {
57
57
  const connDigest = (0, malloy_types_1.safeRecordGet)(connectionDigests, source.connection);
58
58
  if (connDigest) {
59
59
  // Get the SQL for this source to compute BuildID (no opts = full SQL)
60
- const sql = getSourceSQL(source, quoteTablePath, compileQuery);
60
+ const sql = getSourceSQL(source, compileQuery);
61
61
  const buildId = (0, source_def_utils_1.mkBuildID)(connDigest, sql);
62
62
  const entry = buildManifest.entries[buildId];
63
63
  if (entry) {
64
- // Found in manifest - substitute with subquery from persisted table
65
- return `(SELECT * FROM ${quoteTablePath(entry.tableName)})`;
64
+ // Found in manifest - substitute with subquery from persisted table.
65
+ // entry.tableName is canonical SQL, supplied by the manifest builder.
66
+ return `(SELECT * FROM ${entry.tableName})`;
66
67
  }
67
68
  // Not in manifest
68
69
  if (buildManifest.strict) {
69
- const base = `Persist source '${source.sourceID}' not found in manifest (buildId: ${buildId})`;
70
- throw new Error(buildManifest.loadError
70
+ const base = `Persist source '${source.sourceID}' not found in manifest ` +
71
+ `(buildId: ${buildId}); strict manifest mode forbids fallback ` +
72
+ 'to live compilation.';
73
+ throw new malloy_compile_error_1.MalloyCompileError(buildManifest.loadError
71
74
  ? `${base}\n ${buildManifest.loadError}`
72
- : base);
75
+ : base, 'runtime-manifest-strict-miss', source.location);
73
76
  }
74
77
  }
75
78
  }
76
79
  // No manifest or not found - expand inline as subquery
77
- const sql = getSourceSQL(source, quoteTablePath, compileQuery, opts);
80
+ const sql = getSourceSQL(source, compileQuery, opts);
78
81
  return `(${sql})`;
79
82
  }
80
83
  /**
81
84
  * Expand a Query segment.
82
85
  */
83
- function expandQuery(query, opts, _quoteTablePath, compileQuery) {
86
+ function expandQuery(query, opts, compileQuery) {
84
87
  // Set isPartialQuery so CTEs aren't used (they can't be nested in subqueries)
85
88
  const sql = compileQuery(query, { ...opts, isPartialQuery: true });
86
89
  return `(${sql})`;
@@ -89,15 +92,14 @@ function expandQuery(query, opts, _quoteTablePath, compileQuery) {
89
92
  * Get the SQL for a PersistableSourceDef.
90
93
  *
91
94
  * @param source The persistable source to compile
92
- * @param quoteTablePath Dialect function to quote table paths
93
95
  * @param compileQuery Callback to compile a Query to SQL
94
96
  * @param opts Optional - if provided with manifest, nested sources may be substituted.
95
97
  * Omit for "full SQL" (e.g., when computing BuildID).
96
98
  */
97
- function getSourceSQL(source, quoteTablePath, compileQuery, opts) {
99
+ function getSourceSQL(source, compileQuery, opts) {
98
100
  if (source.type === 'sql_select') {
99
101
  // Recursive call for nested sql_select
100
- return getCompiledSQL(source, opts !== null && opts !== void 0 ? opts : {}, quoteTablePath, compileQuery);
102
+ return getCompiledSQL(source, opts !== null && opts !== void 0 ? opts : {}, compileQuery);
101
103
  }
102
104
  // query_source - compile the inner query
103
105
  return compileQuery(source.query, opts);
@@ -194,7 +194,7 @@ function generateSQL(dialect, rows) {
194
194
  const sql = exprToSQL(typedValue.expr, dialect);
195
195
  if (idx === 0) {
196
196
  // First row: include column aliases and explicit casts if needed
197
- const quotedName = dialect.sqlMaybeQuoteIdentifier(colName);
197
+ const quotedName = dialect.sqlQuoteIdentifier(colName);
198
198
  if (typedValue.needsCast) {
199
199
  const sqlType = dialect.malloyTypeToSQLType(typedValue.malloyType);
200
200
  fields.push(`CAST(${sql} AS ${sqlType}) AS ${quotedName}`);
@@ -225,7 +225,7 @@ function generateSQL(dialect, rows) {
225
225
  }
226
226
  // Multiple rows: wrap for sorting
227
227
  const quotedColumns = columnList
228
- .map(col => dialect.sqlMaybeQuoteIdentifier(col))
228
+ .map(col => dialect.sqlQuoteIdentifier(col))
229
229
  .join(', ');
230
230
  const innerQuery = selects.join('\nUNION ALL ');
231
231
  // Generate ORDER BY based on dialect preference
package/dist/version.d.ts CHANGED
@@ -1 +1 @@
1
- export declare const MALLOY_VERSION = "0.0.394";
1
+ export declare const MALLOY_VERSION = "0.0.396";
package/dist/version.js CHANGED
@@ -2,5 +2,5 @@
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.MALLOY_VERSION = void 0;
4
4
  // generated with 'generate-version-file' script; do not edit manually
5
- exports.MALLOY_VERSION = '0.0.394';
5
+ exports.MALLOY_VERSION = '0.0.396';
6
6
  //# sourceMappingURL=version.js.map
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@malloydata/malloy",
3
- "version": "0.0.394",
3
+ "version": "0.0.396",
4
4
  "license": "MIT",
5
5
  "exports": {
6
6
  ".": "./dist/index.js",
@@ -51,9 +51,9 @@
51
51
  "generate-version-file": "VERSION=$(npm pkg get version --workspaces=false | tr -d \\\")\necho \"// generated with 'generate-version-file' script; do not edit manually\\nexport const MALLOY_VERSION = '$VERSION';\" > src/version.ts"
52
52
  },
53
53
  "dependencies": {
54
- "@malloydata/malloy-filter": "0.0.394",
55
- "@malloydata/malloy-interfaces": "0.0.394",
56
- "@malloydata/malloy-tag": "0.0.394",
54
+ "@malloydata/malloy-filter": "0.0.396",
55
+ "@malloydata/malloy-interfaces": "0.0.396",
56
+ "@malloydata/malloy-tag": "0.0.396",
57
57
  "@noble/hashes": "^1.8.0",
58
58
  "antlr4ts": "^0.5.0-alpha.4",
59
59
  "assert": "^2.0.0",