@malloydata/malloy 0.0.433 → 0.0.434

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 (45) hide show
  1. package/dist/api/foundation/compile.d.ts +2 -1
  2. package/dist/api/foundation/compile.js +8 -6
  3. package/dist/api/foundation/config.js +6 -15
  4. package/dist/api/foundation/runtime.d.ts +2 -7
  5. package/dist/api/foundation/runtime.js +10 -16
  6. package/dist/api/foundation/types.d.ts +1 -0
  7. package/dist/connection/validate_table_path.d.ts +1 -2
  8. package/dist/connection/validate_table_path.js +1 -2
  9. package/dist/dialect/databricks/databricks.js +6 -6
  10. package/dist/dialect/dialect.d.ts +21 -2
  11. package/dist/dialect/dialect.js +33 -3
  12. package/dist/dialect/duckdb/duckdb.js +7 -21
  13. package/dist/dialect/mysql/mysql.js +8 -8
  14. package/dist/dialect/pg_impl.js +12 -12
  15. package/dist/dialect/postgres/postgres.js +3 -3
  16. package/dist/dialect/snowflake/snowflake.js +12 -12
  17. package/dist/dialect/standardsql/standardsql.d.ts +1 -1
  18. package/dist/dialect/standardsql/standardsql.js +16 -13
  19. package/dist/dialect/trino/trino.d.ts +3 -3
  20. package/dist/dialect/trino/trino.js +32 -28
  21. package/dist/lang/ast/source-elements/refined-source.js +1 -1
  22. package/dist/lang/ast/source-elements/sql-source.js +2 -1
  23. package/dist/lang/ast/source-properties/timezone-statement.d.ts +4 -3
  24. package/dist/lang/ast/source-properties/timezone-statement.js +14 -12
  25. package/dist/lang/composite-source-utils.js +10 -3
  26. package/dist/lang/malloy-to-ast.js +2 -8
  27. package/dist/lang/parse-malloy.d.ts +3 -2
  28. package/dist/lang/parse-malloy.js +2 -1
  29. package/dist/lang/test/test-translator.d.ts +2 -1
  30. package/dist/lang/test/test-translator.js +1 -1
  31. package/dist/model/join_instance.d.ts +1 -0
  32. package/dist/model/join_instance.js +9 -1
  33. package/dist/model/query_query.d.ts +25 -0
  34. package/dist/model/query_query.js +142 -17
  35. package/dist/model/sql_block.d.ts +2 -2
  36. package/dist/model/sql_block.js +11 -47
  37. package/dist/model/sql_compiled.d.ts +3 -1
  38. package/dist/model/sql_compiled.js +8 -6
  39. package/dist/model/stage_writer.js +3 -3
  40. package/dist/model/utils.d.ts +2 -2
  41. package/dist/model/utils.js +2 -3
  42. package/dist/version.d.ts +1 -1
  43. package/dist/version.js +1 -1
  44. package/femto-config.motly +2 -0
  45. package/package.json +4 -4
@@ -30,6 +30,14 @@ class JoinInstance {
30
30
  }, this.queryStruct));
31
31
  }
32
32
  }
33
+ // A filtered join whose own joins go inside its subquery, so that the
34
+ // subquery has to project them back out for anything outside to name them.
35
+ isSubquery() {
36
+ return (this.children.length > 0 &&
37
+ this.joinFilterConditions !== undefined &&
38
+ (0, malloy_types_1.isJoinedSource)(this.queryStruct.structDef) &&
39
+ this.queryStruct.dialect.supportsComplexFilteredSources);
40
+ }
33
41
  parentRelationship() {
34
42
  if (this.queryStruct.parent === undefined) {
35
43
  return 'root';
@@ -64,7 +72,7 @@ class JoinInstance {
64
72
  }
65
73
  // postgres unnest needs to know the names of the physical fields.
66
74
  getDialectFieldList() {
67
- return (0, utils_1.getDialectFieldList)(this.queryStruct.structDef);
75
+ return (0, utils_1.getDialectFieldList)(this.queryStruct.structDef, this.queryStruct.dialect);
68
76
  }
69
77
  }
70
78
  exports.JoinInstance = JoinInstance;
@@ -34,6 +34,7 @@ export declare class QueryQuery extends QueryField {
34
34
  maxDepth: number;
35
35
  maxGroupSet: number;
36
36
  rootResult: FieldInstanceResultRoot;
37
+ private packedColumns;
37
38
  resultStage: string | undefined;
38
39
  stageWriter: StageWriter | undefined;
39
40
  isJoinedSubquery: boolean;
@@ -90,6 +91,30 @@ export declare class QueryQuery extends QueryField {
90
91
  sql?: string;
91
92
  };
92
93
  generateSQLJoinBlock(stageWriter: StageWriter, ji: JoinInstance, depth: number): string;
94
+ /**
95
+ * Which column of which join a reference needs, or undefined when it needs
96
+ * none. Walking `path` from `from`, the answer is the member of the
97
+ * innermost join along it -- a record is kept whole, since a reference into
98
+ * one is a reference to the column holding it.
99
+ *
100
+ * Two kinds of path need no column. One ending at a computed field
101
+ * contributes that field's name, which is no column of the source; the
102
+ * columns the computation reads arrive as references of their own. And a
103
+ * `having:` or `calculate:` names an *output* field, so its path is rooted
104
+ * in the query's output rather than its source and need not resolve here at
105
+ * all. An output name which happens to match a source column resolves as
106
+ * though it were one, which costs nothing: a query's own source is never
107
+ * packed, only the joins beneath a filtered one.
108
+ */
109
+ private packedColumnFor;
110
+ /**
111
+ * Which columns of each join have to survive being packed into a struct:
112
+ * every column the segment's field usage names, plus each join's primary
113
+ * key, which the compiler reaches for as a distinct key without any
114
+ * expression in the query naming it.
115
+ */
116
+ private packedColumnsByJoin;
117
+ private joinProjectionFieldList;
93
118
  generateSQLPassthroughKeys(qs: QueryStruct): string;
94
119
  generateSQLJoins(stageWriter: StageWriter): string;
95
120
  /**
@@ -39,9 +39,9 @@ function groupByPositions(columns) {
39
39
  return columns.flatMap((c, i) => (c.isDimension ? [i + 1] : []));
40
40
  }
41
41
  function pushDialectField(dl, f) {
42
- const { sqlExpression, sqlOutputName, rawName } = f;
42
+ const { sqlExpression, rawName } = f;
43
43
  if ((0, malloy_types_1.isAtomic)(f.fieldDef)) {
44
- dl.push({ typeDef: f.fieldDef, sqlExpression, sqlOutputName, rawName });
44
+ dl.push({ typeDef: f.fieldDef, sqlExpression, rawName });
45
45
  }
46
46
  }
47
47
  /** Query builder object. */
@@ -539,13 +539,23 @@ class QueryQuery extends query_node_1.QueryField {
539
539
  case 'virtual': {
540
540
  const virtualMap = (_a = qs.prepareResultOptions) === null || _a === void 0 ? void 0 : _a.virtualMap;
541
541
  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);
542
- if (!tablePath) {
542
+ if (tablePath === undefined) {
543
543
  throw new malloy_compile_error_1.MalloyCompileError(`No virtual-map entry for virtual source '${qs.structDef.name}' ` +
544
544
  `on connection '${qs.structDef.connection}'. ` +
545
- 'Add a virtual-map entry via the `virtualMap` runtime option.', 'runtime-virtual-map-missing', qs.structDef.location);
545
+ 'Supply the entry in `virtualMap` when compiling or running the model.', 'runtime-virtual-map-missing', qs.structDef.location);
546
+ }
547
+ // Check the binding immediately before SQL substitution, using the
548
+ // executing query's dialect. Other entries in the map may be unused.
549
+ const dialect = this.parent.dialect;
550
+ const checked = dialect.sqlValidateTableName(tablePath);
551
+ if (!checked.ok || checked.canonical !== tablePath) {
552
+ const reason = checked.ok
553
+ ? `use the canonical path ${checked.canonical}`
554
+ : checked.error;
555
+ throw new malloy_compile_error_1.MalloyCompileError(`virtualMap entry '${qs.structDef.connection}.${qs.structDef.name}' ` +
556
+ `has table path ${JSON.stringify(tablePath)}, which is not a ` +
557
+ `canonical table path for ${dialect.name}: ${reason}`, 'runtime-virtual-map-invalid', qs.structDef.location);
546
558
  }
547
- // virtualMap entries are application-supplied — assumed already
548
- // canonical SQL.
549
559
  return tablePath;
550
560
  }
551
561
  case 'composite':
@@ -676,9 +686,7 @@ class QueryQuery extends query_node_1.QueryField {
676
686
  return tempInstance.generateExpression();
677
687
  });
678
688
  }
679
- if (ji.children.length === 0 ||
680
- conditions === undefined ||
681
- !this.parent.dialect.supportsComplexFilteredSources) {
689
+ if (!ji.isSubquery()) {
682
690
  // LTNOTE: need a check here to see the children's where: conditions are local
683
691
  // to the source and not to any of it's joined children.
684
692
  // In Presto, we're going to get a SQL error if in this case
@@ -700,9 +708,27 @@ class QueryQuery extends query_node_1.QueryField {
700
708
  else {
701
709
  let select = `SELECT ${ji.alias}.*`;
702
710
  let joins = '';
711
+ // Every join below this one lives inside the subquery, so every one of
712
+ // them has to be projected out of it, not just the direct children.
713
+ // A child which became a subquery of its own has already flattened its
714
+ // whole subtree into columns, so everything under it is read from that
715
+ // child rather than packed a second time.
716
+ const projectJoin = (j, from) => {
717
+ const fieldList = this.joinProjectionFieldList(j);
718
+ if (fieldList.length > 0) {
719
+ select +=
720
+ from === undefined
721
+ ? `, ${this.parent.dialect.sqlSelectAliasAsStruct(j.alias, fieldList)} AS ${j.alias}`
722
+ : `, ${from}.${j.alias} AS ${j.alias}`;
723
+ }
724
+ const readFrom = from !== null && from !== void 0 ? from : (j.isSubquery() ? j.alias : undefined);
725
+ for (const below of j.children) {
726
+ projectJoin(below, readFrom);
727
+ }
728
+ };
703
729
  for (const childJoin of ji.children) {
704
730
  joins += this.generateSQLJoinBlock(stageWriter, childJoin, depth + 1);
705
- select += `, ${this.parent.dialect.sqlSelectAliasAsStruct(childJoin.alias, (0, utils_1.getDialectFieldList)(childJoin.queryStruct.structDef))} AS ${childJoin.alias}`;
731
+ projectJoin(childJoin, undefined);
706
732
  }
707
733
  select += `\nFROM ${structSQL} AS ${ji.alias}\n${joins}\nWHERE ${conditions === null || conditions === void 0 ? void 0 : conditions.join(' AND ')}\n`;
708
734
  s += `${matrixOperation} JOIN (\n${(0, utils_1.indent)(select)}) AS ${ji.alias}\n ON ${onCondition}\n`;
@@ -746,6 +772,111 @@ class QueryQuery extends query_node_1.QueryField {
746
772
  }
747
773
  return s;
748
774
  }
775
+ /**
776
+ * Which column of which join a reference needs, or undefined when it needs
777
+ * none. Walking `path` from `from`, the answer is the member of the
778
+ * innermost join along it -- a record is kept whole, since a reference into
779
+ * one is a reference to the column holding it.
780
+ *
781
+ * Two kinds of path need no column. One ending at a computed field
782
+ * contributes that field's name, which is no column of the source; the
783
+ * columns the computation reads arrive as references of their own. And a
784
+ * `having:` or `calculate:` names an *output* field, so its path is rooted
785
+ * in the query's output rather than its source and need not resolve here at
786
+ * all. An output name which happens to match a source column resolves as
787
+ * though it were one, which costs nothing: a query's own source is never
788
+ * packed, only the joins beneath a filtered one.
789
+ */
790
+ packedColumnFor(from, path) {
791
+ let struct = from;
792
+ let alias = struct.getIdentifier();
793
+ let column = undefined;
794
+ for (const name of path) {
795
+ const child = struct.getChildByName(name);
796
+ if (child === undefined)
797
+ return undefined;
798
+ if (!(child instanceof query_node_1.QueryFieldStruct)) {
799
+ column !== null && column !== void 0 ? column : (column = name);
800
+ break;
801
+ }
802
+ struct = child.queryStruct;
803
+ if (struct.getJoinableParent() === struct) {
804
+ // A join: what the path named above it says nothing about what this
805
+ // one has to pack.
806
+ alias = struct.getIdentifier();
807
+ column = undefined;
808
+ }
809
+ else {
810
+ column !== null && column !== void 0 ? column : (column = name);
811
+ }
812
+ }
813
+ return column === undefined ? undefined : [alias, column];
814
+ }
815
+ /**
816
+ * Which columns of each join have to survive being packed into a struct:
817
+ * every column the segment's field usage names, plus each join's primary
818
+ * key, which the compiler reaches for as a distinct key without any
819
+ * expression in the query naming it.
820
+ */
821
+ packedColumnsByJoin() {
822
+ var _a;
823
+ if (this.packedColumns === undefined) {
824
+ const packed = new Map();
825
+ const needed = (found) => {
826
+ if (found === undefined)
827
+ return;
828
+ const [alias, column] = found;
829
+ const columns = packed.get(alias);
830
+ if (columns) {
831
+ columns.add(column);
832
+ }
833
+ else {
834
+ packed.set(alias, new Set([column]));
835
+ }
836
+ };
837
+ const usage = (0, malloy_types_1.isRawSegment)(this.firstSegment)
838
+ ? []
839
+ : ((_a = this.firstSegment.expandedFieldUsage) !== null && _a !== void 0 ? _a : []);
840
+ for (const { path } of usage) {
841
+ needed(this.packedColumnFor(this.parent, path));
842
+ }
843
+ // What a primary key needs is what it reads: a computed key's name is
844
+ // not a column, and the columns its expression reads are named nowhere
845
+ // else in the query.
846
+ for (const ji of this.rootResult.joins.values()) {
847
+ const primaryKey = ji.queryStruct.primaryKey();
848
+ if (primaryKey === undefined)
849
+ continue;
850
+ const keyDef = primaryKey.fieldDef;
851
+ if ((0, malloy_types_1.hasExpression)(keyDef)) {
852
+ for (const { path } of (0, malloy_types_1.fieldUsageFrom)(keyDef.refSummary)) {
853
+ needed(this.packedColumnFor(ji.queryStruct, path));
854
+ }
855
+ }
856
+ else {
857
+ needed(this.packedColumnFor(ji.queryStruct, [(0, malloy_types_1.activeName)(keyDef)]));
858
+ }
859
+ }
860
+ this.packedColumns = packed;
861
+ }
862
+ return this.packedColumns;
863
+ }
864
+ // What a join contributes to the SELECT of a filtered join's subquery: every
865
+ // column named outside the subquery, and the generated __distinct_key when
866
+ // the join needed one.
867
+ joinProjectionFieldList(ji) {
868
+ const packed = this.packedColumnsByJoin().get(ji.alias);
869
+ const dialect = ji.queryStruct.dialect;
870
+ const fieldList = (0, utils_1.getDialectFieldList)(ji.queryStruct.structDef, dialect).filter(d => packed === null || packed === void 0 ? void 0 : packed.has(d.rawName));
871
+ if (ji.makeUniqueKey) {
872
+ fieldList.push({
873
+ typeDef: { type: 'string' },
874
+ sqlExpression: dialect.sqlQuoteIdentifier('__distinct_key'),
875
+ rawName: '__distinct_key',
876
+ });
877
+ }
878
+ return fieldList;
879
+ }
749
880
  // BigQuery has wildcard psudo columns that are treated differently
750
881
  // SELECT * FROM xxx doesn't include these psuedo columns but we need them so
751
882
  // filters can get pushed down properly when generating a UNIQUE key.
@@ -1510,8 +1641,6 @@ class QueryQuery extends query_node_1.QueryField {
1510
1641
  buildDialectFieldList(resultStruct) {
1511
1642
  const dialectFieldList = [];
1512
1643
  for (const [name, field] of resultStruct.allFields) {
1513
- const sqlName = this.parent.dialect.sqlQuoteIdentifier(name);
1514
- //
1515
1644
  if (resultStruct.firstSegment.type === 'reduce' &&
1516
1645
  field instanceof field_instance_1.FieldInstanceResult) {
1517
1646
  const { structDef, repeatedResultType } = this.generateTurtlePipelineSQL(field, new stage_writer_1.StageWriter(true, undefined), '<nosource>');
@@ -1527,7 +1656,6 @@ class QueryQuery extends query_node_1.QueryField {
1527
1656
  typeDef: multiLineNest,
1528
1657
  sqlExpression: this.parent.dialect.sqlQuoteIdentifier(`${name}__${resultStruct.groupSet}`),
1529
1658
  rawName: name,
1530
- sqlOutputName: sqlName,
1531
1659
  });
1532
1660
  }
1533
1661
  else {
@@ -1541,7 +1669,6 @@ class QueryQuery extends query_node_1.QueryField {
1541
1669
  typeDef: oneLineNest,
1542
1670
  sqlExpression: this.parent.dialect.sqlQuoteIdentifier(`${name}__${resultStruct.groupSet}`),
1543
1671
  rawName: name,
1544
- sqlOutputName: sqlName,
1545
1672
  });
1546
1673
  }
1547
1674
  }
@@ -1552,7 +1679,6 @@ class QueryQuery extends query_node_1.QueryField {
1552
1679
  fieldDef: field.f.fieldDef,
1553
1680
  sqlExpression: this.parent.dialect.sqlQuoteIdentifier(`${name}__${resultStruct.groupSet}`),
1554
1681
  rawName: name,
1555
- sqlOutputName: sqlName,
1556
1682
  });
1557
1683
  }
1558
1684
  else if (resultStruct.firstSegment.type === 'project' &&
@@ -1562,7 +1688,6 @@ class QueryQuery extends query_node_1.QueryField {
1562
1688
  fieldDef: field.f.fieldDef,
1563
1689
  sqlExpression: field.generateExpression(),
1564
1690
  rawName: name,
1565
- sqlOutputName: sqlName,
1566
1691
  });
1567
1692
  }
1568
1693
  }
@@ -1684,7 +1809,7 @@ class QueryQuery extends query_node_1.QueryField {
1684
1809
  const inputStruct = {
1685
1810
  type: 'nest_source',
1686
1811
  name: '~pipe~',
1687
- pipeSQL: this.parent.dialect.sqlUnnestPipelineHead(repeatedResultType === 'inline_all_numbers', sourceSQLExpression, (0, utils_1.getDialectFieldList)(structDef)),
1812
+ pipeSQL: this.parent.dialect.sqlUnnestPipelineHead(repeatedResultType === 'inline_all_numbers', sourceSQLExpression, (0, utils_1.getDialectFieldList)(structDef, this.parent.dialect)),
1688
1813
  fields: structDef.fields,
1689
1814
  connection: structDef.connection,
1690
1815
  dialect: structDef.dialect,
@@ -1,8 +1,8 @@
1
1
  import type { SQLSourceRequest } from '../lang/translate-response';
2
- import type { SQLPhraseSegment, ModelDef } from './malloy_types';
2
+ import type { SQLPhraseSegment, ModelDef, VirtualMap } from './malloy_types';
3
3
  /**
4
4
  * The translator needs to know the output schema of an SQLSourceDef
5
5
  * this will prepare an SQL string suitable to answer that question.
6
6
  */
7
- export declare function getSourceRequest(select: SQLPhraseSegment[], connection: string, partialModel: ModelDef | undefined): SQLSourceRequest;
7
+ export declare function getSourceRequest(select: SQLPhraseSegment[], connection: string, partialModel: ModelDef | undefined, virtualMap?: VirtualMap): SQLSourceRequest;
8
8
  export declare function sqlKey(connectionName: string, sql: string): string;
@@ -7,62 +7,26 @@ Object.defineProperty(exports, "__esModule", { value: true });
7
7
  exports.getSourceRequest = getSourceRequest;
8
8
  exports.sqlKey = sqlKey;
9
9
  const query_model_1 = require("./query_model");
10
- const malloy_types_1 = require("./malloy_types");
10
+ const sql_compiled_1 = require("./sql_compiled");
11
11
  const utils_1 = require("./utils");
12
12
  /**
13
13
  * The translator needs to know the output schema of an SQLSourceDef
14
14
  * this will prepare an SQL string suitable to answer that question.
15
15
  */
16
- function getSourceRequest(select, connection, partialModel) {
17
- let queryModel = undefined;
18
- let selectStr = '';
19
- let parenAlready = false;
20
- for (const segment of select) {
21
- if ((0, malloy_types_1.isSegmentSQL)(segment)) {
22
- selectStr += segment.sql;
23
- parenAlready = segment.sql.match(/\(\s*$/) !== null;
24
- }
25
- else if ((0, malloy_types_1.isSegmentSource)(segment)) {
26
- // PersistableSourceDef (sql_select or query_source)
27
- let compiledSql;
28
- if (segment.type === 'sql_select') {
29
- compiledSql = segment.selectStr;
30
- }
31
- else {
32
- // query_source - compile the inner query
33
- if (!queryModel) {
34
- if (!partialModel) {
35
- throw new Error('Internal error: Partial model missing when compiling SQL block');
36
- }
37
- queryModel = (0, query_model_1.makeQueryModel)(partialModel);
38
- }
39
- compiledSql = queryModel.compileQuery(segment.query, {
40
- defaultRowLimit: undefined,
41
- isPartialQuery: true,
42
- }, false).sql;
16
+ function getSourceRequest(select, connection, partialModel, virtualMap) {
17
+ let queryModel;
18
+ const compileQuery = (query, options) => {
19
+ if (!queryModel) {
20
+ if (!partialModel) {
21
+ throw new Error('Internal error: Partial model missing when compiling SQL block');
43
22
  }
44
- selectStr += parenAlready ? compiledSql : `(${compiledSql})`;
45
- parenAlready = false;
23
+ queryModel = (0, query_model_1.makeQueryModel)(partialModel);
46
24
  }
47
- else {
48
- // Query segment
49
- if (!queryModel) {
50
- if (!partialModel) {
51
- throw new Error('Internal error: Partial model missing when compiling SQL block');
52
- }
53
- queryModel = (0, query_model_1.makeQueryModel)(partialModel);
54
- }
55
- const compiledSql = queryModel.compileQuery(segment, {
56
- defaultRowLimit: undefined,
57
- isPartialQuery: true,
58
- }, false).sql;
59
- selectStr += parenAlready ? compiledSql : `(${compiledSql})`;
60
- parenAlready = false;
61
- }
62
- }
25
+ return queryModel.compileQuery(query, { ...options, defaultRowLimit: undefined, isPartialQuery: true }, false).sql;
26
+ };
63
27
  return {
64
28
  connection,
65
- selectStr,
29
+ selectStr: (0, sql_compiled_1.expandSQLSegments)(select, { virtualMap }, compileQuery),
66
30
  };
67
31
  }
68
32
  function sqlKey(connectionName, sql) {
@@ -1,4 +1,4 @@
1
- import type { PrepareResultOptions, SQLSourceDef, PersistableSourceDef, Query, VirtualMap } from './malloy_types';
1
+ import type { 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),
@@ -16,6 +16,8 @@ export type CompileQueryCallback = (query: Query, opts?: PrepareResultOptions) =
16
16
  * @param compileQuery Callback to compile a Query to SQL
17
17
  */
18
18
  export declare function getCompiledSQL(src: SQLSourceDef, opts: PrepareResultOptions, compileQuery: CompileQueryCallback): string;
19
+ /** Expand SQL text and interpolations for schema discovery or execution. */
20
+ export declare function expandSQLSegments(segments: SQLPhraseSegment[], opts: PrepareResultOptions, compileQuery: CompileQueryCallback): string;
19
21
  /**
20
22
  * The options a BuildID's SQL is compiled under — everything the builder and
21
23
  * the compiler must both know, and nothing else.
@@ -5,6 +5,7 @@
5
5
  */
6
6
  Object.defineProperty(exports, "__esModule", { value: true });
7
7
  exports.getCompiledSQL = getCompiledSQL;
8
+ exports.expandSQLSegments = expandSQLSegments;
8
9
  exports.buildIdOptions = buildIdOptions;
9
10
  exports.persistedTableFor = persistedTableFor;
10
11
  exports.getSourceSQL = getSourceSQL;
@@ -26,12 +27,13 @@ function getCompiledSQL(src, opts, compileQuery) {
26
27
  if (!src.selectSegments || src.selectSegments.length === 0) {
27
28
  return src.selectStr;
28
29
  }
29
- // Expand each segment
30
- const parts = [];
31
- for (const segment of src.selectSegments) {
32
- parts.push(expandSegment(segment, opts, compileQuery));
33
- }
34
- return parts.join('');
30
+ return expandSQLSegments(src.selectSegments, opts, compileQuery);
31
+ }
32
+ /** Expand SQL text and interpolations for schema discovery or execution. */
33
+ function expandSQLSegments(segments, opts, compileQuery) {
34
+ return segments
35
+ .map(segment => expandSegment(segment, opts, compileQuery))
36
+ .join('');
35
37
  }
36
38
  /**
37
39
  * Expand a single SQLPhraseSegment to SQL.
@@ -51,7 +51,7 @@ class StageWriter {
51
51
  if (lastStageName === undefined) {
52
52
  throw new Error('Internal Error: no stage to combine');
53
53
  }
54
- sql += dialect.sqlCreateFunctionCombineLastStage(lastStageName, (0, utils_1.getDialectFieldList)(structDef), (_a = structDef.resultMetadata) === null || _a === void 0 ? void 0 : _a.orderBy);
54
+ sql += dialect.sqlCreateFunctionCombineLastStage(lastStageName, (0, utils_1.getDialectFieldList)(structDef, dialect), (_a = structDef.resultMetadata) === null || _a === void 0 ? void 0 : _a.orderBy);
55
55
  const id = `${dialect.udfPrefix}${this.root().udfs.length}`;
56
56
  sql = dialect.sqlCreateFunction(id, sql);
57
57
  this.root().udfs.push(sql);
@@ -98,11 +98,11 @@ class StageWriter {
98
98
  generateCoorelatedSubQuery(dialect, structDef) {
99
99
  var _a, _b;
100
100
  if (!this.useCTE) {
101
- return dialect.sqlCreateFunctionCombineLastStage(`(${this.withs[0]})`, (0, utils_1.getDialectFieldList)(structDef), (_a = structDef.resultMetadata) === null || _a === void 0 ? void 0 : _a.orderBy);
101
+ return dialect.sqlCreateFunctionCombineLastStage(`(${this.withs[0]})`, (0, utils_1.getDialectFieldList)(structDef, dialect), (_a = structDef.resultMetadata) === null || _a === void 0 ? void 0 : _a.orderBy);
102
102
  }
103
103
  else {
104
104
  return (this.combineStages(true).sql +
105
- dialect.sqlCreateFunctionCombineLastStage(this.getName(this.withs.length - 1), (0, utils_1.getDialectFieldList)(structDef), (_b = structDef.resultMetadata) === null || _b === void 0 ? void 0 : _b.orderBy));
105
+ dialect.sqlCreateFunctionCombineLastStage(this.getName(this.withs.length - 1), (0, utils_1.getDialectFieldList)(structDef, dialect), (_b = structDef.resultMetadata) === null || _b === void 0 ? void 0 : _b.orderBy));
106
106
  }
107
107
  }
108
108
  }
@@ -1,5 +1,5 @@
1
1
  import type { AtomicTypeDef, Expr, FieldDef, GenericSQLExpr, GivenTypeDef, ModelDef, ModelID, StructDef } from './malloy_types';
2
- import type { DialectFieldList } from '../dialect';
2
+ import type { Dialect, DialectFieldList } from '../dialect';
3
3
  /**
4
4
  * Format a typeDef as user-readable Malloy type syntax. Used in error
5
5
  * messages where we want to echo a compound type back to the user in the
@@ -58,7 +58,7 @@ export type SQLExprElement = string | Expr;
58
58
  * expression node.
59
59
  */
60
60
  export declare function composeSQLExpr(from: SQLExprElement[]): GenericSQLExpr;
61
- export declare function getDialectFieldList(structDef: StructDef): DialectFieldList;
61
+ export declare function getDialectFieldList(structDef: StructDef, dialect: Dialect): DialectFieldList;
62
62
  /**
63
63
  * A little extra paranoia to save us from fields which contain the special
64
64
  * characters used to build the grouping key.
@@ -251,14 +251,13 @@ function composeSQLExpr(from) {
251
251
  }
252
252
  return ret;
253
253
  }
254
- function getDialectFieldList(structDef) {
254
+ function getDialectFieldList(structDef, dialect) {
255
255
  const dialectFieldList = [];
256
256
  for (const f of structDef.fields.filter(malloy_types_1.fieldIsIntrinsic)) {
257
257
  dialectFieldList.push({
258
258
  typeDef: f,
259
- sqlExpression: (0, malloy_types_1.activeName)(f),
259
+ sqlExpression: dialect.sqlQuoteIdentifier((0, malloy_types_1.activeName)(f)),
260
260
  rawName: (0, malloy_types_1.activeName)(f),
261
- sqlOutputName: (0, malloy_types_1.activeName)(f),
262
261
  });
263
262
  }
264
263
  return dialectFieldList;
package/dist/version.d.ts CHANGED
@@ -1 +1 @@
1
- export declare const MALLOY_VERSION = "0.0.433";
1
+ export declare const MALLOY_VERSION = "0.0.434";
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.433';
5
+ exports.MALLOY_VERSION = '0.0.434';
6
6
  //# sourceMappingURL=version.js.map
@@ -1,3 +1,5 @@
1
+ # Config format: node ../../scripts/femto-build.js --help
2
+
1
3
  lexer: {
2
4
  inputs = ["src/lang/grammar/MalloyLexer.g4"]
3
5
  outputs = ["src/lang/lib/Malloy/MalloyLexer.ts"]
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@malloydata/malloy",
3
- "version": "0.0.433",
3
+ "version": "0.0.434",
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.433",
55
- "@malloydata/malloy-interfaces": "0.0.433",
56
- "@malloydata/malloy-tag": "0.0.433",
54
+ "@malloydata/malloy-filter": "0.0.434",
55
+ "@malloydata/malloy-interfaces": "0.0.434",
56
+ "@malloydata/malloy-tag": "0.0.434",
57
57
  "@noble/hashes": "^1.8.0",
58
58
  "antlr4ts": "^0.5.0-alpha.4",
59
59
  "assert": "^2.0.0",