Skip to content

Commit 969508d

Browse files
feat(duckdb-driver): Fix numeric filter comparisons in DuckDB (#9328)
* feat(duckdb-driver): Fix numeric filter comparisons in DuckDB Fixes [#9281](#9281) by implementing explicit CAST for numeric filters in the DuckDB driver. This ensures correct numeric comparisons and prevents mismatches caused by string-to-number conversions. Added test cases to validate casting behavior for different filter scenarios. * feat(duckdb-driver): Fix numeric and time measure filter comparisons * ci: trigger checks for PR #9281 --------- Co-authored-by: Konstantin Burkalev <[email protected]>
1 parent 2f7a128 commit 969508d

File tree

3 files changed

+582
-7
lines changed

3 files changed

+582
-7
lines changed

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

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,16 @@ const GRANULARITY_TO_INTERVAL: Record<string, (date: string) => string> = {
1212
};
1313

1414
class DuckDBFilter extends BaseFilter {
15+
public castParameter() {
16+
const numberTypes = ['number', 'count', 'count_distinct', 'count_distinct_approx', 'sum', 'avg', 'min', 'max'];
17+
const definition = this.definition();
18+
19+
if (numberTypes.includes(definition.type)) {
20+
return 'CAST(? AS DOUBLE)';
21+
}
22+
23+
return '?';
24+
}
1525
}
1626

1727
export class DuckDBQuery extends BaseQuery {
@@ -55,4 +65,12 @@ export class DuckDBQuery extends BaseQuery {
5565
templates.functions.GREATEST = 'GREATEST({{ args_concat }})';
5666
return templates;
5767
}
68+
69+
public timeStampParam(timeDimension: any) {
70+
if (timeDimension.measure) {
71+
// For time measures, we don't need to check dateFieldType
72+
return super.timeStampCast('?');
73+
}
74+
return super.timeStampParam(timeDimension);
75+
}
5876
}
Lines changed: 38 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,15 @@
11
cube(`Orders`, {
22
sql: `
3-
select id, amount, status from (
4-
select 1 as id, 100 as amount, 'new' status
3+
select id, amount, status, created_at, is_paid from (
4+
select 1 as id, 100 as amount, 'new' status, TIMESTAMPTZ '2020-01-01 00:00:00' created_at, TRUE as is_paid
55
UNION ALL
6-
select 2 as id, 200 as amount, 'new' status
6+
select 2 as id, 200 as amount, 'new' status, TIMESTAMPTZ '2020-01-02 00:00:00' created_at, TRUE as is_paid
77
UNION ALL
8-
select 3 as id, 300 as amount, 'processed' status
8+
select 3 as id, 300 as amount, 'processed' status, TIMESTAMPTZ '2020-01-03 00:00:00' created_at, TRUE as is_paid
99
UNION ALL
10-
select 4 as id, 500 as amount, 'processed' status
10+
select 4 as id, 500 as amount, 'processed' status, TIMESTAMPTZ '2020-01-04 00:00:00' created_at, FALSE as is_paid
1111
UNION ALL
12-
select 5 as id, 600 as amount, 'shipped' status
12+
select 5 as id, 600 as amount, 'shipped' status, TIMESTAMPTZ '2020-01-05 00:00:00' created_at, FALSE as is_paid
1313
)
1414
`,
1515
measures: {
@@ -20,6 +20,26 @@ cube(`Orders`, {
2020
sql: `amount`,
2121
type: `sum`,
2222
},
23+
avgAmount: {
24+
sql: `amount`,
25+
type: `avg`,
26+
},
27+
statusList: {
28+
sql: `status`,
29+
type: `string`,
30+
},
31+
lastStatus: {
32+
sql: `MAX(status)`,
33+
type: `string`,
34+
},
35+
hasUnpaidOrders: {
36+
sql: `BOOL_OR(NOT is_paid)`,
37+
type: `boolean`,
38+
},
39+
maxCreatedAt: {
40+
sql: `MAX(created_at)`,
41+
type: `time`,
42+
},
2343
toRemove: {
2444
type: `count`,
2545
},
@@ -29,5 +49,17 @@ cube(`Orders`, {
2949
sql: `status`,
3050
type: `string`,
3151
},
52+
amount: {
53+
sql: `amount`,
54+
type: `number`,
55+
},
56+
isPaid: {
57+
sql: `is_paid`,
58+
type: `boolean`,
59+
},
60+
createdAt: {
61+
sql: `created_at`,
62+
type: `time`,
63+
},
3264
},
3365
});

0 commit comments

Comments
 (0)