@malloydata/malloy 0.0.138-dev240328203849 → 0.0.138-dev240331175328
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.
|
@@ -1,11 +1,19 @@
|
|
|
1
1
|
import { DialectFragment, Expr, ExtractUnit, Sampling, StructDef, TimeFieldType, TimeValue, TimestampUnit, TypecastFragment, FieldAtomicTypeDef } from '../model/malloy_types';
|
|
2
2
|
import { DialectFunctionOverloadDef } from './functions';
|
|
3
|
+
type DialectFieldTypes = string | 'struct';
|
|
3
4
|
interface DialectField {
|
|
4
|
-
type:
|
|
5
|
+
type: DialectFieldTypes;
|
|
5
6
|
sqlExpression: string;
|
|
6
7
|
rawName: string;
|
|
7
8
|
sqlOutputName: string;
|
|
8
9
|
}
|
|
10
|
+
export interface DialectFieldTypeStruct extends DialectField {
|
|
11
|
+
type: 'struct';
|
|
12
|
+
nestedStruct: DialectFieldList;
|
|
13
|
+
isArray: boolean;
|
|
14
|
+
}
|
|
15
|
+
export declare function isDialectFieldStruct(d: DialectField): d is DialectFieldTypeStruct;
|
|
16
|
+
export type DialectFieldList = DialectField[];
|
|
9
17
|
/**
|
|
10
18
|
* Data which dialect methods need in order to correctly generate SQL.
|
|
11
19
|
* Initially this is just timezone related, but I made this an interface
|
|
@@ -20,7 +28,7 @@ export interface QueryInfo {
|
|
|
20
28
|
export declare const dayIndex: number;
|
|
21
29
|
export declare function inDays(units: string): boolean;
|
|
22
30
|
export declare function qtz(qi: QueryInfo): string | undefined;
|
|
23
|
-
export type
|
|
31
|
+
export type OrderByClauseType = 'output_name' | 'ordinal';
|
|
24
32
|
export declare abstract class Dialect {
|
|
25
33
|
abstract name: string;
|
|
26
34
|
abstract defaultNumberType: string;
|
|
@@ -42,6 +50,7 @@ export declare abstract class Dialect {
|
|
|
42
50
|
supportsPipelinesInViews: boolean;
|
|
43
51
|
supportsArraysInData: boolean;
|
|
44
52
|
readsNestedData: boolean;
|
|
53
|
+
orderByClause: OrderByClauseType;
|
|
45
54
|
abstract getGlobalFunctionDef(name: string): DialectFunctionOverloadDef[] | undefined;
|
|
46
55
|
abstract quoteTablePath(tablePath: string): string;
|
|
47
56
|
abstract sqlGroupSetTable(groupSetCount: number): string;
|
package/dist/dialect/dialect.js
CHANGED
|
@@ -22,8 +22,12 @@
|
|
|
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.qtz = exports.inDays = exports.dayIndex = void 0;
|
|
25
|
+
exports.Dialect = exports.qtz = exports.inDays = exports.dayIndex = exports.isDialectFieldStruct = void 0;
|
|
26
26
|
const malloy_types_1 = require("../model/malloy_types");
|
|
27
|
+
function isDialectFieldStruct(d) {
|
|
28
|
+
return d.type === 'struct';
|
|
29
|
+
}
|
|
30
|
+
exports.isDialectFieldStruct = isDialectFieldStruct;
|
|
27
31
|
const allUnits = [
|
|
28
32
|
'microsecond',
|
|
29
33
|
'millisecond',
|
|
@@ -64,6 +68,8 @@ class Dialect {
|
|
|
64
68
|
this.supportsArraysInData = true;
|
|
65
69
|
// can read some version of ga_sample
|
|
66
70
|
this.readsNestedData = true;
|
|
71
|
+
// ORDER BY 1 DESC
|
|
72
|
+
this.orderByClause = 'ordinal';
|
|
67
73
|
}
|
|
68
74
|
sqlFinalStage(_lastStageName, _fields) {
|
|
69
75
|
throw new Error('Dialect has no final Stage but called Anyway');
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import { Expr, ExtractUnit, Sampling, TimeValue, TimestampUnit, TypecastFragment, FieldAtomicTypeDef } from '../../model/malloy_types';
|
|
1
|
+
import { Expr, ExtractUnit, Sampling, TimeValue, TimestampUnit, TypecastFragment, FieldAtomicTypeDef, DialectFragment } from '../../model/malloy_types';
|
|
2
2
|
import { DialectFunctionOverloadDef } from '../functions';
|
|
3
|
-
import { Dialect, DialectFieldList, QueryInfo } from '../dialect';
|
|
3
|
+
import { Dialect, DialectFieldList, OrderByClauseType, QueryInfo } from '../dialect';
|
|
4
4
|
export declare class TrinoDialect extends Dialect {
|
|
5
5
|
name: string;
|
|
6
6
|
defaultNumberType: string;
|
|
@@ -21,9 +21,12 @@ export declare class TrinoDialect extends Dialect {
|
|
|
21
21
|
supportsSafeCast: boolean;
|
|
22
22
|
supportsNesting: boolean;
|
|
23
23
|
cantPartitionWindowFunctionsOnExpressions: boolean;
|
|
24
|
+
orderByClause: OrderByClauseType;
|
|
24
25
|
quoteTablePath(tablePath: string): string;
|
|
25
26
|
sqlGroupSetTable(groupSetCount: number): string;
|
|
27
|
+
dialectExpr(qi: QueryInfo, df: DialectFragment): Expr;
|
|
26
28
|
sqlAnyValue(groupSet: number, fieldName: string): string;
|
|
29
|
+
buildTypeExpression(fieldList: DialectFieldList): string;
|
|
27
30
|
sqlAggregateTurtle(groupSet: number, fieldList: DialectFieldList, orderBy: string | undefined, limit: number | undefined): string;
|
|
28
31
|
sqlAnyValueTurtle(groupSet: number, fieldList: DialectFieldList): string;
|
|
29
32
|
sqlAnyValueLastTurtle(name: string, groupSet: number, sqlName: string): string;
|
|
@@ -17,7 +17,7 @@
|
|
|
17
17
|
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
|
18
18
|
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
|
19
19
|
* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
|
|
20
|
-
* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
|
|
20
|
+
* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,p
|
|
21
21
|
* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
|
|
22
22
|
* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
|
23
23
|
*/
|
|
@@ -54,15 +54,19 @@ function qtz(qi) {
|
|
|
54
54
|
return tz;
|
|
55
55
|
}
|
|
56
56
|
}
|
|
57
|
+
const trinoTypeMap = {
|
|
58
|
+
'string': 'VARCHAR',
|
|
59
|
+
'number': 'DOUBLE',
|
|
60
|
+
};
|
|
57
61
|
class TrinoDialect extends dialect_1.Dialect {
|
|
58
62
|
constructor() {
|
|
59
63
|
super(...arguments);
|
|
60
64
|
this.name = 'trino';
|
|
61
|
-
this.defaultNumberType = '
|
|
62
|
-
this.defaultDecimalType = '
|
|
65
|
+
this.defaultNumberType = 'DOUBLE';
|
|
66
|
+
this.defaultDecimalType = 'DECIMAL';
|
|
63
67
|
this.udfPrefix = '__udf';
|
|
64
68
|
this.hasFinalStage = false;
|
|
65
|
-
this.divisionIsInteger =
|
|
69
|
+
this.divisionIsInteger = true;
|
|
66
70
|
this.supportsSumDistinctFunction = false;
|
|
67
71
|
this.unnestWithNumbers = false;
|
|
68
72
|
this.defaultSampling = { enable: false };
|
|
@@ -74,6 +78,7 @@ class TrinoDialect extends dialect_1.Dialect {
|
|
|
74
78
|
this.supportsSafeCast = true;
|
|
75
79
|
this.supportsNesting = true;
|
|
76
80
|
this.cantPartitionWindowFunctionsOnExpressions = true;
|
|
81
|
+
this.orderByClause = 'output_name';
|
|
77
82
|
// TODO(figutierrez): update.
|
|
78
83
|
this.keywords = `
|
|
79
84
|
ALL
|
|
@@ -174,24 +179,48 @@ class TrinoDialect extends dialect_1.Dialect {
|
|
|
174
179
|
}
|
|
175
180
|
quoteTablePath(tablePath) {
|
|
176
181
|
// TODO: look into escaping.
|
|
177
|
-
return `${tablePath.replace(/malloytest/g, 'malloy_demo.
|
|
182
|
+
//return `${tablePath.replace(/malloytest/g, 'malloy_demo.malloytest')}`;
|
|
183
|
+
return tablePath;
|
|
178
184
|
}
|
|
179
185
|
sqlGroupSetTable(groupSetCount) {
|
|
180
186
|
return `CROSS JOIN (SELECT row_number() OVER() -1 group_set FROM UNNEST(SEQUENCE(0,${groupSetCount})))`;
|
|
181
187
|
}
|
|
188
|
+
dialectExpr(qi, df) {
|
|
189
|
+
switch (df.function) {
|
|
190
|
+
case 'div': {
|
|
191
|
+
return (0, malloy_types_1.mkExpr) `CAST(${df.numerator} AS DOUBLE)/${df.denominator}`;
|
|
192
|
+
}
|
|
193
|
+
}
|
|
194
|
+
return super.dialectExpr(qi, df);
|
|
195
|
+
}
|
|
182
196
|
sqlAnyValue(groupSet, fieldName) {
|
|
183
197
|
return `ANY_VALUE(CASE WHEN group_set=${groupSet} THEN ${fieldName} END)`;
|
|
184
198
|
}
|
|
199
|
+
buildTypeExpression(fieldList) {
|
|
200
|
+
const fields = [];
|
|
201
|
+
for (const f of fieldList) {
|
|
202
|
+
if ((0, dialect_1.isDialectFieldStruct)(f)) {
|
|
203
|
+
let s = `ROW(${this.buildTypeExpression(f.nestedStruct)})`;
|
|
204
|
+
if (f.isArray) {
|
|
205
|
+
s = `array(${s})`;
|
|
206
|
+
}
|
|
207
|
+
fields.push(s);
|
|
208
|
+
}
|
|
209
|
+
else {
|
|
210
|
+
fields.push(`${f.sqlOutputName} ${trinoTypeMap[f.type] || f.type}`);
|
|
211
|
+
}
|
|
212
|
+
}
|
|
213
|
+
return fields.join(', \n');
|
|
214
|
+
}
|
|
185
215
|
// can array agg or any_value a struct...
|
|
186
216
|
sqlAggregateTurtle(groupSet, fieldList, orderBy, limit) {
|
|
187
|
-
|
|
217
|
+
const expressions = fieldList.map(f => f.sqlExpression).join(',\n ');
|
|
218
|
+
const definitions = this.buildTypeExpression(fieldList);
|
|
219
|
+
let ret = `ARRAY_AGG(CASE WHEN group_set=${groupSet} THEN CAST(ROW(${expressions}) AS ROW(${definitions})) END ${orderBy})`;
|
|
188
220
|
if (limit !== undefined) {
|
|
189
|
-
|
|
221
|
+
ret = `SLICE(${ret}, 1, ${limit})`;
|
|
190
222
|
}
|
|
191
|
-
|
|
192
|
-
.map(f => `\n '${f.sqlOutputName}' VALUE ${f.sqlExpression}`)
|
|
193
|
-
.join(', ');
|
|
194
|
-
return `cast(COALESCE(SLICE(COALESCE(ARRAY_AGG(CASE WHEN group_set=${groupSet} THEN JSON_PARSE(JSON_OBJECT(${fields})) END \n ${orderBy} -- ${tail}\n), ARRAY[]), 1, ${limit}), ARRAY[]) as json)`;
|
|
223
|
+
return ret;
|
|
195
224
|
}
|
|
196
225
|
sqlAnyValueTurtle(groupSet, fieldList) {
|
|
197
226
|
const fields = fieldList
|
|
@@ -235,10 +264,9 @@ class TrinoDialect extends dialect_1.Dialect {
|
|
|
235
264
|
}
|
|
236
265
|
}
|
|
237
266
|
sqlSumDistinctHashedKey(sqlDistinctKey) {
|
|
238
|
-
sqlDistinctKey = `CAST(${sqlDistinctKey} AS
|
|
239
|
-
const upperPart = `cast(
|
|
240
|
-
const lowerPart = `cast(
|
|
241
|
-
// See the comment below on `sql_sum_distinct` for why we multiply by this decimal
|
|
267
|
+
sqlDistinctKey = `CAST(${sqlDistinctKey} AS VARCHAR)`;
|
|
268
|
+
const upperPart = `cast(from_base(substr(to_hex(md5(to_utf8(${sqlDistinctKey}))), 1, 15),16) as DECIMAL) * DECIMAL '4294967296' `;
|
|
269
|
+
const lowerPart = `cast(from_base(substr(to_hex(md5(to_utf8(${sqlDistinctKey}))), 16, 8),16) as DECIMAL) `;
|
|
242
270
|
const precisionShiftMultiplier = '0.000000001';
|
|
243
271
|
return `(${upperPart} + ${lowerPart}) * ${precisionShiftMultiplier}`;
|
|
244
272
|
}
|
|
@@ -425,12 +453,15 @@ ${(0, utils_1.indent)(sql)}
|
|
|
425
453
|
malloyTypeToSQLType(malloyType) {
|
|
426
454
|
if (malloyType.type === 'number') {
|
|
427
455
|
if (malloyType.numberType === 'integer') {
|
|
428
|
-
return '
|
|
456
|
+
return 'BIGINT';
|
|
429
457
|
}
|
|
430
458
|
else {
|
|
431
|
-
return '
|
|
459
|
+
return 'DOUBLE';
|
|
432
460
|
}
|
|
433
461
|
}
|
|
462
|
+
else if (malloyType.type === 'string') {
|
|
463
|
+
return 'VARCHAR';
|
|
464
|
+
}
|
|
434
465
|
return malloyType.type;
|
|
435
466
|
}
|
|
436
467
|
sqlTypeToMalloyType(_sqlType) {
|
|
@@ -438,7 +469,7 @@ ${(0, utils_1.indent)(sql)}
|
|
|
438
469
|
return undefined;
|
|
439
470
|
}
|
|
440
471
|
castToString(expression) {
|
|
441
|
-
return `CAST(${expression} as
|
|
472
|
+
return `CAST(${expression} as VARCHAR)`;
|
|
442
473
|
}
|
|
443
474
|
concat(...values) {
|
|
444
475
|
return values.join(' || ');
|
|
@@ -268,6 +268,7 @@ declare class QueryQuery extends QueryField {
|
|
|
268
268
|
generateDepthNFields(depth: number, resultSet: FieldInstanceResult, output: StageOutputContext, stageWriter: StageWriter): void;
|
|
269
269
|
generateSQLDepthN(depth: number, stageWriter: StageWriter, stageName: string): string;
|
|
270
270
|
genereateSQLCombineTurtles(stageWriter: StageWriter, stage0Name: string): string;
|
|
271
|
+
buildDialectFieldList(resultStruct: FieldInstanceResult): DialectFieldList;
|
|
271
272
|
generateTurtleSQL(resultStruct: FieldInstanceResult, stageWriter: StageWriter, sqlFieldName: string, outputPipelinedSQL: OutputPipelinedSQL[]): string;
|
|
272
273
|
generateTurtlePipelineSQL(fi: FieldInstanceResult, stageWriter: StageWriter, sourceSQLExpression: string): {
|
|
273
274
|
structDef: StructDef;
|
|
@@ -2101,14 +2101,25 @@ class QueryQuery extends QueryField {
|
|
|
2101
2101
|
// convert name to an index
|
|
2102
2102
|
const fi = resultStruct.getField(f.field);
|
|
2103
2103
|
if (fi && fi.fieldUsage.type === 'result') {
|
|
2104
|
-
|
|
2104
|
+
if (this.parent.dialect.orderByClause === 'ordinal') {
|
|
2105
|
+
o.push(`${fi.fieldUsage.resultIndex} ${f.dir || 'ASC'}`);
|
|
2106
|
+
}
|
|
2107
|
+
else if (this.parent.dialect.orderByClause === 'output_name') {
|
|
2108
|
+
o.push(`${f.field}${f.dir || 'ASC'}`);
|
|
2109
|
+
}
|
|
2105
2110
|
}
|
|
2106
2111
|
else {
|
|
2107
2112
|
throw new Error(`Unknown field in ORDER BY ${f.field}`);
|
|
2108
2113
|
}
|
|
2109
2114
|
}
|
|
2110
2115
|
else {
|
|
2111
|
-
|
|
2116
|
+
if (this.parent.dialect.orderByClause === 'ordinal') {
|
|
2117
|
+
o.push(`${f.field} ${f.dir || 'ASC'}`);
|
|
2118
|
+
}
|
|
2119
|
+
else if (this.parent.dialect.orderByClause === 'output_name') {
|
|
2120
|
+
const orderingField = resultStruct.getFieldByNumber(f.field);
|
|
2121
|
+
o.push(`${this.parent.dialect.sqlMaybeQuoteIdentifier(orderingField.name)} ${f.dir || 'ASC'}`);
|
|
2122
|
+
}
|
|
2112
2123
|
}
|
|
2113
2124
|
}
|
|
2114
2125
|
if (o.length > 0) {
|
|
@@ -2446,9 +2457,49 @@ class QueryQuery extends QueryField {
|
|
|
2446
2457
|
this.resultStage = this.generatePipelinedStages(outputPipelinedSQL, this.resultStage, stageWriter);
|
|
2447
2458
|
return this.resultStage;
|
|
2448
2459
|
}
|
|
2460
|
+
// create a simplified version of the StructDef for dialects.
|
|
2461
|
+
buildDialectFieldList(resultStruct) {
|
|
2462
|
+
const dialectFieldList = [];
|
|
2463
|
+
for (const [name, field] of resultStruct.allFields) {
|
|
2464
|
+
const sqlName = this.parent.dialect.sqlMaybeQuoteIdentifier(name);
|
|
2465
|
+
//
|
|
2466
|
+
if (resultStruct.firstSegment.type === 'reduce' &&
|
|
2467
|
+
field instanceof FieldInstanceResult) {
|
|
2468
|
+
const d = {
|
|
2469
|
+
type: 'struct',
|
|
2470
|
+
sqlExpression: this.parent.dialect.sqlMaybeQuoteIdentifier(`${name}__${resultStruct.groupSet}`),
|
|
2471
|
+
rawName: name,
|
|
2472
|
+
sqlOutputName: sqlName,
|
|
2473
|
+
isArray: field.getRepeatedResultType() === 'nested',
|
|
2474
|
+
nestedStruct: this.buildDialectFieldList(field),
|
|
2475
|
+
};
|
|
2476
|
+
dialectFieldList.push(d);
|
|
2477
|
+
}
|
|
2478
|
+
else if (resultStruct.firstSegment.type === 'reduce' &&
|
|
2479
|
+
field instanceof FieldInstanceField &&
|
|
2480
|
+
field.fieldUsage.type === 'result') {
|
|
2481
|
+
dialectFieldList.push({
|
|
2482
|
+
type: field.f.fieldDef.type,
|
|
2483
|
+
sqlExpression: this.parent.dialect.sqlMaybeQuoteIdentifier(`${name}__${resultStruct.groupSet}`),
|
|
2484
|
+
rawName: name,
|
|
2485
|
+
sqlOutputName: sqlName,
|
|
2486
|
+
});
|
|
2487
|
+
}
|
|
2488
|
+
else if (resultStruct.firstSegment.type === 'project' &&
|
|
2489
|
+
field instanceof FieldInstanceField &&
|
|
2490
|
+
field.fieldUsage.type === 'result') {
|
|
2491
|
+
dialectFieldList.push({
|
|
2492
|
+
type: field.type,
|
|
2493
|
+
sqlExpression: field.f.generateExpression(resultStruct),
|
|
2494
|
+
rawName: name,
|
|
2495
|
+
sqlOutputName: sqlName,
|
|
2496
|
+
});
|
|
2497
|
+
}
|
|
2498
|
+
}
|
|
2499
|
+
return dialectFieldList;
|
|
2500
|
+
}
|
|
2449
2501
|
generateTurtleSQL(resultStruct, stageWriter, sqlFieldName, outputPipelinedSQL) {
|
|
2450
2502
|
// let fieldsSQL: string[] = [];
|
|
2451
|
-
const dialectFieldList = [];
|
|
2452
2503
|
let orderBy = '';
|
|
2453
2504
|
const limit = (0, malloy_types_1.isRawSegment)(resultStruct.firstSegment)
|
|
2454
2505
|
? undefined
|
|
@@ -2480,38 +2531,7 @@ class QueryQuery extends QueryField {
|
|
|
2480
2531
|
if (obSQL.length > 0) {
|
|
2481
2532
|
orderBy = ' ' + this.parent.dialect.sqlOrderBy(obSQL);
|
|
2482
2533
|
}
|
|
2483
|
-
|
|
2484
|
-
const sqlName = this.parent.dialect.sqlMaybeQuoteIdentifier(name);
|
|
2485
|
-
//
|
|
2486
|
-
if (resultStruct.firstSegment.type === 'reduce' &&
|
|
2487
|
-
(field instanceof FieldInstanceResult ||
|
|
2488
|
-
(field instanceof FieldInstanceField &&
|
|
2489
|
-
field.fieldUsage.type === 'result'))) {
|
|
2490
|
-
// fieldsSQL.push(`${name}__${resultStruct.groupSet} as ${sqlName}`);
|
|
2491
|
-
// outputFieldNames.push(name);
|
|
2492
|
-
dialectFieldList.push({
|
|
2493
|
-
type: field instanceof FieldInstanceField
|
|
2494
|
-
? field.f.fieldDef.type
|
|
2495
|
-
: 'struct',
|
|
2496
|
-
sqlExpression: this.parent.dialect.sqlMaybeQuoteIdentifier(`${name}__${resultStruct.groupSet}`),
|
|
2497
|
-
rawName: name,
|
|
2498
|
-
sqlOutputName: sqlName,
|
|
2499
|
-
});
|
|
2500
|
-
}
|
|
2501
|
-
else if (resultStruct.firstSegment.type === 'project' &&
|
|
2502
|
-
field instanceof FieldInstanceField &&
|
|
2503
|
-
field.fieldUsage.type === 'result') {
|
|
2504
|
-
// fieldsSQL.push(
|
|
2505
|
-
// `${field.f.generateExpression(resultStruct)} as ${sqlName}`
|
|
2506
|
-
// );
|
|
2507
|
-
dialectFieldList.push({
|
|
2508
|
-
type: field.type,
|
|
2509
|
-
sqlExpression: field.f.generateExpression(resultStruct),
|
|
2510
|
-
rawName: name,
|
|
2511
|
-
sqlOutputName: sqlName,
|
|
2512
|
-
});
|
|
2513
|
-
}
|
|
2514
|
-
}
|
|
2534
|
+
const dialectFieldList = this.buildDialectFieldList(resultStruct);
|
|
2515
2535
|
let resultType;
|
|
2516
2536
|
let ret;
|
|
2517
2537
|
if ((resultType = resultStruct.getRepeatedResultType()) !== 'nested') {
|