@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
@@ -0,0 +1,54 @@
1
+ export type ValidateTablePathResult = {
2
+ ok: true;
3
+ canonical: string;
4
+ } | {
5
+ ok: false;
6
+ error: string;
7
+ };
8
+ export interface TablePathSegment {
9
+ /** Decoded segment value: delimiters stripped, escapes unescaped. */
10
+ value: string;
11
+ /** Whether the segment appeared in quoted form in the input. */
12
+ quoted: boolean;
13
+ }
14
+ export type DecodeDottedTablePathResult = {
15
+ ok: true;
16
+ segments: TablePathSegment[];
17
+ } | {
18
+ ok: false;
19
+ error: string;
20
+ };
21
+ export type TablePathEscapeStyle = 'doubled' | 'backslash';
22
+ export interface DottedTablePathOptions {
23
+ /** Delimiter for quoted segments (`"`, `` ` ``, …). */
24
+ quoteChar: string;
25
+ /**
26
+ * How a literal `quoteChar` is encoded inside a quoted body:
27
+ * - 'doubled': `qq` inside body escapes one literal `q`.
28
+ * - 'backslash': `\X` is a two-character escape; unescaped `q` closes.
29
+ */
30
+ escapeStyle: TablePathEscapeStyle;
31
+ /**
32
+ * Regex matching one bare segment, anchored at the start of the input.
33
+ * Must NOT have global/sticky flags; the parser calls `.match()` on
34
+ * `input.slice(i)` and expects the match to start at position 0.
35
+ */
36
+ bareIdentRegex: RegExp;
37
+ /** Used in error messages only. */
38
+ dialectName: string;
39
+ }
40
+ /**
41
+ * Parse `input` as a dotted table path and require end-of-input. On
42
+ * success, returns the decoded segment values — delimiters stripped,
43
+ * escape sequences unescaped — so callers that need the segments
44
+ * (connection metadata lookups) and callers that only need
45
+ * accept/reject (`validateDottedTablePath`) share one parser.
46
+ */
47
+ export declare function decodeDottedTablePath(input: string, opts: DottedTablePathOptions): DecodeDottedTablePathResult;
48
+ /**
49
+ * Validate `input` as a dotted table path. On success the canonical
50
+ * form is the input verbatim. See `decodeDottedTablePath` for the
51
+ * underlying parser; this is the validate-only wrapper that doesn't
52
+ * expose segment internals.
53
+ */
54
+ export declare function validateDottedTablePath(input: string, opts: DottedTablePathOptions): ValidateTablePathResult;
@@ -0,0 +1,144 @@
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.decodeDottedTablePath = decodeDottedTablePath;
8
+ exports.validateDottedTablePath = validateDottedTablePath;
9
+ /**
10
+ * Parse `input` as a dotted table path and require end-of-input. On
11
+ * success, returns the decoded segment values — delimiters stripped,
12
+ * escape sequences unescaped — so callers that need the segments
13
+ * (connection metadata lookups) and callers that only need
14
+ * accept/reject (`validateDottedTablePath`) share one parser.
15
+ */
16
+ function decodeDottedTablePath(input, opts) {
17
+ const { quoteChar, escapeStyle, bareIdentRegex, dialectName } = opts;
18
+ if (input.length === 0) {
19
+ return { ok: false, error: `${dialectName} table path is empty` };
20
+ }
21
+ const segments = [];
22
+ let i = 0;
23
+ while (true) {
24
+ let segValue;
25
+ let segQuoted;
26
+ if (input[i] === quoteChar) {
27
+ const result = consumeQuotedSegment(input, i, quoteChar, escapeStyle);
28
+ if (result === null) {
29
+ return {
30
+ ok: false,
31
+ error: `Invalid ${dialectName} table path: ${JSON.stringify(input)} — ` +
32
+ 'unterminated quoted segment',
33
+ };
34
+ }
35
+ segValue = result.decoded;
36
+ segQuoted = true;
37
+ i = result.end;
38
+ }
39
+ else {
40
+ const m = input.slice(i).match(bareIdentRegex);
41
+ if (!m || m.index !== 0 || m[0].length === 0) {
42
+ return {
43
+ ok: false,
44
+ error: `Invalid ${dialectName} table path: ${JSON.stringify(input)} — ` +
45
+ `invalid segment at position ${i}`,
46
+ };
47
+ }
48
+ segValue = m[0];
49
+ segQuoted = false;
50
+ i += m[0].length;
51
+ }
52
+ // Defense-in-depth: `;` and `--` are forbidden in any decoded segment,
53
+ // even a legally-quoted one. Real table names don't contain them.
54
+ if (segValue.includes(';') || segValue.includes('--')) {
55
+ return {
56
+ ok: false,
57
+ error: `Invalid ${dialectName} table path: segment ${JSON.stringify(segValue)} ` +
58
+ 'contains forbidden character; even when quoted, table-path ' +
59
+ 'segments may not contain `;` or `--`.',
60
+ };
61
+ }
62
+ segments.push({ value: segValue, quoted: segQuoted });
63
+ if (i === input.length)
64
+ return { ok: true, segments };
65
+ if (input[i] !== '.') {
66
+ return {
67
+ ok: false,
68
+ error: `Invalid ${dialectName} table path: ${JSON.stringify(input)} — ` +
69
+ `expected '.' at position ${i}`,
70
+ };
71
+ }
72
+ i++;
73
+ if (i === input.length) {
74
+ return {
75
+ ok: false,
76
+ error: `Invalid ${dialectName} table path: ${JSON.stringify(input)} — ` +
77
+ 'trailing dot',
78
+ };
79
+ }
80
+ }
81
+ }
82
+ /**
83
+ * Validate `input` as a dotted table path. On success the canonical
84
+ * form is the input verbatim. See `decodeDottedTablePath` for the
85
+ * underlying parser; this is the validate-only wrapper that doesn't
86
+ * expose segment internals.
87
+ */
88
+ function validateDottedTablePath(input, opts) {
89
+ const result = decodeDottedTablePath(input, opts);
90
+ if (!result.ok)
91
+ return result;
92
+ return { ok: true, canonical: input };
93
+ }
94
+ /**
95
+ * Read past a quoted segment starting at `input[i]` (must be `quoteChar`).
96
+ * Returns the segment body (delimiters stripped, escape sequences
97
+ * unescaped) and the index just after the closing quote, or `null` if
98
+ * the segment is unterminated.
99
+ */
100
+ function consumeQuotedSegment(input, i, quoteChar, escapeStyle) {
101
+ // input[i] === quoteChar
102
+ let j = i + 1;
103
+ let decoded = '';
104
+ while (j < input.length) {
105
+ if (escapeStyle === 'backslash' && input[j] === '\\') {
106
+ if (j + 1 >= input.length)
107
+ return null;
108
+ decoded += decodeBackslashEscape(input[j + 1]);
109
+ j += 2;
110
+ continue;
111
+ }
112
+ if (input[j] === quoteChar) {
113
+ if (escapeStyle === 'doubled' && input[j + 1] === quoteChar) {
114
+ decoded += quoteChar;
115
+ j += 2;
116
+ continue;
117
+ }
118
+ return { decoded, end: j + 1 };
119
+ }
120
+ decoded += input[j];
121
+ j++;
122
+ }
123
+ return null;
124
+ }
125
+ /**
126
+ * Decode a single character after a backslash. We intentionally accept
127
+ * any character — we're a translator-time grammar check, not a strict
128
+ * lexical conformance test for any particular engine's quoted-identifier
129
+ * escape table. The engine will surface its own errors at bind time if
130
+ * it doesn't recognize a particular sequence.
131
+ */
132
+ function decodeBackslashEscape(c) {
133
+ switch (c) {
134
+ case 'n':
135
+ return '\n';
136
+ case 't':
137
+ return '\t';
138
+ case 'r':
139
+ return '\r';
140
+ default:
141
+ return c;
142
+ }
143
+ }
144
+ //# sourceMappingURL=table-path.js.map
@@ -30,7 +30,6 @@ export declare class TrinoDialect extends PostgresBase {
30
30
  supportsTempTables: boolean;
31
31
  supportsCountApprox: boolean;
32
32
  supportsHyperLogLog: boolean;
33
- quoteTablePath(tablePath: string): string;
34
33
  sqlGroupSetTable(groupSetCount: number): string;
35
34
  exprToSQL(qi: QueryInfo, df: Expr): string | undefined;
36
35
  sqlAnyValue(groupSet: number, fieldName: string): string;
@@ -62,8 +61,6 @@ export declare class TrinoDialect extends PostgresBase {
62
61
  sqlRegexpMatch(reCmp: RegexMatchExpr): string;
63
62
  sqlMeasureTimeExpr(mf: MeasureTimeExpr): string;
64
63
  sqlSampleTable(tableSQL: string, sample: Sampling | undefined): string;
65
- sqlLiteralString(literal: string): string;
66
- sqlLiteralRegexp(literal: string): string;
67
64
  getDialectFunctionOverrides(): {
68
65
  [name: string]: DialectFunctionOverloadDef[];
69
66
  };
@@ -200,16 +200,9 @@ class TrinoDialect extends pg_impl_1.PostgresBase {
200
200
  WITH
201
201
  WITHIN`.split(/\s/);
202
202
  }
203
- quoteTablePath(tablePath) {
204
- // Quote with double quotes if contains dangerous characters
205
- if (tablePath.match(/[;-]/)) {
206
- return tablePath
207
- .split('.')
208
- .map(part => `"${part}"`)
209
- .join('.');
210
- }
211
- return tablePath;
212
- }
203
+ // Trino bare identifier is strict ANSI (`[A-Za-z_][A-Za-z0-9_]*`),
204
+ // which matches the Dialect default no override needed. Verified
205
+ // against the live engine.
213
206
  sqlGroupSetTable(groupSetCount) {
214
207
  return `CROSS JOIN (SELECT row_number() OVER() -1 group_set FROM UNNEST(SEQUENCE(0,${groupSetCount})))`;
215
208
  }
@@ -300,7 +293,7 @@ class TrinoDialect extends pg_impl_1.PostgresBase {
300
293
  if (childName === '__row_id') {
301
294
  return `__row_id_from_${parentAlias}`;
302
295
  }
303
- return `${parentAlias}.${this.sqlMaybeQuoteIdentifier(childName)}`;
296
+ return `${parentAlias}.${this.sqlQuoteIdentifier(childName)}`;
304
297
  }
305
298
  sqlUnnestPipelineHead(isSingleton, sourceSQLExpression) {
306
299
  let p = sourceSQLExpression;
@@ -471,12 +464,6 @@ ${(0, utils_1.indent)(sql)}
471
464
  }
472
465
  return tableSQL;
473
466
  }
474
- sqlLiteralString(literal) {
475
- return "'" + literal.replace(/'/g, "''") + "'";
476
- }
477
- sqlLiteralRegexp(literal) {
478
- return "'" + literal.replace(/'/g, "''") + "'";
479
- }
480
467
  getDialectFunctionOverrides() {
481
468
  return (0, functions_1.expandOverrideMap)(function_overrides_1.TRINO_MALLOY_STANDARD_OVERLOADS);
482
469
  }
@@ -503,7 +490,7 @@ ${(0, utils_1.indent)(sql)}
503
490
  const typeSpec = [];
504
491
  for (const f of malloyType.fields) {
505
492
  if ((0, malloy_types_1.isAtomic)(f)) {
506
- typeSpec.push(`${this.sqlMaybeQuoteIdentifier(f.name)} ${this.malloyTypeToSQLType(f)}`);
493
+ typeSpec.push(`${this.sqlQuoteIdentifier(f.name)} ${this.malloyTypeToSQLType(f)}`);
507
494
  }
508
495
  }
509
496
  return `ROW(${typeSpec.join(',')})`;
@@ -515,7 +502,7 @@ ${(0, utils_1.indent)(sql)}
515
502
  const typeSpec = [];
516
503
  for (const f of malloyType.fields) {
517
504
  if ((0, malloy_types_1.isAtomic)(f)) {
518
- typeSpec.push(`${this.sqlMaybeQuoteIdentifier(f.name)} ${this.malloyTypeToSQLType(f)}`);
505
+ typeSpec.push(`${this.sqlQuoteIdentifier(f.name)} ${this.malloyTypeToSQLType(f)}`);
519
506
  }
520
507
  }
521
508
  return `ARRAY<ROW(${typeSpec.join(',')})>`;
@@ -603,7 +590,7 @@ ${(0, utils_1.indent)(sql)}
603
590
  const name = (_a = f.as) !== null && _a !== void 0 ? _a : f.name;
604
591
  rowVals.push((_c = (_b = (0, malloy_types_1.safeRecordGet)(lit.kids, name)) === null || _b === void 0 ? void 0 : _b.sql) !== null && _c !== void 0 ? _c : 'internal-error-record-literal');
605
592
  const elType = this.malloyTypeToSQLType(f);
606
- rowTypes.push(`${this.sqlMaybeQuoteIdentifier(name)} ${elType}`);
593
+ rowTypes.push(`${this.sqlQuoteIdentifier(name)} ${elType}`);
607
594
  }
608
595
  }
609
596
  return `CAST(ROW(${rowVals.join(',')}) AS ROW(${rowTypes.join(',')}))`;
package/dist/index.d.ts CHANGED
@@ -1,5 +1,5 @@
1
- export { DuckDBDialect, StandardSQLDialect, TrinoDialect, PostgresDialect, SnowflakeDialect, MySQLDialect, DatabricksDialect, registerDialect, arg, qtz, overload, minScalar, anyExprType, minAggregate, maxScalar, sql, makeParam, param, variadicParam, literal, spread, Dialect, } from './dialect';
2
- export type { DialectFieldList, DialectFunctionOverloadDef, QueryInfo, MalloyStandardFunctionImplementations, DefinitionBlueprint, DefinitionBlueprintMap, OverloadedDefinitionBlueprint, } from './dialect';
1
+ export { DuckDBDialect, StandardSQLDialect, TrinoDialect, PostgresDialect, SnowflakeDialect, MySQLDialect, DatabricksDialect, registerDialect, arg, qtz, overload, minScalar, anyExprType, minAggregate, maxScalar, sql, makeParam, param, variadicParam, literal, spread, Dialect, decodeDottedTablePath, validateDottedTablePath, } from './dialect';
2
+ export type { DialectFieldList, DialectFunctionOverloadDef, QueryInfo, MalloyStandardFunctionImplementations, DefinitionBlueprint, DefinitionBlueprintMap, OverloadedDefinitionBlueprint, DecodeDottedTablePathResult, DottedTablePathOptions, TablePathEscapeStyle, TablePathSegment, ValidateTablePathResult, } from './dialect';
3
3
  export type { QueryRecord, StructDef, TableSourceDef, SQLSourceDef, SourceDef, JoinFieldDef, NamedSourceDefs, MalloyQueryData, DateUnit, ExtractUnit, TimestampUnit, TemporalFieldType, QueryData, QueryValue, Expr, FilterCondition, Argument, Parameter, FieldDef, PipeSegment, QueryFieldDef, IndexFieldDef, TurtleDef, SearchValueMapResult, SearchIndexResult, ModelDef, Query, QueryResult, QueryResultDef, QueryRunStats, QueryScalar, NamedQueryDef, NamedModelObject, ExpressionType, FunctionDef, FunctionOverloadDef, FunctionParameterDef, ExpressionValueType, TypeDesc, FunctionParamTypeDesc, DocumentLocation, DocumentRange, DocumentPosition, Sampling, Annotation, BasicAtomicTypeDef, BasicAtomicDef, AtomicTypeDef, AtomicFieldDef, ArrayDef, ArrayTypeDef, RecordTypeDef, RepeatedRecordTypeDef, RecordDef, RepeatedRecordDef, RecordLiteralNode, StringLiteralNode, ArrayLiteralNode, SourceComponentInfo, DateLiteralNode, TimestampLiteralNode, TimestamptzLiteralNode, TimeLiteralExpr, TypecastExpr, BuildID, BuildManifest, BuildManifestEntry, GivenValue, VirtualMap, } from './model';
4
4
  export { isSourceDef, isAtomic, isBasicAtomic, isCompoundArrayData, isJoined, isJoinedSource, isSamplingEnable, isSamplingPercent, isSamplingRows, isRepeatedRecord, isBasicArray, mkArrayDef, mkFieldDef, expressionIsAggregate, expressionIsAnalytic, expressionIsCalculation, expressionIsScalar, expressionIsUngroupedAggregate, indent, composeSQLExpr, isTimestampUnit, isDateUnit, constantExprToSQL, } from './model';
5
5
  export { malloyToQuery, MalloyTranslator, } from './lang';
package/dist/index.js CHANGED
@@ -33,8 +33,8 @@ var __importStar = (this && this.__importStar) || (function () {
33
33
  };
34
34
  })();
35
35
  Object.defineProperty(exports, "__esModule", { value: true });
36
- exports.Runtime = exports.Malloy = exports.Model = exports.MalloyTranslator = exports.malloyToQuery = exports.constantExprToSQL = exports.isDateUnit = exports.isTimestampUnit = exports.composeSQLExpr = exports.indent = exports.expressionIsUngroupedAggregate = exports.expressionIsScalar = exports.expressionIsCalculation = exports.expressionIsAnalytic = exports.expressionIsAggregate = exports.mkFieldDef = exports.mkArrayDef = exports.isBasicArray = exports.isRepeatedRecord = exports.isSamplingRows = exports.isSamplingPercent = exports.isSamplingEnable = exports.isJoinedSource = exports.isJoined = exports.isCompoundArrayData = exports.isBasicAtomic = exports.isAtomic = exports.isSourceDef = exports.Dialect = exports.spread = exports.literal = exports.variadicParam = exports.param = exports.makeParam = exports.sql = exports.maxScalar = exports.minAggregate = exports.anyExprType = exports.minScalar = exports.overload = exports.qtz = exports.arg = exports.registerDialect = exports.DatabricksDialect = exports.MySQLDialect = exports.SnowflakeDialect = exports.PostgresDialect = exports.TrinoDialect = exports.StandardSQLDialect = exports.DuckDBDialect = void 0;
37
- exports.makeDigest = exports.EMPTY_BUILD_MANIFEST = exports.PersistSource = exports.annotationToTaglines = exports.annotationToTag = exports.sqlKey = exports.API = exports.sourceDefToSourceInfo = exports.modelDefToModelInfo = exports.toAsyncGenerator = exports.createConnectionsFromConfig = exports.getRegisteredConnectionTypes = exports.getConnectionTypeDisplayName = exports.getConnectionProperties = exports.registerConnectionType = exports.discoverConfig = exports.defaultConfigOverlays = exports.contextOverlay = exports.envOverlay = exports.MalloyConfig = exports.Manifest = exports.CacheManager = exports.InMemoryModelCache = exports.Explore = exports.DataWriter = exports.Parse = exports.JSONWriter = exports.CSVWriter = exports.QueryMaterializer = exports.Result = exports.PreparedResult = exports.TimestampTimeframe = exports.DateTimeframe = exports.SourceRelationship = exports.JoinRelationship = exports.MalloyError = exports.FixedConnectionMap = exports.InMemoryURLReader = exports.EmptyURLReader = exports.SingleConnectionRuntime = exports.ConnectionRuntime = exports.AtomicFieldType = void 0;
36
+ exports.Model = exports.MalloyTranslator = exports.malloyToQuery = exports.constantExprToSQL = exports.isDateUnit = exports.isTimestampUnit = exports.composeSQLExpr = exports.indent = exports.expressionIsUngroupedAggregate = exports.expressionIsScalar = exports.expressionIsCalculation = exports.expressionIsAnalytic = exports.expressionIsAggregate = exports.mkFieldDef = exports.mkArrayDef = exports.isBasicArray = exports.isRepeatedRecord = exports.isSamplingRows = exports.isSamplingPercent = exports.isSamplingEnable = exports.isJoinedSource = exports.isJoined = exports.isCompoundArrayData = exports.isBasicAtomic = exports.isAtomic = exports.isSourceDef = exports.validateDottedTablePath = exports.decodeDottedTablePath = exports.Dialect = exports.spread = exports.literal = exports.variadicParam = exports.param = exports.makeParam = exports.sql = exports.maxScalar = exports.minAggregate = exports.anyExprType = exports.minScalar = exports.overload = exports.qtz = exports.arg = exports.registerDialect = exports.DatabricksDialect = exports.MySQLDialect = exports.SnowflakeDialect = exports.PostgresDialect = exports.TrinoDialect = exports.StandardSQLDialect = exports.DuckDBDialect = void 0;
37
+ exports.makeDigest = exports.EMPTY_BUILD_MANIFEST = exports.PersistSource = exports.annotationToTaglines = exports.annotationToTag = exports.sqlKey = exports.API = exports.sourceDefToSourceInfo = exports.modelDefToModelInfo = exports.toAsyncGenerator = exports.createConnectionsFromConfig = exports.getRegisteredConnectionTypes = exports.getConnectionTypeDisplayName = exports.getConnectionProperties = exports.registerConnectionType = exports.discoverConfig = exports.defaultConfigOverlays = exports.contextOverlay = exports.envOverlay = exports.MalloyConfig = exports.Manifest = exports.CacheManager = exports.InMemoryModelCache = exports.Explore = exports.DataWriter = exports.Parse = exports.JSONWriter = exports.CSVWriter = exports.QueryMaterializer = exports.Result = exports.PreparedResult = exports.TimestampTimeframe = exports.DateTimeframe = exports.SourceRelationship = exports.JoinRelationship = exports.MalloyError = exports.FixedConnectionMap = exports.InMemoryURLReader = exports.EmptyURLReader = exports.SingleConnectionRuntime = exports.ConnectionRuntime = exports.AtomicFieldType = exports.Runtime = exports.Malloy = void 0;
38
38
  /*
39
39
  * Copyright 2023 Google LLC
40
40
  *
@@ -80,6 +80,8 @@ Object.defineProperty(exports, "variadicParam", { enumerable: true, get: functio
80
80
  Object.defineProperty(exports, "literal", { enumerable: true, get: function () { return dialect_1.literal; } });
81
81
  Object.defineProperty(exports, "spread", { enumerable: true, get: function () { return dialect_1.spread; } });
82
82
  Object.defineProperty(exports, "Dialect", { enumerable: true, get: function () { return dialect_1.Dialect; } });
83
+ Object.defineProperty(exports, "decodeDottedTablePath", { enumerable: true, get: function () { return dialect_1.decodeDottedTablePath; } });
84
+ Object.defineProperty(exports, "validateDottedTablePath", { enumerable: true, get: function () { return dialect_1.validateDottedTablePath; } });
83
85
  var model_1 = require("./model");
84
86
  Object.defineProperty(exports, "isSourceDef", { enumerable: true, get: function () { return model_1.isSourceDef; } });
85
87
  // Used in Composer Demo
@@ -58,13 +58,25 @@ Object.defineProperty(exports, "__esModule", { value: true });
58
58
  exports.ExprFunc = void 0;
59
59
  const malloy_types_1 = require("../../../model/malloy_types");
60
60
  const ast_utils_1 = require("../ast-utils");
61
+ const utils_1 = require("../../../model/utils");
61
62
  const struct_space_field_base_1 = require("../field-space/struct-space-field-base");
62
63
  const expr_value_1 = require("../types/expr-value");
63
64
  const expression_def_1 = require("../types/expression-def");
64
65
  const field_space_1 = require("../types/field-space");
65
- const utils_1 = require("../../../model/utils");
66
+ const utils_2 = require("../../../model/utils");
66
67
  const TDU = __importStar(require("../typedesc-utils"));
67
68
  const composite_source_utils_1 = require("../../composite-source-utils");
69
+ // Built-in functions that take a string literal of user-supplied SQL
70
+ // and emit it directly. Gated by experimental.sql_functions in plain
71
+ // Malloy; rejected unconditionally in restricted queries because they
72
+ // are a raw-SQL escape hatch by definition.
73
+ const SQL_FUNCTION_NAMES = [
74
+ 'sql_number',
75
+ 'sql_string',
76
+ 'sql_date',
77
+ 'sql_timestamp',
78
+ 'sql_boolean',
79
+ ];
68
80
  class ExprFunc extends expression_def_1.ExpressionDef {
69
81
  constructor(name, args, isRaw, explicitType, source) {
70
82
  super({ args: args });
@@ -86,6 +98,12 @@ class ExprFunc extends expression_def_1.ExpressionDef {
86
98
  return true;
87
99
  }
88
100
  getExpression(fs) {
101
+ if (this.isRaw && this.isRestricted()) {
102
+ const typeStr = this.explicitType
103
+ ? (0, utils_1.typeDefToString)(this.explicitType)
104
+ : '';
105
+ return this.loggedErrorExpr('restricted-construct-forbidden', `\`${this.name}!${typeStr}(...)\` cannot be used in a restricted query — direct SQL function calls (\`!type(...)\`) are not permitted.`);
106
+ }
89
107
  return this.getPropsExpression(fs);
90
108
  }
91
109
  findFunctionDef(dialect) {
@@ -126,7 +144,7 @@ class ExprFunc extends expression_def_1.ExpressionDef {
126
144
  const dataType = (_b = this.explicitType) !== null && _b !== void 0 ? _b : inferredType;
127
145
  return (0, expr_value_1.computedExprValue)({
128
146
  dataType,
129
- value: (0, utils_1.composeSQLExpr)(funcCall),
147
+ value: (0, utils_2.composeSQLExpr)(funcCall),
130
148
  from: argExprsWithoutImplicit,
131
149
  });
132
150
  }
@@ -290,13 +308,10 @@ class ExprFunc extends expression_def_1.ExpressionDef {
290
308
  frag.partitionBy = partitionByFields;
291
309
  }
292
310
  const sqlFunctionFieldUsage = [];
293
- if ([
294
- 'sql_number',
295
- 'sql_string',
296
- 'sql_date',
297
- 'sql_timestamp',
298
- 'sql_boolean',
299
- ].includes(func.name)) {
311
+ if (SQL_FUNCTION_NAMES.includes(func.name)) {
312
+ if (this.isRestricted()) {
313
+ return this.loggedErrorExpr('restricted-construct-forbidden', `\`${this.name}(...)\` cannot be used in a restricted query — the \`sql_*\` function family emits user-supplied SQL directly, which is not permitted.`);
314
+ }
300
315
  if (!this.inExperiment('sql_functions', true)) {
301
316
  return this.loggedErrorExpr('sql-functions-experiment-not-enabled', `Cannot use sql_function \`${func.name}\`; use \`sql_functions\` experiment to enable this behavior`);
302
317
  }
@@ -327,7 +342,11 @@ class ExprFunc extends expression_def_1.ExpressionDef {
327
342
  return this.loggedErrorExpr('filter-expression-error', 'Filter expressions cannot be used in sql_ functions');
328
343
  }
329
344
  if (result.found.refType === 'parameter') {
330
- expr.push({ node: 'parameter', path: part.path });
345
+ expr.push({
346
+ node: 'parameter',
347
+ path: part.path,
348
+ at: this.args[0].location,
349
+ });
331
350
  }
332
351
  else {
333
352
  sqlFunctionFieldUsage.push({
@@ -343,7 +362,7 @@ class ExprFunc extends expression_def_1.ExpressionDef {
343
362
  }
344
363
  }
345
364
  }
346
- funcCall = (0, utils_1.composeSQLExpr)(expr);
365
+ funcCall = (0, utils_2.composeSQLExpr)(expr);
347
366
  }
348
367
  }
349
368
  const maxEvalSpace = (0, malloy_types_1.mergeEvalSpaces)(...argExprs.map(e => e.evalSpace));
@@ -55,6 +55,7 @@ class GivenReference extends expression_def_1.ExpressionDef {
55
55
  node: 'given',
56
56
  id: entry.id,
57
57
  refName: this.name,
58
+ at: this.location,
58
59
  };
59
60
  return {
60
61
  ...(0, expr_value_1.literalExprValue)({ value: refNode, dataType: given.type }),
@@ -88,7 +88,7 @@ class ReferenceField extends space_field_1.SpaceField {
88
88
  if (malloy_types_1.TD.isAtomic(foundType)) {
89
89
  this.queryFieldDef = {
90
90
  ...(0, malloy_types_1.mkFieldDef)(TDU.atomicDef(foundType), path[0]),
91
- e: { node: 'parameter', path },
91
+ e: { node: 'parameter', path, at: this.fieldRef.location },
92
92
  };
93
93
  }
94
94
  else {
@@ -100,6 +100,10 @@ class SQLSource extends source_1.Source {
100
100
  }
101
101
  getSourceDef() {
102
102
  var _a;
103
+ if (this.isRestricted()) {
104
+ this.logError('restricted-construct-forbidden', `\`${this.connectionName.refString}.sql(...)\` cannot be used in a restricted query — raw SQL is not permitted.`);
105
+ return error_factory_1.ErrorFactory.structDef;
106
+ }
103
107
  if (!this.validateConnectionName()) {
104
108
  return error_factory_1.ErrorFactory.structDef;
105
109
  }
@@ -3,7 +3,7 @@ import { Source } from './source';
3
3
  import type { ModelEntryReference } from '../types/malloy-element';
4
4
  type TableInfo = {
5
5
  tablePath: string;
6
- connectionName?: string | undefined;
6
+ connectionName: string;
7
7
  };
8
8
  export declare abstract class TableSource extends Source {
9
9
  abstract getTableInfo(): TableInfo | undefined;
@@ -16,10 +16,4 @@ export declare class TableMethodSource extends TableSource {
16
16
  constructor(connectionName: ModelEntryReference, tablePath: string);
17
17
  getTableInfo(): TableInfo | undefined;
18
18
  }
19
- export declare class TableFunctionSource extends TableSource {
20
- readonly tableURI: string;
21
- elementType: string;
22
- constructor(tableURI: string);
23
- getTableInfo(): TableInfo | undefined;
24
- }
25
19
  export {};
@@ -22,24 +22,38 @@
22
22
  * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
23
23
  */
24
24
  Object.defineProperty(exports, "__esModule", { value: true });
25
- exports.TableFunctionSource = exports.TableMethodSource = exports.TableSource = void 0;
25
+ exports.TableMethodSource = exports.TableSource = void 0;
26
26
  const find_external_references_1 = require("../../parse-tree-walkers/find-external-references");
27
27
  const source_1 = require("./source");
28
28
  const error_factory_1 = require("../error-factory");
29
+ const dialect_1 = require("../../../dialect");
29
30
  class TableSource extends source_1.Source {
30
31
  getSourceDef() {
31
- var _a, _b, _c;
32
+ var _a, _b, _c, _d;
32
33
  const info = this.getTableInfo();
33
34
  if (info === undefined) {
34
35
  return error_factory_1.ErrorFactory.structDef;
35
36
  }
36
- const { tablePath, connectionName } = info;
37
+ const { tablePath: rawTablePath, connectionName } = info;
38
+ // Re-validate the table path. ImportsAndTablesStep validated and
39
+ // silently skipped invalid entries; we re-validate here so we can
40
+ // log a precise translator error at the AST element's location.
41
+ let tablePath = rawTablePath;
42
+ const dialectName = (_a = this.translator()) === null || _a === void 0 ? void 0 : _a.root.connectionDialectZone.get(connectionName);
43
+ if (dialectName !== undefined) {
44
+ const validation = (0, dialect_1.getDialect)(dialectName).sqlValidateTableName(rawTablePath);
45
+ if (!validation.ok) {
46
+ this.logError('invalid-table-path', validation.error);
47
+ return error_factory_1.ErrorFactory.structDef;
48
+ }
49
+ tablePath = validation.canonical;
50
+ }
37
51
  const key = (0, find_external_references_1.constructTableKey)(connectionName, tablePath);
38
- const tableDefEntry = (_a = this.translator()) === null || _a === void 0 ? void 0 : _a.root.schemaZone.getEntry(key);
52
+ const tableDefEntry = (_b = this.translator()) === null || _b === void 0 ? void 0 : _b.root.schemaZone.getEntry(key);
39
53
  let msg = `Schema read failure for table '${tablePath}' for connection '${connectionName}'`;
40
54
  if (tableDefEntry) {
41
55
  if (tableDefEntry.status === 'present') {
42
- (_b = this.document()) === null || _b === void 0 ? void 0 : _b.checkExperimentalDialect(this, tableDefEntry.value.dialect);
56
+ (_c = this.document()) === null || _c === void 0 ? void 0 : _c.checkExperimentalDialect(this, tableDefEntry.value.dialect);
43
57
  tableDefEntry.value.location = this.location;
44
58
  tableDefEntry.value.fields.forEach(field => {
45
59
  field.location = this.location;
@@ -58,7 +72,7 @@ class TableSource extends source_1.Source {
58
72
  }),
59
73
  location: this.location,
60
74
  };
61
- (_c = this.document()) === null || _c === void 0 ? void 0 : _c.rememberToAddModelAnnotations(ret);
75
+ (_d = this.document()) === null || _d === void 0 ? void 0 : _d.rememberToAddModelAnnotations(ret);
62
76
  return ret;
63
77
  }
64
78
  if (tableDefEntry.status === 'error') {
@@ -80,6 +94,10 @@ class TableMethodSource extends TableSource {
80
94
  }
81
95
  getTableInfo() {
82
96
  var _a;
97
+ if (this.isRestricted()) {
98
+ this.logError('restricted-construct-forbidden', `\`${this.connectionName.refString}.table(...)\` cannot be used in a restricted query — direct table access is not permitted.`);
99
+ return undefined;
100
+ }
83
101
  const connection = this.modelEntry(this.connectionName);
84
102
  const name = this.connectionName.refString;
85
103
  if (connection === undefined) {
@@ -96,17 +114,4 @@ class TableMethodSource extends TableSource {
96
114
  }
97
115
  }
98
116
  exports.TableMethodSource = TableMethodSource;
99
- class TableFunctionSource extends TableSource {
100
- constructor(tableURI) {
101
- super();
102
- this.tableURI = tableURI;
103
- this.elementType = 'tableFunctionSource';
104
- }
105
- getTableInfo() {
106
- // This use of `deprecatedParseTableURI` is ok because it is for handling the
107
- // old, soon-to-be-deprecated table syntax.
108
- return (0, find_external_references_1.deprecatedParseTableURI)(this.tableURI);
109
- }
110
- }
111
- exports.TableFunctionSource = TableFunctionSource;
112
117
  //# sourceMappingURL=table-source.js.map
@@ -21,4 +21,5 @@ export declare class DefineGivens extends DocStatementList {
21
21
  elementType: string;
22
22
  readonly givens: GivenDeclaration[];
23
23
  constructor(givens: GivenDeclaration[]);
24
+ executeList(doc: Document): import("../../translate-response").ModelDataRequest;
24
25
  }
@@ -209,6 +209,13 @@ class DefineGivens extends malloy_element_1.DocStatementList {
209
209
  this.elementType = 'defineGivens';
210
210
  this.givens = givens;
211
211
  }
212
+ executeList(doc) {
213
+ if (this.isRestricted()) {
214
+ this.logError('restricted-construct-forbidden', '`given:` cannot declare new givens in a restricted query — only `$NAME` references to existing givens are allowed.');
215
+ return undefined;
216
+ }
217
+ return super.executeList(doc);
218
+ }
212
219
  }
213
220
  exports.DefineGivens = DefineGivens;
214
221
  //# sourceMappingURL=define-given.js.map
@@ -89,6 +89,10 @@ class ImportStatement extends malloy_element_1.ListOf {
89
89
  }
90
90
  execute(doc) {
91
91
  var _a;
92
+ if (this.isRestricted()) {
93
+ this.logError('restricted-construct-forbidden', `\`import "${this.url}"\` cannot be used in a restricted query — file imports are not permitted.`);
94
+ return;
95
+ }
92
96
  const trans = this.translator();
93
97
  if (!trans) {
94
98
  this.logError('no-translator-for-import', 'Cannot import without translation context');
@@ -12,6 +12,7 @@ export declare class ObjectAnnotation extends MalloyElement implements QueryProp
12
12
  }
13
13
  export declare class ModelAnnotation extends ObjectAnnotation implements DocStatement {
14
14
  elementType: string;
15
+ getCompilerFlagNotes(): Note[];
15
16
  getCompilerFlagLines(): string[];
16
17
  execute(doc: Document): void;
17
18
  }
@@ -41,12 +41,19 @@ class ModelAnnotation extends ObjectAnnotation {
41
41
  super(...arguments);
42
42
  this.elementType = 'modelAnnotation';
43
43
  }
44
+ getCompilerFlagNotes() {
45
+ return this.notes.filter(note => note.text.match(COMPILER_FLAG_PREFIX));
46
+ }
44
47
  getCompilerFlagLines() {
45
- return this.notes
46
- .filter(note => note.text.match(COMPILER_FLAG_PREFIX))
47
- .map(note => note.text);
48
+ return this.getCompilerFlagNotes().map(note => note.text);
48
49
  }
49
50
  execute(doc) {
51
+ if (this.isRestricted()) {
52
+ for (const note of this.getCompilerFlagNotes()) {
53
+ const line = note.text.replace(/\n$/, '');
54
+ this.logError('restricted-construct-forbidden', `\`${line}\` cannot be used in a restricted query — compiler-flag annotations are not permitted.`, { at: note.at });
55
+ }
56
+ }
50
57
  if (doc.annotation.notes === undefined) {
51
58
  doc.annotation.notes = [];
52
59
  }
@@ -37,6 +37,7 @@ export declare abstract class MalloyElement {
37
37
  */
38
38
  kupuna(): MalloyElement;
39
39
  translator(): MalloyTranslation | undefined;
40
+ isRestricted(): boolean;
40
41
  setTranslator(x: MalloyTranslation): void;
41
42
  addReference(reference: DocumentReference): void;
42
43
  private get sourceURL();
@@ -158,6 +158,10 @@ class MalloyElement {
158
158
  }
159
159
  return undefined;
160
160
  }
161
+ isRestricted() {
162
+ var _a, _b;
163
+ return (_b = (_a = this.translator()) === null || _a === void 0 ? void 0 : _a.root.restrictedMode) !== null && _b !== void 0 ? _b : false;
164
+ }
161
165
  setTranslator(x) {
162
166
  this.xlate = x;
163
167
  }
@@ -35,10 +35,11 @@ type HasAnnotations = ParserRuleContext & {
35
35
  export declare class MalloyToAST extends AbstractParseTreeVisitor<ast.MalloyElement> implements MalloyParserVisitor<ast.MalloyElement> {
36
36
  readonly parseInfo: MalloyParseInfo;
37
37
  readonly msgLog: MessageLogger;
38
+ readonly restrictedMode: boolean;
38
39
  readonly timer: Timer;
39
40
  private compilerFlagSrc;
40
41
  private compilerFlagTag?;
41
- constructor(parseInfo: MalloyParseInfo, msgLog: MessageLogger, compilerFlagSrc: string[]);
42
+ constructor(parseInfo: MalloyParseInfo, msgLog: MessageLogger, compilerFlagSrc: string[], restrictedMode?: boolean);
42
43
  getCompilerFlags(): Tag;
43
44
  run(): {
44
45
  ast: ast.MalloyElement;