@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.
- package/dist/api/foundation/compile.d.ts +7 -6
- package/dist/api/foundation/compile.js +22 -6
- package/dist/api/foundation/config.d.ts +2 -3
- package/dist/api/foundation/config.js +23 -11
- package/dist/api/foundation/core.js +1 -1
- package/dist/api/foundation/runtime.d.ts +85 -5
- package/dist/api/foundation/runtime.js +204 -14
- package/dist/api/foundation/types.d.ts +2 -0
- package/dist/api/util.js +4 -0
- package/dist/connection/base_connection.js +6 -0
- package/dist/connection/validate_table_path.d.ts +10 -0
- package/dist/connection/validate_table_path.js +56 -0
- package/dist/dialect/databricks/databricks.d.ts +4 -4
- package/dist/dialect/databricks/databricks.js +17 -22
- package/dist/dialect/dialect.d.ts +100 -4
- package/dist/dialect/dialect.js +145 -1
- package/dist/dialect/duckdb/duckdb.d.ts +2 -3
- package/dist/dialect/duckdb/duckdb.js +12 -14
- package/dist/dialect/duckdb/table-path-parser.d.ts +2 -0
- package/dist/dialect/duckdb/table-path-parser.js +57 -0
- package/dist/dialect/index.d.ts +2 -0
- package/dist/dialect/index.js +4 -1
- package/dist/dialect/mysql/mysql.d.ts +4 -4
- package/dist/dialect/mysql/mysql.js +25 -20
- package/dist/dialect/pg_impl.d.ts +3 -1
- package/dist/dialect/pg_impl.js +6 -3
- package/dist/dialect/postgres/postgres.d.ts +1 -3
- package/dist/dialect/postgres/postgres.js +8 -16
- package/dist/dialect/snowflake/snowflake.d.ts +4 -4
- package/dist/dialect/snowflake/snowflake.js +11 -27
- package/dist/dialect/standardsql/standardsql.d.ts +6 -4
- package/dist/dialect/standardsql/standardsql.js +36 -15
- package/dist/dialect/table-path.d.ts +54 -0
- package/dist/dialect/table-path.js +144 -0
- package/dist/dialect/trino/trino.d.ts +0 -3
- package/dist/dialect/trino/trino.js +7 -20
- package/dist/index.d.ts +2 -2
- package/dist/index.js +4 -2
- package/dist/lang/ast/expressions/expr-func.js +30 -11
- package/dist/lang/ast/expressions/expr-given.js +1 -0
- package/dist/lang/ast/field-space/reference-field.js +1 -1
- package/dist/lang/ast/source-elements/sql-source.js +4 -0
- package/dist/lang/ast/source-elements/table-source.d.ts +1 -7
- package/dist/lang/ast/source-elements/table-source.js +24 -19
- package/dist/lang/ast/statements/define-given.d.ts +1 -0
- package/dist/lang/ast/statements/define-given.js +7 -0
- package/dist/lang/ast/statements/import-statement.js +4 -0
- package/dist/lang/ast/types/annotation-elements.d.ts +1 -0
- package/dist/lang/ast/types/annotation-elements.js +10 -3
- package/dist/lang/ast/types/malloy-element.d.ts +1 -0
- package/dist/lang/ast/types/malloy-element.js +4 -0
- package/dist/lang/malloy-to-ast.d.ts +2 -1
- package/dist/lang/malloy-to-ast.js +11 -1
- package/dist/lang/parse-log.d.ts +2 -0
- package/dist/lang/parse-log.js +4 -0
- package/dist/lang/parse-malloy.d.ts +4 -1
- package/dist/lang/parse-malloy.js +63 -11
- package/dist/lang/parse-tree-walkers/find-external-references.d.ts +2 -15
- package/dist/lang/parse-tree-walkers/find-external-references.js +6 -23
- package/dist/lang/test/test-translator.d.ts +19 -5
- package/dist/lang/test/test-translator.js +15 -12
- package/dist/lang/translate-response.d.ts +1 -1
- package/dist/lang/zone.d.ts +2 -0
- package/dist/lang/zone.js +10 -0
- package/dist/model/constant_expression_compiler.js +14 -5
- package/dist/model/expression_compiler.js +19 -17
- package/dist/model/field_instance.js +7 -3
- package/dist/model/filter_compilers.js +1 -1
- package/dist/model/given_binding.js +26 -21
- package/dist/model/index.d.ts +1 -0
- package/dist/model/index.js +3 -1
- package/dist/model/malloy_compile_error.d.ts +13 -0
- package/dist/model/malloy_compile_error.js +23 -0
- package/dist/model/malloy_types.d.ts +2 -0
- package/dist/model/query_model_impl.js +9 -8
- package/dist/model/query_node.d.ts +5 -5
- package/dist/model/query_node.js +21 -16
- package/dist/model/query_query.js +60 -44
- package/dist/model/sql_compiled.d.ts +2 -4
- package/dist/model/sql_compiled.js +20 -18
- package/dist/test/test-models.js +2 -2
- package/dist/version.d.ts +1 -1
- package/dist/version.js +1 -1
- package/package.json +4 -4
|
@@ -8,6 +8,7 @@
|
|
|
8
8
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
9
9
|
exports.BaseConnection = void 0;
|
|
10
10
|
const sql_block_1 = require("../model/sql_block");
|
|
11
|
+
const validate_table_path_1 = require("./validate_table_path");
|
|
11
12
|
class BaseConnection {
|
|
12
13
|
constructor() {
|
|
13
14
|
this.schemaCache = {};
|
|
@@ -47,6 +48,11 @@ class BaseConnection {
|
|
|
47
48
|
const schemas = {};
|
|
48
49
|
const errors = {};
|
|
49
50
|
for (const [tableName, tablePath] of Object.entries(missing)) {
|
|
51
|
+
const invalid = (0, validate_table_path_1.validateCanonicalTablePath)(this.dialectName, tablePath);
|
|
52
|
+
if (invalid !== undefined) {
|
|
53
|
+
errors[tableName] = invalid;
|
|
54
|
+
continue;
|
|
55
|
+
}
|
|
50
56
|
const inCache = await this.checkSchemaCache(tablePath, 'table', async () => await this.fetchTableSchema(tableName, tablePath), refreshTimestamp);
|
|
51
57
|
if (inCache.schema) {
|
|
52
58
|
schemas[tableName] = inCache.schema;
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
/** Validate against a known dialect. Returns an error string or undefined. */
|
|
2
|
+
export declare function validateCanonicalTablePath(dialectName: string, tablePath: string): string | undefined;
|
|
3
|
+
/**
|
|
4
|
+
* Validate against any registered dialect. Used at boundaries where the
|
|
5
|
+
* destination dialect isn't synchronously known (virtualMap, manifest
|
|
6
|
+
* entries).
|
|
7
|
+
*/
|
|
8
|
+
export declare function validateCanonicalTablePathAnyDialect(tablePath: string): string | undefined;
|
|
9
|
+
/** Throw if `tablePath` isn't canonical SQL in any registered dialect. */
|
|
10
|
+
export declare function requireCanonicalTablePathAnyDialect(tablePath: string, prefix: string): void;
|
|
@@ -0,0 +1,56 @@
|
|
|
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.validateCanonicalTablePath = validateCanonicalTablePath;
|
|
8
|
+
exports.validateCanonicalTablePathAnyDialect = validateCanonicalTablePathAnyDialect;
|
|
9
|
+
exports.requireCanonicalTablePathAnyDialect = requireCanonicalTablePathAnyDialect;
|
|
10
|
+
const dialect_map_1 = require("../dialect/dialect_map");
|
|
11
|
+
/** Validate against a known dialect. Returns an error string or undefined. */
|
|
12
|
+
function validateCanonicalTablePath(dialectName, tablePath) {
|
|
13
|
+
let dialect;
|
|
14
|
+
try {
|
|
15
|
+
dialect = (0, dialect_map_1.getDialect)(dialectName);
|
|
16
|
+
}
|
|
17
|
+
catch {
|
|
18
|
+
return `tablePath '${tablePath}' cannot be validated: unknown dialect '${dialectName}'`;
|
|
19
|
+
}
|
|
20
|
+
const result = dialect.sqlValidateTableName(tablePath);
|
|
21
|
+
if (!result.ok) {
|
|
22
|
+
return `tablePath '${tablePath}' is not canonical SQL for the ${dialectName} dialect; the translator must validate before passing it here. (${result.error})`;
|
|
23
|
+
}
|
|
24
|
+
if (result.canonical !== tablePath) {
|
|
25
|
+
return `tablePath '${tablePath}' is not canonical SQL for the ${dialectName} dialect; the translator must validate before passing it here.`;
|
|
26
|
+
}
|
|
27
|
+
return undefined;
|
|
28
|
+
}
|
|
29
|
+
/**
|
|
30
|
+
* Validate against any registered dialect. Used at boundaries where the
|
|
31
|
+
* destination dialect isn't synchronously known (virtualMap, manifest
|
|
32
|
+
* entries).
|
|
33
|
+
*/
|
|
34
|
+
function validateCanonicalTablePathAnyDialect(tablePath) {
|
|
35
|
+
let suggestion;
|
|
36
|
+
for (const dialect of (0, dialect_map_1.getDialects)()) {
|
|
37
|
+
const result = dialect.sqlValidateTableName(tablePath);
|
|
38
|
+
if (result.ok) {
|
|
39
|
+
if (result.canonical === tablePath)
|
|
40
|
+
return undefined;
|
|
41
|
+
if (suggestion === undefined)
|
|
42
|
+
suggestion = result.canonical;
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
if (suggestion !== undefined) {
|
|
46
|
+
return `value '${tablePath}' is not canonical SQL; did you mean '${suggestion}'?`;
|
|
47
|
+
}
|
|
48
|
+
return `value '${tablePath}' is not a valid canonical table path in any registered dialect`;
|
|
49
|
+
}
|
|
50
|
+
/** Throw if `tablePath` isn't canonical SQL in any registered dialect. */
|
|
51
|
+
function requireCanonicalTablePathAnyDialect(tablePath, prefix) {
|
|
52
|
+
const err = validateCanonicalTablePathAnyDialect(tablePath);
|
|
53
|
+
if (err !== undefined)
|
|
54
|
+
throw new Error(`${prefix}: ${err}`);
|
|
55
|
+
}
|
|
56
|
+
//# sourceMappingURL=validate_table_path.js.map
|
|
@@ -4,6 +4,9 @@ import { Dialect } from '../dialect';
|
|
|
4
4
|
import type { DialectFunctionOverloadDef } from '../functions';
|
|
5
5
|
export declare class DatabricksDialect extends Dialect {
|
|
6
6
|
name: string;
|
|
7
|
+
stringLiteralStyle: "backslash";
|
|
8
|
+
identifierEscapeStyle: "doubled";
|
|
9
|
+
identifierQuoteChar: string;
|
|
7
10
|
defaultNumberType: string;
|
|
8
11
|
defaultDecimalType: string;
|
|
9
12
|
udfPrefix: string;
|
|
@@ -37,9 +40,9 @@ export declare class DatabricksDialect extends Dialect {
|
|
|
37
40
|
hasTimestamptz: boolean;
|
|
38
41
|
supportsBigIntPrecision: boolean;
|
|
39
42
|
maxIdentifierLength: number;
|
|
43
|
+
tablePathBareIdentRegex: RegExp;
|
|
40
44
|
malloyTypeToSQLType(malloyType: AtomicTypeDef): string;
|
|
41
45
|
sqlTypeToMalloyType(sqlType: string): BasicAtomicTypeDef;
|
|
42
|
-
quoteTablePath(tablePath: string): string;
|
|
43
46
|
sqlGroupSetTable(groupSetCount: number): string;
|
|
44
47
|
sqlLateralJoinBag(expressions: LateralJoinExpression[]): string;
|
|
45
48
|
sqlOrderBy(orderTerms: string[], obr?: OrderByRequest): string;
|
|
@@ -60,7 +63,6 @@ export declare class DatabricksDialect extends Dialect {
|
|
|
60
63
|
sqlCreateFunction(id: string, funcText: string): string;
|
|
61
64
|
sqlCreateFunctionCombineLastStage(lastStageName: string, fieldList: DialectFieldList): string;
|
|
62
65
|
sqlSelectAliasAsStruct(alias: string, fieldList: DialectFieldList): string;
|
|
63
|
-
sqlMaybeQuoteIdentifier(identifier: string): string;
|
|
64
66
|
sqlCreateTableAsSelect(tableName: string, sql: string): string;
|
|
65
67
|
sqlNowExpr(): string;
|
|
66
68
|
sqlConvertToCivilTime(expr: string, timezone: string, _typeDef: AtomicTypeDef): {
|
|
@@ -78,8 +80,6 @@ export declare class DatabricksDialect extends Dialect {
|
|
|
78
80
|
sqlTimestamptzLiteral(_qi: QueryInfo, _literal: string, _timezone: string): string;
|
|
79
81
|
sqlMeasureTimeExpr(df: MeasureTimeExpr): string;
|
|
80
82
|
sqlSampleTable(tableSQL: string, sample: Sampling | undefined): string;
|
|
81
|
-
sqlLiteralString(literal: string): string;
|
|
82
|
-
sqlLiteralRegexp(literal: string): string;
|
|
83
83
|
getDialectFunctionOverrides(): {
|
|
84
84
|
[name: string]: DialectFunctionOverloadDef[];
|
|
85
85
|
};
|
|
@@ -43,6 +43,9 @@ class DatabricksDialect extends dialect_1.Dialect {
|
|
|
43
43
|
constructor() {
|
|
44
44
|
super(...arguments);
|
|
45
45
|
this.name = 'databricks';
|
|
46
|
+
this.stringLiteralStyle = dialect_1.EscapeStyle.Backslash;
|
|
47
|
+
this.identifierEscapeStyle = dialect_1.EscapeStyle.Doubled;
|
|
48
|
+
this.identifierQuoteChar = '`';
|
|
46
49
|
this.defaultNumberType = 'DOUBLE';
|
|
47
50
|
this.defaultDecimalType = 'DECIMAL';
|
|
48
51
|
this.udfPrefix = '__udf';
|
|
@@ -74,6 +77,10 @@ class DatabricksDialect extends dialect_1.Dialect {
|
|
|
74
77
|
this.hasTimestamptz = false;
|
|
75
78
|
this.supportsBigIntPrecision = false;
|
|
76
79
|
this.maxIdentifierLength = 255;
|
|
80
|
+
// Databricks bare identifiers may start with a digit, but cannot be
|
|
81
|
+
// entirely digits (or they lex as number literals). Verified against
|
|
82
|
+
// the live engine: `1foo` resolves; `$` is rejected.
|
|
83
|
+
this.tablePathBareIdentRegex = /^[A-Za-z0-9_]*[A-Za-z_][A-Za-z0-9_]*/;
|
|
77
84
|
}
|
|
78
85
|
malloyTypeToSQLType(malloyType) {
|
|
79
86
|
switch (malloyType.type) {
|
|
@@ -95,7 +102,7 @@ class DatabricksDialect extends dialect_1.Dialect {
|
|
|
95
102
|
const fields = [];
|
|
96
103
|
for (const f of malloyType.fields) {
|
|
97
104
|
if ((0, malloy_types_1.isAtomic)(f)) {
|
|
98
|
-
fields.push(`${this.
|
|
105
|
+
fields.push(`${this.sqlQuoteIdentifier(f.name)}: ${this.malloyTypeToSQLType(f)}`);
|
|
99
106
|
}
|
|
100
107
|
}
|
|
101
108
|
return `STRUCT<${fields.join(', ')}>`;
|
|
@@ -105,7 +112,7 @@ class DatabricksDialect extends dialect_1.Dialect {
|
|
|
105
112
|
const fields = [];
|
|
106
113
|
for (const f of malloyType.fields) {
|
|
107
114
|
if ((0, malloy_types_1.isAtomic)(f)) {
|
|
108
|
-
fields.push(`${this.
|
|
115
|
+
fields.push(`${this.sqlQuoteIdentifier(f.name)}: ${this.malloyTypeToSQLType(f)}`);
|
|
109
116
|
}
|
|
110
117
|
}
|
|
111
118
|
return `ARRAY<STRUCT<${fields.join(', ')}>>`;
|
|
@@ -128,12 +135,6 @@ class DatabricksDialect extends dialect_1.Dialect {
|
|
|
128
135
|
rawType: baseSqlType,
|
|
129
136
|
});
|
|
130
137
|
}
|
|
131
|
-
quoteTablePath(tablePath) {
|
|
132
|
-
return tablePath
|
|
133
|
-
.split('.')
|
|
134
|
-
.map(part => (/^[a-zA-Z_]\w*$/.test(part) ? part : `\`${part}\``))
|
|
135
|
-
.join('.');
|
|
136
|
-
}
|
|
137
138
|
sqlGroupSetTable(groupSetCount) {
|
|
138
139
|
return `LATERAL VIEW EXPLODE(SEQUENCE(0, ${groupSetCount})) group_set AS group_set`;
|
|
139
140
|
}
|
|
@@ -163,7 +164,9 @@ class DatabricksDialect extends dialect_1.Dialect {
|
|
|
163
164
|
// field names) in Databricks.
|
|
164
165
|
buildNamedStructExpression(fieldList) {
|
|
165
166
|
return ('named_struct(' +
|
|
166
|
-
fieldList
|
|
167
|
+
fieldList
|
|
168
|
+
.map(f => `${this.sqlLiteralString(f.rawName)}, ${f.sqlExpression}`)
|
|
169
|
+
.join(', ') +
|
|
167
170
|
')');
|
|
168
171
|
}
|
|
169
172
|
sqlAggregateTurtle(groupSet, fieldList, orderBy) {
|
|
@@ -207,7 +210,9 @@ class DatabricksDialect extends dialect_1.Dialect {
|
|
|
207
210
|
sqlCoaleseMeasuresInline(groupSet, fieldList) {
|
|
208
211
|
const namedStruct = this.buildNamedStructExpression(fieldList);
|
|
209
212
|
const nullStruct = 'named_struct(' +
|
|
210
|
-
fieldList
|
|
213
|
+
fieldList
|
|
214
|
+
.map(f => `${this.sqlLiteralString(f.rawName)}, NULL`)
|
|
215
|
+
.join(', ') +
|
|
211
216
|
')';
|
|
212
217
|
return `COALESCE(FIRST(CASE WHEN group_set=${groupSet} THEN ${namedStruct} END) IGNORE NULLS, ${nullStruct})`;
|
|
213
218
|
}
|
|
@@ -268,7 +273,7 @@ class DatabricksDialect extends dialect_1.Dialect {
|
|
|
268
273
|
if (childName === '__row_id') {
|
|
269
274
|
return `__row_id_from_${parentAlias}`;
|
|
270
275
|
}
|
|
271
|
-
return `${parentAlias}.${this.
|
|
276
|
+
return `${parentAlias}.${this.sqlQuoteIdentifier(childName)}`;
|
|
272
277
|
}
|
|
273
278
|
sqlCreateFunction(id, funcText) {
|
|
274
279
|
return `CREATE TEMPORARY FUNCTION ${id}(param STRING) RETURNS STRING RETURN (\n${(0, utils_1.indent)(funcText)}\n);\n`;
|
|
@@ -279,13 +284,10 @@ class DatabricksDialect extends dialect_1.Dialect {
|
|
|
279
284
|
}
|
|
280
285
|
sqlSelectAliasAsStruct(alias, fieldList) {
|
|
281
286
|
const fields = fieldList
|
|
282
|
-
.map(f => `${alias}.${this.
|
|
287
|
+
.map(f => `${alias}.${this.sqlQuoteIdentifier(f.rawName)}`)
|
|
283
288
|
.join(', ');
|
|
284
289
|
return `STRUCT(${fields})`;
|
|
285
290
|
}
|
|
286
|
-
sqlMaybeQuoteIdentifier(identifier) {
|
|
287
|
-
return '`' + identifier.replace(/`/g, '``') + '`';
|
|
288
|
-
}
|
|
289
291
|
sqlCreateTableAsSelect(tableName, sql) {
|
|
290
292
|
return `CREATE TABLE ${tableName} AS ${sql}`;
|
|
291
293
|
}
|
|
@@ -406,13 +408,6 @@ class DatabricksDialect extends dialect_1.Dialect {
|
|
|
406
408
|
}
|
|
407
409
|
return tableSQL;
|
|
408
410
|
}
|
|
409
|
-
sqlLiteralString(literal) {
|
|
410
|
-
const noVirgule = literal.replace(/\\/g, '\\\\');
|
|
411
|
-
return "'" + noVirgule.replace(/'/g, "\\'") + "'";
|
|
412
|
-
}
|
|
413
|
-
sqlLiteralRegexp(literal) {
|
|
414
|
-
return "'" + literal.replace(/'/g, "''") + "'";
|
|
415
|
-
}
|
|
416
411
|
getDialectFunctionOverrides() {
|
|
417
412
|
return (0, functions_1.expandOverrideMap)(function_overrides_1.DATABRICKS_MALLOY_STANDARD_OVERLOADS);
|
|
418
413
|
}
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import type { Expr, Sampling, AtomicTypeDef, MeasureTimeExpr, TimeExtractExpr, TypecastExpr, RegexMatchExpr, TimeLiteralExpr, RecordLiteralNode, ArrayLiteralNode, BasicAtomicTypeDef, OrderBy, TimestampUnit, ATimestampTypeDef, TimeExpr, TemporalFieldType } from '../model/malloy_types';
|
|
2
2
|
import type { DialectFunctionOverloadDef } from './functions';
|
|
3
|
+
import type { ValidateTablePathResult } from './table-path';
|
|
3
4
|
interface DialectField {
|
|
4
5
|
typeDef: AtomicTypeDef;
|
|
5
6
|
sqlExpression: string;
|
|
@@ -35,6 +36,18 @@ export declare const MIN_INT128: bigint;
|
|
|
35
36
|
export declare const MAX_INT128: bigint;
|
|
36
37
|
export declare const MIN_DECIMAL38: bigint;
|
|
37
38
|
export declare const MAX_DECIMAL38: bigint;
|
|
39
|
+
/**
|
|
40
|
+
* Allowed values for `Dialect.stringLiteralStyle` and
|
|
41
|
+
* `Dialect.identifierEscapeStyle`. Subclasses set their style with
|
|
42
|
+
* e.g. `stringLiteralStyle = EscapeStyle.Backslash`; the `as const`
|
|
43
|
+
* is centralized here so dialect files stay free of it.
|
|
44
|
+
*/
|
|
45
|
+
export declare const EscapeStyle: {
|
|
46
|
+
readonly Doubled: "doubled";
|
|
47
|
+
readonly Backslash: "backslash";
|
|
48
|
+
readonly Unset: "unset";
|
|
49
|
+
};
|
|
50
|
+
export type EscapeStyleValue = (typeof EscapeStyle)[keyof typeof EscapeStyle];
|
|
38
51
|
/**
|
|
39
52
|
* Data which dialect methods need in order to correctly generate SQL.
|
|
40
53
|
* Initially this is just timezone related, but I made this an interface
|
|
@@ -132,7 +145,34 @@ export declare abstract class Dialect {
|
|
|
132
145
|
abstract getDialectFunctions(): {
|
|
133
146
|
[name: string]: DialectFunctionOverloadDef[];
|
|
134
147
|
};
|
|
135
|
-
|
|
148
|
+
/**
|
|
149
|
+
* Regex matching one bare (unquoted) table-path segment for this
|
|
150
|
+
* dialect, anchored at the start of the input. Drives the default
|
|
151
|
+
* `sqlValidateTableName` along with `identifierQuoteChar` and
|
|
152
|
+
* `identifierEscapeStyle`.
|
|
153
|
+
*
|
|
154
|
+
* The default is strict ANSI: `[A-Za-z_][A-Za-z0-9_]*`. Override to
|
|
155
|
+
* widen the char set (Postgres allows `$`, MySQL allows digit-start
|
|
156
|
+
* with caveats, BigQuery allows dashes, …). The per-dialect regexes
|
|
157
|
+
* were verified by probing live engines.
|
|
158
|
+
*/
|
|
159
|
+
tablePathBareIdentRegex: RegExp;
|
|
160
|
+
/**
|
|
161
|
+
* Validate a user-supplied table-path string for this dialect. On
|
|
162
|
+
* success, the canonical form is the SQL fragment that gets pasted
|
|
163
|
+
* into `FROM` clauses and stored in `StructDef.tablePath`. Canonical
|
|
164
|
+
* equals input verbatim except where a Malloy convenience needs
|
|
165
|
+
* translating into dialect SQL (today: DuckDB's file-path branch
|
|
166
|
+
* wraps the input in single quotes).
|
|
167
|
+
*
|
|
168
|
+
* The default implementation handles every dialect whose table-path
|
|
169
|
+
* grammar is a dotted sequence of `bare | quoted` segments — every
|
|
170
|
+
* dialect we ship except DuckDB. New dialects of that shape need
|
|
171
|
+
* only override `tablePathBareIdentRegex`; override
|
|
172
|
+
* `sqlValidateTableName` itself only if your grammar is structurally
|
|
173
|
+
* different.
|
|
174
|
+
*/
|
|
175
|
+
sqlValidateTableName(input: string): ValidateTablePathResult;
|
|
136
176
|
abstract sqlGroupSetTable(groupSetCount: number): string;
|
|
137
177
|
abstract sqlAnyValue(groupSet: number, fieldName: string): string;
|
|
138
178
|
abstract sqlAggregateTurtle(groupSet: number, fieldList: DialectFieldList, orderBy: CompiledOrderBy[] | undefined): string;
|
|
@@ -151,7 +191,52 @@ export declare abstract class Dialect {
|
|
|
151
191
|
abstract sqlSelectAliasAsStruct(alias: string, fieldList: DialectFieldList): string;
|
|
152
192
|
sqlFinalStage(_lastStageName: string, _fields: string[]): string;
|
|
153
193
|
sqlDateToString(sqlDateExp: string): string;
|
|
154
|
-
|
|
194
|
+
/**
|
|
195
|
+
* The character the dialect uses to quote identifiers. Most dialects
|
|
196
|
+
* use ANSI double-quote `"`; MySQL, BigQuery and Databricks use the
|
|
197
|
+
* backtick `` ` ``. The dialect must escape this character by doubling
|
|
198
|
+
* inside a quoted identifier.
|
|
199
|
+
*
|
|
200
|
+
* Defaults to the empty string sentinel — concrete dialects must set
|
|
201
|
+
* a real value (or override `sqlQuoteIdentifier`), otherwise the
|
|
202
|
+
* base method throws to surface the omission immediately.
|
|
203
|
+
*/
|
|
204
|
+
identifierQuoteChar: string;
|
|
205
|
+
/**
|
|
206
|
+
* How the dialect escapes the closing quote inside a string literal.
|
|
207
|
+
* Set via `EscapeStyle` from this module:
|
|
208
|
+
*
|
|
209
|
+
* - `EscapeStyle.Doubled`: `''` escapes `'`. Backslash is a literal
|
|
210
|
+
* character. (ANSI standard; Postgres, DuckDB, Trino, Presto.)
|
|
211
|
+
* - `EscapeStyle.Backslash`: `\'` escapes `'`, `\\` escapes `\`.
|
|
212
|
+
* (BigQuery, Snowflake, MySQL default mode, Databricks.)
|
|
213
|
+
* - `EscapeStyle.Unset` (default): base methods throw if reached. A
|
|
214
|
+
* new dialect must set this (or override the literal methods).
|
|
215
|
+
*
|
|
216
|
+
* `sqlLiteralString` and `sqlLiteralRegexp` share this style — the
|
|
217
|
+
* regex engine receives whatever the SQL parser decodes, and the two
|
|
218
|
+
* must agree or regex patterns containing backslashes silently break.
|
|
219
|
+
*/
|
|
220
|
+
stringLiteralStyle: EscapeStyleValue;
|
|
221
|
+
/**
|
|
222
|
+
* How the dialect escapes the quote character inside a quoted
|
|
223
|
+
* identifier. Mirrors `stringLiteralStyle`:
|
|
224
|
+
*
|
|
225
|
+
* - `EscapeStyle.Doubled`: doubling the quote char escapes it (ANSI
|
|
226
|
+
* standard; most dialects).
|
|
227
|
+
* - `EscapeStyle.Backslash`: backslash-style escape, with `\\` for
|
|
228
|
+
* backslash and `\<quote>` for the quote char. (BigQuery — quoted
|
|
229
|
+
* identifiers use string-literal escape sequences.)
|
|
230
|
+
* - `EscapeStyle.Unset` (default): base method throws if reached.
|
|
231
|
+
*/
|
|
232
|
+
identifierEscapeStyle: EscapeStyleValue;
|
|
233
|
+
/**
|
|
234
|
+
* Wrap an identifier in the dialect's quote character, escaping any
|
|
235
|
+
* embedded quote characters per the dialect's `identifierEscapeStyle`.
|
|
236
|
+
* This is the only safe way to render a user-controlled identifier
|
|
237
|
+
* in SQL.
|
|
238
|
+
*/
|
|
239
|
+
sqlQuoteIdentifier(identifier: string): string;
|
|
155
240
|
abstract castToString(expression: string): string;
|
|
156
241
|
abstract concat(...values: string[]): string;
|
|
157
242
|
sqlLiteralNumber(literal: string): string;
|
|
@@ -354,8 +439,19 @@ export declare abstract class Dialect {
|
|
|
354
439
|
* the civil time in the specified timezone
|
|
355
440
|
*/
|
|
356
441
|
abstract sqlTimestamptzLiteral(qi: QueryInfo, literal: string, timezone: string): string;
|
|
357
|
-
|
|
358
|
-
|
|
442
|
+
/**
|
|
443
|
+
* Render a Malloy string as a SQL string literal. The escape style is
|
|
444
|
+
* driven by `stringLiteralStyle`; dialects normally do not override
|
|
445
|
+
* this method.
|
|
446
|
+
*/
|
|
447
|
+
sqlLiteralString(literal: string): string;
|
|
448
|
+
/**
|
|
449
|
+
* Render a Malloy regex literal as a SQL string literal. Defaults to
|
|
450
|
+
* `sqlLiteralString` — the regex engine receives whatever bytes the
|
|
451
|
+
* SQL parser decodes, and `sqlLiteralString` already produces a
|
|
452
|
+
* correctly decoding literal for both escape styles.
|
|
453
|
+
*/
|
|
454
|
+
sqlLiteralRegexp(literal: string): string;
|
|
359
455
|
abstract sqlLiteralArray(lit: ArrayLiteralNode): string;
|
|
360
456
|
abstract sqlLiteralRecord(lit: RecordLiteralNode): string;
|
|
361
457
|
/**
|
package/dist/dialect/dialect.js
CHANGED
|
@@ -22,10 +22,11 @@
|
|
|
22
22
|
* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
|
23
23
|
*/
|
|
24
24
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
25
|
-
exports.Dialect = exports.dayIndex = exports.MAX_DECIMAL38 = exports.MIN_DECIMAL38 = exports.MAX_INT128 = exports.MIN_INT128 = exports.MAX_INT64 = exports.MIN_INT64 = exports.MAX_INT32 = exports.MIN_INT32 = void 0;
|
|
25
|
+
exports.Dialect = exports.dayIndex = exports.EscapeStyle = exports.MAX_DECIMAL38 = exports.MIN_DECIMAL38 = exports.MAX_INT128 = exports.MIN_INT128 = exports.MAX_INT64 = exports.MIN_INT64 = exports.MAX_INT32 = exports.MIN_INT32 = void 0;
|
|
26
26
|
exports.inDays = inDays;
|
|
27
27
|
exports.qtz = qtz;
|
|
28
28
|
const malloy_types_1 = require("../model/malloy_types");
|
|
29
|
+
const table_path_1 = require("./table-path");
|
|
29
30
|
/*
|
|
30
31
|
* Standard integer type limits.
|
|
31
32
|
* Use these in dialect integerTypeMappings definitions.
|
|
@@ -42,6 +43,17 @@ exports.MAX_INT128 = BigInt('170141183460469231731687303715884105727'); // 2^127
|
|
|
42
43
|
// Decimal(38,0) limits (for Snowflake NUMBER(38,0))
|
|
43
44
|
exports.MIN_DECIMAL38 = BigInt('-99999999999999999999999999999999999999'); // -(10^38 - 1)
|
|
44
45
|
exports.MAX_DECIMAL38 = BigInt('99999999999999999999999999999999999999'); // 10^38 - 1
|
|
46
|
+
/**
|
|
47
|
+
* Allowed values for `Dialect.stringLiteralStyle` and
|
|
48
|
+
* `Dialect.identifierEscapeStyle`. Subclasses set their style with
|
|
49
|
+
* e.g. `stringLiteralStyle = EscapeStyle.Backslash`; the `as const`
|
|
50
|
+
* is centralized here so dialect files stay free of it.
|
|
51
|
+
*/
|
|
52
|
+
exports.EscapeStyle = {
|
|
53
|
+
Doubled: 'doubled',
|
|
54
|
+
Backslash: 'backslash',
|
|
55
|
+
Unset: 'unset',
|
|
56
|
+
};
|
|
45
57
|
const allUnits = [
|
|
46
58
|
'microsecond',
|
|
47
59
|
'millisecond',
|
|
@@ -138,6 +150,57 @@ class Dialect {
|
|
|
138
150
|
{ min: BigInt(exports.MIN_INT32), max: BigInt(exports.MAX_INT32), numberType: 'integer' },
|
|
139
151
|
{ min: exports.MIN_INT64, max: exports.MAX_INT64, numberType: 'bigint' },
|
|
140
152
|
];
|
|
153
|
+
/**
|
|
154
|
+
* Regex matching one bare (unquoted) table-path segment for this
|
|
155
|
+
* dialect, anchored at the start of the input. Drives the default
|
|
156
|
+
* `sqlValidateTableName` along with `identifierQuoteChar` and
|
|
157
|
+
* `identifierEscapeStyle`.
|
|
158
|
+
*
|
|
159
|
+
* The default is strict ANSI: `[A-Za-z_][A-Za-z0-9_]*`. Override to
|
|
160
|
+
* widen the char set (Postgres allows `$`, MySQL allows digit-start
|
|
161
|
+
* with caveats, BigQuery allows dashes, …). The per-dialect regexes
|
|
162
|
+
* were verified by probing live engines.
|
|
163
|
+
*/
|
|
164
|
+
this.tablePathBareIdentRegex = /^[A-Za-z_][A-Za-z0-9_]*/;
|
|
165
|
+
/**
|
|
166
|
+
* The character the dialect uses to quote identifiers. Most dialects
|
|
167
|
+
* use ANSI double-quote `"`; MySQL, BigQuery and Databricks use the
|
|
168
|
+
* backtick `` ` ``. The dialect must escape this character by doubling
|
|
169
|
+
* inside a quoted identifier.
|
|
170
|
+
*
|
|
171
|
+
* Defaults to the empty string sentinel — concrete dialects must set
|
|
172
|
+
* a real value (or override `sqlQuoteIdentifier`), otherwise the
|
|
173
|
+
* base method throws to surface the omission immediately.
|
|
174
|
+
*/
|
|
175
|
+
this.identifierQuoteChar = '';
|
|
176
|
+
/**
|
|
177
|
+
* How the dialect escapes the closing quote inside a string literal.
|
|
178
|
+
* Set via `EscapeStyle` from this module:
|
|
179
|
+
*
|
|
180
|
+
* - `EscapeStyle.Doubled`: `''` escapes `'`. Backslash is a literal
|
|
181
|
+
* character. (ANSI standard; Postgres, DuckDB, Trino, Presto.)
|
|
182
|
+
* - `EscapeStyle.Backslash`: `\'` escapes `'`, `\\` escapes `\`.
|
|
183
|
+
* (BigQuery, Snowflake, MySQL default mode, Databricks.)
|
|
184
|
+
* - `EscapeStyle.Unset` (default): base methods throw if reached. A
|
|
185
|
+
* new dialect must set this (or override the literal methods).
|
|
186
|
+
*
|
|
187
|
+
* `sqlLiteralString` and `sqlLiteralRegexp` share this style — the
|
|
188
|
+
* regex engine receives whatever the SQL parser decodes, and the two
|
|
189
|
+
* must agree or regex patterns containing backslashes silently break.
|
|
190
|
+
*/
|
|
191
|
+
this.stringLiteralStyle = exports.EscapeStyle.Unset;
|
|
192
|
+
/**
|
|
193
|
+
* How the dialect escapes the quote character inside a quoted
|
|
194
|
+
* identifier. Mirrors `stringLiteralStyle`:
|
|
195
|
+
*
|
|
196
|
+
* - `EscapeStyle.Doubled`: doubling the quote char escapes it (ANSI
|
|
197
|
+
* standard; most dialects).
|
|
198
|
+
* - `EscapeStyle.Backslash`: backslash-style escape, with `\\` for
|
|
199
|
+
* backslash and `\<quote>` for the quote char. (BigQuery — quoted
|
|
200
|
+
* identifiers use string-literal escape sequences.)
|
|
201
|
+
* - `EscapeStyle.Unset` (default): base method throws if reached.
|
|
202
|
+
*/
|
|
203
|
+
this.identifierEscapeStyle = exports.EscapeStyle.Unset;
|
|
141
204
|
}
|
|
142
205
|
// Generate the lateral join bag clause for window function partitioning.
|
|
143
206
|
// The expressions are dimension fields that need to be referenced by name
|
|
@@ -218,6 +281,34 @@ class Dialect {
|
|
|
218
281
|
},
|
|
219
282
|
};
|
|
220
283
|
}
|
|
284
|
+
/**
|
|
285
|
+
* Validate a user-supplied table-path string for this dialect. On
|
|
286
|
+
* success, the canonical form is the SQL fragment that gets pasted
|
|
287
|
+
* into `FROM` clauses and stored in `StructDef.tablePath`. Canonical
|
|
288
|
+
* equals input verbatim except where a Malloy convenience needs
|
|
289
|
+
* translating into dialect SQL (today: DuckDB's file-path branch
|
|
290
|
+
* wraps the input in single quotes).
|
|
291
|
+
*
|
|
292
|
+
* The default implementation handles every dialect whose table-path
|
|
293
|
+
* grammar is a dotted sequence of `bare | quoted` segments — every
|
|
294
|
+
* dialect we ship except DuckDB. New dialects of that shape need
|
|
295
|
+
* only override `tablePathBareIdentRegex`; override
|
|
296
|
+
* `sqlValidateTableName` itself only if your grammar is structurally
|
|
297
|
+
* different.
|
|
298
|
+
*/
|
|
299
|
+
sqlValidateTableName(input) {
|
|
300
|
+
if (this.identifierEscapeStyle !== exports.EscapeStyle.Doubled &&
|
|
301
|
+
this.identifierEscapeStyle !== exports.EscapeStyle.Backslash) {
|
|
302
|
+
throw new Error(`${this.name}: sqlValidateTableName requires identifierEscapeStyle ` +
|
|
303
|
+
'to be set to Doubled or Backslash (or override sqlValidateTableName).');
|
|
304
|
+
}
|
|
305
|
+
return (0, table_path_1.validateDottedTablePath)(input, {
|
|
306
|
+
quoteChar: this.identifierQuoteChar,
|
|
307
|
+
escapeStyle: this.identifierEscapeStyle,
|
|
308
|
+
bareIdentRegex: this.tablePathBareIdentRegex,
|
|
309
|
+
dialectName: this.name,
|
|
310
|
+
});
|
|
311
|
+
}
|
|
221
312
|
// Format a CompiledOrderBy[] into an ORDER BY clause string for use
|
|
222
313
|
// inside an aggregate turtle expression. Dialects which support ORDER BY
|
|
223
314
|
// inside aggregate functions can call this helper from sqlAggregateTurtle.
|
|
@@ -232,6 +323,33 @@ class Dialect {
|
|
|
232
323
|
sqlDateToString(sqlDateExp) {
|
|
233
324
|
return this.castToString(`DATE(${sqlDateExp})`);
|
|
234
325
|
}
|
|
326
|
+
/**
|
|
327
|
+
* Wrap an identifier in the dialect's quote character, escaping any
|
|
328
|
+
* embedded quote characters per the dialect's `identifierEscapeStyle`.
|
|
329
|
+
* This is the only safe way to render a user-controlled identifier
|
|
330
|
+
* in SQL.
|
|
331
|
+
*/
|
|
332
|
+
sqlQuoteIdentifier(identifier) {
|
|
333
|
+
const q = this.identifierQuoteChar;
|
|
334
|
+
if (!q) {
|
|
335
|
+
throw new Error(`${this.name}: identifierQuoteChar is not set. ` +
|
|
336
|
+
'Set it on the dialect (e.g. \'"\' or "`"), ' +
|
|
337
|
+
'or override sqlQuoteIdentifier.');
|
|
338
|
+
}
|
|
339
|
+
if (this.identifierEscapeStyle === exports.EscapeStyle.Doubled) {
|
|
340
|
+
return q + identifier.split(q).join(q + q) + q;
|
|
341
|
+
}
|
|
342
|
+
if (this.identifierEscapeStyle === exports.EscapeStyle.Backslash) {
|
|
343
|
+
const escaped = identifier
|
|
344
|
+
.replace(/\\/g, '\\\\')
|
|
345
|
+
.split(q)
|
|
346
|
+
.join('\\' + q);
|
|
347
|
+
return q + escaped + q;
|
|
348
|
+
}
|
|
349
|
+
throw new Error(`${this.name}: identifierEscapeStyle is not set. ` +
|
|
350
|
+
'Set it to EscapeStyle.Doubled or EscapeStyle.Backslash on the dialect, ' +
|
|
351
|
+
'or override sqlQuoteIdentifier.');
|
|
352
|
+
}
|
|
235
353
|
sqlLiteralNumber(literal) {
|
|
236
354
|
return literal;
|
|
237
355
|
}
|
|
@@ -330,6 +448,32 @@ class Dialect {
|
|
|
330
448
|
}
|
|
331
449
|
return sql;
|
|
332
450
|
}
|
|
451
|
+
/**
|
|
452
|
+
* Render a Malloy string as a SQL string literal. The escape style is
|
|
453
|
+
* driven by `stringLiteralStyle`; dialects normally do not override
|
|
454
|
+
* this method.
|
|
455
|
+
*/
|
|
456
|
+
sqlLiteralString(literal) {
|
|
457
|
+
if (this.stringLiteralStyle === 'doubled') {
|
|
458
|
+
return "'" + literal.split("'").join("''") + "'";
|
|
459
|
+
}
|
|
460
|
+
if (this.stringLiteralStyle === 'backslash') {
|
|
461
|
+
const escaped = literal.replace(/\\/g, '\\\\').replace(/'/g, "\\'");
|
|
462
|
+
return "'" + escaped + "'";
|
|
463
|
+
}
|
|
464
|
+
throw new Error(`${this.name}: stringLiteralStyle is not set. ` +
|
|
465
|
+
'Set it to EscapeStyle.Doubled or EscapeStyle.Backslash on the dialect, ' +
|
|
466
|
+
'or override sqlLiteralString.');
|
|
467
|
+
}
|
|
468
|
+
/**
|
|
469
|
+
* Render a Malloy regex literal as a SQL string literal. Defaults to
|
|
470
|
+
* `sqlLiteralString` — the regex engine receives whatever bytes the
|
|
471
|
+
* SQL parser decodes, and `sqlLiteralString` already produces a
|
|
472
|
+
* correctly decoding literal for both escape styles.
|
|
473
|
+
*/
|
|
474
|
+
sqlLiteralRegexp(literal) {
|
|
475
|
+
return this.sqlLiteralString(literal);
|
|
476
|
+
}
|
|
333
477
|
/**
|
|
334
478
|
* The dialect has a chance to over-ride how expressions are translated. If
|
|
335
479
|
* "undefined" is returned then the translation is left to the query translator.
|
|
@@ -2,6 +2,7 @@ import type { Sampling, AtomicTypeDef, RegexMatchExpr, MeasureTimeExpr, BasicAto
|
|
|
2
2
|
import type { DialectFunctionOverloadDef } from '../functions';
|
|
3
3
|
import type { CompiledOrderBy, DialectFieldList, FieldReferenceType, IntegerTypeMapping } from '../dialect';
|
|
4
4
|
import { PostgresBase } from '../pg_impl';
|
|
5
|
+
import type { ValidateTablePathResult } from '../table-path';
|
|
5
6
|
export declare class DuckDBDialect extends PostgresBase {
|
|
6
7
|
name: string;
|
|
7
8
|
experimental: boolean;
|
|
@@ -25,7 +26,7 @@ export declare class DuckDBDialect extends PostgresBase {
|
|
|
25
26
|
requiresExplicitUnnestOrdering: boolean;
|
|
26
27
|
integerTypeMappings: IntegerTypeMapping[];
|
|
27
28
|
get udfPrefix(): string;
|
|
28
|
-
|
|
29
|
+
sqlValidateTableName(input: string): ValidateTablePathResult;
|
|
29
30
|
sqlGroupSetTable(groupSetCount: number): string;
|
|
30
31
|
sqlAnyValue(groupSet: number, fieldName: string): string;
|
|
31
32
|
sqlLiteralNumber(literal: string): string;
|
|
@@ -48,8 +49,6 @@ export declare class DuckDBDialect extends PostgresBase {
|
|
|
48
49
|
sqlAggDistinct(key: string, values: string[], func: (valNames: string[]) => string): string;
|
|
49
50
|
sqlSampleTable(tableSQL: string, sample: Sampling | undefined): string;
|
|
50
51
|
sqlOrderBy(orderTerms: string[]): string;
|
|
51
|
-
sqlLiteralString(literal: string): string;
|
|
52
|
-
sqlLiteralRegexp(literal: string): string;
|
|
53
52
|
getDialectFunctionOverrides(): {
|
|
54
53
|
[name: string]: DialectFunctionOverloadDef[];
|
|
55
54
|
};
|
|
@@ -31,6 +31,7 @@ const pg_impl_1 = require("../pg_impl");
|
|
|
31
31
|
const dialect_functions_1 = require("./dialect_functions");
|
|
32
32
|
const function_overrides_1 = require("./function_overrides");
|
|
33
33
|
const tiny_parser_1 = require("../tiny_parser");
|
|
34
|
+
const table_path_parser_1 = require("./table-path-parser");
|
|
34
35
|
// need to refactor runSQL to take a SQLBlock instead of just a sql string.
|
|
35
36
|
const hackSplitComment = '-- hack: split on this';
|
|
36
37
|
const duckDBToMalloyTypes = {
|
|
@@ -85,9 +86,12 @@ class DuckDBDialect extends pg_impl_1.PostgresBase {
|
|
|
85
86
|
get udfPrefix() {
|
|
86
87
|
return `__udf${Math.floor(Math.random() * 100000)}`;
|
|
87
88
|
}
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
89
|
+
// DuckDB's table-path grammar is too rich for the shared ANSI parser
|
|
90
|
+
// (it has file-path and explicit-single-quoted-literal branches in
|
|
91
|
+
// addition to dotted identifier paths). See
|
|
92
|
+
// `duckdb/table-path-parser.ts` for the grammar.
|
|
93
|
+
sqlValidateTableName(input) {
|
|
94
|
+
return (0, table_path_parser_1.validateDuckDBTablePath)(input);
|
|
91
95
|
}
|
|
92
96
|
sqlGroupSetTable(groupSetCount) {
|
|
93
97
|
return `CROSS JOIN (SELECT UNNEST(GENERATE_SERIES(0,${groupSetCount},1)) as group_set ) as group_set`;
|
|
@@ -174,7 +178,7 @@ class DuckDBDialect extends pg_impl_1.PostgresBase {
|
|
|
174
178
|
return parentAlias;
|
|
175
179
|
}
|
|
176
180
|
else {
|
|
177
|
-
return `${parentAlias}.${this.
|
|
181
|
+
return `${parentAlias}.${this.sqlQuoteIdentifier(childName)}`;
|
|
178
182
|
}
|
|
179
183
|
}
|
|
180
184
|
sqlUnnestPipelineHead(isSingleton, sourceSQLExpression) {
|
|
@@ -204,7 +208,7 @@ class DuckDBDialect extends pg_impl_1.PostgresBase {
|
|
|
204
208
|
}
|
|
205
209
|
}
|
|
206
210
|
return `SELECT LIST(STRUCT_PACK(${dialectFieldList
|
|
207
|
-
.map(d => this.
|
|
211
|
+
.map(d => this.sqlQuoteIdentifier(d.sqlOutputName))
|
|
208
212
|
.join(',')})${o}) FROM ${lastStageName}\n`;
|
|
209
213
|
}
|
|
210
214
|
sqlSelectAliasAsStruct(alias, dialectFieldList) {
|
|
@@ -263,12 +267,6 @@ class DuckDBDialect extends pg_impl_1.PostgresBase {
|
|
|
263
267
|
sqlOrderBy(orderTerms) {
|
|
264
268
|
return `ORDER BY ${orderTerms.map(t => `${t} NULLS LAST`).join(',')}`;
|
|
265
269
|
}
|
|
266
|
-
sqlLiteralString(literal) {
|
|
267
|
-
return "'" + literal.replace(/'/g, "''") + "'";
|
|
268
|
-
}
|
|
269
|
-
sqlLiteralRegexp(literal) {
|
|
270
|
-
return "'" + literal.replace(/'/g, "''") + "'";
|
|
271
|
-
}
|
|
272
270
|
getDialectFunctionOverrides() {
|
|
273
271
|
return (0, functions_1.expandOverrideMap)(function_overrides_1.DUCKDB_MALLOY_STANDARD_OVERLOADS);
|
|
274
272
|
}
|
|
@@ -297,7 +295,7 @@ class DuckDBDialect extends pg_impl_1.PostgresBase {
|
|
|
297
295
|
const typeSpec = [];
|
|
298
296
|
for (const f of malloyType.fields) {
|
|
299
297
|
if ((0, malloy_types_1.isAtomic)(f)) {
|
|
300
|
-
typeSpec.push(`${this.
|
|
298
|
+
typeSpec.push(`${this.sqlQuoteIdentifier(f.name)} ${this.malloyTypeToSQLType(f)}`);
|
|
301
299
|
}
|
|
302
300
|
}
|
|
303
301
|
return `STRUCT(${typeSpec.join(', ')})`;
|
|
@@ -307,7 +305,7 @@ class DuckDBDialect extends pg_impl_1.PostgresBase {
|
|
|
307
305
|
const typeSpec = [];
|
|
308
306
|
for (const f of malloyType.fields) {
|
|
309
307
|
if ((0, malloy_types_1.isAtomic)(f)) {
|
|
310
|
-
typeSpec.push(`${this.
|
|
308
|
+
typeSpec.push(`${this.sqlQuoteIdentifier(f.name)} ${this.malloyTypeToSQLType(f)}`);
|
|
311
309
|
}
|
|
312
310
|
}
|
|
313
311
|
return `STRUCT(${typeSpec.join(', ')})[]`;
|
|
@@ -389,7 +387,7 @@ class DuckDBDialect extends pg_impl_1.PostgresBase {
|
|
|
389
387
|
return `DATE_SUB('${df.units}', ${lVal}, ${rVal})`;
|
|
390
388
|
}
|
|
391
389
|
sqlLiteralRecord(lit) {
|
|
392
|
-
const pairs = Object.entries(lit.kids).map(([propName, propVal]) => `${this.
|
|
390
|
+
const pairs = Object.entries(lit.kids).map(([propName, propVal]) => `${this.sqlQuoteIdentifier(propName)}:${propVal.sql}`);
|
|
393
391
|
return '{' + pairs.join(',') + '}';
|
|
394
392
|
}
|
|
395
393
|
}
|