@malloydata/malloy 0.0.222-dev241212001744 → 0.0.222-dev241212021944

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.
@@ -55,6 +55,7 @@ export declare class DuckDBDialect extends PostgresBase {
55
55
  [name: string]: DialectFunctionOverloadDef[];
56
56
  };
57
57
  malloyTypeToSQLType(malloyType: AtomicTypeDef): string;
58
+ parseDuckDBType(sqlType: string): AtomicTypeDef;
58
59
  sqlTypeToMalloyType(sqlType: string): LeafAtomicTypeDef;
59
60
  castToString(expression: string): string;
60
61
  concat(...values: string[]): string;
@@ -30,6 +30,7 @@ const dialect_1 = require("../dialect");
30
30
  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
+ const tiny_parser_1 = require("../tiny_parser");
33
34
  // need to refactor runSQL to take a SQLBlock instead of just a sql string.
34
35
  const hackSplitComment = '-- hack: split on this';
35
36
  const duckDBToMalloyTypes = {
@@ -287,6 +288,20 @@ class DuckDBDialect extends pg_impl_1.PostgresBase {
287
288
  }
288
289
  return malloyType.type;
289
290
  }
291
+ parseDuckDBType(sqlType) {
292
+ const parser = new DuckDBTypeParser(sqlType);
293
+ try {
294
+ return parser.typeDef();
295
+ }
296
+ catch (e) {
297
+ if (e instanceof tiny_parser_1.TinyParseError) {
298
+ return { type: 'sql native', rawType: sqlType };
299
+ }
300
+ else {
301
+ throw e;
302
+ }
303
+ }
304
+ }
290
305
  sqlTypeToMalloyType(sqlType) {
291
306
  var _a, _b, _c;
292
307
  // Remove decimal precision
@@ -350,4 +365,113 @@ class DuckDBDialect extends pg_impl_1.PostgresBase {
350
365
  }
351
366
  }
352
367
  exports.DuckDBDialect = DuckDBDialect;
368
+ class DuckDBTypeParser extends tiny_parser_1.TinyParser {
369
+ constructor(input) {
370
+ super(input, {
371
+ /* whitespace */ space: /^\s+/,
372
+ /* single quoted string */ qsingle: /^'([^']|'')*'/,
373
+ /* double quoted string */ qdouble: /^"([^"]|"")*"/,
374
+ /* (n) size */ size: /^\(\d+\)/,
375
+ /* (n1,n2) precision */ precision: /^\(\d+,\d+\)/,
376
+ /* T[] -> array of T */ arrayOf: /^\[]/,
377
+ /* other punctuation */ char: /^[,:[\]()-]/,
378
+ /* unquoted word */ id: /^\w+/,
379
+ });
380
+ }
381
+ unquoteName(token) {
382
+ if (token.type === 'qsingle') {
383
+ return token.text.replace("''", '');
384
+ }
385
+ else if (token.type === 'qdouble') {
386
+ return token.text.replace('""', '');
387
+ }
388
+ return token.text;
389
+ }
390
+ sqlID(token) {
391
+ return token.text.toUpperCase();
392
+ }
393
+ typeDef() {
394
+ const unknownStart = this.parseCursor;
395
+ const wantID = this.next('id');
396
+ const id = this.sqlID(wantID);
397
+ let baseType;
398
+ if (id === 'VARCHAR') {
399
+ if (this.peek().type === 'size') {
400
+ this.next();
401
+ }
402
+ }
403
+ if ((id === 'DECIMAL' || id === 'NUMERIC') &&
404
+ this.peek().type === 'precision') {
405
+ this.next();
406
+ baseType = { type: 'number', numberType: 'float' };
407
+ }
408
+ else if (id === 'TIMESTAMP') {
409
+ if (this.peek().text === 'WITH') {
410
+ this.nextText('WITH', 'TIME', 'ZONE');
411
+ }
412
+ baseType = { type: 'timestamp' };
413
+ }
414
+ else if (duckDBToMalloyTypes[id]) {
415
+ baseType = duckDBToMalloyTypes[id];
416
+ }
417
+ else if (id === 'STRUCT') {
418
+ this.next('(');
419
+ baseType = { type: 'record', fields: [] };
420
+ for (;;) {
421
+ const fieldName = this.next();
422
+ if (fieldName.type === 'qsingle' ||
423
+ fieldName.type === 'qdouble' ||
424
+ fieldName.type === 'id') {
425
+ const fieldType = this.typeDef();
426
+ baseType.fields.push((0, malloy_types_1.mkFieldDef)(fieldType, this.unquoteName(fieldName), 'duckdb'));
427
+ }
428
+ else {
429
+ if (fieldName.type !== ')') {
430
+ throw this.parseError('Expected identifier or ) to end STRUCT');
431
+ }
432
+ break;
433
+ }
434
+ if (this.peek().type === ',') {
435
+ this.next();
436
+ }
437
+ }
438
+ }
439
+ else {
440
+ if (wantID.type === 'id') {
441
+ for (;;) {
442
+ const next = this.peek();
443
+ // Might be WEIRDTYP(a,b)[] ... stop at the []
444
+ if (next.type === 'arrayOf' || next.type === 'eof') {
445
+ break;
446
+ }
447
+ this.next();
448
+ }
449
+ baseType = {
450
+ type: 'sql native',
451
+ rawType: this.input.slice(unknownStart, this.parseCursor - unknownStart + 1),
452
+ };
453
+ }
454
+ else {
455
+ throw this.parseError('Could not understand type');
456
+ }
457
+ }
458
+ while (this.peek().type === 'arrayOf') {
459
+ this.next();
460
+ if (baseType.type === 'record') {
461
+ baseType = {
462
+ type: 'array',
463
+ elementTypeDef: { type: 'record_element' },
464
+ fields: baseType.fields,
465
+ };
466
+ }
467
+ else {
468
+ baseType = {
469
+ type: 'array',
470
+ elementTypeDef: baseType,
471
+ };
472
+ }
473
+ }
474
+ return baseType;
475
+ }
476
+ }
353
477
  //# sourceMappingURL=duckdb.js.map
@@ -9,10 +9,12 @@ export interface TinyToken {
9
9
  *
10
10
  * NOTE: All parse errors are exceptions.
11
11
  */
12
+ export declare class TinyParseError extends Error {
13
+ }
12
14
  export declare class TinyParser {
13
15
  readonly input: string;
14
16
  private tokens;
15
- private parseCursor;
17
+ protected parseCursor: number;
16
18
  private lookAhead?;
17
19
  private tokenMap;
18
20
  /**
@@ -26,7 +28,7 @@ export declare class TinyParser {
26
28
  * last characters stripped
27
29
  */
28
30
  constructor(input: string, tokenMap?: Record<string, RegExp>);
29
- parseError(str: string): Error;
31
+ parseError(str: string): TinyParseError;
30
32
  peek(): TinyToken;
31
33
  private getNext;
32
34
  /**
@@ -36,6 +38,8 @@ export declare class TinyParser {
36
38
  * @returns The last token read
37
39
  */
38
40
  next(...types: string[]): TinyToken;
41
+ nextText(...texts: string[]): TinyToken;
39
42
  skipTo(type: string): void;
43
+ dump(): TinyToken[];
40
44
  private tokenize;
41
45
  }
@@ -6,7 +6,7 @@
6
6
  * LICENSE file in the root directory of this source tree.
7
7
  */
8
8
  Object.defineProperty(exports, "__esModule", { value: true });
9
- exports.TinyParser = void 0;
9
+ exports.TinyParser = exports.TinyParseError = void 0;
10
10
  /**
11
11
  * Simple framework for writing schema parsers. The parsers using this felt
12
12
  * better than the more ad-hoc code they replaced, and are smaller than
@@ -14,6 +14,9 @@ exports.TinyParser = void 0;
14
14
  *
15
15
  * NOTE: All parse errors are exceptions.
16
16
  */
17
+ class TinyParseError extends Error {
18
+ }
19
+ exports.TinyParseError = TinyParseError;
17
20
  class TinyParser {
18
21
  /**
19
22
  * The token map is tested in order. Return TinyToken
@@ -28,19 +31,19 @@ class TinyParser {
28
31
  constructor(input, tokenMap) {
29
32
  this.input = input;
30
33
  this.parseCursor = 0;
31
- this.tokens = this.tokenize(input);
32
34
  this.tokenMap = tokenMap !== null && tokenMap !== void 0 ? tokenMap : {
33
35
  space: /^\s+/,
34
36
  char: /^[,:[\]()-]/,
35
37
  id: /^\w+/,
36
38
  qstr: /^"\w+"/,
37
39
  };
40
+ this.tokens = this.tokenize(input);
38
41
  }
39
42
  parseError(str) {
40
43
  const errText = `INTERNAL ERROR parsing schema: ${str}\n` +
41
44
  `${this.input}\n` +
42
45
  `${' '.repeat(this.parseCursor)}^`;
43
- return new Error(errText);
46
+ return new TinyParseError(errText);
44
47
  }
45
48
  peek() {
46
49
  if (this.lookAhead) {
@@ -80,7 +83,24 @@ class TinyParser {
80
83
  }
81
84
  if (next)
82
85
  return next;
83
- throw this.parseError(`Expected ${expected}`);
86
+ throw this.parseError(`Expected token type '${expected}'`);
87
+ }
88
+ nextText(...texts) {
89
+ if (texts.length === 0)
90
+ return this.getNext();
91
+ let next = undefined;
92
+ let expected = texts[0];
93
+ for (const txt of texts) {
94
+ next = this.getNext();
95
+ expected = txt;
96
+ if (next.text !== txt) {
97
+ next = undefined;
98
+ break;
99
+ }
100
+ }
101
+ if (next)
102
+ return next;
103
+ throw this.parseError(`Expected '${expected}'`);
84
104
  }
85
105
  skipTo(type) {
86
106
  for (;;) {
@@ -93,6 +113,12 @@ class TinyParser {
93
113
  }
94
114
  }
95
115
  }
116
+ dump() {
117
+ const p = this.parseCursor;
118
+ const parts = [...this.tokenize(this.input)];
119
+ this.parseCursor = p;
120
+ return parts;
121
+ }
96
122
  *tokenize(src) {
97
123
  const tokenList = this.tokenMap;
98
124
  while (this.parseCursor < src.length) {
@@ -112,6 +138,7 @@ class TinyParser {
112
138
  type: tokenType === 'char' ? tokenText : tokenType,
113
139
  text: tokenText,
114
140
  };
141
+ break;
115
142
  }
116
143
  }
117
144
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@malloydata/malloy",
3
- "version": "0.0.222-dev241212001744",
3
+ "version": "0.0.222-dev241212021944",
4
4
  "license": "MIT",
5
5
  "exports": {
6
6
  ".": "./dist/index.js",