@malloydata/db-trino 0.0.227 → 0.0.228-dev250116203644

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/index.d.ts CHANGED
@@ -1,3 +1,3 @@
1
1
  export type { BaseRunner } from './trino_connection';
2
- export { PrestoConnection, PrestoExplainParser, TrinoConnection, TrinoPrestoConnection, } from './trino_connection';
2
+ export { PrestoConnection, TrinoConnection, TrinoPrestoConnection, } from './trino_connection';
3
3
  export { TrinoExecutor } from './trino_executor';
package/dist/index.js CHANGED
@@ -22,10 +22,9 @@
22
22
  * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
23
23
  */
24
24
  Object.defineProperty(exports, "__esModule", { value: true });
25
- exports.TrinoExecutor = exports.TrinoPrestoConnection = exports.TrinoConnection = exports.PrestoExplainParser = exports.PrestoConnection = void 0;
25
+ exports.TrinoExecutor = exports.TrinoPrestoConnection = exports.TrinoConnection = exports.PrestoConnection = void 0;
26
26
  var trino_connection_1 = require("./trino_connection");
27
27
  Object.defineProperty(exports, "PrestoConnection", { enumerable: true, get: function () { return trino_connection_1.PrestoConnection; } });
28
- Object.defineProperty(exports, "PrestoExplainParser", { enumerable: true, get: function () { return trino_connection_1.PrestoExplainParser; } });
29
28
  Object.defineProperty(exports, "TrinoConnection", { enumerable: true, get: function () { return trino_connection_1.TrinoConnection; } });
30
29
  Object.defineProperty(exports, "TrinoPrestoConnection", { enumerable: true, get: function () { return trino_connection_1.TrinoPrestoConnection; } });
31
30
  var trino_executor_1 = require("./trino_executor");
@@ -1,4 +1,4 @@
1
- import { Connection, ConnectionConfig, MalloyQueryData, PersistSQLResults, QueryData, QueryOptionsReader, QueryRunStats, RunSQLOptions, TrinoDialect, StructDef, TableSourceDef, SQLSourceDef, AtomicTypeDef, Dialect, FieldDef, TinyParser } from '@malloydata/malloy';
1
+ import { Connection, ConnectionConfig, MalloyQueryData, PersistSQLResults, QueryData, QueryOptionsReader, QueryRunStats, RunSQLOptions, TrinoDialect, StructDef, TableSourceDef, SQLSourceDef, AtomicTypeDef, FieldDef } from '@malloydata/malloy';
2
2
  import { BaseConnection } from '@malloydata/malloy/connection';
3
3
  export interface TrinoManagerOptions {
4
4
  credentials?: {
@@ -54,8 +54,7 @@ export declare abstract class TrinoPrestoConnection extends BaseConnection imple
54
54
  fetchSelectSchema(sqlRef: SQLSourceDef): Promise<SQLSourceDef>;
55
55
  protected abstract fillStructDefForSqlBlockSchema(sql: string, structDef: StructDef): Promise<void>;
56
56
  protected executeAndWait(sqlBlock: string): Promise<void>;
57
- splitColumns(s: string): string[];
58
- malloyTypeFromTrinoType(name: string, trinoType: string): AtomicTypeDef;
57
+ malloyTypeFromTrinoType(trinoType: string): AtomicTypeDef;
59
58
  structDefFromSchema(rows: string[][], structDef: StructDef): void;
60
59
  protected loadSchemaForSqlBlock(sqlBlock: string, structDef: StructDef, element: string): Promise<StructDef>;
61
60
  estimateQueryCost(_sqlCommand: string): Promise<QueryRunStats>;
@@ -75,21 +74,3 @@ export declare class TrinoConnection extends TrinoPrestoConnection {
75
74
  constructor(option: TrinoConnectionOptions, queryOptions?: QueryOptionsReader);
76
75
  protected fillStructDefForSqlBlockSchema(sql: string, structDef: StructDef): Promise<void>;
77
76
  }
78
- /**
79
- * A hand built parser for schema lines, roughly this grammar
80
- * SCHEMA_LINE: - Output [PlanName N] [NAME_LIST] => [TYPE_LIST]
81
- * NAME_LIST: NAME (, NAME)*
82
- * TYPE_LIST: TYPE_SPEC (, TYPE_SPEC)*
83
- * TYPE_SPEC: exprN ':' TYPE
84
- * TYPE: REC_TYPE | ARRAY_TYPE | SQL_TYPE
85
- * ARRAY_TYPE: ARRAY '(' TYPE ')'
86
- * REC_TYPE: REC '(' "name" TYPE (, "name" TYPE)* ')'
87
- */
88
- export declare class PrestoExplainParser extends TinyParser {
89
- readonly input: string;
90
- readonly dialect: Dialect;
91
- constructor(input: string, dialect: Dialect);
92
- fieldNameList(): string[];
93
- parseExplain(): FieldDef[];
94
- typeDef(): AtomicTypeDef;
95
- }
@@ -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.PrestoExplainParser = exports.TrinoConnection = exports.PrestoConnection = exports.TrinoPrestoConnection = void 0;
25
+ exports.TrinoConnection = exports.PrestoConnection = exports.TrinoPrestoConnection = void 0;
26
26
  const malloy_1 = require("@malloydata/malloy");
27
27
  const connection_1 = require("@malloydata/malloy/connection");
28
28
  const presto_js_client_1 = require("@prestodb/presto-js-client");
@@ -182,7 +182,7 @@ class TrinoPrestoConnection extends connection_1.BaseConnection {
182
182
  }
183
183
  const inputRows = r.rows;
184
184
  const columns = r.columns;
185
- const malloyColumns = columns.map(c => this.malloyTypeFromTrinoType(c.name, c.type));
185
+ const malloyColumns = columns.map(c => (0, malloy_1.mkFieldDef)(this.malloyTypeFromTrinoType(c.type), c.name));
186
186
  const malloyRows = [];
187
187
  const rows = inputRows !== null && inputRows !== void 0 ? inputRows : [];
188
188
  for (const row of rows) {
@@ -249,96 +249,15 @@ class TrinoPrestoConnection extends connection_1.BaseConnection {
249
249
  // TODO: make sure failure is handled correctly.
250
250
  //while (!(await result.next()).done);
251
251
  }
252
- splitColumns(s) {
253
- const columns = [];
254
- let parens = 0;
255
- let column = '';
256
- let eatSpaces = true;
257
- for (let idx = 0; idx < s.length; idx++) {
258
- const c = s.charAt(idx);
259
- if (eatSpaces && c === ' ') {
260
- // Eat space
261
- }
262
- else {
263
- eatSpaces = false;
264
- if (!parens && c === ',') {
265
- columns.push(column);
266
- column = '';
267
- eatSpaces = true;
268
- }
269
- else {
270
- column += c;
271
- }
272
- if (c === '(') {
273
- parens += 1;
274
- }
275
- else if (c === ')') {
276
- parens -= 1;
277
- }
278
- }
279
- }
280
- columns.push(column);
281
- return columns;
282
- }
283
- malloyTypeFromTrinoType(name, trinoType) {
284
- // Arrays look like `array(type)`
285
- const arrayMatch = trinoType.match(/^(([^,])+\s)?array\((.*)\)$/);
286
- // Structs look like `row(name type, name type)`
287
- const structMatch = trinoType.match(/^(([^,])+\s)?row\((.*)\)$/);
288
- if (arrayMatch) {
289
- const arrayType = arrayMatch[3];
290
- const innerType = this.malloyTypeFromTrinoType(name, arrayType);
291
- if (innerType.type === 'record') {
292
- const complexStruct = {
293
- type: 'array',
294
- elementTypeDef: { type: 'record_element' },
295
- fields: innerType.fields,
296
- };
297
- return complexStruct;
298
- }
299
- else {
300
- const arrayStruct = {
301
- type: 'array',
302
- elementTypeDef: innerType,
303
- };
304
- return arrayStruct;
305
- }
306
- }
307
- else if (structMatch) {
308
- // TODO: Trino doesn't quote or escape commas in field names,
309
- // so some magic is going to need to be applied before we get here
310
- // to avoid confusion if a field name contains a comma
311
- const innerTypes = this.splitColumns(structMatch[3]);
312
- const recordType = {
313
- type: 'record',
314
- fields: [],
315
- };
316
- for (let innerType of innerTypes) {
317
- // TODO: Handle time zone type annotation, which is an
318
- // exception to the types not containing spaces assumption
319
- innerType = innerType.replace(/ with time zone$/, '');
320
- let parts = innerType.match(/^(.+?)\s((array\(|row\().*)$/);
321
- if (parts === null) {
322
- parts = innerType.match(/^(.+)\s(\S+)$/);
323
- }
324
- if (parts) {
325
- // remove quotes from the name
326
- const innerName = parts[1].replace(/^"(.+(?="$))"$/, '$1');
327
- const innerTrinoType = parts[2];
328
- const innerMalloyType = this.malloyTypeFromTrinoType(innerName, innerTrinoType);
329
- recordType.fields.push((0, malloy_1.mkFieldDef)(innerMalloyType, innerName));
330
- }
331
- }
332
- return recordType;
333
- }
334
- return this.dialect.sqlTypeToMalloyType(trinoType);
252
+ malloyTypeFromTrinoType(trinoType) {
253
+ const typeParse = new TrinoPrestoSchemaParser(trinoType, this.dialect);
254
+ return typeParse.typeDef();
335
255
  }
336
256
  structDefFromSchema(rows, structDef) {
337
257
  for (const row of rows) {
338
258
  const name = row[0];
339
259
  const type = row[4] || row[1];
340
- const malloyType = this.malloyTypeFromTrinoType(name, type);
341
- // console.log('>', row, '\n<', malloyType);
260
+ const malloyType = (0, malloy_1.mkFieldDef)(this.malloyTypeFromTrinoType(type), name);
342
261
  structDef.fields.push((0, malloy_1.mkFieldDef)(malloyType, name));
343
262
  }
344
263
  }
@@ -408,8 +327,8 @@ class PrestoConnection extends TrinoPrestoConnection {
408
327
  if ((lines === null || lines === void 0 ? void 0 : lines.length) === 0) {
409
328
  throw new Error('Received invalid explain result when trying to fetch schema.');
410
329
  }
411
- const schemaDesc = new PrestoExplainParser(lines[0], this.dialect);
412
- structDef.fields = schemaDesc.parseExplain();
330
+ const schemaDesc = new TrinoPrestoSchemaParser(lines[0], this.dialect);
331
+ structDef.fields = schemaDesc.parseQueryPlan();
413
332
  }
414
333
  unpackArray(data) {
415
334
  return JSON.parse(data);
@@ -428,23 +347,26 @@ class TrinoConnection extends TrinoPrestoConnection {
428
347
  }
429
348
  exports.TrinoConnection = TrinoConnection;
430
349
  /**
431
- * A hand built parser for schema lines, roughly this grammar
350
+ * A hand built parser for schema lines, it parses two things ...
351
+ * A presto query plan
432
352
  * SCHEMA_LINE: - Output [PlanName N] [NAME_LIST] => [TYPE_LIST]
433
353
  * NAME_LIST: NAME (, NAME)*
434
354
  * TYPE_LIST: TYPE_SPEC (, TYPE_SPEC)*
435
355
  * TYPE_SPEC: exprN ':' TYPE
356
+ *
357
+ * And a presto/trino type
436
358
  * TYPE: REC_TYPE | ARRAY_TYPE | SQL_TYPE
437
359
  * ARRAY_TYPE: ARRAY '(' TYPE ')'
438
360
  * REC_TYPE: REC '(' "name" TYPE (, "name" TYPE)* ')'
439
361
  */
440
- class PrestoExplainParser extends malloy_1.TinyParser {
362
+ class TrinoPrestoSchemaParser extends malloy_1.TinyParser {
441
363
  constructor(input, dialect) {
442
364
  super(input, {
443
365
  space: /^\s+/,
444
366
  arrow: /^=>/,
445
367
  char: /^[,:[\]()-]/,
446
368
  id: /^\w+/,
447
- quoted_name: /^"\w+"/,
369
+ quoted_name: /^"(\\"|[^"])*"/,
448
370
  });
449
371
  this.input = input;
450
372
  this.dialect = dialect;
@@ -467,7 +389,7 @@ class PrestoExplainParser extends malloy_1.TinyParser {
467
389
  }
468
390
  return fieldNames;
469
391
  }
470
- parseExplain() {
392
+ parseQueryPlan() {
471
393
  const fieldNames = this.fieldNameList();
472
394
  const fields = [];
473
395
  this.next('arrow', '[');
@@ -498,7 +420,10 @@ class PrestoExplainParser extends malloy_1.TinyParser {
498
420
  else if (typToken.text === 'row' && this.next('(')) {
499
421
  const fields = [];
500
422
  for (;;) {
501
- const name = this.next('quoted_name');
423
+ const name = this.next();
424
+ if (name.type !== 'id' && name.type !== 'quoted_name') {
425
+ throw this.parseError(`Expected property name, got '${name.type}'`);
426
+ }
502
427
  const getDef = this.typeDef();
503
428
  fields.push((0, malloy_1.mkFieldDef)(getDef, name.text));
504
429
  const sep = this.next();
@@ -527,20 +452,38 @@ class PrestoExplainParser extends malloy_1.TinyParser {
527
452
  : { type: 'array', elementTypeDef: elType };
528
453
  }
529
454
  else if (typToken.type === 'id') {
530
- const sqlType = typToken.text;
531
- const def = this.dialect.sqlTypeToMalloyType(sqlType);
532
- if (def === undefined) {
533
- throw this.parseError(`Can't parse presto type ${sqlType}`);
534
- }
455
+ const sqlType = typToken.text.toLowerCase();
535
456
  if (sqlType === 'varchar') {
536
457
  if (this.peek().type === '(') {
537
458
  this.next('(', 'id', ')');
538
459
  }
539
460
  }
540
- return def;
461
+ else if (sqlType === 'timestamp') {
462
+ if (this.peek().text === '(') {
463
+ this.next('(', 'id', ')');
464
+ }
465
+ if (this.peek().text === 'with') {
466
+ this.nextText('with', 'time', 'zone');
467
+ }
468
+ }
469
+ const typeDef = this.dialect.sqlTypeToMalloyType(sqlType);
470
+ if (typeDef.type === 'number' && sqlType === 'decimal') {
471
+ this.next('(', 'id');
472
+ if (this.peek().type === ',') {
473
+ this.next(',', 'id');
474
+ typeDef.numberType = 'float';
475
+ }
476
+ else {
477
+ typeDef.numberType = 'integer';
478
+ }
479
+ this.next(')');
480
+ }
481
+ if (typeDef === undefined) {
482
+ throw this.parseError(`Can't parse presto type ${sqlType}`);
483
+ }
484
+ return typeDef;
541
485
  }
542
486
  throw this.parseError(`'${typToken.text}' unexpected while looking for a type`);
543
487
  }
544
488
  }
545
- exports.PrestoExplainParser = PrestoExplainParser;
546
489
  //# sourceMappingURL=trino_connection.js.map
@@ -49,29 +49,47 @@ describe('Trino connection', () => {
49
49
  });
50
50
  describe('schema parser', () => {
51
51
  it('parses arrays', () => {
52
- expect(connection.malloyTypeFromTrinoType('test', ARRAY_SCHEMA)).toEqual({
52
+ expect(connection.malloyTypeFromTrinoType(ARRAY_SCHEMA)).toEqual({
53
53
  type: 'array',
54
54
  elementTypeDef: intType,
55
55
  });
56
56
  });
57
57
  it('parses inline', () => {
58
- expect(connection.malloyTypeFromTrinoType('test', INLINE_SCHEMA)).toEqual({
58
+ expect(connection.malloyTypeFromTrinoType(INLINE_SCHEMA)).toEqual({
59
59
  'type': 'record',
60
60
  'fields': recordSchema,
61
61
  });
62
62
  });
63
63
  it('parses nested', () => {
64
- expect(connection.malloyTypeFromTrinoType('test', NESTED_SCHEMA)).toEqual({
64
+ expect(connection.malloyTypeFromTrinoType(NESTED_SCHEMA)).toEqual({
65
65
  'type': 'array',
66
66
  'elementTypeDef': { type: 'record_element' },
67
67
  'fields': recordSchema,
68
68
  });
69
69
  });
70
70
  it('parses a simple type', () => {
71
- expect(connection.malloyTypeFromTrinoType('test', 'varchar(60)')).toEqual(stringType);
71
+ expect(connection.malloyTypeFromTrinoType('varchar(60)')).toEqual(stringType);
72
+ });
73
+ it('parses a decimal integer type', () => {
74
+ expect(connection.malloyTypeFromTrinoType('decimal(10)')).toEqual({
75
+ type: 'number',
76
+ numberType: 'integer',
77
+ });
78
+ });
79
+ it('parses a decimal float type', () => {
80
+ expect(connection.malloyTypeFromTrinoType('decimal(10,10)')).toEqual({
81
+ type: 'number',
82
+ numberType: 'float',
83
+ });
84
+ });
85
+ it('parses row with timestamp(3)', () => {
86
+ expect(connection.malloyTypeFromTrinoType('row(la_time timestamp(3))')).toEqual({
87
+ type: 'record',
88
+ fields: [{ name: 'la_time', type: 'timestamp' }],
89
+ });
72
90
  });
73
91
  it('parses deep nesting', () => {
74
- expect(connection.malloyTypeFromTrinoType('test', DEEP_SCHEMA)).toEqual({
92
+ expect(connection.malloyTypeFromTrinoType(DEEP_SCHEMA)).toEqual({
75
93
  'type': 'array',
76
94
  'elementTypeDef': { type: 'record_element' },
77
95
  'fields': [
@@ -90,15 +108,5 @@ describe('Trino connection', () => {
90
108
  });
91
109
  });
92
110
  });
93
- describe('splitColumns', () => {
94
- it('handles internal rows', () => {
95
- const nested = 'popular_name varchar, airport_count double, by_state array(row(state varchar, airport_count double))';
96
- expect(connection.splitColumns(nested)).toEqual([
97
- 'popular_name varchar',
98
- 'airport_count double',
99
- 'by_state array(row(state varchar, airport_count double))',
100
- ]);
101
- });
102
- });
103
111
  });
104
112
  //# sourceMappingURL=trino_connection.spec.js.map
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@malloydata/db-trino",
3
- "version": "0.0.227",
3
+ "version": "0.0.228-dev250116203644",
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.227",
25
+ "@malloydata/malloy": "^0.0.228-dev250116203644",
26
26
  "@prestodb/presto-js-client": "^1.0.0",
27
27
  "gaxios": "^4.2.0",
28
28
  "trino-client": "^0.2.2"