@malloydata/db-trino 0.0.220-dev241203204946 → 0.0.220-dev241205013400
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/trino_connection.d.ts +3 -2
- package/dist/trino_connection.js +150 -45
- package/package.json +2 -2
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { Connection, ConnectionConfig, MalloyQueryData, PersistSQLResults, QueryData, QueryOptionsReader, QueryRunStats, RunSQLOptions, StructDef, TableSourceDef, SQLSourceDef, AtomicTypeDef } from '@malloydata/malloy';
|
|
1
|
+
import { Connection, ConnectionConfig, MalloyQueryData, PersistSQLResults, QueryData, QueryOptionsReader, QueryRunStats, RunSQLOptions, TrinoDialect, StructDef, TableSourceDef, SQLSourceDef, AtomicTypeDef } from '@malloydata/malloy';
|
|
2
2
|
import { BaseConnection } from '@malloydata/malloy/connection';
|
|
3
3
|
export interface TrinoManagerOptions {
|
|
4
4
|
credentials?: {
|
|
@@ -31,7 +31,7 @@ export interface BaseRunner {
|
|
|
31
31
|
}
|
|
32
32
|
export declare abstract class TrinoPrestoConnection extends BaseConnection implements Connection, PersistSQLResults {
|
|
33
33
|
name: string;
|
|
34
|
-
|
|
34
|
+
protected readonly dialect: TrinoDialect;
|
|
35
35
|
static DEFAULT_QUERY_OPTIONS: RunSQLOptions;
|
|
36
36
|
private queryOptions?;
|
|
37
37
|
private client;
|
|
@@ -45,6 +45,7 @@ export declare abstract class TrinoPrestoConnection extends BaseConnection imple
|
|
|
45
45
|
convertRow(structDef: StructDef, rawRow: unknown): {};
|
|
46
46
|
convertNest(structDef: StructDef, _data: unknown): unknown[];
|
|
47
47
|
runSQL(sqlCommand: string, options?: RunSQLOptions, _rowIndex?: number): Promise<MalloyQueryData>;
|
|
48
|
+
private resultRow;
|
|
48
49
|
runSQLBlockAndFetchResultSchema(_sqlBlock: SQLSourceDef, _options?: RunSQLOptions): Promise<{
|
|
49
50
|
data: MalloyQueryData;
|
|
50
51
|
schema: StructDef;
|
package/dist/trino_connection.js
CHANGED
|
@@ -194,30 +194,39 @@ class TrinoPrestoConnection extends connection_1.BaseConnection {
|
|
|
194
194
|
for (let i = 0; i < columns.length; i++) {
|
|
195
195
|
const column = columns[i];
|
|
196
196
|
const schemaColumn = malloyColumns[i];
|
|
197
|
-
|
|
198
|
-
malloyRow[column.name] = this.convertRow(schemaColumn, row[i]);
|
|
199
|
-
}
|
|
200
|
-
else if (schemaColumn.type === 'array') {
|
|
201
|
-
malloyRow[column.name] = this.convertNest(schemaColumn, row[i]);
|
|
202
|
-
}
|
|
203
|
-
else if (schemaColumn.type === 'number' &&
|
|
204
|
-
typeof row[i] === 'string') {
|
|
205
|
-
// decimal numbers come back as strings
|
|
206
|
-
malloyRow[column.name] = Number(row[i]);
|
|
207
|
-
}
|
|
208
|
-
else if (schemaColumn.type === 'timestamp' &&
|
|
209
|
-
typeof row[i] === 'string') {
|
|
210
|
-
// timestamps come back as strings
|
|
211
|
-
malloyRow[column.name] = new Date(row[i]);
|
|
212
|
-
}
|
|
213
|
-
else {
|
|
214
|
-
malloyRow[column.name] = row[i];
|
|
215
|
-
}
|
|
197
|
+
malloyRow[column.name] = this.resultRow(schemaColumn, row[i]);
|
|
216
198
|
}
|
|
217
199
|
malloyRows.push(malloyRow);
|
|
218
200
|
}
|
|
219
201
|
return { rows: malloyRows, totalRows: malloyRows.length };
|
|
220
202
|
}
|
|
203
|
+
resultRow(colSchema, rawRow) {
|
|
204
|
+
if (colSchema.type === 'record') {
|
|
205
|
+
return this.convertRow(colSchema, rawRow);
|
|
206
|
+
}
|
|
207
|
+
else if (colSchema.type === 'array') {
|
|
208
|
+
const elType = colSchema.elementTypeDef;
|
|
209
|
+
if (elType.type === 'record_element') {
|
|
210
|
+
return this.convertNest(colSchema, rawRow);
|
|
211
|
+
}
|
|
212
|
+
let theArray = this.unpackArray(rawRow);
|
|
213
|
+
if (elType.type === 'array') {
|
|
214
|
+
theArray = theArray.map(el => this.resultRow(elType, el));
|
|
215
|
+
}
|
|
216
|
+
return theArray;
|
|
217
|
+
}
|
|
218
|
+
else if (colSchema.type === 'number' && typeof rawRow === 'string') {
|
|
219
|
+
// decimal numbers come back as strings
|
|
220
|
+
return Number(rawRow);
|
|
221
|
+
}
|
|
222
|
+
else if (colSchema.type === 'timestamp' && typeof rawRow === 'string') {
|
|
223
|
+
// timestamps come back as strings
|
|
224
|
+
return new Date(rawRow);
|
|
225
|
+
}
|
|
226
|
+
else {
|
|
227
|
+
return rawRow;
|
|
228
|
+
}
|
|
229
|
+
}
|
|
221
230
|
async runSQLBlockAndFetchResultSchema(_sqlBlock, _options) {
|
|
222
231
|
throw new Error('Not implemented 3');
|
|
223
232
|
}
|
|
@@ -413,32 +422,8 @@ class PrestoConnection extends TrinoPrestoConnection {
|
|
|
413
422
|
if ((lines === null || lines === void 0 ? void 0 : lines.length) === 0) {
|
|
414
423
|
throw new Error('Received invalid explain result when trying to fetch schema.');
|
|
415
424
|
}
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
outputLine = outputLine.substring(namesIndex + 2);
|
|
419
|
-
const lineParts = outputLine.split('] => [');
|
|
420
|
-
if (lineParts.length !== 2) {
|
|
421
|
-
throw new Error('There was a problem parsing schema from Explain.');
|
|
422
|
-
}
|
|
423
|
-
const fieldNamesPart = lineParts[0];
|
|
424
|
-
const fieldNames = fieldNamesPart.split(',').map(e => e.trim());
|
|
425
|
-
let schemaData = lineParts[1];
|
|
426
|
-
schemaData = schemaData.substring(0, schemaData.length - 1);
|
|
427
|
-
const rawFieldsTarget = schemaData
|
|
428
|
-
.split(',')
|
|
429
|
-
.map(e => e.trim())
|
|
430
|
-
.map(e => e.split(':'));
|
|
431
|
-
if (rawFieldsTarget.length !== fieldNames.length) {
|
|
432
|
-
throw new Error('There was a problem parsing schema from Explain. Field names size do not match target fields with types.');
|
|
433
|
-
}
|
|
434
|
-
for (let index = 0; index < fieldNames.length; index++) {
|
|
435
|
-
const name = fieldNames[index];
|
|
436
|
-
const type = rawFieldsTarget[index][1];
|
|
437
|
-
structDef.fields.push({
|
|
438
|
-
name,
|
|
439
|
-
...this.malloyTypeFromTrinoType(name, type),
|
|
440
|
-
});
|
|
441
|
-
}
|
|
425
|
+
const schemaDesc = new PrestoExplainParser(lines[0], this.dialect);
|
|
426
|
+
structDef.fields = schemaDesc.parseExplain();
|
|
442
427
|
}
|
|
443
428
|
unpackArray(data) {
|
|
444
429
|
return JSON.parse(data);
|
|
@@ -456,4 +441,124 @@ class TrinoConnection extends TrinoPrestoConnection {
|
|
|
456
441
|
}
|
|
457
442
|
}
|
|
458
443
|
exports.TrinoConnection = TrinoConnection;
|
|
444
|
+
/**
|
|
445
|
+
* A hand built parser for schema lines, roughly this grammar
|
|
446
|
+
* SCHEMA_LINE: - Output [PlanName N] [NAME_LIST] => [TYPE_LIST]
|
|
447
|
+
* NAME_LIST: NAME (, NAME)*
|
|
448
|
+
* TYPE_LIST: TYPE_SPEC (, TYPE_SPEC)*
|
|
449
|
+
* TYPE_SPEC: exprN ':' TYPE
|
|
450
|
+
* TYPE: REC_TYPE | ARRAY_TYPE | SQL_TYPE
|
|
451
|
+
* ARRAY_TYPE: ARRAY '(' TYPE ')'
|
|
452
|
+
* REC_TYPE: REC '(' "name" TYPE (, "name" TYPE)* ')'
|
|
453
|
+
*/
|
|
454
|
+
class PrestoExplainParser extends malloy_1.TinyParser {
|
|
455
|
+
constructor(input, dialect) {
|
|
456
|
+
super(input, {
|
|
457
|
+
space: /^\s+/,
|
|
458
|
+
arrow: /^=>/,
|
|
459
|
+
char: /^[,:[\]()-]/,
|
|
460
|
+
id: /^\w+/,
|
|
461
|
+
quoted_name: /^"\w+"/,
|
|
462
|
+
});
|
|
463
|
+
this.input = input;
|
|
464
|
+
this.dialect = dialect;
|
|
465
|
+
}
|
|
466
|
+
fieldNameList() {
|
|
467
|
+
this.skipTo(']'); // Skip to end of plan
|
|
468
|
+
this.next('['); // Expect start of name list
|
|
469
|
+
const fieldNames = [];
|
|
470
|
+
for (;;) {
|
|
471
|
+
const nmToken = this.next('id');
|
|
472
|
+
fieldNames.push(nmToken.text);
|
|
473
|
+
const sep = this.next();
|
|
474
|
+
if (sep.type === ',') {
|
|
475
|
+
continue;
|
|
476
|
+
}
|
|
477
|
+
if (sep.type !== ']') {
|
|
478
|
+
throw this.parseError(`Unexpected '${sep.text}' while getting field name list`);
|
|
479
|
+
}
|
|
480
|
+
break;
|
|
481
|
+
}
|
|
482
|
+
return fieldNames;
|
|
483
|
+
}
|
|
484
|
+
parseExplain() {
|
|
485
|
+
const fieldNames = this.fieldNameList();
|
|
486
|
+
const fields = [];
|
|
487
|
+
this.next('arrow', '[');
|
|
488
|
+
for (let nameIndex = 0;; nameIndex += 1) {
|
|
489
|
+
const name = fieldNames[nameIndex];
|
|
490
|
+
this.next('id', ':');
|
|
491
|
+
const nextType = this.typeDef();
|
|
492
|
+
fields.push({ ...nextType, name });
|
|
493
|
+
const sep = this.next();
|
|
494
|
+
if (sep.text === ',') {
|
|
495
|
+
continue;
|
|
496
|
+
}
|
|
497
|
+
if (sep.text !== ']') {
|
|
498
|
+
throw this.parseError(`Unexpected '${sep.text}' between field types`);
|
|
499
|
+
}
|
|
500
|
+
break;
|
|
501
|
+
}
|
|
502
|
+
if (fields.length !== fieldNames.length) {
|
|
503
|
+
throw new Error(`Presto schema error mismatched ${fields.length} types and ${fieldNames.length} fields`);
|
|
504
|
+
}
|
|
505
|
+
return fields;
|
|
506
|
+
}
|
|
507
|
+
typeDef() {
|
|
508
|
+
const typToken = this.next();
|
|
509
|
+
if (typToken.type === 'eof') {
|
|
510
|
+
throw this.parseError('Unexpected EOF parsing type, expected a type name');
|
|
511
|
+
}
|
|
512
|
+
else if (typToken.text === 'row' && this.next('(')) {
|
|
513
|
+
const fields = [];
|
|
514
|
+
for (;;) {
|
|
515
|
+
const name = this.next('quoted_name');
|
|
516
|
+
const getDef = this.typeDef();
|
|
517
|
+
fields.push({ ...getDef, name: name.text });
|
|
518
|
+
const sep = this.next();
|
|
519
|
+
if (sep.text === ')') {
|
|
520
|
+
break;
|
|
521
|
+
}
|
|
522
|
+
if (sep.text === ',') {
|
|
523
|
+
continue;
|
|
524
|
+
}
|
|
525
|
+
}
|
|
526
|
+
const def = {
|
|
527
|
+
type: 'record',
|
|
528
|
+
name: '',
|
|
529
|
+
join: 'one',
|
|
530
|
+
dialect: this.dialect.name,
|
|
531
|
+
fields,
|
|
532
|
+
};
|
|
533
|
+
return def;
|
|
534
|
+
}
|
|
535
|
+
else if (typToken.text === 'array' && this.next('(')) {
|
|
536
|
+
const elType = this.typeDef();
|
|
537
|
+
this.next(')');
|
|
538
|
+
const def = {
|
|
539
|
+
type: 'array',
|
|
540
|
+
name: '',
|
|
541
|
+
dialect: this.dialect.name,
|
|
542
|
+
join: 'many',
|
|
543
|
+
elementTypeDef: elType.type === 'record' ? { type: 'record_element' } : elType,
|
|
544
|
+
fields: elType.type === 'record' ? elType.fields : (0, malloy_1.arrayEachFields)(elType),
|
|
545
|
+
};
|
|
546
|
+
return def;
|
|
547
|
+
}
|
|
548
|
+
else if (typToken.type === 'id') {
|
|
549
|
+
const sqlType = typToken.text;
|
|
550
|
+
const def = this.dialect.sqlTypeToMalloyType(sqlType);
|
|
551
|
+
if (def === undefined) {
|
|
552
|
+
throw this.parseError(`Can't parse presto type ${sqlType}`);
|
|
553
|
+
}
|
|
554
|
+
if (sqlType === 'varchar') {
|
|
555
|
+
if (this.peek().type === '(') {
|
|
556
|
+
this.next('(', 'id', ')');
|
|
557
|
+
}
|
|
558
|
+
}
|
|
559
|
+
return def;
|
|
560
|
+
}
|
|
561
|
+
throw this.parseError(`'${typToken.text}' unexpected while looking for a type`);
|
|
562
|
+
}
|
|
563
|
+
}
|
|
459
564
|
//# sourceMappingURL=trino_connection.js.map
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@malloydata/db-trino",
|
|
3
|
-
"version": "0.0.220-
|
|
3
|
+
"version": "0.0.220-dev241205013400",
|
|
4
4
|
"license": "MIT",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"types": "dist/index.d.ts",
|
|
@@ -22,7 +22,7 @@
|
|
|
22
22
|
"prepublishOnly": "npm run build"
|
|
23
23
|
},
|
|
24
24
|
"dependencies": {
|
|
25
|
-
"@malloydata/malloy": "^0.0.220-
|
|
25
|
+
"@malloydata/malloy": "^0.0.220-dev241205013400",
|
|
26
26
|
"@prestodb/presto-js-client": "^1.0.0",
|
|
27
27
|
"gaxios": "^4.2.0",
|
|
28
28
|
"trino-client": "^0.2.2"
|