@malloydata/db-duckdb 0.0.195-dev241003204905 → 0.0.195

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.
@@ -24,6 +24,7 @@
24
24
  Object.defineProperty(exports, "__esModule", { value: true });
25
25
  const duckdb_common_1 = require("./duckdb_common");
26
26
  const duckdb_connection_1 = require("./duckdb_connection");
27
+ const malloy_1 = require("@malloydata/malloy");
27
28
  const test_1 = require("@malloydata/malloy/test");
28
29
  const [describe] = (0, test_1.describeIfDatabaseAvailable)(['duckdb']);
29
30
  /*
@@ -69,17 +70,17 @@ describe('DuckDBConnection', () => {
69
70
  expect(runRawSQL).toHaveBeenCalledTimes(2);
70
71
  });
71
72
  it('caches sql schema', async () => {
72
- await connection.fetchSchemaForSQLBlock(SQL_BLOCK_1, {});
73
+ await connection.fetchSchemaForSQLStruct(SQL_BLOCK_1, {});
73
74
  expect(runRawSQL).toHaveBeenCalledTimes(1);
74
75
  await new Promise(resolve => setTimeout(resolve));
75
- await connection.fetchSchemaForSQLBlock(SQL_BLOCK_1, {});
76
+ await connection.fetchSchemaForSQLStruct(SQL_BLOCK_1, {});
76
77
  expect(runRawSQL).toHaveBeenCalledTimes(1);
77
78
  });
78
79
  it('refreshes sql schema', async () => {
79
- await connection.fetchSchemaForSQLBlock(SQL_BLOCK_2, {});
80
+ await connection.fetchSchemaForSQLStruct(SQL_BLOCK_2, {});
80
81
  expect(runRawSQL).toHaveBeenCalledTimes(1);
81
82
  await new Promise(resolve => setTimeout(resolve));
82
- await connection.fetchSchemaForSQLBlock(SQL_BLOCK_2, {
83
+ await connection.fetchSchemaForSQLStruct(SQL_BLOCK_2, {
83
84
  refreshTimestamp: Date.now() + 10,
84
85
  });
85
86
  expect(runRawSQL).toHaveBeenCalledTimes(2);
@@ -106,24 +107,12 @@ describe('DuckDBConnection', () => {
106
107
  const structDef = makeStructDef();
107
108
  connection.fillStructDefFromTypeMap(structDef, { test: ARRAY_SCHEMA });
108
109
  expect(structDef.fields[0]).toEqual({
109
- 'name': 'test',
110
- 'type': 'struct',
111
- 'dialect': 'duckdb',
112
- 'structRelationship': {
113
- 'fieldName': 'test',
114
- 'isArray': true,
115
- 'type': 'nested',
116
- },
117
- 'structSource': {
118
- 'type': 'nested',
119
- },
120
- 'fields': [
121
- {
122
- 'name': 'value',
123
- 'type': 'number',
124
- 'numberType': 'integer',
125
- },
126
- ],
110
+ name: 'test',
111
+ type: 'array',
112
+ elementTypeDef: intTyp,
113
+ join: 'many',
114
+ dialect: 'duckdb',
115
+ fields: (0, malloy_1.arrayEachFields)({ type: 'number', numberType: 'integer' }),
127
116
  });
128
117
  });
129
118
  it('parses inline', () => {
@@ -131,29 +120,13 @@ describe('DuckDBConnection', () => {
131
120
  connection.fillStructDefFromTypeMap(structDef, { test: INLINE_SCHEMA });
132
121
  expect(structDef.fields[0]).toEqual({
133
122
  'name': 'test',
134
- 'type': 'struct',
123
+ 'type': 'record',
135
124
  'dialect': 'duckdb',
136
- 'structRelationship': {
137
- 'type': 'inline',
138
- },
139
- 'structSource': {
140
- 'type': 'inline',
141
- },
125
+ 'join': 'one',
142
126
  'fields': [
143
- {
144
- 'name': 'a',
145
- 'type': 'number',
146
- 'numberType': 'float',
147
- },
148
- {
149
- 'name': 'b',
150
- 'type': 'number',
151
- 'numberType': 'integer',
152
- },
153
- {
154
- 'name': 'c',
155
- 'type': 'string',
156
- },
127
+ { 'name': 'a', ...dblType },
128
+ { 'name': 'b', ...intTyp },
129
+ { 'name': 'c', ...strTyp },
157
130
  ],
158
131
  });
159
132
  });
@@ -162,14 +135,10 @@ describe('DuckDBConnection', () => {
162
135
  connection.fillStructDefFromTypeMap(structDef, { test: NESTED_SCHEMA });
163
136
  expect(structDef.fields[0]).toEqual({
164
137
  'name': 'test',
165
- 'type': 'struct',
138
+ 'type': 'array',
139
+ 'elementTypeDef': { type: 'record_element' },
166
140
  'dialect': 'duckdb',
167
- 'structRelationship': {
168
- 'fieldName': 'test',
169
- 'isArray': false,
170
- 'type': 'nested',
171
- },
172
- 'structSource': { 'type': 'nested' },
141
+ 'join': 'many',
173
142
  'fields': [
174
143
  { 'name': 'a', 'numberType': 'float', 'type': 'number' },
175
144
  { 'name': 'b', 'numberType': 'integer', 'type': 'number' },
@@ -195,14 +164,11 @@ describe('DuckDBConnection', () => {
195
164
  */
196
165
  const makeStructDef = () => {
197
166
  return {
198
- type: 'struct',
167
+ type: 'table',
199
168
  name: 'test',
200
169
  dialect: 'duckdb',
201
- structSource: { type: 'table', tablePath: 'test' },
202
- structRelationship: {
203
- type: 'basetable',
204
- connectionName: 'duckdb',
205
- },
170
+ tablePath: 'test',
171
+ connection: 'duckdb',
206
172
  fields: [],
207
173
  };
208
174
  };
@@ -212,8 +178,11 @@ const makeStructDef = () => {
212
178
  //
213
179
  // Uses string value for table
214
180
  const SQL_BLOCK_1 = {
215
- type: 'sqlBlock',
181
+ type: 'sql_select',
216
182
  name: 'block1',
183
+ dialect: 'duckdb',
184
+ connection: 'duckdb',
185
+ fields: [],
217
186
  selectStr: `
218
187
  SELECT
219
188
  created_at,
@@ -230,8 +199,11 @@ FROM "inventory_items.parquet"
230
199
  };
231
200
  // Uses read_parquet() for table
232
201
  const SQL_BLOCK_2 = {
233
- type: 'sqlBlock',
202
+ type: 'sql_select',
234
203
  name: 'block2',
204
+ dialect: 'duckdb',
205
+ connection: 'duckdb',
206
+ fields: [],
235
207
  selectStr: `
236
208
  SELECT
237
209
  created_at,
@@ -255,4 +227,7 @@ const ARRAY_SCHEMA = 'integer[]';
255
227
  const INLINE_SCHEMA = 'STRUCT(a double, b integer, c varchar(60))';
256
228
  // STRUCT(....)[] is nested
257
229
  const NESTED_SCHEMA = 'STRUCT(a double, b integer, c varchar(60))[]';
230
+ const intTyp = { type: 'number', numberType: 'integer' };
231
+ const strTyp = { type: 'string' };
232
+ const dblType = { type: 'number', numberType: 'float' };
258
233
  //# sourceMappingURL=duckdb.spec.js.map
@@ -1,4 +1,4 @@
1
- import { FetchSchemaOptions, MalloyQueryData, PersistSQLResults, PooledConnection, QueryDataRow, QueryOptionsReader, QueryRunStats, RunSQLOptions, SQLBlock, StreamingConnection, StructDef, TestableConnection } from '@malloydata/malloy';
1
+ import { MalloyQueryData, PersistSQLResults, PooledConnection, QueryDataRow, QueryOptionsReader, QueryRunStats, RunSQLOptions, StreamingConnection, StructDef, TestableConnection, SQLSourceDef, TableSourceDef } from '@malloydata/malloy';
2
2
  import { BaseConnection } from '@malloydata/malloy/connection';
3
3
  export interface DuckDBQueryOptions {
4
4
  rowLimit: number;
@@ -9,8 +9,6 @@ export declare abstract class DuckDBCommon extends BaseConnection implements Tes
9
9
  protected motherDuckToken: string | undefined;
10
10
  private readonly dialect;
11
11
  static DEFAULT_QUERY_OPTIONS: DuckDBQueryOptions;
12
- private schemaCache;
13
- private sqlSchemaCache;
14
12
  readonly name: string;
15
13
  get dialectName(): string;
16
14
  protected readQueryOptions(): DuckDBQueryOptions;
@@ -28,7 +26,7 @@ export declare abstract class DuckDBCommon extends BaseConnection implements Tes
28
26
  }>;
29
27
  runSQL(sql: string, options?: RunSQLOptions): Promise<MalloyQueryData>;
30
28
  abstract runSQLStream(sql: string, options: RunSQLOptions): AsyncIterableIterator<QueryDataRow>;
31
- private getSQLBlockSchema;
29
+ fetchSelectSchema(sqlRef: SQLSourceDef): Promise<SQLSourceDef | string>;
32
30
  estimateQueryCost(_: string): Promise<QueryRunStats>;
33
31
  /**
34
32
  * Split's a structs columns declaration into individual columns
@@ -46,18 +44,7 @@ export declare abstract class DuckDBCommon extends BaseConnection implements Tes
46
44
  [name: string]: string;
47
45
  }): void;
48
46
  private schemaFromQuery;
49
- fetchSchemaForSQLBlock(sqlRef: SQLBlock, { refreshTimestamp }: FetchSchemaOptions): Promise<{
50
- structDef: StructDef;
51
- error?: undefined;
52
- } | {
53
- error: string;
54
- structDef?: undefined;
55
- }>;
56
- fetchSchemaForTables(tables: Record<string, string>, { refreshTimestamp }: FetchSchemaOptions): Promise<{
57
- schemas: Record<string, StructDef>;
58
- errors: Record<string, string>;
59
- }>;
60
- private getTableSchema;
47
+ fetchTableSchema(tableKey: string, tablePath: string): Promise<TableSourceDef>;
61
48
  canStream(): this is StreamingConnection;
62
49
  test(): Promise<void>;
63
50
  abstract createHash(sqlCommand: string): Promise<string>;
@@ -34,7 +34,7 @@ const unquoteName = (name) => {
34
34
  };
35
35
  class DuckDBCommon extends connection_1.BaseConnection {
36
36
  get dialectName() {
37
- return 'duckdb';
37
+ return this.dialect.name;
38
38
  }
39
39
  readQueryOptions() {
40
40
  const options = DuckDBCommon.DEFAULT_QUERY_OPTIONS;
@@ -55,8 +55,6 @@ class DuckDBCommon extends connection_1.BaseConnection {
55
55
  this.queryOptions = queryOptions;
56
56
  this.isMotherDuck = false;
57
57
  this.dialect = new malloy_1.DuckDBDialect();
58
- this.schemaCache = new Map();
59
- this.sqlSchemaCache = new Map();
60
58
  this.name = 'duckdb_common';
61
59
  }
62
60
  isPool() {
@@ -85,24 +83,10 @@ class DuckDBCommon extends connection_1.BaseConnection {
85
83
  }
86
84
  return { rows: result, totalRows: result.length };
87
85
  }
88
- async getSQLBlockSchema(sqlRef) {
89
- const structDef = {
90
- type: 'struct',
91
- dialect: 'duckdb',
92
- name: sqlRef.name,
93
- structSource: {
94
- type: 'sql',
95
- method: 'subquery',
96
- sqlBlock: sqlRef,
97
- },
98
- structRelationship: {
99
- type: 'basetable',
100
- connectionName: this.name,
101
- },
102
- fields: [],
103
- };
104
- await this.schemaFromQuery(`DESCRIBE SELECT * FROM (${sqlRef.selectStr})`, structDef);
105
- return structDef;
86
+ async fetchSelectSchema(sqlRef) {
87
+ const sqlDef = { ...sqlRef };
88
+ await this.schemaFromQuery(`DESCRIBE SELECT * FROM (${sqlRef.selectStr})`, sqlDef);
89
+ return sqlDef;
106
90
  }
107
91
  async estimateQueryCost(_) {
108
92
  return {};
@@ -168,8 +152,6 @@ class DuckDBCommon extends connection_1.BaseConnection {
168
152
  let duckDBType = typeMap[fieldName];
169
153
  // Remove quotes from field name
170
154
  const name = unquoteName(fieldName);
171
- // Remove DECIMAL(x,y) precision to simplify lookup
172
- duckDBType = duckDBType.replace(/^DECIMAL\(\d+,\d+\)/g, 'DECIMAL');
173
155
  let malloyType = this.dialect.sqlTypeToMalloyType(duckDBType);
174
156
  const arrayMatch = duckDBType.match(/(?<duckDBType>.*)\[\]$/);
175
157
  if (arrayMatch && arrayMatch.groups) {
@@ -178,22 +160,23 @@ class DuckDBCommon extends connection_1.BaseConnection {
178
160
  const structMatch = duckDBType.match(/^STRUCT\((?<fields>.*)\)$/);
179
161
  if (structMatch && structMatch.groups) {
180
162
  const newTypeMap = this.stringToTypeMap(structMatch.groups['fields']);
181
- const innerStructDef = {
182
- type: 'struct',
183
- name,
184
- dialect: this.dialectName,
185
- structSource: { type: arrayMatch ? 'nested' : 'inline' },
186
- structRelationship: arrayMatch
187
- ? {
188
- type: 'nested',
189
- fieldName: name,
190
- isArray: false,
191
- }
192
- : {
193
- type: 'inline',
194
- },
195
- fields: [],
196
- };
163
+ let innerStructDef;
164
+ const structhead = { name, dialect: this.dialectName, fields: [] };
165
+ if (arrayMatch) {
166
+ innerStructDef = {
167
+ type: 'array',
168
+ elementTypeDef: { type: 'record_element' },
169
+ join: 'many',
170
+ ...structhead,
171
+ };
172
+ }
173
+ else {
174
+ innerStructDef = {
175
+ type: 'record',
176
+ join: 'one',
177
+ ...structhead,
178
+ };
179
+ }
197
180
  this.fillStructDefFromTypeMap(innerStructDef, newTypeMap);
198
181
  structDef.fields.push(innerStructDef);
199
182
  }
@@ -201,30 +184,17 @@ class DuckDBCommon extends connection_1.BaseConnection {
201
184
  if (arrayMatch) {
202
185
  malloyType = this.dialect.sqlTypeToMalloyType(duckDBType);
203
186
  const innerStructDef = {
204
- type: 'struct',
187
+ type: 'array',
188
+ elementTypeDef: malloyType,
205
189
  name,
206
190
  dialect: this.dialectName,
207
- structSource: { type: 'nested' },
208
- structRelationship: {
209
- type: 'nested',
210
- fieldName: name,
211
- isArray: true,
212
- },
213
- fields: [{ ...malloyType, name: 'value' }],
191
+ join: 'many',
192
+ fields: (0, malloy_1.arrayEachFields)(malloyType),
214
193
  };
215
194
  structDef.fields.push(innerStructDef);
216
195
  }
217
196
  else {
218
- if (malloyType) {
219
- structDef.fields.push({ ...malloyType, name });
220
- }
221
- else {
222
- structDef.fields.push({
223
- type: 'sql native',
224
- rawType: duckDBType.toLowerCase(),
225
- name,
226
- });
227
- }
197
+ structDef.fields.push({ ...malloyType, name });
228
198
  }
229
199
  }
230
200
  }
@@ -237,64 +207,13 @@ class DuckDBCommon extends connection_1.BaseConnection {
237
207
  }
238
208
  this.fillStructDefFromTypeMap(structDef, typeMap);
239
209
  }
240
- async fetchSchemaForSQLBlock(sqlRef, { refreshTimestamp }) {
241
- const key = sqlRef.name;
242
- let inCache = this.sqlSchemaCache.get(key);
243
- if (!inCache ||
244
- (refreshTimestamp && refreshTimestamp > inCache.timestamp)) {
245
- const timestamp = refreshTimestamp !== null && refreshTimestamp !== void 0 ? refreshTimestamp : Date.now();
246
- try {
247
- inCache = {
248
- structDef: await this.getSQLBlockSchema(sqlRef),
249
- timestamp,
250
- };
251
- }
252
- catch (error) {
253
- inCache = { error: error.message, timestamp };
254
- }
255
- this.sqlSchemaCache.set(key, inCache);
256
- }
257
- return inCache;
258
- }
259
- async fetchSchemaForTables(tables, { refreshTimestamp }) {
260
- const schemas = {};
261
- const errors = {};
262
- for (const tableKey in tables) {
263
- let inCache = this.schemaCache.get(tableKey);
264
- if (!inCache ||
265
- (refreshTimestamp && refreshTimestamp > inCache.timestamp)) {
266
- const timestamp = refreshTimestamp !== null && refreshTimestamp !== void 0 ? refreshTimestamp : Date.now();
267
- const tablePath = tables[tableKey];
268
- try {
269
- inCache = {
270
- schema: await this.getTableSchema(tableKey, tablePath),
271
- timestamp,
272
- };
273
- this.schemaCache.set(tableKey, inCache);
274
- }
275
- catch (error) {
276
- inCache = { error: error.message, timestamp };
277
- }
278
- }
279
- if (inCache.schema !== undefined) {
280
- schemas[tableKey] = inCache.schema;
281
- }
282
- else {
283
- errors[tableKey] = inCache.error || 'Unknown schema fetch error';
284
- }
285
- }
286
- return { schemas, errors };
287
- }
288
- async getTableSchema(tableKey, tablePath) {
210
+ async fetchTableSchema(tableKey, tablePath) {
289
211
  const structDef = {
290
- type: 'struct',
212
+ type: 'table',
291
213
  name: tableKey,
292
- dialect: 'duckdb',
293
- structSource: { type: 'table', tablePath },
294
- structRelationship: {
295
- type: 'basetable',
296
- connectionName: this.name,
297
- },
214
+ dialect: this.dialectName,
215
+ tablePath,
216
+ connection: this.name,
298
217
  fields: [],
299
218
  };
300
219
  const quotedTablePath = tablePath.match(/[:*/]/)
@@ -1,5 +1,5 @@
1
1
  import * as duckdb from '@duckdb/duckdb-wasm';
2
- import { FetchSchemaOptions, QueryDataRow, QueryOptionsReader, RunSQLOptions, StructDef, SQLBlock, ConnectionConfig } from '@malloydata/malloy';
2
+ import { FetchSchemaOptions, QueryDataRow, QueryOptionsReader, RunSQLOptions, SQLSourceDef, ConnectionConfig, TableSourceDef } from '@malloydata/malloy';
3
3
  import { StructRow } from 'apache-arrow';
4
4
  import { DuckDBCommon } from './duckdb_common';
5
5
  /**
@@ -53,15 +53,15 @@ export declare abstract class DuckDBWASMConnection extends DuckDBCommon {
53
53
  }>;
54
54
  runSQLStream(sql: string, { rowLimit, abortSignal }?: RunSQLOptions): AsyncIterableIterator<QueryDataRow>;
55
55
  private findTables;
56
- fetchSchemaForSQLBlock(sqlRef: SQLBlock, options: FetchSchemaOptions): Promise<{
57
- structDef: StructDef;
56
+ fetchSchemaForSQLStruct(sqlRef: SQLSourceDef, options: FetchSchemaOptions): Promise<{
57
+ structDef: SQLSourceDef;
58
58
  error?: undefined;
59
59
  } | {
60
60
  error: string;
61
61
  structDef?: undefined;
62
62
  }>;
63
63
  fetchSchemaForTables(missing: Record<string, string>, options: FetchSchemaOptions): Promise<{
64
- schemas: Record<string, StructDef>;
64
+ schemas: Record<string, TableSourceDef>;
65
65
  errors: Record<string, string>;
66
66
  }>;
67
67
  close(): Promise<void>;
@@ -336,7 +336,7 @@ class DuckDBWASMConnection extends duckdb_common_1.DuckDBCommon {
336
336
  await this.remoteFileStatus[tablePath];
337
337
  }
338
338
  }
339
- async fetchSchemaForSQLBlock(sqlRef, options) {
339
+ async fetchSchemaForSQLStruct(sqlRef, options) {
340
340
  const tables = [];
341
341
  for (const match of sqlRef.selectStr.matchAll(TABLE_MATCH)) {
342
342
  tables.push(match[2] || match[3]);
@@ -345,7 +345,7 @@ class DuckDBWASMConnection extends duckdb_common_1.DuckDBCommon {
345
345
  tables.push(match[2] || match[3]);
346
346
  }
347
347
  await this.findTables(tables, options);
348
- return super.fetchSchemaForSQLBlock(sqlRef, options);
348
+ return super.fetchSchemaForSQLStruct(sqlRef, options);
349
349
  }
350
350
  async fetchSchemaForTables(missing, options) {
351
351
  const tables = Object.values(missing);
@@ -39,10 +39,8 @@ describe('DuckDBWasmConnection', () => {
39
39
  });
40
40
  beforeEach(() => {
41
41
  jest
42
- .spyOn(duckdb_common_1.DuckDBCommon.prototype, 'fetchSchemaForSQLBlock')
43
- .mockResolvedValue({
44
- error: 'mocked',
45
- });
42
+ .spyOn(duckdb_common_1.DuckDBCommon.prototype, 'fetchSelectSchema')
43
+ .mockResolvedValue('mocked');
46
44
  // eslint-disable-next-line @typescript-eslint/no-explicit-any
47
45
  findTables = jest.spyOn(connection, 'findTables');
48
46
  });
@@ -50,7 +48,7 @@ describe('DuckDBWasmConnection', () => {
50
48
  jest.resetAllMocks();
51
49
  });
52
50
  it('finds simple tables in SQL', async () => {
53
- await connection.fetchSchemaForSQLBlock({
51
+ await connection.fetchSchemaForSQLStruct({
54
52
  selectStr: `
55
53
  SELECT
56
54
  created_at,
@@ -68,7 +66,7 @@ FROM "inventory_items.parquet"
68
66
  expect(findTables).toHaveBeenCalledWith(['order_items.parquet', 'inventory_items.parquet'], {});
69
67
  });
70
68
  it('finds table functions in SQL', async () => {
71
- await connection.fetchSchemaForSQLBlock({
69
+ await connection.fetchSchemaForSQLStruct({
72
70
  selectStr: `
73
71
  SELECT
74
72
  created_at,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@malloydata/db-duckdb",
3
- "version": "0.0.195-dev241003204905",
3
+ "version": "0.0.195",
4
4
  "license": "MIT",
5
5
  "main": "./dist/index.js",
6
6
  "types": "./dist/index.d.ts",
@@ -41,7 +41,7 @@
41
41
  },
42
42
  "dependencies": {
43
43
  "@duckdb/duckdb-wasm": "1.28.1-dev242.0",
44
- "@malloydata/malloy": "^0.0.195-dev241003204905",
44
+ "@malloydata/malloy": "^0.0.195",
45
45
  "@motherduck/wasm-client": "^0.6.3",
46
46
  "apache-arrow": "^16.0.0",
47
47
  "duckdb": "1.0.0",