Skip to content

feat(schema-compiler): Support custom granularities with Year-Month intervals for AWS Redshift dialect #9489

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Apr 23, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
69 changes: 69 additions & 0 deletions packages/cubejs-schema-compiler/src/adapter/RedshiftQuery.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import { parseSqlInterval } from '@cubejs-backend/shared';
import { PostgresQuery } from './PostgresQuery';

export class RedshiftQuery extends PostgresQuery {
Expand All @@ -12,6 +13,74 @@
return 'GETDATE()';
}

/**
* Redshift doesn't support Interval values with month or year parts (as Postgres does)
* so we need to make date math on our own.
*/
public override subtractInterval(date: string, interval: string): string {
const intervalParsed = parseSqlInterval(interval);
let result = date;

Check warning on line 22 in packages/cubejs-schema-compiler/src/adapter/RedshiftQuery.ts

View check run for this annotation

Codecov / codecov/patch

packages/cubejs-schema-compiler/src/adapter/RedshiftQuery.ts#L20-L22

Added lines #L20 - L22 were not covered by tests

for (const [datePart, intervalValue] of Object.entries(intervalParsed)) {
result = `DATEADD(${datePart}, -${intervalValue}, ${result})`;

Check warning on line 25 in packages/cubejs-schema-compiler/src/adapter/RedshiftQuery.ts

View check run for this annotation

Codecov / codecov/patch

packages/cubejs-schema-compiler/src/adapter/RedshiftQuery.ts#L24-L25

Added lines #L24 - L25 were not covered by tests
}

return result;

Check warning on line 28 in packages/cubejs-schema-compiler/src/adapter/RedshiftQuery.ts

View check run for this annotation

Codecov / codecov/patch

packages/cubejs-schema-compiler/src/adapter/RedshiftQuery.ts#L28

Added line #L28 was not covered by tests
}

/**
* Redshift doesn't support Interval values with month or year parts (as Postgres does)
* so we need to make date math on our own.
*/
public override addInterval(date: string, interval: string): string {
const intervalParsed = parseSqlInterval(interval);
let result = date;

Check warning on line 37 in packages/cubejs-schema-compiler/src/adapter/RedshiftQuery.ts

View check run for this annotation

Codecov / codecov/patch

packages/cubejs-schema-compiler/src/adapter/RedshiftQuery.ts#L35-L37

Added lines #L35 - L37 were not covered by tests

for (const [datePart, intervalValue] of Object.entries(intervalParsed)) {
result = `DATEADD(${datePart}, ${intervalValue}, ${result})`;

Check warning on line 40 in packages/cubejs-schema-compiler/src/adapter/RedshiftQuery.ts

View check run for this annotation

Codecov / codecov/patch

packages/cubejs-schema-compiler/src/adapter/RedshiftQuery.ts#L39-L40

Added lines #L39 - L40 were not covered by tests
}

return result;

Check warning on line 43 in packages/cubejs-schema-compiler/src/adapter/RedshiftQuery.ts

View check run for this annotation

Codecov / codecov/patch

packages/cubejs-schema-compiler/src/adapter/RedshiftQuery.ts#L43

Added line #L43 was not covered by tests
}

/**
* Redshift doesn't support Interval values with month or year parts (as Postgres does)
* so we need to make date math on our own.
*/
public override dateBin(interval: string, source: string, origin: string): string {
const intervalParsed = parseSqlInterval(interval);

Check warning on line 51 in packages/cubejs-schema-compiler/src/adapter/RedshiftQuery.ts

View check run for this annotation

Codecov / codecov/patch

packages/cubejs-schema-compiler/src/adapter/RedshiftQuery.ts#L50-L51

Added lines #L50 - L51 were not covered by tests

if ((intervalParsed.year || intervalParsed.month || intervalParsed.quarter) &&
(intervalParsed.week || intervalParsed.day || intervalParsed.hour || intervalParsed.minute || intervalParsed.second)) {
throw new Error(`Complex intervals like "${interval}" are not supported. Please use Year to Month or Day to second intervals`);

Check warning on line 55 in packages/cubejs-schema-compiler/src/adapter/RedshiftQuery.ts

View check run for this annotation

Codecov / codecov/patch

packages/cubejs-schema-compiler/src/adapter/RedshiftQuery.ts#L55

Added line #L55 was not covered by tests
}

if (intervalParsed.year || intervalParsed.month || intervalParsed.quarter) {
let totalMonths = 0;

Check warning on line 59 in packages/cubejs-schema-compiler/src/adapter/RedshiftQuery.ts

View check run for this annotation

Codecov / codecov/patch

packages/cubejs-schema-compiler/src/adapter/RedshiftQuery.ts#L59

Added line #L59 was not covered by tests

if (intervalParsed.year) {
totalMonths += intervalParsed.year * 12;

Check warning on line 62 in packages/cubejs-schema-compiler/src/adapter/RedshiftQuery.ts

View check run for this annotation

Codecov / codecov/patch

packages/cubejs-schema-compiler/src/adapter/RedshiftQuery.ts#L62

Added line #L62 was not covered by tests
}

if (intervalParsed.quarter) {
totalMonths += intervalParsed.quarter * 3;

Check warning on line 66 in packages/cubejs-schema-compiler/src/adapter/RedshiftQuery.ts

View check run for this annotation

Codecov / codecov/patch

packages/cubejs-schema-compiler/src/adapter/RedshiftQuery.ts#L66

Added line #L66 was not covered by tests
}

if (intervalParsed.month) {
totalMonths += intervalParsed.month;

Check warning on line 70 in packages/cubejs-schema-compiler/src/adapter/RedshiftQuery.ts

View check run for this annotation

Codecov / codecov/patch

packages/cubejs-schema-compiler/src/adapter/RedshiftQuery.ts#L70

Added line #L70 was not covered by tests
}

return `DATEADD(

Check warning on line 73 in packages/cubejs-schema-compiler/src/adapter/RedshiftQuery.ts

View check run for this annotation

Codecov / codecov/patch

packages/cubejs-schema-compiler/src/adapter/RedshiftQuery.ts#L73

Added line #L73 was not covered by tests
month,
(FLOOR(DATEDIFF(month, ${this.dateTimeCast(`'${origin}'`)}, ${source}) / ${totalMonths}) * ${totalMonths})::int,
${this.dateTimeCast(`'${origin}'`)}
)`;
}

// For days and lower intervals - we can reuse Postgres version
return super.dateBin(interval, source, origin);

Check warning on line 81 in packages/cubejs-schema-compiler/src/adapter/RedshiftQuery.ts

View check run for this annotation

Codecov / codecov/patch

packages/cubejs-schema-compiler/src/adapter/RedshiftQuery.ts#L81

Added line #L81 was not covered by tests
}

public sqlTemplates() {
const templates = super.sqlTemplates();
templates.functions.DLOG10 = 'LOG(10, {{ args_concat }})';
Expand Down
9 changes: 1 addition & 8 deletions packages/cubejs-testing-drivers/fixtures/redshift.json
Original file line number Diff line number Diff line change
Expand Up @@ -155,17 +155,10 @@
},
"skip": [
"---------------------------------------",
"Error: Interval values with month or year parts are not supported",
"Error: Complex intervals like \"3 months 2 weeks 3 days\" are not supported. @see dateBin impl in ReshiftQuery",
"---------------------------------------",
"querying BigECommerce: rolling window by 2 month",
"querying custom granularities ECommerce: count by half_year + no dimension",
"querying custom granularities ECommerce: count by half_year_by_1st_april + no dimension",
"querying custom granularities ECommerce: count by three_months_by_march + no dimension",
"querying custom granularities ECommerce: count by half_year + dimension",
"querying custom granularities ECommerce: count by half_year_by_1st_april + dimension",
"querying custom granularities ECommerce: count by three_months_by_march + dimension",
"querying custom granularities ECommerce: count by two_mo_by_feb + no dimension + rollingCountByTrailing",
"querying custom granularities ECommerce: count by two_mo_by_feb + no dimension + rollingCountByLeading",

"---------------------------------------",
"SKIPPED FOR ALL ",
Expand Down
Loading
Loading