@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.
- package/dist/api/foundation/config.d.ts +2 -3
- package/dist/api/foundation/config.js +23 -11
- package/dist/api/foundation/core.d.ts +0 -4
- package/dist/api/foundation/core.js +14 -11
- package/dist/api/foundation/runtime.js +21 -1
- 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-compare.d.ts +15 -0
- package/dist/lang/ast/expressions/expr-compare.js +82 -2
- package/dist/lang/ast/source-elements/table-source.d.ts +1 -7
- package/dist/lang/ast/source-elements/table-source.js +20 -19
- package/dist/lang/ast/statements/define-given.d.ts +2 -1
- package/dist/lang/ast/statements/define-given.js +52 -1
- package/dist/lang/ast/types/malloy-element.js +2 -0
- package/dist/lang/lib/Malloy/MalloyParser.d.ts +188 -167
- package/dist/lang/lib/Malloy/MalloyParser.js +2582 -2442
- package/dist/lang/lib/Malloy/MalloyParserListener.d.ts +24 -0
- package/dist/lang/lib/Malloy/MalloyParserVisitor.d.ts +15 -0
- package/dist/lang/malloy-to-ast.d.ts +9 -2
- package/dist/lang/malloy-to-ast.js +37 -2
- package/dist/lang/parse-log.d.ts +23 -0
- package/dist/lang/parse-log.js +6 -0
- package/dist/lang/parse-malloy.js +37 -7
- 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/expr-to-str.js +3 -0
- package/dist/lang/translate-response.d.ts +1 -1
- package/dist/model/expression_compiler.js +38 -11
- package/dist/model/filter_compilers.js +1 -1
- package/dist/model/given_binding.d.ts +15 -0
- package/dist/model/given_binding.js +35 -0
- package/dist/model/inline_expr.d.ts +30 -0
- package/dist/model/inline_expr.js +184 -0
- package/dist/model/malloy_types.d.ts +19 -1
- package/dist/model/query_model_impl.js +7 -7
- package/dist/model/query_query.d.ts +1 -1
- package/dist/model/query_query.js +37 -33
- package/dist/model/sql_compiled.d.ts +2 -4
- package/dist/model/sql_compiled.js +14 -15
- 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
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
|
}
|
|
@@ -0,0 +1,57 @@
|
|
|
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.validateDuckDBTablePath = validateDuckDBTablePath;
|
|
8
|
+
const table_path_1 = require("../table-path");
|
|
9
|
+
const DUCKDB_FILE_PATH_RE = /^[A-Za-z0-9._\-/:?*]+$/;
|
|
10
|
+
const DUCKDB_SINGLE_QUOTED_RE = /^'(?:[^']|'')*'$/;
|
|
11
|
+
function validateDuckDBTablePath(input) {
|
|
12
|
+
if (input.length === 0) {
|
|
13
|
+
return { ok: false, error: 'DuckDB table path is empty' };
|
|
14
|
+
}
|
|
15
|
+
// Branch 1: explicit single-quoted literal.
|
|
16
|
+
if (input[0] === "'") {
|
|
17
|
+
if (DUCKDB_SINGLE_QUOTED_RE.test(input)) {
|
|
18
|
+
const body = input.slice(1, -1).replace(/''/g, "'");
|
|
19
|
+
if (body.includes(';') || body.includes('--')) {
|
|
20
|
+
return {
|
|
21
|
+
ok: false,
|
|
22
|
+
error: `Invalid DuckDB table path: ${JSON.stringify(input)} — ` +
|
|
23
|
+
'forbidden character `;` or `--` in single-quoted body.',
|
|
24
|
+
};
|
|
25
|
+
}
|
|
26
|
+
return { ok: true, canonical: input };
|
|
27
|
+
}
|
|
28
|
+
return {
|
|
29
|
+
ok: false,
|
|
30
|
+
error: `Invalid DuckDB table path: ${JSON.stringify(input)} — ` +
|
|
31
|
+
'unterminated or trailing-junk single-quoted literal.',
|
|
32
|
+
};
|
|
33
|
+
}
|
|
34
|
+
// Branch 2: identifier path (same grammar as ANSI dialects).
|
|
35
|
+
const id = (0, table_path_1.validateDottedTablePath)(input, {
|
|
36
|
+
quoteChar: '"',
|
|
37
|
+
escapeStyle: 'doubled',
|
|
38
|
+
bareIdentRegex: /^[A-Za-z_][A-Za-z0-9_]*/,
|
|
39
|
+
dialectName: 'DuckDB',
|
|
40
|
+
});
|
|
41
|
+
if (id.ok)
|
|
42
|
+
return id;
|
|
43
|
+
// Branch 3: file-path convenience.
|
|
44
|
+
if (DUCKDB_FILE_PATH_RE.test(input)) {
|
|
45
|
+
return { ok: true, canonical: `'${input}'` };
|
|
46
|
+
}
|
|
47
|
+
return {
|
|
48
|
+
ok: false,
|
|
49
|
+
error: `Invalid DuckDB table path: ${JSON.stringify(input)} — expected an ` +
|
|
50
|
+
'identifier path, a quoted identifier path, a single-quoted ' +
|
|
51
|
+
"literal ('foo.csv'), or a file-path-shaped string of letters, " +
|
|
52
|
+
'digits, and `._-/:?*`. For table-valued function calls (e.g. ' +
|
|
53
|
+
"read_parquet('foo.parquet')) or other table expressions, use a " +
|
|
54
|
+
'SQL block instead: connection.sql("""SELECT * FROM …""").',
|
|
55
|
+
};
|
|
56
|
+
}
|
|
57
|
+
//# sourceMappingURL=table-path-parser.js.map
|
package/dist/dialect/index.d.ts
CHANGED
|
@@ -14,3 +14,5 @@ export { getMalloyStandardFunctions } from './functions';
|
|
|
14
14
|
export type { MalloyStandardFunctionImplementations } from './functions';
|
|
15
15
|
export type { TinyToken } from './tiny_parser';
|
|
16
16
|
export { TinyParser } from './tiny_parser';
|
|
17
|
+
export type { DecodeDottedTablePathResult, DottedTablePathOptions, TablePathEscapeStyle, TablePathSegment, ValidateTablePathResult, } from './table-path';
|
|
18
|
+
export { decodeDottedTablePath, validateDottedTablePath } from './table-path';
|
package/dist/dialect/index.js
CHANGED
|
@@ -22,7 +22,7 @@
|
|
|
22
22
|
* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
|
23
23
|
*/
|
|
24
24
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
25
|
-
exports.TinyParser = exports.getMalloyStandardFunctions = exports.registerDialect = exports.getDialect = exports.DatabricksDialect = exports.MySQLDialect = exports.TrinoDialect = exports.SnowflakeDialect = exports.DuckDBDialect = exports.PostgresDialect = exports.StandardSQLDialect = exports.qtz = exports.Dialect = exports.sql = exports.literal = exports.variadicParam = exports.param = exports.spread = exports.maxScalar = exports.minAggregate = exports.minScalar = exports.overload = exports.makeParam = exports.anyExprType = exports.arg = void 0;
|
|
25
|
+
exports.validateDottedTablePath = exports.decodeDottedTablePath = exports.TinyParser = exports.getMalloyStandardFunctions = exports.registerDialect = exports.getDialect = exports.DatabricksDialect = exports.MySQLDialect = exports.TrinoDialect = exports.SnowflakeDialect = exports.DuckDBDialect = exports.PostgresDialect = exports.StandardSQLDialect = exports.qtz = exports.Dialect = exports.sql = exports.literal = exports.variadicParam = exports.param = exports.spread = exports.maxScalar = exports.minAggregate = exports.minScalar = exports.overload = exports.makeParam = exports.anyExprType = exports.arg = void 0;
|
|
26
26
|
var util_1 = require("./functions/util");
|
|
27
27
|
Object.defineProperty(exports, "arg", { enumerable: true, get: function () { return util_1.arg; } });
|
|
28
28
|
Object.defineProperty(exports, "anyExprType", { enumerable: true, get: function () { return util_1.anyExprType; } });
|
|
@@ -60,4 +60,7 @@ var functions_1 = require("./functions");
|
|
|
60
60
|
Object.defineProperty(exports, "getMalloyStandardFunctions", { enumerable: true, get: function () { return functions_1.getMalloyStandardFunctions; } });
|
|
61
61
|
var tiny_parser_1 = require("./tiny_parser");
|
|
62
62
|
Object.defineProperty(exports, "TinyParser", { enumerable: true, get: function () { return tiny_parser_1.TinyParser; } });
|
|
63
|
+
var table_path_1 = require("./table-path");
|
|
64
|
+
Object.defineProperty(exports, "decodeDottedTablePath", { enumerable: true, get: function () { return table_path_1.decodeDottedTablePath; } });
|
|
65
|
+
Object.defineProperty(exports, "validateDottedTablePath", { enumerable: true, get: function () { return table_path_1.validateDottedTablePath; } });
|
|
63
66
|
//# sourceMappingURL=index.js.map
|
|
@@ -4,6 +4,9 @@ import { Dialect } from '../dialect';
|
|
|
4
4
|
import type { DialectFunctionOverloadDef } from '../functions';
|
|
5
5
|
export declare class MySQLDialect 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;
|
|
@@ -32,9 +35,9 @@ export declare class MySQLDialect extends Dialect {
|
|
|
32
35
|
booleanType: BooleanTypeSupport;
|
|
33
36
|
orderByClause: OrderByClauseType;
|
|
34
37
|
maxIdentifierLength: number;
|
|
38
|
+
tablePathBareIdentRegex: RegExp;
|
|
35
39
|
malloyTypeToSQLType(malloyType: AtomicTypeDef): string;
|
|
36
40
|
sqlTypeToMalloyType(sqlType: string): BasicAtomicTypeDef;
|
|
37
|
-
quoteTablePath(tablePath: string): string;
|
|
38
41
|
sqlGroupSetTable(groupSetCount: number): string;
|
|
39
42
|
sqlAnyValue(_groupSet: number, fieldName: string): string;
|
|
40
43
|
private mapFields;
|
|
@@ -54,7 +57,6 @@ export declare class MySQLDialect extends Dialect {
|
|
|
54
57
|
sqlCreateFunction(id: string, funcText: string): string;
|
|
55
58
|
sqlCreateFunctionCombineLastStage(lastStageName: string): string;
|
|
56
59
|
sqlSelectAliasAsStruct(_alias: string, _fieldList: DialectFieldList): string;
|
|
57
|
-
sqlMaybeQuoteIdentifier(identifier: string): string;
|
|
58
60
|
sqlCreateTableAsSelect(_tableName: string, _sql: string): string;
|
|
59
61
|
sqlNowExpr(): string;
|
|
60
62
|
sqlConvertToCivilTime(expr: string, timezone: string, _typeDef: AtomicTypeDef): {
|
|
@@ -73,8 +75,6 @@ export declare class MySQLDialect extends Dialect {
|
|
|
73
75
|
sqlMeasureTimeExpr(df: MeasureTimeExpr): string;
|
|
74
76
|
sqlAggDistinct(_key: string, _values: string[], _func: (valNames: string[]) => string): string;
|
|
75
77
|
sqlSampleTable(tableSQL: string, sample: Sampling | undefined): string;
|
|
76
|
-
sqlLiteralString(literal: string): string;
|
|
77
|
-
sqlLiteralRegexp(literal: string): string;
|
|
78
78
|
getDialectFunctionOverrides(): {
|
|
79
79
|
[name: string]: DialectFunctionOverloadDef[];
|
|
80
80
|
};
|
|
@@ -94,6 +94,9 @@ class MySQLDialect extends dialect_1.Dialect {
|
|
|
94
94
|
constructor() {
|
|
95
95
|
super(...arguments);
|
|
96
96
|
this.name = 'mysql';
|
|
97
|
+
this.stringLiteralStyle = dialect_1.EscapeStyle.Backslash;
|
|
98
|
+
this.identifierEscapeStyle = dialect_1.EscapeStyle.Doubled;
|
|
99
|
+
this.identifierQuoteChar = '`';
|
|
97
100
|
this.defaultNumberType = 'DOUBLE PRECISION';
|
|
98
101
|
this.defaultDecimalType = 'DECIMAL';
|
|
99
102
|
this.udfPrefix = 'ms_temp.__udf';
|
|
@@ -121,6 +124,11 @@ class MySQLDialect extends dialect_1.Dialect {
|
|
|
121
124
|
this.booleanType = 'simulated';
|
|
122
125
|
this.orderByClause = 'ordinal';
|
|
123
126
|
this.maxIdentifierLength = 64;
|
|
127
|
+
// MySQL bare identifiers allow `$` and may start with a digit, but
|
|
128
|
+
// cannot be entirely digits (or they lex as number literals). The
|
|
129
|
+
// regex requires at least one non-digit char somewhere in the run.
|
|
130
|
+
// Verified against the live engine.
|
|
131
|
+
this.tablePathBareIdentRegex = /^[A-Za-z0-9_$]*[A-Za-z_$][A-Za-z0-9_$]*/;
|
|
124
132
|
}
|
|
125
133
|
malloyTypeToSQLType(malloyType) {
|
|
126
134
|
switch (malloyType.type) {
|
|
@@ -154,12 +162,6 @@ class MySQLDialect extends dialect_1.Dialect {
|
|
|
154
162
|
rawType: baseSqlType,
|
|
155
163
|
});
|
|
156
164
|
}
|
|
157
|
-
quoteTablePath(tablePath) {
|
|
158
|
-
return tablePath
|
|
159
|
-
.split('.')
|
|
160
|
-
.map(part => `\`${part}\``)
|
|
161
|
-
.join('.');
|
|
162
|
-
}
|
|
163
165
|
sqlGroupSetTable(groupSetCount) {
|
|
164
166
|
return `CROSS JOIN (select number - 1 as group_set from JSON_TABLE(cast(concat("[1", repeat(",1", ${groupSetCount}), "]") as JSON),"$[*]" COLUMNS(number FOR ORDINALITY)) group_set) as group_set`;
|
|
165
167
|
}
|
|
@@ -167,7 +169,11 @@ class MySQLDialect extends dialect_1.Dialect {
|
|
|
167
169
|
return `MAX(${fieldName})`;
|
|
168
170
|
}
|
|
169
171
|
mapFields(fieldList) {
|
|
170
|
-
|
|
172
|
+
// JSON_OBJECT key is a string value. Routing rawName through
|
|
173
|
+
// sqlLiteralString also keeps the SQL valid under ANSI_QUOTES mode.
|
|
174
|
+
return fieldList
|
|
175
|
+
.map(f => `${this.sqlLiteralString(f.rawName)}, ${f.sqlExpression}`)
|
|
176
|
+
.join(', ');
|
|
171
177
|
}
|
|
172
178
|
sqlAggregateTurtle(groupSet, fieldList, orderBy) {
|
|
173
179
|
const separator = ',';
|
|
@@ -218,7 +224,12 @@ class MySQLDialect extends dialect_1.Dialect {
|
|
|
218
224
|
((_a = f.typeDef.rawType) === null || _a === void 0 ? void 0 : _a.match(/json/))) {
|
|
219
225
|
fType = f.typeDef.rawType.toUpperCase();
|
|
220
226
|
}
|
|
221
|
-
|
|
227
|
+
// JSON_TABLE PATH argument is a string literal containing a
|
|
228
|
+
// JSONPath. Render rawName through sqlLiteralString so a single
|
|
229
|
+
// quote in the user-controlled field name cannot close the SQL
|
|
230
|
+
// string literal.
|
|
231
|
+
const jsonPathLit = this.sqlLiteralString('$.' + f.rawName);
|
|
232
|
+
fields.push(`${this.sqlQuoteIdentifier(f.sqlOutputName)} ${fType} PATH ${jsonPathLit}`);
|
|
222
233
|
}
|
|
223
234
|
return fields.join(',\n');
|
|
224
235
|
}
|
|
@@ -266,7 +277,11 @@ class MySQLDialect extends dialect_1.Dialect {
|
|
|
266
277
|
}
|
|
267
278
|
sqlFieldReference(parentAlias, parentType, childName, childType) {
|
|
268
279
|
if (parentType === 'array[scalar]' || parentType === 'record') {
|
|
269
|
-
|
|
280
|
+
// childName comes from user-controlled record field names; render
|
|
281
|
+
// the JSON path through sqlLiteralString so a single quote in the
|
|
282
|
+
// name cannot close the SQL string literal.
|
|
283
|
+
const jsonPathLit = this.sqlLiteralString('$.' + childName);
|
|
284
|
+
let ret = `JSON_UNQUOTE(JSON_EXTRACT(${parentAlias},${jsonPathLit}))`;
|
|
270
285
|
if (parentType === 'array[scalar]') {
|
|
271
286
|
ret = `JSON_UNQUOTE(${parentAlias}.\`value\`)`;
|
|
272
287
|
}
|
|
@@ -280,7 +295,7 @@ class MySQLDialect extends dialect_1.Dialect {
|
|
|
280
295
|
return `CAST(${ret} as JSON)`;
|
|
281
296
|
}
|
|
282
297
|
}
|
|
283
|
-
const child = this.
|
|
298
|
+
const child = this.sqlQuoteIdentifier(childName);
|
|
284
299
|
return `${parentAlias}.${child}`;
|
|
285
300
|
}
|
|
286
301
|
sqlCreateFunction(id, funcText) {
|
|
@@ -297,9 +312,6 @@ class MySQLDialect extends dialect_1.Dialect {
|
|
|
297
312
|
// .map(name => `'${name.replace(/`/g, '')}', \`${alias}\`.${name}`)
|
|
298
313
|
// .join(',')})`;
|
|
299
314
|
}
|
|
300
|
-
sqlMaybeQuoteIdentifier(identifier) {
|
|
301
|
-
return '`' + identifier.replace(/`/g, '``') + '`';
|
|
302
|
-
}
|
|
303
315
|
// TODO: Check what this is.
|
|
304
316
|
sqlCreateTableAsSelect(_tableName, _sql) {
|
|
305
317
|
throw new Error('Not implemented Yet');
|
|
@@ -441,13 +453,6 @@ class MySQLDialect extends dialect_1.Dialect {
|
|
|
441
453
|
}
|
|
442
454
|
return tableSQL;
|
|
443
455
|
}
|
|
444
|
-
sqlLiteralString(literal) {
|
|
445
|
-
const noVirgule = literal.replace(/\\/g, '\\\\');
|
|
446
|
-
return "'" + noVirgule.replace(/'/g, "\\'") + "'";
|
|
447
|
-
}
|
|
448
|
-
sqlLiteralRegexp(literal) {
|
|
449
|
-
return "'" + literal.replace(/'/g, "''") + "'";
|
|
450
|
-
}
|
|
451
456
|
getDialectFunctionOverrides() {
|
|
452
457
|
return (0, functions_1.expandOverrideMap)(function_overrides_1.MYSQL_MALLOY_STANDARD_OVERLOADS);
|
|
453
458
|
}
|
|
@@ -9,6 +9,9 @@ export declare const timeExtractMap: Record<string, string>;
|
|
|
9
9
|
export declare abstract class PostgresBase extends Dialect {
|
|
10
10
|
hasTimestamptz: boolean;
|
|
11
11
|
supportsBigIntPrecision: boolean;
|
|
12
|
+
stringLiteralStyle: "doubled";
|
|
13
|
+
identifierEscapeStyle: "doubled";
|
|
14
|
+
identifierQuoteChar: string;
|
|
12
15
|
sqlNowExpr(): string;
|
|
13
16
|
sqlTimeExtractExpr(qi: QueryInfo, from: TimeExtractExpr): string;
|
|
14
17
|
sqlCast(qi: QueryInfo, cast: TypecastExpr): string;
|
|
@@ -18,7 +21,6 @@ export declare abstract class PostgresBase extends Dialect {
|
|
|
18
21
|
sqlTimestamptzLiteral(_qi: QueryInfo, literal: string, timezone: string): string;
|
|
19
22
|
sqlLiteralRecord(_lit: RecordLiteralNode): string;
|
|
20
23
|
sqlLiteralArray(lit: ArrayLiteralNode): string;
|
|
21
|
-
sqlMaybeQuoteIdentifier(identifier: string): string;
|
|
22
24
|
sqlConvertToCivilTime(expr: string, timezone: string, typeDef: AtomicTypeDef): {
|
|
23
25
|
sql: string;
|
|
24
26
|
typeDef: AtomicTypeDef;
|
package/dist/dialect/pg_impl.js
CHANGED
|
@@ -23,6 +23,12 @@ class PostgresBase extends dialect_1.Dialect {
|
|
|
23
23
|
this.hasTimestamptz = true;
|
|
24
24
|
// Postgres-family dialects use JSON serialization which loses bigint precision
|
|
25
25
|
this.supportsBigIntPrecision = false;
|
|
26
|
+
// All current Postgres-family dialects (Postgres, DuckDB, Trino, Presto)
|
|
27
|
+
// use ANSI doubled-quote literal escaping and ANSI double-quote identifiers.
|
|
28
|
+
// Subclasses may override.
|
|
29
|
+
this.stringLiteralStyle = dialect_1.EscapeStyle.Doubled;
|
|
30
|
+
this.identifierEscapeStyle = dialect_1.EscapeStyle.Doubled;
|
|
31
|
+
this.identifierQuoteChar = '"';
|
|
26
32
|
}
|
|
27
33
|
sqlNowExpr() {
|
|
28
34
|
return 'LOCALTIMESTAMP';
|
|
@@ -96,9 +102,6 @@ class PostgresBase extends dialect_1.Dialect {
|
|
|
96
102
|
const array = lit.kids.values.map(val => val.sql);
|
|
97
103
|
return 'ARRAY[' + array.join(',') + ']';
|
|
98
104
|
}
|
|
99
|
-
sqlMaybeQuoteIdentifier(identifier) {
|
|
100
|
-
return '"' + identifier.replace(/"/g, '""') + '"';
|
|
101
|
-
}
|
|
102
105
|
sqlConvertToCivilTime(expr, timezone, typeDef) {
|
|
103
106
|
// PostgreSQL/DuckDB: AT TIME ZONE is polymorphic
|
|
104
107
|
// For timestamptz (TIMESTAMPTZ): AT TIME ZONE converts to plain TIMESTAMP (civil in timezone)
|
|
@@ -27,7 +27,7 @@ export declare class PostgresDialect extends PostgresBase {
|
|
|
27
27
|
compoundObjectInSchema: boolean;
|
|
28
28
|
likeEscape: boolean;
|
|
29
29
|
maxIdentifierLength: number;
|
|
30
|
-
|
|
30
|
+
tablePathBareIdentRegex: RegExp;
|
|
31
31
|
sqlGroupSetTable(groupSetCount: number): string;
|
|
32
32
|
sqlAnyValue(groupSet: number, fieldName: string): string;
|
|
33
33
|
mapFields(fieldList: DialectFieldList): string;
|
|
@@ -52,8 +52,6 @@ export declare class PostgresDialect extends PostgresBase {
|
|
|
52
52
|
sqlAggDistinct(key: string, values: string[], func: (valNames: string[]) => string): string;
|
|
53
53
|
sqlSampleTable(tableSQL: string, sample: Sampling | undefined): string;
|
|
54
54
|
sqlOrderBy(orderTerms: string[]): string;
|
|
55
|
-
sqlLiteralString(literal: string): string;
|
|
56
|
-
sqlLiteralRegexp(literal: string): string;
|
|
57
55
|
getDialectFunctionOverrides(): {
|
|
58
56
|
[name: string]: DialectFunctionOverloadDef[];
|
|
59
57
|
};
|
|
@@ -99,12 +99,9 @@ class PostgresDialect extends pg_impl_1.PostgresBase {
|
|
|
99
99
|
this.compoundObjectInSchema = false;
|
|
100
100
|
this.likeEscape = false;
|
|
101
101
|
this.maxIdentifierLength = 63;
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
.split('.')
|
|
106
|
-
.map(part => `"${part}"`)
|
|
107
|
-
.join('.');
|
|
102
|
+
// Postgres bare-identifier continuation allows `$` (verified against
|
|
103
|
+
// the live engine: `postgres.table('foo$bar')` resolves successfully).
|
|
104
|
+
this.tablePathBareIdentRegex = /^[A-Za-z_][A-Za-z0-9_$]*/;
|
|
108
105
|
}
|
|
109
106
|
sqlGroupSetTable(groupSetCount) {
|
|
110
107
|
return `CROSS JOIN GENERATE_SERIES(0,${groupSetCount},1) as group_set`;
|
|
@@ -164,7 +161,8 @@ class PostgresDialect extends pg_impl_1.PostgresBase {
|
|
|
164
161
|
return `(${parentAlias}->>'__row_id')`;
|
|
165
162
|
}
|
|
166
163
|
if (parentType !== 'table') {
|
|
167
|
-
|
|
164
|
+
const nameLit = this.sqlLiteralString(childName);
|
|
165
|
+
let ret = `JSONB_EXTRACT_PATH_TEXT(${parentAlias},${nameLit})`;
|
|
168
166
|
switch (childType) {
|
|
169
167
|
case 'string':
|
|
170
168
|
break;
|
|
@@ -176,13 +174,13 @@ class PostgresDialect extends pg_impl_1.PostgresBase {
|
|
|
176
174
|
case 'record':
|
|
177
175
|
case 'array[record]':
|
|
178
176
|
case 'sql native':
|
|
179
|
-
ret = `JSONB_EXTRACT_PATH(${parentAlias}
|
|
177
|
+
ret = `JSONB_EXTRACT_PATH(${parentAlias},${nameLit})`;
|
|
180
178
|
break;
|
|
181
179
|
}
|
|
182
180
|
return ret;
|
|
183
181
|
}
|
|
184
182
|
else {
|
|
185
|
-
const child = this.
|
|
183
|
+
const child = this.sqlQuoteIdentifier(childName);
|
|
186
184
|
return `${parentAlias}.${child}`;
|
|
187
185
|
}
|
|
188
186
|
}
|
|
@@ -307,12 +305,6 @@ class PostgresDialect extends pg_impl_1.PostgresBase {
|
|
|
307
305
|
sqlOrderBy(orderTerms) {
|
|
308
306
|
return `ORDER BY ${orderTerms.map(t => `${t} NULLS LAST`).join(',')}`;
|
|
309
307
|
}
|
|
310
|
-
sqlLiteralString(literal) {
|
|
311
|
-
return "'" + literal.replace(/'/g, "''") + "'";
|
|
312
|
-
}
|
|
313
|
-
sqlLiteralRegexp(literal) {
|
|
314
|
-
return "'" + literal.replace(/'/g, "''") + "'";
|
|
315
|
-
}
|
|
316
308
|
getDialectFunctionOverrides() {
|
|
317
309
|
return (0, functions_1.expandOverrideMap)(function_overrides_1.POSTGRES_MALLOY_STANDARD_OVERLOADS);
|
|
318
310
|
}
|
|
@@ -372,7 +364,7 @@ class PostgresDialect extends pg_impl_1.PostgresBase {
|
|
|
372
364
|
sqlLiteralRecord(lit) {
|
|
373
365
|
const props = [];
|
|
374
366
|
for (const [kName, kVal] of Object.entries(lit.kids)) {
|
|
375
|
-
props.push(
|
|
367
|
+
props.push(`${this.sqlLiteralString(kName)},${kVal.sql}`);
|
|
376
368
|
}
|
|
377
369
|
return `JSONB_BUILD_OBJECT(${props.join(', ')})`;
|
|
378
370
|
}
|