@lightdash/warehouses 2.210.0 → 2.211.1

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.
@@ -6,6 +6,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
6
6
  /* eslint-disable prefer-arrow-callback, func-names */
7
7
  const StatusError_1 = __importDefault(require("@databricks/sql/dist/errors/StatusError"));
8
8
  const TCLIService_types_1 = require("@databricks/sql/thrift/TCLIService_types");
9
+ const common_1 = require("@lightdash/common");
9
10
  const DatabricksWarehouseClient_1 = require("./DatabricksWarehouseClient");
10
11
  const DatabricksWarehouseClient_mock_1 = require("./DatabricksWarehouseClient.mock");
11
12
  const WarehouseClient_mock_1 = require("./WarehouseClient.mock");
@@ -163,12 +164,273 @@ describe('DatabricksWarehouseClient', () => {
163
164
  const columns = (name, type) => [
164
165
  { COLUMN_NAME: name, TYPE_NAME: type },
165
166
  ];
167
+ const describedTable = (sql) => /`([^`]+)` AS JSON$/.exec(sql)?.[1] ?? '';
168
+ const jsonDescription = (cols) => [
169
+ {
170
+ json_metadata: JSON.stringify({
171
+ columns: cols.map((col) => ({
172
+ name: col.COLUMN_NAME,
173
+ type: { name: col.TYPE_NAME.toLowerCase() },
174
+ })),
175
+ }),
176
+ },
177
+ ];
166
178
  const columnsSession = (resolve, lostTables) => createSession({
167
- getColumns: vi.fn(async ({ tableName }) => lostTables.has(tableName)
168
- ? Promise.reject(sessionLostError())
169
- : createOperation({
170
- fetchAll: vi.fn(async () => resolve(tableName)),
171
- })),
179
+ executeStatement: vi.fn(async (sql) => {
180
+ const table = describedTable(sql);
181
+ return lostTables.has(table)
182
+ ? Promise.reject(sessionLostError())
183
+ : createOperation({
184
+ fetchAll: vi.fn(async () => jsonDescription(resolve(table))),
185
+ });
186
+ }),
187
+ });
188
+ // Captured from DESCRIBE TABLE EXTENDED ... AS JSON on a serverless warehouse.
189
+ const describedTransactions = {
190
+ columns: [
191
+ {
192
+ name: 'order_id',
193
+ type: { name: 'string', collation: 'UTF8_BINARY' },
194
+ nullable: true,
195
+ },
196
+ { name: 'amount', type: { name: 'double' }, nullable: true },
197
+ {
198
+ name: 'created_at',
199
+ type: { name: 'timestamp_ltz' },
200
+ nullable: true,
201
+ },
202
+ {
203
+ name: 'updated_at',
204
+ type: { name: 'timestamp_ntz' },
205
+ nullable: true,
206
+ },
207
+ {
208
+ name: 'customer',
209
+ type: {
210
+ name: 'struct',
211
+ fields: [
212
+ {
213
+ name: 'id',
214
+ type: { name: 'string' },
215
+ nullable: true,
216
+ },
217
+ {
218
+ name: 'location',
219
+ type: { name: 'string' },
220
+ nullable: true,
221
+ },
222
+ ],
223
+ },
224
+ nullable: true,
225
+ },
226
+ {
227
+ name: 'product',
228
+ type: {
229
+ name: 'array',
230
+ element_type: {
231
+ name: 'struct',
232
+ fields: [
233
+ {
234
+ name: 'sku',
235
+ type: { name: 'string' },
236
+ nullable: true,
237
+ },
238
+ {
239
+ name: 'price',
240
+ type: { name: 'double' },
241
+ nullable: true,
242
+ },
243
+ {
244
+ name: 'attributes',
245
+ type: {
246
+ name: 'struct',
247
+ fields: [
248
+ {
249
+ name: 'colour',
250
+ type: { name: 'string' },
251
+ nullable: true,
252
+ },
253
+ ],
254
+ },
255
+ nullable: true,
256
+ },
257
+ {
258
+ name: 'variants',
259
+ type: {
260
+ name: 'array',
261
+ element_type: {
262
+ name: 'struct',
263
+ fields: [
264
+ {
265
+ name: 'size',
266
+ type: { name: 'string' },
267
+ nullable: true,
268
+ },
269
+ {
270
+ name: 'stock',
271
+ type: { name: 'int' },
272
+ nullable: true,
273
+ },
274
+ ],
275
+ },
276
+ element_nullable: true,
277
+ },
278
+ nullable: true,
279
+ },
280
+ ],
281
+ },
282
+ element_nullable: true,
283
+ },
284
+ nullable: true,
285
+ },
286
+ {
287
+ name: 'tags',
288
+ type: {
289
+ name: 'array',
290
+ element_type: { name: 'string' },
291
+ element_nullable: true,
292
+ },
293
+ nullable: true,
294
+ },
295
+ {
296
+ name: 'scores',
297
+ type: { name: 'array', element_type: { name: 'int' } },
298
+ nullable: true,
299
+ },
300
+ {
301
+ name: 'matrix',
302
+ type: {
303
+ name: 'array',
304
+ element_type: {
305
+ name: 'array',
306
+ element_type: { name: 'int' },
307
+ },
308
+ },
309
+ nullable: true,
310
+ },
311
+ {
312
+ name: 'labels',
313
+ type: {
314
+ name: 'map',
315
+ key_type: { name: 'string' },
316
+ value_type: { name: 'string' },
317
+ value_nullable: true,
318
+ },
319
+ nullable: true,
320
+ },
321
+ ],
322
+ };
323
+ const describeSession = (describe, flatColumns = []) => createSession({
324
+ executeStatement: vi.fn(async (sql) => createOperation({ fetchAll: vi.fn(() => describe(sql)) })),
325
+ getColumns: vi.fn(async () => createOperation({
326
+ fetchAll: vi.fn(async () => flatColumns),
327
+ })),
328
+ });
329
+ it('reads dotted paths and nested shapes from the JSON table description', async () => {
330
+ const session = describeSession(async () => [
331
+ { json_metadata: JSON.stringify(describedTransactions) },
332
+ ]);
333
+ mocks.openSession.mockResolvedValue(session);
334
+ const warehouse = new DatabricksWarehouseClient_1.DatabricksWarehouseClient(DatabricksWarehouseClient_mock_1.credentials);
335
+ const catalog = await warehouse.getCatalog([
336
+ tableRequest('transactions'),
337
+ ]);
338
+ const table = catalog[DatabricksWarehouseClient_mock_1.credentials.catalog ?? 'DEFAULT'].schema.transactions;
339
+ expect(session.executeStatement).toHaveBeenCalledWith('DESCRIBE TABLE EXTENDED `database`.`schema`.`transactions` AS JSON');
340
+ expect(session.getColumns).not.toHaveBeenCalled();
341
+ expect(table).toEqual({
342
+ order_id: common_1.DimensionType.STRING,
343
+ amount: common_1.DimensionType.NUMBER,
344
+ created_at: common_1.DimensionType.TIMESTAMP,
345
+ updated_at: common_1.DimensionType.TIMESTAMP,
346
+ customer: common_1.DimensionType.STRING,
347
+ 'customer.id': common_1.DimensionType.STRING,
348
+ 'customer.location': common_1.DimensionType.STRING,
349
+ product: common_1.DimensionType.STRING,
350
+ 'product.sku': common_1.DimensionType.STRING,
351
+ 'product.price': common_1.DimensionType.NUMBER,
352
+ 'product.attributes': common_1.DimensionType.STRING,
353
+ 'product.attributes.colour': common_1.DimensionType.STRING,
354
+ 'product.variants': common_1.DimensionType.STRING,
355
+ 'product.variants.size': common_1.DimensionType.STRING,
356
+ 'product.variants.stock': common_1.DimensionType.NUMBER,
357
+ tags: common_1.DimensionType.STRING,
358
+ scores: common_1.DimensionType.NUMBER,
359
+ matrix: common_1.DimensionType.STRING,
360
+ labels: common_1.DimensionType.STRING,
361
+ });
362
+ const shape = (path) => (0, common_1.getCatalogNestedColumnShape)(catalog, DatabricksWarehouseClient_mock_1.credentials.catalog ?? 'DEFAULT', 'schema', 'transactions', path);
363
+ expect(shape('customer')).toEqual({
364
+ repeated: false,
365
+ record: true,
366
+ });
367
+ expect(shape('product')).toEqual({ repeated: true, record: true });
368
+ expect(shape('product.attributes')).toEqual({
369
+ repeated: false,
370
+ record: true,
371
+ });
372
+ expect(shape('product.variants')).toEqual({
373
+ repeated: true,
374
+ record: true,
375
+ });
376
+ expect(shape('tags')).toEqual({ repeated: true, record: false });
377
+ expect(shape('scores')).toEqual({ repeated: true, record: false });
378
+ expect(shape('matrix')).toBeUndefined();
379
+ expect(shape('labels')).toBeUndefined();
380
+ expect(shape('product.sku')).toBeUndefined();
381
+ const domain = (path) => (0, common_1.getCatalogTimestampDomain)(catalog, DatabricksWarehouseClient_mock_1.credentials.catalog ?? 'DEFAULT', 'schema', 'transactions', path);
382
+ expect(domain('created_at')).toEqual('aware');
383
+ expect(domain('updated_at')).toEqual('naive');
384
+ expect((0, common_1.getCatalogNestedColumnsUnavailableReason)(catalog, DatabricksWarehouseClient_mock_1.credentials.catalog ?? 'DEFAULT', 'schema', 'transactions')).toBeUndefined();
385
+ });
386
+ it('keeps the flat column list and records why when the JSON description is refused', async () => {
387
+ const session = describeSession(() => Promise.reject(statusError("[PARSE_SYNTAX_ERROR] Syntax error at or near 'JSON'. SQLSTATE: 42601")), [
388
+ { COLUMN_NAME: 'order_id', TYPE_NAME: 'STRING' },
389
+ {
390
+ COLUMN_NAME: 'product',
391
+ TYPE_NAME: 'ARRAY<STRUCT<sku: STRING>>',
392
+ },
393
+ ]);
394
+ mocks.openSession.mockResolvedValue(session);
395
+ const warehouse = new DatabricksWarehouseClient_1.DatabricksWarehouseClient(DatabricksWarehouseClient_mock_1.credentials);
396
+ const catalog = await warehouse.getCatalog([
397
+ tableRequest('transactions'),
398
+ ]);
399
+ const database = DatabricksWarehouseClient_mock_1.credentials.catalog ?? 'DEFAULT';
400
+ expect(catalog[database].schema.transactions).toEqual({
401
+ order_id: common_1.DimensionType.STRING,
402
+ product: common_1.DimensionType.STRING,
403
+ });
404
+ expect((0, common_1.getCatalogNestedColumnShape)(catalog, database, 'schema', 'transactions', 'product')).toBeUndefined();
405
+ expect((0, common_1.getCatalogNestedColumnsUnavailableReason)(catalog, database, 'schema', 'transactions')).toEqual("Databricks did not describe transactions as JSON ([PARSE_SYNTAX_ERROR] Syntax error at or near 'JSON'. SQLSTATE: 42601); nested columns need a SQL warehouse or Databricks Runtime 16.2 or newer.");
406
+ });
407
+ it('reports no columns, and no reason, for a table that does not exist', async () => {
408
+ const session = describeSession(() => Promise.reject(statusError('[TABLE_OR_VIEW_NOT_FOUND] The table or view `database`.`schema`.`missing` cannot be found.')));
409
+ mocks.openSession.mockResolvedValue(session);
410
+ const warehouse = new DatabricksWarehouseClient_1.DatabricksWarehouseClient(DatabricksWarehouseClient_mock_1.credentials);
411
+ const catalog = await warehouse.getCatalog([
412
+ tableRequest('missing'),
413
+ ]);
414
+ const database = DatabricksWarehouseClient_mock_1.credentials.catalog ?? 'DEFAULT';
415
+ expect(catalog[database].schema.missing).toEqual({});
416
+ expect(session.getColumns).not.toHaveBeenCalled();
417
+ expect((0, common_1.getCatalogNestedColumnsUnavailableReason)(catalog, database, 'schema', 'missing')).toBeUndefined();
418
+ });
419
+ it('fails the fetch on errors that are not the missing JSON form', async () => {
420
+ const session = describeSession(() => Promise.reject(statusError('[INSUFFICIENT_PERMISSIONS] User does not have USE SCHEMA on Schema `schema`.')));
421
+ mocks.openSession.mockResolvedValue(session);
422
+ const warehouse = new DatabricksWarehouseClient_1.DatabricksWarehouseClient(DatabricksWarehouseClient_mock_1.credentials);
423
+ await expect(warehouse.getCatalog([tableRequest('transactions')])).rejects.toThrow('INSUFFICIENT_PERMISSIONS');
424
+ expect(session.getColumns).not.toHaveBeenCalled();
425
+ });
426
+ it('fails the fetch when the JSON description cannot be read', async () => {
427
+ const session = describeSession(async () => [
428
+ { json_metadata: 'not json' },
429
+ ]);
430
+ mocks.openSession.mockResolvedValue(session);
431
+ const warehouse = new DatabricksWarehouseClient_1.DatabricksWarehouseClient(DatabricksWarehouseClient_mock_1.credentials);
432
+ await expect(warehouse.getCatalog([tableRequest('transactions')])).rejects.toThrow('Could not read the description of');
433
+ expect(session.getColumns).not.toHaveBeenCalled();
172
434
  });
173
435
  it('resumes the remaining tables on a replacement session', async () => {
174
436
  const firstSession = columnsSession(() => columns('id', 'BIGINT'), new Set(['table_two', 'table_three']));
@@ -187,34 +449,37 @@ describe('DatabricksWarehouseClient', () => {
187
449
  table_two: { name: 'string' },
188
450
  table_three: { name: 'string' },
189
451
  });
190
- expect(firstSession.getColumns).toHaveBeenCalledTimes(3);
452
+ expect(firstSession.executeStatement).toHaveBeenCalledTimes(3);
191
453
  expect(firstSession.close).toHaveBeenCalledOnce();
192
- expect(secondSession.getColumns.mock.calls.map((call) => call[0].tableName)).toEqual(['table_two', 'table_three']);
454
+ expect(secondSession.executeStatement.mock.calls.map((call) => describedTable(call[0]))).toEqual(['table_two', 'table_three']);
193
455
  });
194
456
  it('lets the in-flight batch settle before closing the lost session', async () => {
195
457
  const order = [];
196
458
  const firstSession = createSession({
197
- getColumns: vi.fn(({ tableName }) => new Promise((resolve, reject) => {
198
- setTimeout(() => {
199
- order.push(`getColumns:${tableName}`);
200
- reject(sessionLostError());
201
- }, tableName === 'table_one' ? 10 : 100);
202
- })),
459
+ executeStatement: vi.fn((sql) => {
460
+ const tableName = describedTable(sql);
461
+ return new Promise((resolve, reject) => {
462
+ setTimeout(() => {
463
+ order.push(`describe:${tableName}`);
464
+ reject(sessionLostError());
465
+ }, tableName === 'table_one' ? 10 : 100);
466
+ });
467
+ }),
203
468
  });
204
469
  firstSession.close.mockImplementation(async () => {
205
470
  order.push('close');
206
471
  });
207
472
  mocks.openSession
208
473
  .mockResolvedValueOnce(firstSession)
209
- .mockResolvedValueOnce(createSession());
474
+ .mockResolvedValueOnce(columnsSession(() => [], new Set()));
210
475
  const warehouse = new DatabricksWarehouseClient_1.DatabricksWarehouseClient(DatabricksWarehouseClient_mock_1.credentials);
211
476
  await withTimers(() => warehouse.getCatalog([
212
477
  tableRequest('table_one'),
213
478
  tableRequest('table_two'),
214
479
  ]));
215
480
  expect(order).toEqual([
216
- 'getColumns:table_one',
217
- 'getColumns:table_two',
481
+ 'describe:table_one',
482
+ 'describe:table_two',
218
483
  'close',
219
484
  ]);
220
485
  });
@@ -226,9 +491,11 @@ describe('DatabricksWarehouseClient', () => {
226
491
  });
227
492
  it('fails fast on errors that are not warehouse startup errors', async () => {
228
493
  const session = createSession({
229
- getColumns: vi.fn(async ({ tableName }) => tableName === 'table_two'
494
+ executeStatement: vi.fn(async (sql) => describedTable(sql) === 'table_two'
230
495
  ? Promise.reject(statusError('PERMISSION_DENIED on table_two'))
231
- : createOperation()),
496
+ : createOperation({
497
+ fetchAll: vi.fn(async () => jsonDescription([])),
498
+ })),
232
499
  });
233
500
  mocks.openSession.mockResolvedValueOnce(session);
234
501
  const warehouse = new DatabricksWarehouseClient_1.DatabricksWarehouseClient(DatabricksWarehouseClient_mock_1.credentials);
@@ -243,6 +510,18 @@ describe('DatabricksWarehouseClient', () => {
243
510
  });
244
511
  });
245
512
  });
513
+ describe('getDatabricksErrorCondition', () => {
514
+ // Messages as a serverless SQL warehouse returned them on 2026-09-14.
515
+ test('reads the condition Databricks prefixes to SQL errors', () => {
516
+ expect((0, DatabricksWarehouseClient_1.getDatabricksErrorCondition)(statusError('[TABLE_OR_VIEW_NOT_FOUND] The table or view `lightdash_staging`.`nested`.`odd_names` cannot be found. Verify the spelling and correctness of the schema and catalog. SQLSTATE: 42P01; line 1 pos 24'))).toBe(DatabricksWarehouseClient_1.DatabricksErrorCondition.TableOrViewNotFound);
517
+ expect((0, DatabricksWarehouseClient_1.getDatabricksErrorCondition)(statusError("[PARSE_SYNTAX_ERROR] Syntax error at or near 'JSON'. SQLSTATE: 42601 (line 1, pos 37)"))).toBe(DatabricksWarehouseClient_1.DatabricksErrorCondition.ParseSyntaxError);
518
+ });
519
+ test('ignores conditions we do not handle and messages without a prefix', () => {
520
+ expect((0, DatabricksWarehouseClient_1.getDatabricksErrorCondition)(statusError('[INSUFFICIENT_PERMISSIONS] User does not have USE SCHEMA on Schema `schema`.'))).toBeUndefined();
521
+ expect((0, DatabricksWarehouseClient_1.getDatabricksErrorCondition)(statusError('Query could not be scheduled: TEMPORARILY_UNAVAILABLE'))).toBeUndefined();
522
+ expect((0, DatabricksWarehouseClient_1.getDatabricksErrorCondition)(new Error('mentions TABLE_OR_VIEW_NOT_FOUND mid-sentence'))).toBeUndefined();
523
+ });
524
+ });
246
525
  describe('DatabricksSqlBuilder escaping', () => {
247
526
  const databricksSqlBuilder = new DatabricksWarehouseClient_1.DatabricksSqlBuilder();
248
527
  test('Should escape backslashes and quotes in Databricks', () => {
@@ -1,4 +1,4 @@
1
- import { AnyType, CreateWarehouseCredentials, DimensionType, Metric, SupportedDbtAdapter, TimeIntervalUnit, WarehouseCatalog, WarehouseResults, WarehouseSqlBuilder, WarehouseTables, WeekDay, type TimestampDomain, type WarehouseExecuteAsyncQuery, type WarehouseExecuteAsyncQueryArgs, type WarehouseQueryPhase } from '@lightdash/common';
1
+ import { AnyType, CreateWarehouseCredentials, DimensionType, Metric, SupportedDbtAdapter, TimeIntervalUnit, UnnestSql, UnnestSqlArgs, WarehouseCatalog, WarehouseResults, WarehouseSqlBuilder, WarehouseTables, WeekDay, type TimestampDomain, type WarehouseExecuteAsyncQuery, type WarehouseExecuteAsyncQueryArgs, type WarehouseQueryPhase } from '@lightdash/common';
2
2
  import { type WarehouseClient } from '../types';
3
3
  export default abstract class WarehouseBaseClient<T extends CreateWarehouseCredentials> implements WarehouseClient {
4
4
  credentials: T;
@@ -9,6 +9,7 @@ export default abstract class WarehouseBaseClient<T extends CreateWarehouseCrede
9
9
  getStringQuoteChar(): string;
10
10
  getEscapeStringQuoteChar(): string;
11
11
  getFieldQuoteChar(): string;
12
+ getUnnestSql(args: UnnestSqlArgs): UnnestSql | null;
12
13
  getFloatingType(): string;
13
14
  getNullSafeEqualSql(left: string, right: string): string;
14
15
  getNullSafeEqualJoinSql(left: string, right: string): string;
@@ -1 +1 @@
1
- {"version":3,"file":"WarehouseBaseClient.d.ts","sourceRoot":"","sources":["../../src/warehouseClients/WarehouseBaseClient.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"WarehouseBaseClient.d.ts","sourceRoot":"","sources":["../../src/warehouseClients/WarehouseBaseClient.ts"],"names":[],"mappings":"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"}
@@ -21,6 +21,9 @@ class WarehouseBaseClient {
21
21
  getFieldQuoteChar() {
22
22
  return this.sqlBuilder.getFieldQuoteChar();
23
23
  }
24
+ getUnnestSql(args) {
25
+ return this.sqlBuilder.getUnnestSql(args);
26
+ }
24
27
  getFloatingType() {
25
28
  return this.sqlBuilder.getFloatingType();
26
29
  }
@@ -1,4 +1,4 @@
1
- import { Metric, SupportedDbtAdapter, TimeIntervalUnit, WarehouseSqlBuilder, WeekDay } from '@lightdash/common';
1
+ import { Metric, SupportedDbtAdapter, TimeIntervalUnit, UnnestSql, UnnestSqlArgs, WarehouseSqlBuilder, WeekDay } from '@lightdash/common';
2
2
  export default abstract class WarehouseBaseSqlBuilder implements WarehouseSqlBuilder {
3
3
  protected startOfWeek: WeekDay | null | undefined;
4
4
  constructor(startOfWeek?: WeekDay | null);
@@ -23,5 +23,6 @@ export default abstract class WarehouseBaseSqlBuilder implements WarehouseSqlBui
23
23
  getMedianSql(valueSql: string): string;
24
24
  buildArray(elements: string[]): string;
25
25
  buildArrayAgg(expression: string, orderBy?: string): string;
26
+ getUnnestSql(_args: UnnestSqlArgs): UnnestSql | null;
26
27
  }
27
28
  //# sourceMappingURL=WarehouseBaseSqlBuilder.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"WarehouseBaseSqlBuilder.d.ts","sourceRoot":"","sources":["../../src/warehouseClients/WarehouseBaseSqlBuilder.ts"],"names":[],"mappings":"AAAA,OAAO,EAEH,MAAM,EACN,mBAAmB,EACnB,gBAAgB,EAChB,mBAAmB,EAEnB,OAAO,EACV,MAAM,mBAAmB,CAAC;AAG3B,MAAM,CAAC,OAAO,CAAC,QAAQ,OAAO,uBAAwB,YAAW,mBAAmB;IAChF,SAAS,CAAC,WAAW,EAAE,OAAO,GAAG,IAAI,GAAG,SAAS,CAAC;IAElD,YAAY,WAAW,CAAC,EAAE,OAAO,GAAG,IAAI,EAEvC;IAED,cAAc,IAAI,OAAO,GAAG,IAAI,GAAG,SAAS,CAE3C;IAED,QAAQ,CAAC,cAAc,IAAI,mBAAmB,CAAC;IAK/C,0BAA0B,IAAI,OAAO,CAEpC;IAED,iBAAiB,IAAI,MAAM,CAE1B;IAED,kBAAkB,IAAI,MAAM,CAE3B;IAED,wBAAwB,IAAI,MAAM,CAEjC;IAED,eAAe,IAAI,MAAM,CAExB;IAED,mBAAmB,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,MAAM,CAEvD;IAED,uBAAuB,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,MAAM,CAE3D;IAED,YAAY,CAAC,GAAG,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,MAAM,CAEhD;IAED,YAAY,CAAC,GAAG,IAAI,EAAE,MAAM,EAAE,GAAG,MAAM,CAEtC;IAED,YAAY,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAiBlC;IAED,eAAe,CAAC,IAAI,EAAE,IAAI,GAAG,MAAM,CAIlC;IAED,UAAU,CAAC,IAAI,EAAE,IAAI,GAAG,MAAM,CAE7B;IAED,oBAAoB,CAAC,IAAI,EAAE,IAAI,GAAG,MAAM,CAGvC;IAED,SAAS,CAAC,MAAM,CAAC,QAAQ,CAAC,qBAAqB,EAAE,MAAM,CACnD,gBAAgB,EAChB,MAAM,CACT,CAQC;IAEF,cAAc,CAAC,KAAK,EAAE,MAAM,EAAE,IAAI,EAAE,gBAAgB,GAAG,MAAM,CAI5D;IAED,uBAAuB,CACnB,iBAAiB,EAAE,MAAM,EACzB,eAAe,EAAE,MAAM,GACxB,MAAM,CAGR;IAED,YAAY,CAAC,QAAQ,EAAE,MAAM,GAAG,MAAM,CAGrC;IAED,UAAU,CAAC,QAAQ,EAAE,MAAM,EAAE,GAAG,MAAM,CAGrC;IAED,aAAa,CAAC,UAAU,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,MAAM,GAAG,MAAM,CAM1D;CACJ"}
1
+ {"version":3,"file":"WarehouseBaseSqlBuilder.d.ts","sourceRoot":"","sources":["../../src/warehouseClients/WarehouseBaseSqlBuilder.ts"],"names":[],"mappings":"AAAA,OAAO,EAEH,MAAM,EACN,mBAAmB,EACnB,gBAAgB,EAChB,SAAS,EACT,aAAa,EACb,mBAAmB,EAEnB,OAAO,EACV,MAAM,mBAAmB,CAAC;AAG3B,MAAM,CAAC,OAAO,CAAC,QAAQ,OAAO,uBAAwB,YAAW,mBAAmB;IAChF,SAAS,CAAC,WAAW,EAAE,OAAO,GAAG,IAAI,GAAG,SAAS,CAAC;IAElD,YAAY,WAAW,CAAC,EAAE,OAAO,GAAG,IAAI,EAEvC;IAED,cAAc,IAAI,OAAO,GAAG,IAAI,GAAG,SAAS,CAE3C;IAED,QAAQ,CAAC,cAAc,IAAI,mBAAmB,CAAC;IAK/C,0BAA0B,IAAI,OAAO,CAEpC;IAED,iBAAiB,IAAI,MAAM,CAE1B;IAED,kBAAkB,IAAI,MAAM,CAE3B;IAED,wBAAwB,IAAI,MAAM,CAEjC;IAED,eAAe,IAAI,MAAM,CAExB;IAED,mBAAmB,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,MAAM,CAEvD;IAED,uBAAuB,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,MAAM,CAE3D;IAED,YAAY,CAAC,GAAG,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,MAAM,CAEhD;IAED,YAAY,CAAC,GAAG,IAAI,EAAE,MAAM,EAAE,GAAG,MAAM,CAEtC;IAED,YAAY,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAiBlC;IAED,eAAe,CAAC,IAAI,EAAE,IAAI,GAAG,MAAM,CAIlC;IAED,UAAU,CAAC,IAAI,EAAE,IAAI,GAAG,MAAM,CAE7B;IAED,oBAAoB,CAAC,IAAI,EAAE,IAAI,GAAG,MAAM,CAGvC;IAED,SAAS,CAAC,MAAM,CAAC,QAAQ,CAAC,qBAAqB,EAAE,MAAM,CACnD,gBAAgB,EAChB,MAAM,CACT,CAQC;IAEF,cAAc,CAAC,KAAK,EAAE,MAAM,EAAE,IAAI,EAAE,gBAAgB,GAAG,MAAM,CAI5D;IAED,uBAAuB,CACnB,iBAAiB,EAAE,MAAM,EACzB,eAAe,EAAE,MAAM,GACxB,MAAM,CAGR;IAED,YAAY,CAAC,QAAQ,EAAE,MAAM,GAAG,MAAM,CAGrC;IAED,UAAU,CAAC,QAAQ,EAAE,MAAM,EAAE,GAAG,MAAM,CAGrC;IAED,aAAa,CAAC,UAAU,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,MAAM,GAAG,MAAM,CAM1D;IAGD,YAAY,CAAC,KAAK,EAAE,aAAa,GAAG,SAAS,GAAG,IAAI,CAEnD;CACJ"}
@@ -90,6 +90,10 @@ class WarehouseBaseSqlBuilder {
90
90
  }
91
91
  return `ARRAY_AGG(${expression})`;
92
92
  }
93
+ // Repeated columns stay opaque unless the warehouse can unnest them.
94
+ getUnnestSql(_args) {
95
+ return null;
96
+ }
93
97
  }
94
98
  WarehouseBaseSqlBuilder.intervalUnitsSingular = {
95
99
  [common_1.TimeIntervalUnit.SECOND]: 'SECOND',
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@lightdash/warehouses",
3
- "version": "2.210.0",
3
+ "version": "2.211.1",
4
4
  "description": "Warehouse connectors for Lightdash",
5
5
  "license": "MIT",
6
6
  "repository": {
@@ -21,7 +21,7 @@
21
21
  "@databricks/sql": "1.13.0",
22
22
  "@duckdb/node-api": "1.5.2-r.2",
23
23
  "@google-cloud/bigquery": "8.3.1",
24
- "@lightdash/common": "2.210.0",
24
+ "@lightdash/common": "2.211.1",
25
25
  "node-fetch": "2.7.0",
26
26
  "pg": "8.13.1",
27
27
  "pg-cursor": "2.10.0",