@malloydata/malloy 0.0.432 → 0.0.434
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/api/foundation/compile.d.ts +2 -1
- package/dist/api/foundation/compile.js +8 -6
- package/dist/api/foundation/config.js +6 -15
- package/dist/api/foundation/runtime.d.ts +2 -7
- package/dist/api/foundation/runtime.js +10 -16
- package/dist/api/foundation/types.d.ts +1 -0
- package/dist/connection/validate_table_path.d.ts +1 -2
- package/dist/connection/validate_table_path.js +1 -2
- package/dist/dialect/databricks/databricks.js +6 -6
- package/dist/dialect/dialect.d.ts +21 -2
- package/dist/dialect/dialect.js +33 -3
- package/dist/dialect/duckdb/duckdb.js +7 -21
- package/dist/dialect/mysql/mysql.js +70 -29
- package/dist/dialect/pg_impl.js +12 -12
- package/dist/dialect/postgres/postgres.js +3 -3
- package/dist/dialect/snowflake/snowflake.js +12 -12
- package/dist/dialect/standardsql/standardsql.d.ts +1 -1
- package/dist/dialect/standardsql/standardsql.js +16 -13
- package/dist/dialect/trino/trino.d.ts +3 -3
- package/dist/dialect/trino/trino.js +32 -28
- package/dist/lang/ast/expressions/expr-aggregate-function.js +20 -26
- package/dist/lang/ast/field-space/def-space.js +1 -0
- package/dist/lang/ast/field-space/index-field-space.js +7 -33
- package/dist/lang/ast/field-space/parameter-space.js +2 -0
- package/dist/lang/ast/field-space/query-spaces.d.ts +10 -0
- package/dist/lang/ast/field-space/query-spaces.js +33 -32
- package/dist/lang/ast/field-space/static-space.d.ts +34 -1
- package/dist/lang/ast/field-space/static-space.js +118 -75
- package/dist/lang/ast/source-elements/refined-source.js +1 -1
- package/dist/lang/ast/source-elements/sql-source.js +2 -1
- package/dist/lang/ast/source-properties/timezone-statement.d.ts +4 -3
- package/dist/lang/ast/source-properties/timezone-statement.js +14 -12
- package/dist/lang/ast/types/lookup-result.d.ts +2 -0
- package/dist/lang/composite-source-utils.js +10 -3
- package/dist/lang/malloy-to-ast.js +2 -8
- package/dist/lang/parse-log.d.ts +1 -3
- package/dist/lang/parse-malloy.d.ts +3 -2
- package/dist/lang/parse-malloy.js +2 -1
- package/dist/lang/test/test-translator.d.ts +2 -1
- package/dist/lang/test/test-translator.js +1 -1
- package/dist/model/join_instance.d.ts +1 -0
- package/dist/model/join_instance.js +9 -1
- package/dist/model/query_query.d.ts +25 -0
- package/dist/model/query_query.js +142 -17
- package/dist/model/sql_block.d.ts +2 -2
- package/dist/model/sql_block.js +11 -47
- package/dist/model/sql_compiled.d.ts +3 -1
- package/dist/model/sql_compiled.js +8 -6
- package/dist/model/stage_writer.js +3 -3
- package/dist/model/utils.d.ts +2 -2
- package/dist/model/utils.js +2 -3
- package/dist/version.d.ts +1 -1
- package/dist/version.js +1 -1
- package/femto-config.motly +2 -0
- package/package.json +4 -4
package/dist/dialect/pg_impl.js
CHANGED
|
@@ -45,7 +45,7 @@ class PostgresBase extends dialect_1.Dialect {
|
|
|
45
45
|
if (malloy_types_1.TD.isAnyTimestamp(from.e.typeDef)) {
|
|
46
46
|
const tz = (0, dialect_1.qtz)(qi);
|
|
47
47
|
if (tz) {
|
|
48
|
-
extractFrom = `(${extractFrom}::TIMESTAMPTZ AT TIME ZONE
|
|
48
|
+
extractFrom = `(${extractFrom}::TIMESTAMPTZ AT TIME ZONE ${this.sqlTimezoneLiteral(tz)})`;
|
|
49
49
|
}
|
|
50
50
|
}
|
|
51
51
|
const extracted = `EXTRACT(${units} FROM ${extractFrom})`;
|
|
@@ -59,23 +59,23 @@ class PostgresBase extends dialect_1.Dialect {
|
|
|
59
59
|
if (tz && srcTypeDef && dstTypeDef) {
|
|
60
60
|
// TIMESTAMP → DATE: convert via TIMESTAMPTZ to query timezone
|
|
61
61
|
if (malloy_types_1.TD.isTimestamp(srcTypeDef) && malloy_types_1.TD.isDate(dstTypeDef)) {
|
|
62
|
-
return `CAST((${expr}::TIMESTAMPTZ) AT TIME ZONE
|
|
62
|
+
return `CAST((${expr}::TIMESTAMPTZ) AT TIME ZONE ${this.sqlTimezoneLiteral(tz)} AS DATE)`;
|
|
63
63
|
}
|
|
64
64
|
// TIMESTAMPTZ → DATE: convert to query timezone
|
|
65
65
|
if (malloy_types_1.TD.isTimestamptz(srcTypeDef) && malloy_types_1.TD.isDate(dstTypeDef)) {
|
|
66
|
-
return `CAST((${expr}) AT TIME ZONE
|
|
66
|
+
return `CAST((${expr}) AT TIME ZONE ${this.sqlTimezoneLiteral(tz)} AS DATE)`;
|
|
67
67
|
}
|
|
68
68
|
// DATE → TIMESTAMP: interpret date in query timezone, return UTC timestamp
|
|
69
69
|
if (malloy_types_1.TD.isDate(srcTypeDef) && malloy_types_1.TD.isTimestamp(dstTypeDef)) {
|
|
70
|
-
return `CAST((${expr})::TIMESTAMP AT TIME ZONE
|
|
70
|
+
return `CAST((${expr})::TIMESTAMP AT TIME ZONE ${this.sqlTimezoneLiteral(tz)} AS TIMESTAMP)`;
|
|
71
71
|
}
|
|
72
72
|
// DATE → TIMESTAMPTZ: interpret date in query timezone
|
|
73
73
|
if (malloy_types_1.TD.isDate(srcTypeDef) && malloy_types_1.TD.isTimestamptz(dstTypeDef)) {
|
|
74
|
-
return `(${expr})::TIMESTAMP AT TIME ZONE
|
|
74
|
+
return `(${expr})::TIMESTAMP AT TIME ZONE ${this.sqlTimezoneLiteral(tz)}`;
|
|
75
75
|
}
|
|
76
76
|
// TIMESTAMPTZ → TIMESTAMP: convert to query timezone (returns TIMESTAMP)
|
|
77
77
|
if (malloy_types_1.TD.isTimestamptz(srcTypeDef) && malloy_types_1.TD.isTimestamp(dstTypeDef)) {
|
|
78
|
-
return `(${expr}) AT TIME ZONE
|
|
78
|
+
return `(${expr}) AT TIME ZONE ${this.sqlTimezoneLiteral(tz)}`;
|
|
79
79
|
}
|
|
80
80
|
}
|
|
81
81
|
// No special handling needed, or no query timezone
|
|
@@ -94,12 +94,12 @@ class PostgresBase extends dialect_1.Dialect {
|
|
|
94
94
|
sqlTimestampLiteral(qi, literal, timezone) {
|
|
95
95
|
const tz = timezone || (0, dialect_1.qtz)(qi);
|
|
96
96
|
if (tz) {
|
|
97
|
-
return `TIMESTAMPTZ '${literal} ${tz}'::TIMESTAMP`;
|
|
97
|
+
return `TIMESTAMPTZ '${literal} ${this.checkedTimezoneName(tz)}'::TIMESTAMP`;
|
|
98
98
|
}
|
|
99
99
|
return `TIMESTAMP '${literal}'`;
|
|
100
100
|
}
|
|
101
101
|
sqlTimestamptzLiteral(_qi, literal, timezone) {
|
|
102
|
-
return `TIMESTAMPTZ '${literal} ${timezone}'`;
|
|
102
|
+
return `TIMESTAMPTZ '${literal} ${this.checkedTimezoneName(timezone)}'`;
|
|
103
103
|
}
|
|
104
104
|
sqlLiteralRecord(_lit) {
|
|
105
105
|
throw new Error('Cannot create a record literal for postgres base dialect');
|
|
@@ -113,22 +113,22 @@ class PostgresBase extends dialect_1.Dialect {
|
|
|
113
113
|
// For timestamptz (TIMESTAMPTZ): AT TIME ZONE converts to plain TIMESTAMP (civil in timezone)
|
|
114
114
|
if (typeDef.type === 'timestamptz') {
|
|
115
115
|
return {
|
|
116
|
-
sql: `(${expr}) AT TIME ZONE
|
|
116
|
+
sql: `(${expr}) AT TIME ZONE ${this.sqlTimezoneLiteral(timezone)}`,
|
|
117
117
|
typeDef: { type: 'timestamp' },
|
|
118
118
|
};
|
|
119
119
|
}
|
|
120
120
|
// For plain timestamps: cast to TIMESTAMPTZ (interprets as UTC)
|
|
121
121
|
// Then AT TIME ZONE converts to plain TIMESTAMP (civil in timezone)
|
|
122
122
|
return {
|
|
123
|
-
sql: `(${expr})::TIMESTAMPTZ AT TIME ZONE
|
|
123
|
+
sql: `(${expr})::TIMESTAMPTZ AT TIME ZONE ${this.sqlTimezoneLiteral(timezone)}`,
|
|
124
124
|
typeDef: { type: 'timestamp' },
|
|
125
125
|
};
|
|
126
126
|
}
|
|
127
127
|
sqlConvertFromCivilTime(expr, timezone, destTypeDef) {
|
|
128
128
|
if (destTypeDef.type === 'timestamptz') {
|
|
129
|
-
return `(${expr}) AT TIME ZONE
|
|
129
|
+
return `(${expr}) AT TIME ZONE ${this.sqlTimezoneLiteral(timezone)}`;
|
|
130
130
|
}
|
|
131
|
-
return `((${expr}) AT TIME ZONE
|
|
131
|
+
return `((${expr}) AT TIME ZONE ${this.sqlTimezoneLiteral(timezone)})::TIMESTAMP`;
|
|
132
132
|
}
|
|
133
133
|
sqlTruncate(expr, unit, _typeDef, inCivilTime, _timezone) {
|
|
134
134
|
// PostgreSQL/DuckDB starts weeks on Monday, Malloy wants Sunday
|
|
@@ -94,7 +94,7 @@ class PostgresDialect extends pg_impl_1.PostgresBase {
|
|
|
94
94
|
}
|
|
95
95
|
mapFields(fieldList) {
|
|
96
96
|
return fieldList
|
|
97
|
-
.map(f => `\n ${f.sqlExpression}${f.typeDef.type === 'number' ? `::${this.defaultNumberType}` : ''} as ${f.
|
|
97
|
+
.map(f => `\n ${f.sqlExpression}${f.typeDef.type === 'number' ? `::${this.defaultNumberType}` : ''} as ${this.sqlQuoteIdentifier(f.rawName)}`)
|
|
98
98
|
.join(', ');
|
|
99
99
|
}
|
|
100
100
|
sqlAggregateTurtle(groupSet, fieldList, orderBy, limit, filterSQL) {
|
|
@@ -110,7 +110,7 @@ class PostgresDialect extends pg_impl_1.PostgresBase {
|
|
|
110
110
|
}
|
|
111
111
|
sqlAnyValueTurtle(groupSet, fieldList) {
|
|
112
112
|
const fields = fieldList
|
|
113
|
-
.map(f => `${f.sqlExpression} as ${f.
|
|
113
|
+
.map(f => `${f.sqlExpression} as ${this.sqlQuoteIdentifier(f.rawName)}`)
|
|
114
114
|
.join(', ');
|
|
115
115
|
return `ANY_VALUE(CASE WHEN group_set=${groupSet} THEN STRUCT(${fields}))`;
|
|
116
116
|
}
|
|
@@ -393,7 +393,7 @@ class PostgresDialect extends pg_impl_1.PostgresBase {
|
|
|
393
393
|
if (malloy_types_1.TD.isAnyTimestamp(from.e.typeDef)) {
|
|
394
394
|
const tz = (0, dialect_1.qtz)(qi);
|
|
395
395
|
if (tz) {
|
|
396
|
-
extractFrom = `(${extractFrom}::TIMESTAMPTZ AT TIME ZONE
|
|
396
|
+
extractFrom = `(${extractFrom}::TIMESTAMPTZ AT TIME ZONE ${this.sqlTimezoneLiteral(tz)})`;
|
|
397
397
|
}
|
|
398
398
|
}
|
|
399
399
|
// PostgreSQL before 14 returns a double precision for EXTRACT, cast to integer
|
|
@@ -105,7 +105,7 @@ class SnowflakeDialect extends dialect_1.Dialect {
|
|
|
105
105
|
}
|
|
106
106
|
mapFields(fieldList) {
|
|
107
107
|
return fieldList
|
|
108
|
-
.map(f => `\n ${f.sqlExpression} as ${f.
|
|
108
|
+
.map(f => `\n ${f.sqlExpression} as ${this.sqlQuoteIdentifier(f.rawName)}`)
|
|
109
109
|
.join(', ');
|
|
110
110
|
}
|
|
111
111
|
mapFieldsForObjectConstruct(fieldList) {
|
|
@@ -138,7 +138,7 @@ class SnowflakeDialect extends dialect_1.Dialect {
|
|
|
138
138
|
sqlCoaleseMeasuresInline(groupSet, fieldList) {
|
|
139
139
|
const fields = this.mapFieldsForObjectConstruct(fieldList);
|
|
140
140
|
const nullValues = fieldList
|
|
141
|
-
.map(f =>
|
|
141
|
+
.map(f => `${this.sqlLiteralString(f.rawName)}, NULL`)
|
|
142
142
|
.join(', ');
|
|
143
143
|
return `COALESCE(ARRAY_AGG(CASE WHEN group_set=${groupSet} THEN OBJECT_CONSTRUCT_KEEP_NULL(${fields}) END)[0], OBJECT_CONSTRUCT_KEEP_NULL(${nullValues}))`;
|
|
144
144
|
}
|
|
@@ -254,21 +254,21 @@ ${(0, utils_1.indent)(sql)}
|
|
|
254
254
|
// Returns TIMESTAMP_TZ with timezone preserved
|
|
255
255
|
if (typeDef.type === 'timestamptz') {
|
|
256
256
|
return {
|
|
257
|
-
sql: `CONVERT_TIMEZONE(
|
|
257
|
+
sql: `CONVERT_TIMEZONE(${this.sqlTimezoneLiteral(timezone)}, ${expr})`,
|
|
258
258
|
typeDef: { type: 'timestamptz' },
|
|
259
259
|
};
|
|
260
260
|
}
|
|
261
261
|
// For plain timestamps (TIMESTAMP_NTZ): use 3-arg form
|
|
262
262
|
// Must cast to TIMESTAMP_NTZ first, returns TIMESTAMP_NTZ
|
|
263
263
|
return {
|
|
264
|
-
sql: `CONVERT_TIMEZONE('UTC',
|
|
264
|
+
sql: `CONVERT_TIMEZONE('UTC', ${this.sqlTimezoneLiteral(timezone)}, (${expr})::TIMESTAMP_NTZ)`,
|
|
265
265
|
typeDef: { type: 'timestamp' },
|
|
266
266
|
};
|
|
267
267
|
}
|
|
268
268
|
sqlConvertFromCivilTime(expr, timezone, _destTypeDef) {
|
|
269
269
|
// After civil time operations, we have a TIMESTAMP_NTZ in the target timezone
|
|
270
270
|
// Convert from timezone to UTC, returning TIMESTAMP_NTZ
|
|
271
|
-
return `CONVERT_TIMEZONE(
|
|
271
|
+
return `CONVERT_TIMEZONE(${this.sqlTimezoneLiteral(timezone)}, 'UTC', (${expr})::TIMESTAMP_NTZ)`;
|
|
272
272
|
}
|
|
273
273
|
sqlTruncate(expr, unit, _typeDef, _inCivilTime, _timezone) {
|
|
274
274
|
// Snowflake session is configured with WEEK_START=7 (Sunday)
|
|
@@ -287,7 +287,7 @@ ${(0, utils_1.indent)(sql)}
|
|
|
287
287
|
let extractFrom = from.e.sql;
|
|
288
288
|
const tz = (0, dialect_1.qtz)(qi);
|
|
289
289
|
if (tz && malloy_types_1.TD.isAnyTimestamp(from.e.typeDef)) {
|
|
290
|
-
extractFrom = `CONVERT_TIMEZONE(
|
|
290
|
+
extractFrom = `CONVERT_TIMEZONE(${this.sqlTimezoneLiteral(tz)}, ${extractFrom})`;
|
|
291
291
|
}
|
|
292
292
|
return `EXTRACT(${extractUnits} FROM ${extractFrom})`;
|
|
293
293
|
}
|
|
@@ -295,7 +295,7 @@ ${(0, utils_1.indent)(sql)}
|
|
|
295
295
|
if (tz !== undefined) {
|
|
296
296
|
return `(
|
|
297
297
|
TO_CHAR(${sqlExpr}::TIMESTAMP_NTZ, 'YYYY-MM-DD HH24:MI:SS.FF9') ||
|
|
298
|
-
TO_CHAR(CONVERT_TIMEZONE(
|
|
298
|
+
TO_CHAR(CONVERT_TIMEZONE(${this.sqlTimezoneLiteral(tz)}, '1970-01-01 00:00:00'), 'TZHTZM')
|
|
299
299
|
)::TIMESTAMP_TZ`;
|
|
300
300
|
}
|
|
301
301
|
return `${sqlExpr}::TIMESTAMP_NTZ`;
|
|
@@ -321,11 +321,11 @@ ${(0, utils_1.indent)(sql)}
|
|
|
321
321
|
if (tz && srcTypeDef && dstTypeDef) {
|
|
322
322
|
// TIMESTAMP → DATE: convert to query timezone, then to date
|
|
323
323
|
if (malloy_types_1.TD.isTimestamp(srcTypeDef) && malloy_types_1.TD.isDate(dstTypeDef)) {
|
|
324
|
-
return `TO_DATE(CONVERT_TIMEZONE(
|
|
324
|
+
return `TO_DATE(CONVERT_TIMEZONE(${this.sqlTimezoneLiteral(tz)}, ${src}))`;
|
|
325
325
|
}
|
|
326
326
|
// TIMESTAMPTZ → DATE: convert to query timezone, then to date
|
|
327
327
|
if (malloy_types_1.TD.isTimestamptz(srcTypeDef) && malloy_types_1.TD.isDate(dstTypeDef)) {
|
|
328
|
-
return `TO_DATE(CONVERT_TIMEZONE(
|
|
328
|
+
return `TO_DATE(CONVERT_TIMEZONE(${this.sqlTimezoneLiteral(tz)}, ${src}))`;
|
|
329
329
|
}
|
|
330
330
|
// DATE → TIMESTAMP: interpret date in query timezone, return UTC timestamp
|
|
331
331
|
if (malloy_types_1.TD.isDate(srcTypeDef) && malloy_types_1.TD.isTimestamp(dstTypeDef)) {
|
|
@@ -339,7 +339,7 @@ ${(0, utils_1.indent)(sql)}
|
|
|
339
339
|
}
|
|
340
340
|
// TIMESTAMPTZ → TIMESTAMP: convert to query timezone, get UTC wall clock
|
|
341
341
|
if (malloy_types_1.TD.isTimestamptz(srcTypeDef) && malloy_types_1.TD.isTimestamp(dstTypeDef)) {
|
|
342
|
-
return `CONVERT_TIMEZONE(
|
|
342
|
+
return `CONVERT_TIMEZONE(${this.sqlTimezoneLiteral(tz)}, ${src})::TIMESTAMP_NTZ`;
|
|
343
343
|
}
|
|
344
344
|
// TIMESTAMP → TIMESTAMPTZ: interpret as UTC, convert to TIMESTAMPTZ
|
|
345
345
|
if (malloy_types_1.TD.isTimestamp(srcTypeDef) && malloy_types_1.TD.isTimestamptz(dstTypeDef)) {
|
|
@@ -357,7 +357,7 @@ ${(0, utils_1.indent)(sql)}
|
|
|
357
357
|
let ret = `'${literal}'::TIMESTAMP_NTZ`;
|
|
358
358
|
if (tz) {
|
|
359
359
|
// Interpret the literal as being in query timezone, convert to UTC
|
|
360
|
-
ret = `CONVERT_TIMEZONE(
|
|
360
|
+
ret = `CONVERT_TIMEZONE(${this.sqlTimezoneLiteral(tz)}, 'UTC', ${ret})`;
|
|
361
361
|
}
|
|
362
362
|
return ret;
|
|
363
363
|
}
|
|
@@ -374,7 +374,7 @@ ${(0, utils_1.indent)(sql)}
|
|
|
374
374
|
const minute = dt.minute;
|
|
375
375
|
const second = dt.second;
|
|
376
376
|
const nanosecond = dt.millisecond * 1000000;
|
|
377
|
-
return `TIMESTAMP_TZ_FROM_PARTS(${year}, ${month}, ${day}, ${hour}, ${minute}, ${second}, ${nanosecond},
|
|
377
|
+
return `TIMESTAMP_TZ_FROM_PARTS(${year}, ${month}, ${day}, ${hour}, ${minute}, ${second}, ${nanosecond}, ${this.sqlTimezoneLiteral(timezone)})`;
|
|
378
378
|
}
|
|
379
379
|
sqlMeasureTimeExpr(df) {
|
|
380
380
|
const from = df.kids.left;
|
|
@@ -55,7 +55,7 @@ export declare class StandardSQLDialect extends Dialect {
|
|
|
55
55
|
sqlCreateFunction(id: string, funcText: string): string;
|
|
56
56
|
sqlCreateTableAsSelect(tableName: string, sql: string): string;
|
|
57
57
|
sqlCreateFunctionCombineLastStage(lastStageName: string): string;
|
|
58
|
-
sqlSelectAliasAsStruct(alias: string): string;
|
|
58
|
+
sqlSelectAliasAsStruct(alias: string, dialectFieldList: DialectFieldList): string;
|
|
59
59
|
sqlNowExpr(): string;
|
|
60
60
|
sqlTimeExtractExpr(qi: QueryInfo, te: TimeExtractExpr): string;
|
|
61
61
|
sqlConvertToCivilTime(expr: string, timezone: string, _typeDef: AtomicTypeDef): {
|
|
@@ -150,7 +150,7 @@ class StandardSQLDialect extends dialect_1.Dialect {
|
|
|
150
150
|
// can array agg or any_value a struct...
|
|
151
151
|
sqlAggregateTurtle(groupSet, fieldList, orderBy, limit, filterSQL) {
|
|
152
152
|
const fields = fieldList
|
|
153
|
-
.map(f => `\n ${f.sqlExpression} as ${f.
|
|
153
|
+
.map(f => `\n ${f.sqlExpression} as ${this.sqlQuoteIdentifier(f.rawName)}`)
|
|
154
154
|
.join(', ');
|
|
155
155
|
const orderByClause = orderBy ? this.sqlTurtleOrderByClause(orderBy) : '';
|
|
156
156
|
// BigQuery ARRAY_AGG takes LIMIT as its final sub-clause, after ORDER BY.
|
|
@@ -162,7 +162,7 @@ class StandardSQLDialect extends dialect_1.Dialect {
|
|
|
162
162
|
}
|
|
163
163
|
sqlAnyValueTurtle(groupSet, fieldList) {
|
|
164
164
|
const fields = fieldList
|
|
165
|
-
.map(f => `${f.sqlExpression} as ${f.
|
|
165
|
+
.map(f => `${f.sqlExpression} as ${this.sqlQuoteIdentifier(f.rawName)}`)
|
|
166
166
|
.join(', ');
|
|
167
167
|
return `ANY_VALUE(CASE WHEN group_set=${groupSet} THEN STRUCT(${fields}) END)`;
|
|
168
168
|
}
|
|
@@ -171,10 +171,10 @@ class StandardSQLDialect extends dialect_1.Dialect {
|
|
|
171
171
|
}
|
|
172
172
|
sqlCoaleseMeasuresInline(groupSet, fieldList) {
|
|
173
173
|
const fields = fieldList
|
|
174
|
-
.map(f => `${f.sqlExpression} as ${f.
|
|
174
|
+
.map(f => `${f.sqlExpression} as ${this.sqlQuoteIdentifier(f.rawName)}`)
|
|
175
175
|
.join(', ');
|
|
176
176
|
const nullValues = fieldList
|
|
177
|
-
.map(f => `NULL as ${f.
|
|
177
|
+
.map(f => `NULL as ${this.sqlQuoteIdentifier(f.rawName)}`)
|
|
178
178
|
.join(', ');
|
|
179
179
|
return `COALESCE(ANY_VALUE(CASE WHEN group_set=${groupSet} THEN STRUCT(${fields}) END), STRUCT(${nullValues}))`;
|
|
180
180
|
}
|
|
@@ -240,8 +240,11 @@ ${(0, utils_1.indent)(sql)}
|
|
|
240
240
|
sqlCreateFunctionCombineLastStage(lastStageName) {
|
|
241
241
|
return `SELECT ARRAY((SELECT AS STRUCT * FROM ${lastStageName}))\n`;
|
|
242
242
|
}
|
|
243
|
-
sqlSelectAliasAsStruct(alias) {
|
|
244
|
-
|
|
243
|
+
sqlSelectAliasAsStruct(alias, dialectFieldList) {
|
|
244
|
+
const fields = dialectFieldList
|
|
245
|
+
.map(d => `${alias}.${this.sqlQuoteIdentifier(d.rawName)}`)
|
|
246
|
+
.join(', ');
|
|
247
|
+
return `(SELECT AS STRUCT ${fields})`;
|
|
245
248
|
}
|
|
246
249
|
sqlNowExpr() {
|
|
247
250
|
return 'CURRENT_TIMESTAMP()';
|
|
@@ -249,18 +252,18 @@ ${(0, utils_1.indent)(sql)}
|
|
|
249
252
|
sqlTimeExtractExpr(qi, te) {
|
|
250
253
|
const extractTo = extractMap[te.units] || te.units;
|
|
251
254
|
const tz = malloy_types_1.TD.isAnyTimestamp(te.e.typeDef) && qtz(qi);
|
|
252
|
-
const tzAdd = tz ? ` AT TIME ZONE
|
|
255
|
+
const tzAdd = tz ? ` AT TIME ZONE ${this.sqlTimezoneLiteral(tz)}` : '';
|
|
253
256
|
return `EXTRACT(${extractTo} FROM ${te.e.sql}${tzAdd})`;
|
|
254
257
|
}
|
|
255
258
|
sqlConvertToCivilTime(expr, timezone, _typeDef) {
|
|
256
259
|
// BigQuery has no timestamptz type, so typeDef.timestamptz will never be true
|
|
257
260
|
return {
|
|
258
|
-
sql: `DATETIME(${expr},
|
|
261
|
+
sql: `DATETIME(${expr}, ${this.sqlTimezoneLiteral(timezone)})`,
|
|
259
262
|
typeDef: { type: 'timestamp' },
|
|
260
263
|
};
|
|
261
264
|
}
|
|
262
265
|
sqlConvertFromCivilTime(expr, timezone, _destTypeDef) {
|
|
263
|
-
return `TIMESTAMP(${expr},
|
|
266
|
+
return `TIMESTAMP(${expr}, ${this.sqlTimezoneLiteral(timezone)})`;
|
|
264
267
|
}
|
|
265
268
|
sqlTruncate(expr, unit, typeDef, inCivilTime, timezone) {
|
|
266
269
|
if (inCivilTime) {
|
|
@@ -272,7 +275,7 @@ ${(0, utils_1.indent)(sql)}
|
|
|
272
275
|
return `DATE_TRUNC(${expr}, ${unit})`;
|
|
273
276
|
}
|
|
274
277
|
// TIMESTAMP truncation with optional timezone
|
|
275
|
-
const tzParam = timezone ? `,
|
|
278
|
+
const tzParam = timezone ? `, ${this.sqlTimezoneLiteral(timezone)}` : '';
|
|
276
279
|
return `TIMESTAMP_TRUNC(${expr}, ${unit}${tzParam})`;
|
|
277
280
|
}
|
|
278
281
|
sqlOffsetTime(expr, op, magnitude, unit, typeDef, inCivilTime, _timezone) {
|
|
@@ -299,10 +302,10 @@ ${(0, utils_1.indent)(sql)}
|
|
|
299
302
|
const tz = qtz(qi);
|
|
300
303
|
const src = cast.e.sql || '';
|
|
301
304
|
if (op === 'timestamp::date' && tz) {
|
|
302
|
-
return `DATE(${src}
|
|
305
|
+
return `DATE(${src},${this.sqlTimezoneLiteral(tz)})`;
|
|
303
306
|
}
|
|
304
307
|
if (op === 'date::timestamp' && tz) {
|
|
305
|
-
return `TIMESTAMP(${src},
|
|
308
|
+
return `TIMESTAMP(${src}, ${this.sqlTimezoneLiteral(tz)})`;
|
|
306
309
|
}
|
|
307
310
|
if (!malloy_types_1.TD.eq(srcTypeDef, dstTypeDef)) {
|
|
308
311
|
const castFunc = cast.safe ? 'SAFE_CAST' : 'CAST';
|
|
@@ -320,7 +323,7 @@ ${(0, utils_1.indent)(sql)}
|
|
|
320
323
|
let timestampArgs = `'${literal}'`;
|
|
321
324
|
const tz = timezone || qtz(qi);
|
|
322
325
|
if (tz && tz !== 'UTC') {
|
|
323
|
-
timestampArgs +=
|
|
326
|
+
timestampArgs += `,${this.sqlTimezoneLiteral(tz)}`;
|
|
324
327
|
}
|
|
325
328
|
return `TIMESTAMP(${timestampArgs})`;
|
|
326
329
|
}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { Expr, Sampling, AtomicTypeDef, ATimestampTypeDef, TypecastExpr, RegexMatchExpr, MeasureTimeExpr, TimeExtractExpr, BasicAtomicTypeDef, RecordLiteralNode } from '../../model/malloy_types';
|
|
1
|
+
import type { Expr, Sampling, AtomicTypeDef, ATimestampTypeDef, TypecastExpr, RegexMatchExpr, MeasureTimeExpr, TimeExtractExpr, BasicAtomicTypeDef, RecordLiteralNode, OrderBy } from '../../model/malloy_types';
|
|
2
2
|
import type { DialectFunctionOverloadDef } from '../functions';
|
|
3
3
|
import type { CompiledOrderBy, DialectFieldList, OrderByClauseType, QueryInfo } from '../dialect';
|
|
4
4
|
import { PostgresBase } from '../pg_impl';
|
|
@@ -49,8 +49,8 @@ export declare class TrinoDialect extends PostgresBase {
|
|
|
49
49
|
sqlUnnestPipelineHead(isSingleton: boolean, sourceSQLExpression: string): string;
|
|
50
50
|
sqlCreateFunction(id: string, funcText: string): string;
|
|
51
51
|
sqlCreateTableAsSelect(tableName: string, sql: string): string;
|
|
52
|
-
sqlCreateFunctionCombineLastStage(lastStageName: string, fieldList: DialectFieldList): string;
|
|
53
|
-
sqlSelectAliasAsStruct(alias: string, fieldList:
|
|
52
|
+
sqlCreateFunctionCombineLastStage(lastStageName: string, fieldList: DialectFieldList, orderBy: OrderBy[] | undefined): string;
|
|
53
|
+
sqlSelectAliasAsStruct(alias: string, fieldList: DialectFieldList): string;
|
|
54
54
|
keywords: string[];
|
|
55
55
|
sqlConvertToCivilTime(expr: string, timezone: string, typeDef: AtomicTypeDef): {
|
|
56
56
|
sql: string;
|
|
@@ -82,7 +82,7 @@ class TrinoDialect extends pg_impl_1.PostgresBase {
|
|
|
82
82
|
this.orderByClause = 'output_name';
|
|
83
83
|
this.nullMatchesFunctionSignature = false;
|
|
84
84
|
this.supportsSelectReplace = false;
|
|
85
|
-
this.supportsComplexFilteredSources =
|
|
85
|
+
this.supportsComplexFilteredSources = true;
|
|
86
86
|
this.supportsTempTables = false;
|
|
87
87
|
this.supportsCountApprox = true;
|
|
88
88
|
this.supportsHyperLogLog = true;
|
|
@@ -223,7 +223,8 @@ class TrinoDialect extends pg_impl_1.PostgresBase {
|
|
|
223
223
|
}
|
|
224
224
|
buildTypeExpression(fieldList) {
|
|
225
225
|
return fieldList
|
|
226
|
-
.map(dlf => `${
|
|
226
|
+
.map(dlf => `${this.sqlQuoteIdentifier(dlf.rawName)} ` +
|
|
227
|
+
this.malloyTypeToSQLType(dlf.typeDef))
|
|
227
228
|
.join(', \n');
|
|
228
229
|
}
|
|
229
230
|
// can array agg or any_value a struct...
|
|
@@ -325,15 +326,18 @@ ${(0, utils_1.indent)(sql)}
|
|
|
325
326
|
);
|
|
326
327
|
`;
|
|
327
328
|
}
|
|
328
|
-
sqlCreateFunctionCombineLastStage(lastStageName, fieldList) {
|
|
329
|
+
sqlCreateFunctionCombineLastStage(lastStageName, fieldList, orderBy) {
|
|
329
330
|
const fields = fieldList.map(f => f.sqlExpression).join(', ');
|
|
330
331
|
const definitions = this.buildTypeExpression(fieldList);
|
|
331
|
-
|
|
332
|
+
const o = this.sqlCombineLastStageOrderBy(orderBy, fieldList);
|
|
333
|
+
return `SELECT ARRAY_AGG(CAST(ROW(${fields}) as ROW(${definitions}))${o}) FROM ${lastStageName}\n`;
|
|
332
334
|
}
|
|
333
335
|
sqlSelectAliasAsStruct(alias, fieldList) {
|
|
334
|
-
const fields = fieldList
|
|
336
|
+
const fields = fieldList
|
|
337
|
+
.map(f => `${alias}.${this.sqlQuoteIdentifier(f.rawName)}`)
|
|
338
|
+
.join(', ');
|
|
335
339
|
const definitions = this.buildTypeExpression(fieldList);
|
|
336
|
-
return `CAST(ROW(${fields}) as ROW(${definitions})`;
|
|
340
|
+
return `CAST(ROW(${fields}) as ROW(${definitions}))`;
|
|
337
341
|
}
|
|
338
342
|
sqlConvertToCivilTime(expr, timezone, typeDef) {
|
|
339
343
|
// Trino civil time = TIMESTAMPTZ with query timezone as stored timezone
|
|
@@ -341,13 +345,13 @@ ${(0, utils_1.indent)(sql)}
|
|
|
341
345
|
if (typeDef.type === 'timestamp') {
|
|
342
346
|
// TIMESTAMP (UTC wall clock) → TIMESTAMPTZ in query timezone
|
|
343
347
|
return {
|
|
344
|
-
sql: `at_timezone(with_timezone(${expr}, 'UTC'),
|
|
348
|
+
sql: `at_timezone(with_timezone(${expr}, 'UTC'), ${this.sqlTimezoneLiteral(timezone)})`,
|
|
345
349
|
typeDef: { type: 'timestamptz' },
|
|
346
350
|
};
|
|
347
351
|
}
|
|
348
352
|
// TIMESTAMPTZ → TIMESTAMPTZ in query timezone (same instant, different stored tz)
|
|
349
353
|
return {
|
|
350
|
-
sql: `at_timezone(${expr},
|
|
354
|
+
sql: `at_timezone(${expr}, ${this.sqlTimezoneLiteral(timezone)})`,
|
|
351
355
|
typeDef: { type: 'timestamptz' },
|
|
352
356
|
};
|
|
353
357
|
}
|
|
@@ -392,27 +396,27 @@ ${(0, utils_1.indent)(sql)}
|
|
|
392
396
|
if (tz && srcTypeDef && dstTypeDef) {
|
|
393
397
|
// TIMESTAMP → DATE: interpret as UTC, convert to query timezone
|
|
394
398
|
if (malloy_types_1.TD.isTimestamp(srcTypeDef) && malloy_types_1.TD.isDate(dstTypeDef)) {
|
|
395
|
-
return `CAST(at_timezone(with_timezone(${expr}, 'UTC'),
|
|
399
|
+
return `CAST(at_timezone(with_timezone(${expr}, 'UTC'), ${this.sqlTimezoneLiteral(tz)}) AS DATE)`;
|
|
396
400
|
}
|
|
397
401
|
// TIMESTAMPTZ → DATE: convert to query timezone
|
|
398
402
|
if (malloy_types_1.TD.isTimestamptz(srcTypeDef) && malloy_types_1.TD.isDate(dstTypeDef)) {
|
|
399
|
-
return `CAST(at_timezone(${expr},
|
|
403
|
+
return `CAST(at_timezone(${expr}, ${this.sqlTimezoneLiteral(tz)}) AS DATE)`;
|
|
400
404
|
}
|
|
401
405
|
// DATE → TIMESTAMP: interpret date in query timezone, return UTC wall clock
|
|
402
406
|
if (malloy_types_1.TD.isDate(srcTypeDef) && malloy_types_1.TD.isTimestamp(dstTypeDef)) {
|
|
403
|
-
return `CAST(at_timezone(with_timezone(CAST(${expr} AS TIMESTAMP),
|
|
407
|
+
return `CAST(at_timezone(with_timezone(CAST(${expr} AS TIMESTAMP), ${this.sqlTimezoneLiteral(tz)}), 'UTC') AS TIMESTAMP)`;
|
|
404
408
|
}
|
|
405
409
|
// DATE → TIMESTAMPTZ: interpret date in query timezone
|
|
406
410
|
if (malloy_types_1.TD.isDate(srcTypeDef) && malloy_types_1.TD.isTimestamptz(dstTypeDef)) {
|
|
407
|
-
return `with_timezone(CAST(${expr} AS TIMESTAMP),
|
|
411
|
+
return `with_timezone(CAST(${expr} AS TIMESTAMP), ${this.sqlTimezoneLiteral(tz)})`;
|
|
408
412
|
}
|
|
409
413
|
// TIMESTAMPTZ → TIMESTAMP: convert to query timezone wall clock
|
|
410
414
|
if (malloy_types_1.TD.isTimestamptz(srcTypeDef) && malloy_types_1.TD.isTimestamp(dstTypeDef)) {
|
|
411
|
-
return `CAST(at_timezone(${expr},
|
|
415
|
+
return `CAST(at_timezone(${expr}, ${this.sqlTimezoneLiteral(tz)}) AS TIMESTAMP)`;
|
|
412
416
|
}
|
|
413
417
|
// TIMESTAMP → TIMESTAMPTZ: interpret TIMESTAMP as being in query timezone
|
|
414
418
|
if (malloy_types_1.TD.isTimestamp(srcTypeDef) && malloy_types_1.TD.isTimestamptz(dstTypeDef)) {
|
|
415
|
-
return `with_timezone(${expr},
|
|
419
|
+
return `with_timezone(${expr}, ${this.sqlTimezoneLiteral(tz)})`;
|
|
416
420
|
}
|
|
417
421
|
}
|
|
418
422
|
// No special handling needed, or no query timezone
|
|
@@ -567,13 +571,13 @@ ${(0, utils_1.indent)(sql)}
|
|
|
567
571
|
const tz = timezone || qtz(qi);
|
|
568
572
|
if (tz) {
|
|
569
573
|
// Interpret wall clock time in timezone, convert to UTC wall clock, cast to TIMESTAMP
|
|
570
|
-
return `CAST(at_timezone(with_timezone(TIMESTAMP '${literal}',
|
|
574
|
+
return `CAST(at_timezone(with_timezone(TIMESTAMP '${literal}', ${this.sqlTimezoneLiteral(tz)}), 'UTC') AS TIMESTAMP)`;
|
|
571
575
|
}
|
|
572
576
|
return `TIMESTAMP '${literal}'`;
|
|
573
577
|
}
|
|
574
578
|
sqlTimestamptzLiteral(_qi, literal, timezone) {
|
|
575
579
|
// Use with_timezone to create a TIMESTAMP WITH TIME ZONE
|
|
576
|
-
return `with_timezone(TIMESTAMP '${literal}',
|
|
580
|
+
return `with_timezone(TIMESTAMP '${literal}', ${this.sqlTimezoneLiteral(timezone)})`;
|
|
577
581
|
}
|
|
578
582
|
sqlTimeExtractExpr(qi, from) {
|
|
579
583
|
const pgUnits = pg_impl_1.timeExtractMap[from.units] || from.units;
|
|
@@ -584,11 +588,11 @@ ${(0, utils_1.indent)(sql)}
|
|
|
584
588
|
// Convert both TIMESTAMP and TIMESTAMPTZ to query timezone for extraction
|
|
585
589
|
if (from.e.typeDef.type === 'timestamp') {
|
|
586
590
|
// TIMESTAMP: interpret as UTC, convert to query timezone
|
|
587
|
-
extractFrom = `at_timezone(with_timezone(${extractFrom}, 'UTC'),
|
|
591
|
+
extractFrom = `at_timezone(with_timezone(${extractFrom}, 'UTC'), ${this.sqlTimezoneLiteral(tz)})`;
|
|
588
592
|
}
|
|
589
593
|
else {
|
|
590
594
|
// TIMESTAMPTZ: convert to query timezone
|
|
591
|
-
extractFrom = `at_timezone(${extractFrom},
|
|
595
|
+
extractFrom = `at_timezone(${extractFrom}, ${this.sqlTimezoneLiteral(tz)})`;
|
|
592
596
|
}
|
|
593
597
|
}
|
|
594
598
|
}
|
|
@@ -628,18 +632,18 @@ class PrestoDialect extends TrinoDialect {
|
|
|
628
632
|
sqlTimestampLiteral(qi, literal, timezone) {
|
|
629
633
|
const tz = timezone || qtz(qi);
|
|
630
634
|
if (tz) {
|
|
631
|
-
return `CAST(TIMESTAMP '${literal} ${tz}' AT TIME ZONE 'UTC' AS TIMESTAMP)`;
|
|
635
|
+
return `CAST(TIMESTAMP '${literal} ${this.checkedTimezoneName(tz)}' AT TIME ZONE 'UTC' AS TIMESTAMP)`;
|
|
632
636
|
}
|
|
633
637
|
return `TIMESTAMP '${literal}'`;
|
|
634
638
|
}
|
|
635
639
|
sqlTimestamptzLiteral(_qi, literal, timezone) {
|
|
636
|
-
return `TIMESTAMP '${literal} ${timezone}'`;
|
|
640
|
+
return `TIMESTAMP '${literal} ${this.checkedTimezoneName(timezone)}'`;
|
|
637
641
|
}
|
|
638
642
|
sqlConvertToCivilTime(expr, timezone, _typeDef) {
|
|
639
643
|
// Presto's AT TIME ZONE operator (not function) produces TIMESTAMPTZ
|
|
640
644
|
// Reinterprets the instant in the target timezone
|
|
641
645
|
return {
|
|
642
|
-
sql: `${expr} AT TIME ZONE
|
|
646
|
+
sql: `${expr} AT TIME ZONE ${this.sqlTimezoneLiteral(timezone)}`,
|
|
643
647
|
typeDef: { type: 'timestamptz' },
|
|
644
648
|
};
|
|
645
649
|
}
|
|
@@ -658,26 +662,26 @@ class PrestoDialect extends TrinoDialect {
|
|
|
658
662
|
if (tz && srcTypeDef && dstTypeDef) {
|
|
659
663
|
// TIMESTAMP → DATE: interpret as UTC, convert to query timezone
|
|
660
664
|
if (malloy_types_1.TD.isTimestamp(srcTypeDef) && malloy_types_1.TD.isDate(dstTypeDef)) {
|
|
661
|
-
return `CAST((${expr} AT TIME ZONE 'UTC') AT TIME ZONE
|
|
665
|
+
return `CAST((${expr} AT TIME ZONE 'UTC') AT TIME ZONE ${this.sqlTimezoneLiteral(tz)} AS DATE)`;
|
|
662
666
|
}
|
|
663
667
|
// TIMESTAMPTZ → DATE: convert to query timezone
|
|
664
668
|
if (malloy_types_1.TD.isTimestamptz(srcTypeDef) && malloy_types_1.TD.isDate(dstTypeDef)) {
|
|
665
|
-
return `CAST(${expr} AT TIME ZONE
|
|
669
|
+
return `CAST(${expr} AT TIME ZONE ${this.sqlTimezoneLiteral(tz)} AS DATE)`;
|
|
666
670
|
}
|
|
667
671
|
// DATE → TIMESTAMP: interpret date in query timezone, return UTC wall clock
|
|
668
672
|
// Presto doesn't have a way to interpret TIMESTAMP in a non-UTC timezone,
|
|
669
673
|
// so we build a TIMESTAMPTZ literal string and cast it
|
|
670
674
|
if (malloy_types_1.TD.isDate(srcTypeDef) && malloy_types_1.TD.isTimestamp(dstTypeDef)) {
|
|
671
|
-
const tstzLiteral = `CAST(CAST(${expr} AS VARCHAR) || ' 00:00:00 ${tz}' AS TIMESTAMP WITH TIME ZONE)`;
|
|
675
|
+
const tstzLiteral = `CAST(CAST(${expr} AS VARCHAR) || ' 00:00:00 ${this.checkedTimezoneName(tz)}' AS TIMESTAMP WITH TIME ZONE)`;
|
|
672
676
|
return `CAST(${tstzLiteral} AS TIMESTAMP)`;
|
|
673
677
|
}
|
|
674
678
|
// DATE → TIMESTAMPTZ: interpret date in query timezone
|
|
675
679
|
if (malloy_types_1.TD.isDate(srcTypeDef) && malloy_types_1.TD.isTimestamptz(dstTypeDef)) {
|
|
676
|
-
return `CAST(CAST(${expr} AS VARCHAR) || ' 00:00:00 ${tz}' AS TIMESTAMP WITH TIME ZONE)`;
|
|
680
|
+
return `CAST(CAST(${expr} AS VARCHAR) || ' 00:00:00 ${this.checkedTimezoneName(tz)}' AS TIMESTAMP WITH TIME ZONE)`;
|
|
677
681
|
}
|
|
678
682
|
// TIMESTAMPTZ → TIMESTAMP: convert to query timezone wall clock
|
|
679
683
|
if (malloy_types_1.TD.isTimestamptz(srcTypeDef) && malloy_types_1.TD.isTimestamp(dstTypeDef)) {
|
|
680
|
-
return `CAST(${expr} AT TIME ZONE
|
|
684
|
+
return `CAST(${expr} AT TIME ZONE ${this.sqlTimezoneLiteral(tz)} AS TIMESTAMP)`;
|
|
681
685
|
}
|
|
682
686
|
// TIMESTAMP → TIMESTAMPTZ: interpret TIMESTAMP as UTC
|
|
683
687
|
if (malloy_types_1.TD.isTimestamp(srcTypeDef) && malloy_types_1.TD.isTimestamptz(dstTypeDef)) {
|
|
@@ -701,11 +705,11 @@ class PrestoDialect extends TrinoDialect {
|
|
|
701
705
|
if (from.e.typeDef.type === 'timestamp') {
|
|
702
706
|
// TIMESTAMP: interpret as UTC, convert to query timezone
|
|
703
707
|
// Presto uses AT TIME ZONE operator
|
|
704
|
-
extractFrom = `(${extractFrom} AT TIME ZONE 'UTC') AT TIME ZONE
|
|
708
|
+
extractFrom = `(${extractFrom} AT TIME ZONE 'UTC') AT TIME ZONE ${this.sqlTimezoneLiteral(tz)}`;
|
|
705
709
|
}
|
|
706
710
|
else {
|
|
707
711
|
// TIMESTAMPTZ: convert to query timezone
|
|
708
|
-
extractFrom = `${extractFrom} AT TIME ZONE
|
|
712
|
+
extractFrom = `${extractFrom} AT TIME ZONE ${this.sqlTimezoneLiteral(tz)}`;
|
|
709
713
|
}
|
|
710
714
|
}
|
|
711
715
|
}
|
|
@@ -45,6 +45,7 @@ const static_space_1 = require("../field-space/static-space");
|
|
|
45
45
|
const TDU = __importStar(require("../typedesc-utils"));
|
|
46
46
|
const field_references_1 = require("../query-items/field-references");
|
|
47
47
|
const expression_def_1 = require("../types/expression-def");
|
|
48
|
+
const field_space_1 = require("../types/field-space");
|
|
48
49
|
const space_field_1 = require("../types/space-field");
|
|
49
50
|
const expr_id_reference_1 = require("./expr-id-reference");
|
|
50
51
|
class ExprAggregateFunction extends expression_def_1.ExpressionDef {
|
|
@@ -224,37 +225,30 @@ function joinPathEq(a1, a2) {
|
|
|
224
225
|
}
|
|
225
226
|
function getJoinUsage(fs, expr) {
|
|
226
227
|
const result = [];
|
|
228
|
+
/**
|
|
229
|
+
* Walk a path from translated IR. Every name in it is known to resolve at
|
|
230
|
+
* private level, so a failure is an internal error. The names are
|
|
231
|
+
* unparented; readEntry records no references for them.
|
|
232
|
+
*/
|
|
227
233
|
const lookupWithPath = (fs, path) => {
|
|
228
|
-
const
|
|
229
|
-
const
|
|
230
|
-
const
|
|
231
|
-
if (
|
|
232
|
-
throw new Error(
|
|
233
|
-
}
|
|
234
|
-
if (def instanceof static_space_1.StructSpaceField && rest.length > 0) {
|
|
235
|
-
const restDef = lookupWithPath(def.fieldSpace, rest);
|
|
236
|
-
return {
|
|
237
|
-
...restDef,
|
|
238
|
-
joinPath: [{ ...def.joinPathElement, name: head }, ...restDef.joinPath],
|
|
239
|
-
};
|
|
234
|
+
const names = path.map(n => new field_space_1.FieldName(n));
|
|
235
|
+
const last = names[names.length - 1];
|
|
236
|
+
const ns = (0, static_space_1.resolveNamespace)(fs, names.slice(0, -1), 'private', last.name);
|
|
237
|
+
if (ns.error) {
|
|
238
|
+
throw new Error(ns.error.message);
|
|
240
239
|
}
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
}
|
|
245
|
-
const fieldDef = def.fieldDef();
|
|
246
|
-
if (fieldDef) {
|
|
247
|
-
return {
|
|
248
|
-
fs,
|
|
249
|
-
def: fieldDef,
|
|
250
|
-
joinPath: [],
|
|
251
|
-
};
|
|
252
|
-
}
|
|
253
|
-
throw new Error('No field def');
|
|
240
|
+
const read = (0, static_space_1.readEntry)(ns.space, last, 'private');
|
|
241
|
+
if (read.error) {
|
|
242
|
+
throw new Error(read.error.message);
|
|
254
243
|
}
|
|
255
|
-
|
|
244
|
+
if (!(read.found instanceof space_field_1.SpaceField)) {
|
|
256
245
|
throw new Error('expected a field def or struct');
|
|
257
246
|
}
|
|
247
|
+
const def = read.found.fieldDef();
|
|
248
|
+
if (def === undefined) {
|
|
249
|
+
throw new Error('No field def');
|
|
250
|
+
}
|
|
251
|
+
return { fs: ns.space, def, joinPath: ns.joinPath };
|
|
258
252
|
};
|
|
259
253
|
for (const frag of (0, utils_1.exprWalk)(expr)) {
|
|
260
254
|
if (frag.node === 'field') {
|