@malloydata/malloy 0.0.393 → 0.0.395

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 (71) hide show
  1. package/dist/api/foundation/config.d.ts +2 -3
  2. package/dist/api/foundation/config.js +23 -11
  3. package/dist/api/foundation/core.d.ts +0 -4
  4. package/dist/api/foundation/core.js +14 -11
  5. package/dist/api/foundation/runtime.js +21 -1
  6. package/dist/api/util.js +4 -0
  7. package/dist/connection/base_connection.js +6 -0
  8. package/dist/connection/validate_table_path.d.ts +10 -0
  9. package/dist/connection/validate_table_path.js +56 -0
  10. package/dist/dialect/databricks/databricks.d.ts +4 -4
  11. package/dist/dialect/databricks/databricks.js +17 -22
  12. package/dist/dialect/dialect.d.ts +100 -4
  13. package/dist/dialect/dialect.js +145 -1
  14. package/dist/dialect/duckdb/duckdb.d.ts +2 -3
  15. package/dist/dialect/duckdb/duckdb.js +12 -14
  16. package/dist/dialect/duckdb/table-path-parser.d.ts +2 -0
  17. package/dist/dialect/duckdb/table-path-parser.js +57 -0
  18. package/dist/dialect/index.d.ts +2 -0
  19. package/dist/dialect/index.js +4 -1
  20. package/dist/dialect/mysql/mysql.d.ts +4 -4
  21. package/dist/dialect/mysql/mysql.js +25 -20
  22. package/dist/dialect/pg_impl.d.ts +3 -1
  23. package/dist/dialect/pg_impl.js +6 -3
  24. package/dist/dialect/postgres/postgres.d.ts +1 -3
  25. package/dist/dialect/postgres/postgres.js +8 -16
  26. package/dist/dialect/snowflake/snowflake.d.ts +4 -4
  27. package/dist/dialect/snowflake/snowflake.js +11 -27
  28. package/dist/dialect/standardsql/standardsql.d.ts +6 -4
  29. package/dist/dialect/standardsql/standardsql.js +36 -15
  30. package/dist/dialect/table-path.d.ts +54 -0
  31. package/dist/dialect/table-path.js +144 -0
  32. package/dist/dialect/trino/trino.d.ts +0 -3
  33. package/dist/dialect/trino/trino.js +7 -20
  34. package/dist/index.d.ts +2 -2
  35. package/dist/index.js +4 -2
  36. package/dist/lang/ast/expressions/expr-compare.d.ts +15 -0
  37. package/dist/lang/ast/expressions/expr-compare.js +82 -2
  38. package/dist/lang/ast/source-elements/table-source.d.ts +1 -7
  39. package/dist/lang/ast/source-elements/table-source.js +20 -19
  40. package/dist/lang/ast/statements/define-given.d.ts +2 -1
  41. package/dist/lang/ast/statements/define-given.js +52 -1
  42. package/dist/lang/ast/types/malloy-element.js +2 -0
  43. package/dist/lang/lib/Malloy/MalloyParser.d.ts +188 -167
  44. package/dist/lang/lib/Malloy/MalloyParser.js +2582 -2442
  45. package/dist/lang/lib/Malloy/MalloyParserListener.d.ts +24 -0
  46. package/dist/lang/lib/Malloy/MalloyParserVisitor.d.ts +15 -0
  47. package/dist/lang/malloy-to-ast.d.ts +9 -2
  48. package/dist/lang/malloy-to-ast.js +37 -2
  49. package/dist/lang/parse-log.d.ts +23 -0
  50. package/dist/lang/parse-log.js +6 -0
  51. package/dist/lang/parse-malloy.js +37 -7
  52. package/dist/lang/parse-tree-walkers/find-external-references.d.ts +2 -15
  53. package/dist/lang/parse-tree-walkers/find-external-references.js +6 -23
  54. package/dist/lang/test/expr-to-str.js +3 -0
  55. package/dist/lang/translate-response.d.ts +1 -1
  56. package/dist/model/expression_compiler.js +38 -11
  57. package/dist/model/filter_compilers.js +1 -1
  58. package/dist/model/given_binding.d.ts +15 -0
  59. package/dist/model/given_binding.js +35 -0
  60. package/dist/model/inline_expr.d.ts +30 -0
  61. package/dist/model/inline_expr.js +184 -0
  62. package/dist/model/malloy_types.d.ts +19 -1
  63. package/dist/model/query_model_impl.js +7 -7
  64. package/dist/model/query_query.d.ts +1 -1
  65. package/dist/model/query_query.js +37 -33
  66. package/dist/model/sql_compiled.d.ts +2 -4
  67. package/dist/model/sql_compiled.js +14 -15
  68. package/dist/test/test-models.js +2 -2
  69. package/dist/version.d.ts +1 -1
  70. package/dist/version.js +1 -1
  71. package/package.json +4 -4
@@ -0,0 +1,184 @@
1
+ "use strict";
2
+ /*
3
+ * Copyright Contributors to the Malloy project
4
+ * SPDX-License-Identifier: MIT
5
+ */
6
+ Object.defineProperty(exports, "__esModule", { value: true });
7
+ exports.INLINE_LEAVES = exports.INLINE_OPS = void 0;
8
+ exports.inlineExpr = inlineExpr;
9
+ /**
10
+ * Operator node types allowed inside an `inline` given's default. Single
11
+ * source of truth — both the translator-side validator
12
+ * (`GivenDeclaration.execute`) and the bind-time evaluator below read
13
+ * this set when deciding whether an expression is reducible to a
14
+ * literal at bind time.
15
+ *
16
+ * Add a new operator here AND a corresponding `case` to `inlineExpr`'s
17
+ * switch in the same commit; the two are intentionally co-located so
18
+ * they can't drift apart.
19
+ */
20
+ exports.INLINE_OPS = new Set([
21
+ 'and',
22
+ 'or',
23
+ 'not',
24
+ '=',
25
+ '!=',
26
+ '>',
27
+ '<',
28
+ '>=',
29
+ '<=',
30
+ 'inGiven',
31
+ '()',
32
+ ]);
33
+ /**
34
+ * Leaf-shaped node types that are valid inside an inline expression but
35
+ * aren't "operators." Literals are returned as-is by the evaluator;
36
+ * `given` nodes resolve through the bound values map.
37
+ */
38
+ exports.INLINE_LEAVES = new Set([
39
+ 'stringLiteral',
40
+ 'numberLiteral',
41
+ 'true',
42
+ 'false',
43
+ 'null',
44
+ 'arrayLiteral',
45
+ 'given',
46
+ ]);
47
+ /**
48
+ * Bind-time evaluator for `inline` given defaults. Walks the Expr tree,
49
+ * recursing on bound values for given-refs, and returns a literal Expr
50
+ * (string/number/boolean/null/arrayLiteral).
51
+ *
52
+ * Throws on any node outside `INLINE_OPS ∪ INLINE_LEAVES`. The
53
+ * translator's pre-flight check should have rejected such defaults
54
+ * already, so a throw here flags a compiler bug rather than a caller
55
+ * error.
56
+ */
57
+ function inlineExpr(e, bound) {
58
+ switch (e.node) {
59
+ case 'stringLiteral':
60
+ case 'numberLiteral':
61
+ case 'true':
62
+ case 'false':
63
+ case 'null':
64
+ case 'arrayLiteral':
65
+ return e;
66
+ case 'given': {
67
+ const v = bound.get(e.id);
68
+ if (v === undefined) {
69
+ throw new Error(`inlineExpr: given '${e.refName}' has no bound value and no default — translator should have caught this earlier`);
70
+ }
71
+ return inlineExpr(v, bound);
72
+ }
73
+ case '()':
74
+ return inlineExpr(e.e, bound);
75
+ case 'not': {
76
+ const inner = inlineExpr(e.e, bound);
77
+ return toBoolLiteral(!exprAsBool(inner));
78
+ }
79
+ case 'and':
80
+ case 'or': {
81
+ const left = exprAsBool(inlineExpr(e.kids.left, bound));
82
+ const right = exprAsBool(inlineExpr(e.kids.right, bound));
83
+ return toBoolLiteral(e.node === 'and' ? left && right : left || right);
84
+ }
85
+ case '=':
86
+ case '!=':
87
+ case '>':
88
+ case '<':
89
+ case '>=':
90
+ case '<=': {
91
+ const left = inlineExpr(e.kids.left, bound);
92
+ const right = inlineExpr(e.kids.right, bound);
93
+ return toBoolLiteral(compareLiterals(e.node, left, right));
94
+ }
95
+ case 'inGiven': {
96
+ const lhs = inlineExpr(e.e, bound);
97
+ const arrBound = bound.get(e.givenRef.id);
98
+ if (arrBound === undefined) {
99
+ throw new Error(`inlineExpr: given '${e.givenRef.refName}' has no bound value and no default — translator should have caught this earlier`);
100
+ }
101
+ if (arrBound.node === 'null') {
102
+ return toBoolLiteral(e.not);
103
+ }
104
+ if (arrBound.node !== 'arrayLiteral') {
105
+ throw new Error(`inlineExpr: 'inGiven' bound to '${arrBound.node}', expected 'arrayLiteral'`);
106
+ }
107
+ const found = arrBound.kids.values.some(v => compareLiterals('=', lhs, inlineExpr(v, bound)));
108
+ return toBoolLiteral(e.not ? !found : found);
109
+ }
110
+ default:
111
+ throw new Error(`inlineExpr: unexpected node '${e.node}' — translator should have rejected this`);
112
+ }
113
+ }
114
+ function toBoolLiteral(b) {
115
+ return { node: b ? 'true' : 'false' };
116
+ }
117
+ /**
118
+ * Read a literal Expr as a JS boolean. The evaluator only ever produces
119
+ * 'true' / 'false' / 'null' / scalar literals here; anything else means
120
+ * we tried to use a non-boolean where a boolean was expected, which is a
121
+ * translator type-check bug.
122
+ */
123
+ function exprAsBool(e) {
124
+ if (e.node === 'true')
125
+ return true;
126
+ if (e.node === 'false')
127
+ return false;
128
+ // `null` in boolean position is treated as false — matches the
129
+ // implicit SQL coalesce that Malloy does for filter expressions.
130
+ if (e.node === 'null')
131
+ return false;
132
+ throw new Error(`inlineExpr: expected boolean literal, got '${e.node}' — translator should have type-checked this`);
133
+ }
134
+ /**
135
+ * JS-side comparison of two literal Exprs. Only the literal nodes
136
+ * produced by the evaluator are supported.
137
+ *
138
+ * SQL nullability: any operand being null produces false (matching
139
+ * Malloy's COALESCE-to-true / COALESCE-to-false behavior elsewhere).
140
+ */
141
+ function compareLiterals(op, left, right) {
142
+ if (left.node === 'null' || right.node === 'null') {
143
+ // Mirror SQL: comparisons with NULL are unknown; treat as false for
144
+ // = / > / < / >= / <=, true for !=.
145
+ return op === '!=';
146
+ }
147
+ const l = literalToJS(left);
148
+ const r = literalToJS(right);
149
+ switch (op) {
150
+ case '=':
151
+ return l === r;
152
+ case '!=':
153
+ return l !== r;
154
+ case '>':
155
+ return l > r;
156
+ case '<':
157
+ return l < r;
158
+ case '>=':
159
+ return l >= r;
160
+ case '<=':
161
+ return l <= r;
162
+ }
163
+ }
164
+ /**
165
+ * Convert a literal Expr to a comparable JS value. Numbers come through
166
+ * `numberLiteral` as a string (full precision preserved); we parse to a
167
+ * JS number for ordering — bind-time precision loss here is fine
168
+ * because the result of an inline expression is a boolean for SQL.
169
+ */
170
+ function literalToJS(e) {
171
+ switch (e.node) {
172
+ case 'stringLiteral':
173
+ return e.literal;
174
+ case 'numberLiteral':
175
+ return Number(e.literal);
176
+ case 'true':
177
+ return true;
178
+ case 'false':
179
+ return false;
180
+ default:
181
+ throw new Error(`inlineExpr: cannot compare non-literal node '${e.node}'`);
182
+ }
183
+ }
184
+ //# sourceMappingURL=inline_expr.js.map
@@ -36,7 +36,7 @@ export type AnyExpr = ExprE | ExprOptionalE | ExprWithKids | ExprLeaf;
36
36
  export declare function exprHasKids(e: AnyExpr): e is ExprWithKids;
37
37
  export declare function exprHasE(e: AnyExpr): e is ExprE;
38
38
  export declare function exprIsLeaf(e: AnyExpr): boolean;
39
- export type Expr = BinaryExpr | UnaryExpr | FunctionCallNode | OutputFieldNode | FilterCondition | FilteredExpr | AggregateExpr | EmptyExpr | UngroupNode | FunctionParameterNode | SpreadExpr | AggregateOrderByNode | AggregateLimitNode | FieldnameNode | SourceReferenceNode | ParameterNode | GivenRefNode | NowNode | MeasureTimeExpr | TimeExtractExpr | TimeDeltaExpr | TimeTruncExpr | DateLiteralNode | TimestampLiteralNode | TimestamptzLiteralNode | TypecastExpr | RegexMatchExpr | RegexLiteralNode | FilterMatchExpr | FilterLiteralExpr | StringLiteralNode | NumberLiteralNode | BooleanLiteralNode | RecordLiteralNode | ArrayLiteralNode | FunctionOrderBy | GenericSQLExpr | NullNode | CaseExpr | InCompareExpr | CompositeFieldExpr | ErrorNode;
39
+ export type Expr = BinaryExpr | UnaryExpr | FunctionCallNode | OutputFieldNode | FilterCondition | FilteredExpr | AggregateExpr | EmptyExpr | UngroupNode | FunctionParameterNode | SpreadExpr | AggregateOrderByNode | AggregateLimitNode | FieldnameNode | SourceReferenceNode | ParameterNode | GivenRefNode | NowNode | MeasureTimeExpr | TimeExtractExpr | TimeDeltaExpr | TimeTruncExpr | DateLiteralNode | TimestampLiteralNode | TimestamptzLiteralNode | TypecastExpr | RegexMatchExpr | RegexLiteralNode | FilterMatchExpr | FilterLiteralExpr | StringLiteralNode | NumberLiteralNode | BooleanLiteralNode | RecordLiteralNode | ArrayLiteralNode | FunctionOrderBy | GenericSQLExpr | NullNode | CaseExpr | InCompareExpr | InGivenExpr | CompositeFieldExpr | ErrorNode;
40
40
  export type BinaryOperator = '+' | '-' | '*' | '%' | '/' | 'and' | 'or' | '=' | '!=' | '>' | '<' | '>=' | '<=' | 'coalesce' | 'like' | '!like';
41
41
  export interface BinaryExpr extends ExprWithKids {
42
42
  node: BinaryOperator;
@@ -289,6 +289,19 @@ export interface InCompareExpr extends ExprWithKids {
289
289
  oneOf: Expr[];
290
290
  };
291
291
  }
292
+ /**
293
+ * Test against a runtime-bound array given: `expr in $ARRAY_GIVEN`.
294
+ *
295
+ * Uses ExprE (one child `e`, the LHS). The given reference is embedded
296
+ * as a top-level field rather than a kid so the auto-visitor in
297
+ * `compileExpr` doesn't descend into it; resolution and per-element
298
+ * SQL emission happen in the `case 'inGiven':` handler.
299
+ */
300
+ export interface InGivenExpr extends ExprE {
301
+ node: 'inGiven';
302
+ not: boolean;
303
+ givenRef: GivenRefNode;
304
+ }
292
305
  export type ExpressionType = 'scalar' | 'aggregate' | 'scalar_analytic' | 'aggregate_analytic' | 'ungrouped_aggregate';
293
306
  export interface Expression {
294
307
  e?: Expr;
@@ -341,6 +354,11 @@ export interface Given extends HasLocation, HasAnnotation {
341
354
  * its own default. Empty/undefined when the default is a closed
342
355
  * literal. */
343
356
  givenUsage?: GivenUsage;
357
+ /** Marked with the `inline` modifier — the default is eager-evaluated
358
+ * to a literal at bind time and substituted as that literal in SQL.
359
+ * Translator validates the default is eager-evaluable; bind-time
360
+ * evaluator (`model/inline_expr.ts`) performs the reduction. */
361
+ inline?: boolean;
344
362
  }
345
363
  export interface GivenEntry {
346
364
  type: 'given';
@@ -107,12 +107,12 @@ class QueryModelImpl {
107
107
  const fieldNames = [];
108
108
  for (const f of ret.outputStruct.fields) {
109
109
  if ((0, malloy_types_1.isAtomic)(f)) {
110
- const quoted = q.parent.dialect.sqlMaybeQuoteIdentifier(f.name);
110
+ const quoted = q.parent.dialect.sqlQuoteIdentifier(f.name);
111
111
  fieldNames.push(quoted);
112
112
  }
113
113
  }
114
114
  // const fieldNames = getAtomicFields(ret.outputStruct).map(fieldDef =>
115
- // q.parent.dialect.sqlMaybeQuoteIdentifier(fieldDef.name)
115
+ // q.parent.dialect.sqlQuoteIdentifier(fieldDef.name)
116
116
  // );
117
117
  ret.lastStageName = stageWriter.addStage(q.parent.dialect.sqlFinalStage(ret.lastStageName, fieldNames));
118
118
  }
@@ -217,11 +217,11 @@ class QueryModelImpl {
217
217
  },
218
218
  ],
219
219
  };
220
- const fieldNameColumn = d.sqlMaybeQuoteIdentifier('fieldName');
221
- const fieldPathColumn = d.sqlMaybeQuoteIdentifier('fieldPath');
222
- const fieldValueColumn = d.sqlMaybeQuoteIdentifier('fieldValue');
223
- const fieldTypeColumn = d.sqlMaybeQuoteIdentifier('fieldType');
224
- const weightColumn = d.sqlMaybeQuoteIdentifier('weight');
220
+ const fieldNameColumn = d.sqlQuoteIdentifier('fieldName');
221
+ const fieldPathColumn = d.sqlQuoteIdentifier('fieldPath');
222
+ const fieldValueColumn = d.sqlQuoteIdentifier('fieldValue');
223
+ const fieldTypeColumn = d.sqlQuoteIdentifier('fieldType');
224
+ const weightColumn = d.sqlQuoteIdentifier('weight');
225
225
  // if we've compiled the SQL before use it otherwise
226
226
  let sqlPDT = this.exploreSearchSQLMap.get(explore);
227
227
  if (sqlPDT === undefined) {
@@ -103,7 +103,7 @@ export declare class QueryQuery extends QueryField {
103
103
  generateTurtlePipelineSQL(fi: FieldInstanceResult, stageWriter: StageWriter, sourceSQLExpression: string): {
104
104
  structDef: QueryResultDef;
105
105
  pipeOut: any;
106
- repeatedResultType: "nested" | "inline_all_numbers" | "inline";
106
+ repeatedResultType: "inline" | "nested" | "inline_all_numbers";
107
107
  };
108
108
  generateComplexSQL(stageWriter: StageWriter): string;
109
109
  generateSQL(stageWriter: StageWriter): string;
@@ -517,14 +517,17 @@ class QueryQuery extends query_node_1.QueryField {
517
517
  var _a, _b, _c, _d;
518
518
  switch (qs.structDef.type) {
519
519
  case 'table':
520
- return this.parent.dialect.quoteTablePath(qs.structDef.tablePath);
520
+ // tablePath is canonical SQL — translator pre-validated.
521
+ return qs.structDef.tablePath;
521
522
  case 'virtual': {
522
523
  const virtualMap = (_a = qs.prepareResultOptions) === null || _a === void 0 ? void 0 : _a.virtualMap;
523
524
  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
525
  if (!tablePath) {
525
526
  throw new Error(`No virtual map entry for '${qs.structDef.name}' on connection '${qs.structDef.connection}'`);
526
527
  }
527
- return this.parent.dialect.quoteTablePath(tablePath);
528
+ // virtualMap entries are application-supplied — assumed already
529
+ // canonical SQL.
530
+ return tablePath;
528
531
  }
529
532
  case 'composite':
530
533
  // TODO: throw an error here; not simple because we call into this
@@ -533,7 +536,7 @@ class QueryQuery extends query_node_1.QueryField {
533
536
  case 'finalize':
534
537
  return qs.structDef.name;
535
538
  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) => {
539
+ return `(${(0, sql_compiled_1.getCompiledSQL)(qs.structDef, (_c = qs.prepareResultOptions) !== null && _c !== void 0 ? _c : {}, (query, opts) => {
537
540
  // Compile query to isolated SQL (not into parent's stageWriter)
538
541
  const ret = this.compileQueryToStages(query, opts !== null && opts !== void 0 ? opts : {}, undefined, false);
539
542
  return ret.sql;
@@ -551,8 +554,9 @@ class QueryQuery extends query_node_1.QueryField {
551
554
  const buildId = (0, source_def_utils_1.mkBuildID)(connDigest, fullRet.sql);
552
555
  const entry = buildManifest.entries[buildId];
553
556
  if (entry) {
554
- // Found in manifest - use persisted table
555
- return this.parent.dialect.quoteTablePath(entry.tableName);
557
+ // Found in manifest - use persisted table.
558
+ // entry.tableName comes from the manifest, assumed canonical.
559
+ return entry.tableName;
556
560
  }
557
561
  if (buildManifest.strict) {
558
562
  const base = `Persist source '${qs.structDef.sourceID}' not found in manifest (buildId: ${buildId})`;
@@ -631,7 +635,7 @@ class QueryQuery extends query_node_1.QueryField {
631
635
  }
632
636
  if (ji.makeUniqueKey) {
633
637
  const passKeys = this.generateSQLPassthroughKeys(qs);
634
- structSQL = `(SELECT ${qs.dialect.sqlGenerateUUID()} as ${qs.dialect.sqlMaybeQuoteIdentifier('__distinct_key')}, x.* ${passKeys} FROM ${structSQL} as x)`;
638
+ structSQL = `(SELECT ${qs.dialect.sqlGenerateUUID()} as ${qs.dialect.sqlQuoteIdentifier('__distinct_key')}, x.* ${passKeys} FROM ${structSQL} as x)`;
635
639
  }
636
640
  let onCondition = '';
637
641
  if (qs.parent === undefined) {
@@ -764,7 +768,7 @@ class QueryQuery extends query_node_1.QueryField {
764
768
  if ((0, malloy_types_1.isBaseTable)(qs.structDef)) {
765
769
  if (ji.makeUniqueKey) {
766
770
  const passKeys = this.generateSQLPassthroughKeys(qs);
767
- structSQL = `(SELECT ${qs.dialect.sqlGenerateUUID()} as ${qs.dialect.sqlMaybeQuoteIdentifier('__distinct_key')}, x.* ${passKeys} FROM ${structSQL} as x)`;
771
+ structSQL = `(SELECT ${qs.dialect.sqlGenerateUUID()} as ${qs.dialect.sqlQuoteIdentifier('__distinct_key')}, x.* ${passKeys} FROM ${structSQL} as x)`;
768
772
  }
769
773
  s += `FROM ${structSQL} as ${ji.alias}\n`;
770
774
  }
@@ -846,7 +850,7 @@ class QueryQuery extends query_node_1.QueryField {
846
850
  o.push(`${fi.fieldUsage.resultIndex} ${f.dir || 'ASC'}`);
847
851
  }
848
852
  else if (this.parent.dialect.orderByClause === 'output_name') {
849
- o.push(`${this.parent.dialect.sqlMaybeQuoteIdentifier(f.field)} ${f.dir || 'ASC'}`);
853
+ o.push(`${this.parent.dialect.sqlQuoteIdentifier(f.field)} ${f.dir || 'ASC'}`);
850
854
  }
851
855
  else if (this.parent.dialect.orderByClause === 'expression') {
852
856
  const fieldExpr = fi.getSQL();
@@ -863,7 +867,7 @@ class QueryQuery extends query_node_1.QueryField {
863
867
  }
864
868
  else if (this.parent.dialect.orderByClause === 'output_name') {
865
869
  const orderingField = resultStruct.getFieldByNumber(f.field);
866
- o.push(`${this.parent.dialect.sqlMaybeQuoteIdentifier(orderingField.name)} ${f.dir || 'ASC'}`);
870
+ o.push(`${this.parent.dialect.sqlQuoteIdentifier(orderingField.name)} ${f.dir || 'ASC'}`);
867
871
  }
868
872
  else if (this.parent.dialect.orderByClause === 'expression') {
869
873
  const orderingField = resultStruct.getFieldByNumber(f.field);
@@ -887,7 +891,7 @@ class QueryQuery extends query_node_1.QueryField {
887
891
  const fields = [];
888
892
  for (const [name, field] of this.rootResult.allFields) {
889
893
  const fi = field;
890
- const sqlName = this.parent.dialect.sqlMaybeQuoteIdentifier(name);
894
+ const sqlName = this.parent.dialect.sqlQuoteIdentifier(name);
891
895
  if (fi.fieldUsage.type === 'result') {
892
896
  fields.push(` ${fi.generateExpression()} as ${sqlName}`);
893
897
  }
@@ -937,7 +941,7 @@ class QueryQuery extends query_node_1.QueryField {
937
941
  .map(o => `${o.pipelineSQL} as ${o.sqlFieldName}`)
938
942
  .join(',\n');
939
943
  const outputFields = outputPipelinedSQL.map(f => f.sqlFieldName);
940
- const allFields = Array.from(this.rootResult.allFields.keys()).map(f => this.parent.dialect.sqlMaybeQuoteIdentifier(f));
944
+ const allFields = Array.from(this.rootResult.allFields.keys()).map(f => this.parent.dialect.sqlQuoteIdentifier(f));
941
945
  const fields = allFields.filter(f => outputFields.indexOf(f) === -1);
942
946
  retSQL = `SELECT ${fields.length > 0 ? fields.join(', ') + ',' : ''} ${pipelinesSQL} FROM ${lastStageName}`;
943
947
  }
@@ -956,7 +960,7 @@ class QueryQuery extends query_node_1.QueryField {
956
960
  }
957
961
  const orderedFields = [...scalarFields, ...otherFields];
958
962
  for (const [name, fi] of orderedFields) {
959
- const outputName = this.parent.dialect.sqlMaybeQuoteIdentifier(`${name}__${resultSet.groupSet}`);
963
+ const outputName = this.parent.dialect.sqlQuoteIdentifier(`${name}__${resultSet.groupSet}`);
960
964
  if (fi instanceof field_instance_1.FieldInstanceField) {
961
965
  if (fi.fieldUsage.type === 'result') {
962
966
  const exp = fi.getSQL();
@@ -975,7 +979,7 @@ class QueryQuery extends query_node_1.QueryField {
975
979
  });
976
980
  output.sql.push(outputFieldName);
977
981
  if (fi.f.fieldDef.type === 'number') {
978
- const outputNameString = this.parent.dialect.sqlMaybeQuoteIdentifier(`${name}__${resultSet.groupSet}_string`);
982
+ const outputNameString = this.parent.dialect.sqlQuoteIdentifier(`${name}__${resultSet.groupSet}_string`);
979
983
  const outputFieldNameString = `__lateral_join_bag.${outputNameString}`;
980
984
  output.sql.push(outputFieldNameString);
981
985
  output.dimensionIndexes.push(output.fieldIndex++);
@@ -1086,7 +1090,7 @@ class QueryQuery extends query_node_1.QueryField {
1086
1090
  let r = result;
1087
1091
  while (r) {
1088
1092
  for (const name of r.fieldNames(fi => (0, query_node_1.isScalarField)(fi.f))) {
1089
- dimensions.push(this.parent.dialect.sqlMaybeQuoteIdentifier(`${name}__${r.groupSet}`));
1093
+ dimensions.push(this.parent.dialect.sqlQuoteIdentifier(`${name}__${r.groupSet}`));
1090
1094
  }
1091
1095
  r = r.parent;
1092
1096
  }
@@ -1120,7 +1124,7 @@ class QueryQuery extends query_node_1.QueryField {
1120
1124
  orderingField = result.getFieldByNumber(ordering.field);
1121
1125
  }
1122
1126
  obSQL.push(' ' +
1123
- this.parent.dialect.sqlMaybeQuoteIdentifier(`${orderingField.name}__${result.groupSet}`) +
1127
+ this.parent.dialect.sqlQuoteIdentifier(`${orderingField.name}__${result.groupSet}`) +
1124
1128
  ` ${ordering.dir || 'ASC'}`);
1125
1129
  }
1126
1130
  // partition for a row number is the parent if it exists.
@@ -1225,7 +1229,7 @@ class QueryQuery extends query_node_1.QueryField {
1225
1229
  generateDepthNFields(depth, resultSet, output, stageWriter) {
1226
1230
  const groupsToMap = [];
1227
1231
  for (const [name, fi] of resultSet.allFields) {
1228
- const sqlFieldName = this.parent.dialect.sqlMaybeQuoteIdentifier(`${name}__${resultSet.groupSet}`);
1232
+ const sqlFieldName = this.parent.dialect.sqlQuoteIdentifier(`${name}__${resultSet.groupSet}`);
1229
1233
  if (fi instanceof field_instance_1.FieldInstanceField) {
1230
1234
  if (fi.fieldUsage.type === 'result') {
1231
1235
  if ((0, query_node_1.isScalarField)(fi.f)) {
@@ -1329,15 +1333,15 @@ class QueryQuery extends query_node_1.QueryField {
1329
1333
  const outputPipelinedSQL = [];
1330
1334
  const dimensionIndexes = [];
1331
1335
  for (const [name, fi] of this.rootResult.allFields) {
1332
- const sqlName = this.parent.dialect.sqlMaybeQuoteIdentifier(name);
1336
+ const sqlName = this.parent.dialect.sqlQuoteIdentifier(name);
1333
1337
  if (fi instanceof field_instance_1.FieldInstanceField) {
1334
1338
  if (fi.fieldUsage.type === 'result') {
1335
1339
  if ((0, query_node_1.isScalarField)(fi.f)) {
1336
- fieldsSQL.push(this.parent.dialect.sqlMaybeQuoteIdentifier(`${name}__${this.rootResult.groupSet}`) + ` as ${sqlName}`);
1340
+ fieldsSQL.push(this.parent.dialect.sqlQuoteIdentifier(`${name}__${this.rootResult.groupSet}`) + ` as ${sqlName}`);
1337
1341
  dimensionIndexes.push(fieldIndex++);
1338
1342
  }
1339
1343
  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));
1344
+ fieldsSQL.push(this.parent.dialect.sqlAnyValueLastTurtle(this.parent.dialect.sqlQuoteIdentifier(`${name}__${this.rootResult.groupSet}`), this.rootResult.groupSet, sqlName));
1341
1345
  fieldIndex++;
1342
1346
  }
1343
1347
  }
@@ -1348,7 +1352,7 @@ class QueryQuery extends query_node_1.QueryField {
1348
1352
  fieldIndex++;
1349
1353
  }
1350
1354
  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));
1355
+ fieldsSQL.push(this.parent.dialect.sqlAnyValueLastTurtle(this.parent.dialect.sqlQuoteIdentifier(`${name}__${this.rootResult.groupSet}`), this.rootResult.groupSet, sqlName));
1352
1356
  fieldIndex++;
1353
1357
  }
1354
1358
  }
@@ -1375,7 +1379,7 @@ class QueryQuery extends query_node_1.QueryField {
1375
1379
  buildDialectFieldList(resultStruct) {
1376
1380
  const dialectFieldList = [];
1377
1381
  for (const [name, field] of resultStruct.allFields) {
1378
- const sqlName = this.parent.dialect.sqlMaybeQuoteIdentifier(name);
1382
+ const sqlName = this.parent.dialect.sqlQuoteIdentifier(name);
1379
1383
  //
1380
1384
  if (resultStruct.firstSegment.type === 'reduce' &&
1381
1385
  field instanceof field_instance_1.FieldInstanceResult) {
@@ -1390,7 +1394,7 @@ class QueryQuery extends query_node_1.QueryField {
1390
1394
  };
1391
1395
  dialectFieldList.push({
1392
1396
  typeDef: multiLineNest,
1393
- sqlExpression: this.parent.dialect.sqlMaybeQuoteIdentifier(`${name}__${resultStruct.groupSet}`),
1397
+ sqlExpression: this.parent.dialect.sqlQuoteIdentifier(`${name}__${resultStruct.groupSet}`),
1394
1398
  rawName: name,
1395
1399
  sqlOutputName: sqlName,
1396
1400
  });
@@ -1404,7 +1408,7 @@ class QueryQuery extends query_node_1.QueryField {
1404
1408
  };
1405
1409
  dialectFieldList.push({
1406
1410
  typeDef: oneLineNest,
1407
- sqlExpression: this.parent.dialect.sqlMaybeQuoteIdentifier(`${name}__${resultStruct.groupSet}`),
1411
+ sqlExpression: this.parent.dialect.sqlQuoteIdentifier(`${name}__${resultStruct.groupSet}`),
1408
1412
  rawName: name,
1409
1413
  sqlOutputName: sqlName,
1410
1414
  });
@@ -1415,7 +1419,7 @@ class QueryQuery extends query_node_1.QueryField {
1415
1419
  field.fieldUsage.type === 'result') {
1416
1420
  pushDialectField(dialectFieldList, {
1417
1421
  fieldDef: field.f.fieldDef,
1418
- sqlExpression: this.parent.dialect.sqlMaybeQuoteIdentifier(`${name}__${resultStruct.groupSet}`),
1422
+ sqlExpression: this.parent.dialect.sqlQuoteIdentifier(`${name}__${resultStruct.groupSet}`),
1419
1423
  rawName: name,
1420
1424
  sqlOutputName: sqlName,
1421
1425
  });
@@ -1449,10 +1453,10 @@ class QueryQuery extends query_node_1.QueryField {
1449
1453
  else {
1450
1454
  orderingField = resultStruct.getFieldByNumber(ordering.field);
1451
1455
  }
1452
- const structField = this.parent.dialect.sqlMaybeQuoteIdentifier(orderingField.name);
1456
+ const structField = this.parent.dialect.sqlQuoteIdentifier(orderingField.name);
1453
1457
  if (resultStruct.firstSegment.type === 'reduce') {
1454
1458
  compiledOrderBy.push({
1455
- field: this.parent.dialect.sqlMaybeQuoteIdentifier(`${orderingField.name}__${resultStruct.groupSet}`),
1459
+ field: this.parent.dialect.sqlQuoteIdentifier(`${orderingField.name}__${resultStruct.groupSet}`),
1456
1460
  structField,
1457
1461
  dir: ordering.dir || 'asc',
1458
1462
  });
@@ -1636,12 +1640,12 @@ class QueryQueryIndexStage extends QueryQuery {
1636
1640
  generateSQL(stageWriter) {
1637
1641
  let measureSQL = 'COUNT(*)';
1638
1642
  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');
1643
+ const fieldNameColumn = dialect.sqlQuoteIdentifier('fieldName');
1644
+ const fieldPathColumn = dialect.sqlQuoteIdentifier('fieldPath');
1645
+ const fieldValueColumn = dialect.sqlQuoteIdentifier('fieldValue');
1646
+ const fieldTypeColumn = dialect.sqlQuoteIdentifier('fieldType');
1647
+ const fieldRangeColumn = dialect.sqlQuoteIdentifier('fieldRange');
1648
+ const weightColumn = dialect.sqlQuoteIdentifier('weight');
1645
1649
  const measureName = this.firstSegment.weightMeasure;
1646
1650
  if (measureName) {
1647
1651
  measureSQL = this.rootResult.getField(measureName).generateExpression();
@@ -1819,7 +1823,7 @@ class QueryQueryRaw extends QueryQuery {
1819
1823
  if (this.parent.structDef.type !== 'sql_select') {
1820
1824
  throw new Error('Invalid struct for QueryQueryRaw, currently only supports SQL');
1821
1825
  }
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) => {
1826
+ return stageWriter.addStage((0, sql_compiled_1.getCompiledSQL)(this.parent.structDef, (_a = this.parent.prepareResultOptions) !== null && _a !== void 0 ? _a : {}, (query, opts) => {
1823
1827
  // Compile query to isolated SQL (not into parent's stageWriter)
1824
1828
  const ret = this.compileQueryToStages(query, opts !== null && opts !== void 0 ? opts : {}, undefined, false);
1825
1829
  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;
@@ -16,10 +16,9 @@ const source_def_utils_1 = require("./source_def_utils");
16
16
  *
17
17
  * @param src The SQLSourceDef to compile
18
18
  * @param opts PrepareResultOptions with buildManifest and connectionDigests
19
- * @param quoteTablePath Dialect function to safely quote a table path
20
19
  * @param compileQuery Callback to compile a Query to SQL
21
20
  */
22
- function getCompiledSQL(src, opts, quoteTablePath, compileQuery) {
21
+ function getCompiledSQL(src, opts, compileQuery) {
23
22
  // If no segments, just return the pre-computed selectStr
24
23
  if (!src.selectSegments || src.selectSegments.length === 0) {
25
24
  return src.selectStr;
@@ -27,42 +26,43 @@ function getCompiledSQL(src, opts, quoteTablePath, compileQuery) {
27
26
  // Expand each segment
28
27
  const parts = [];
29
28
  for (const segment of src.selectSegments) {
30
- parts.push(expandSegment(segment, opts, quoteTablePath, compileQuery));
29
+ parts.push(expandSegment(segment, opts, compileQuery));
31
30
  }
32
31
  return parts.join('');
33
32
  }
34
33
  /**
35
34
  * Expand a single SQLPhraseSegment to SQL.
36
35
  */
37
- function expandSegment(segment, opts, quoteTablePath, compileQuery) {
36
+ function expandSegment(segment, opts, compileQuery) {
38
37
  // Plain SQL string
39
38
  if ((0, malloy_types_1.isSegmentSQL)(segment)) {
40
39
  return segment.sql;
41
40
  }
42
41
  // PersistableSourceDef (sql_select or query_source)
43
42
  if ((0, malloy_types_1.isSegmentSource)(segment)) {
44
- return expandPersistableSource(segment, opts, quoteTablePath, compileQuery);
43
+ return expandPersistableSource(segment, opts, compileQuery);
45
44
  }
46
45
  // Query segment
47
- return expandQuery(segment, opts, quoteTablePath, compileQuery);
46
+ return expandQuery(segment, opts, compileQuery);
48
47
  }
49
48
  /**
50
49
  * Expand a PersistableSourceDef, checking manifest for pre-built table.
51
50
  * Always returns a subquery form: (SELECT * FROM table) or (inline SQL)
52
51
  */
53
- function expandPersistableSource(source, opts, quoteTablePath, compileQuery) {
52
+ function expandPersistableSource(source, opts, compileQuery) {
54
53
  const { buildManifest, connectionDigests } = opts;
55
54
  // Try manifest lookup if we have the required info (only for persistent sources)
56
55
  if (buildManifest && connectionDigests && source.persistent) {
57
56
  const connDigest = (0, malloy_types_1.safeRecordGet)(connectionDigests, source.connection);
58
57
  if (connDigest) {
59
58
  // Get the SQL for this source to compute BuildID (no opts = full SQL)
60
- const sql = getSourceSQL(source, quoteTablePath, compileQuery);
59
+ const sql = getSourceSQL(source, compileQuery);
61
60
  const buildId = (0, source_def_utils_1.mkBuildID)(connDigest, sql);
62
61
  const entry = buildManifest.entries[buildId];
63
62
  if (entry) {
64
- // Found in manifest - substitute with subquery from persisted table
65
- return `(SELECT * FROM ${quoteTablePath(entry.tableName)})`;
63
+ // Found in manifest - substitute with subquery from persisted table.
64
+ // entry.tableName is canonical SQL, supplied by the manifest builder.
65
+ return `(SELECT * FROM ${entry.tableName})`;
66
66
  }
67
67
  // Not in manifest
68
68
  if (buildManifest.strict) {
@@ -74,13 +74,13 @@ function expandPersistableSource(source, opts, quoteTablePath, compileQuery) {
74
74
  }
75
75
  }
76
76
  // No manifest or not found - expand inline as subquery
77
- const sql = getSourceSQL(source, quoteTablePath, compileQuery, opts);
77
+ const sql = getSourceSQL(source, compileQuery, opts);
78
78
  return `(${sql})`;
79
79
  }
80
80
  /**
81
81
  * Expand a Query segment.
82
82
  */
83
- function expandQuery(query, opts, _quoteTablePath, compileQuery) {
83
+ function expandQuery(query, opts, compileQuery) {
84
84
  // Set isPartialQuery so CTEs aren't used (they can't be nested in subqueries)
85
85
  const sql = compileQuery(query, { ...opts, isPartialQuery: true });
86
86
  return `(${sql})`;
@@ -89,15 +89,14 @@ function expandQuery(query, opts, _quoteTablePath, compileQuery) {
89
89
  * Get the SQL for a PersistableSourceDef.
90
90
  *
91
91
  * @param source The persistable source to compile
92
- * @param quoteTablePath Dialect function to quote table paths
93
92
  * @param compileQuery Callback to compile a Query to SQL
94
93
  * @param opts Optional - if provided with manifest, nested sources may be substituted.
95
94
  * Omit for "full SQL" (e.g., when computing BuildID).
96
95
  */
97
- function getSourceSQL(source, quoteTablePath, compileQuery, opts) {
96
+ function getSourceSQL(source, compileQuery, opts) {
98
97
  if (source.type === 'sql_select') {
99
98
  // Recursive call for nested sql_select
100
- return getCompiledSQL(source, opts !== null && opts !== void 0 ? opts : {}, quoteTablePath, compileQuery);
99
+ return getCompiledSQL(source, opts !== null && opts !== void 0 ? opts : {}, compileQuery);
101
100
  }
102
101
  // query_source - compile the inner query
103
102
  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.393";
1
+ export declare const MALLOY_VERSION = "0.0.395";