Skip to content

Commit 3e1389b

Browse files
authored
fix(duckdb-driver): Compatibility issue with Cube Store for DATE type (#7394)
1 parent 3c9ffd4 commit 3e1389b

File tree

2 files changed

+57
-35
lines changed

2 files changed

+57
-35
lines changed

packages/cubejs-duckdb-driver/src/DuckDBDriver.ts

Lines changed: 18 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,9 @@ import {
22
BaseDriver,
33
DriverInterface,
44
StreamOptions,
5-
QueryOptions, StreamTableData,
5+
QueryOptions,
6+
StreamTableData,
7+
GenericDataBaseType,
68
} from '@cubejs-backend/base-driver';
79
import { getEnv } from '@cubejs-backend/shared';
810
import { promisify } from 'util';
@@ -24,6 +26,13 @@ type InitPromise = {
2426
db: Database;
2527
};
2628

29+
const DuckDBToGenericType: Record<string, GenericDataBaseType> = {
30+
// DATE_TRUNC returns DATE, but Cube Store still doesn't support DATE type
31+
// DuckDB's driver transform date/timestamp to Date object, but HydrationStream converts any Date object to ISO timestamp
32+
// That's why It's safe to use timestamp here
33+
date: 'timestamp',
34+
};
35+
2736
export class DuckDBDriver extends BaseDriver implements DriverInterface {
2837
protected initPromise: Promise<InitPromise> | null = null;
2938

@@ -37,6 +46,14 @@ export class DuckDBDriver extends BaseDriver implements DriverInterface {
3746
this.schema = this.config.schema || getEnv('duckdbSchema', this.config);
3847
}
3948

49+
public toGenericType(columnType: string): GenericDataBaseType {
50+
if (columnType.toLowerCase() in DuckDBToGenericType) {
51+
return DuckDBToGenericType[columnType.toLowerCase()];
52+
}
53+
54+
return super.toGenericType(columnType.toLowerCase());
55+
}
56+
4057
protected async init(): Promise<InitPromise> {
4158
const token = getEnv('duckdbMotherDuckToken', this.config);
4259

packages/cubejs-duckdb-driver/test/DuckDBDriver.test.ts

Lines changed: 39 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -9,63 +9,68 @@ describe('DuckDBDriver', () => {
99
beforeAll(async () => {
1010
driver = new DuckDBDriver({});
1111
await driver.query('CREATE SCHEMA IF NOT EXISTS test;', []);
12-
});
13-
14-
afterAll(async () => {
15-
await driver.release();
16-
});
17-
18-
test('query', async () => {
1912
await driver.uploadTable(
20-
'test.querying_test',
13+
'test.select_test',
2114
[
2215
{ name: 'id', type: 'bigint' },
23-
{ name: 'created', type: 'date' },
16+
{ name: 'created', type: 'timestamp' },
17+
{ name: 'created_date', type: 'date' },
2418
{ name: 'price', type: 'decimal' },
2519
],
2620
{
2721
rows: [
28-
{ id: 1, created: '2020-01-01', price: '100' },
29-
{ id: 2, created: '2020-01-02', price: '200' },
30-
{ id: 3, created: '2020-01-03', price: '300' }
22+
{ id: 1, created: '2020-01-01 01:01:01.11111', created_date: '2020-01-01', price: '100' },
23+
{ id: 2, created: '2020-02-02 02:02:02.22222', created_date: '2020-02-02', price: '200' },
24+
{ id: 3, created: '2020-03-03 03:03:03.33333', created_date: '2020-03-03', price: '300' }
3125
]
3226
}
3327
);
28+
});
3429

35-
const result = await driver.query('select * from test.querying_test', []);
30+
afterAll(async () => {
31+
await driver.release();
32+
});
33+
34+
test('query', async () => {
35+
const result = await driver.query('select * from test.select_test ORDER BY id ASC', []);
3636
expect(result).toEqual([
37-
{ id: '1', created: '2020-01-01T00:00:00.000Z', price: '100' },
38-
{ id: '2', created: '2020-01-02T00:00:00.000Z', price: '200' },
39-
{ id: '3', created: '2020-01-03T00:00:00.000Z', price: '300' }
37+
{ id: '1', created: '2020-01-01T01:01:01.111Z', created_date: '2020-01-01T00:00:00.000Z', price: '100' },
38+
{ id: '2', created: '2020-02-02T02:02:02.222Z', created_date: '2020-02-02T00:00:00.000Z', price: '200' },
39+
{ id: '3', created: '2020-03-03T03:03:03.333Z', created_date: '2020-03-03T00:00:00.000Z', price: '300' }
4040
]);
4141
});
4242

43-
test('stream', async () => {
44-
await driver.uploadTable(
45-
'test.streaming_test',
46-
[
47-
{ name: 'id', type: 'bigint' },
48-
{ name: 'created', type: 'date' },
49-
{ name: 'price', type: 'decimal' },
50-
],
43+
test('column types', async () => {
44+
expect(await driver.tableColumnTypes('test.select_test')).toEqual([
5145
{
52-
rows: [
53-
{ id: 1, created: '2020-01-01', price: '100' },
54-
{ id: 2, created: '2020-01-02', price: '200' },
55-
{ id: 3, created: '2020-01-03', price: '300' }
56-
]
46+
name: 'id',
47+
type: 'bigint',
48+
},
49+
{
50+
name: 'created',
51+
type: 'timestamp',
52+
},
53+
{
54+
name: 'created_date',
55+
type: 'timestamp',
56+
},
57+
{
58+
name: 'price',
59+
type: 'decimal(18,3)',
5760
}
58-
);
61+
]);
62+
});
5963

60-
const tableData = await driver.stream('select * from test.streaming_test', [], {
64+
test('stream', async () => {
65+
const tableData = await driver.stream('select * from test.select_test ORDER BY id ASC', [], {
6166
highWaterMark: 1000,
6267
});
6368

6469
expect(await tableData.types).toEqual(undefined);
6570
expect(await streamToArray(tableData.rowStream as any)).toEqual([
66-
{ id: '1', created: '2020-01-01T00:00:00.000Z', price: '100' },
67-
{ id: '2', created: '2020-01-02T00:00:00.000Z', price: '200' },
68-
{ id: '3', created: '2020-01-03T00:00:00.000Z', price: '300' }
71+
{ id: '1', created: '2020-01-01T01:01:01.111Z', created_date: '2020-01-01T00:00:00.000Z', price: '100' },
72+
{ id: '2', created: '2020-02-02T02:02:02.222Z', created_date: '2020-02-02T00:00:00.000Z', price: '200' },
73+
{ id: '3', created: '2020-03-03T03:03:03.333Z', created_date: '2020-03-03T00:00:00.000Z', price: '300' }
6974
]);
7075
});
7176
});

0 commit comments

Comments
 (0)