Skip to content
Draft
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
Original file line number Diff line number Diff line change
Expand Up @@ -69,17 +69,17 @@ export class ClickHouseQuery extends BaseQuery {
public dateBin(interval: string, source: string, origin: string): string {
// Pass timezone to dateTimeCast to ensure origin is in the same timezone as a source, because ClickHouse aligns
// both timestamps internally to the same timezone before computing the difference, causing an unintended offset.
const alignedOrigin = this.dateTimeCast(`'${origin}'`, this.timezone);
const originAligned = this.dateTimeCast(`'${origin}'`, this.timezone);
const intervalFormatted = this.formatInterval(interval);
const timeUnit = this.diffTimeUnitForInterval(interval);
const beginOfTime = 'fromUnixTimestamp(0)';

return `date_add(${timeUnit},
FLOOR(
date_diff(${timeUnit}, ${alignedOrigin}, ${source}) /
date_diff(${timeUnit}, ${originAligned}, ${source}) /
date_diff(${timeUnit}, ${beginOfTime}, ${beginOfTime} + ${intervalFormatted})
) * date_diff(${timeUnit}, ${beginOfTime}, ${beginOfTime} + ${intervalFormatted}),
${alignedOrigin}
${originAligned}
)`;
}

Expand Down
20 changes: 14 additions & 6 deletions packages/cubejs-schema-compiler/src/adapter/MssqlQuery.ts
Original file line number Diff line number Diff line change
Expand Up @@ -90,7 +90,12 @@ export class MssqlQuery extends BaseQuery {
}

public convertTz(field) {
return `TODATETIMEOFFSET(${field}, '${moment().tz(this.timezone).format('Z')}')`;
const offset = moment().tz(this.timezone).format('Z');

// 1. Treating the field as UTC (add '+00:00' offset)
// 2. Switch to target timezone offset
// 3. Cast to DATETIME2 to get naive timestamp in target timezone
return `CAST(SWITCHOFFSET(TODATETIMEOFFSET(${field}, '+00:00'), '${offset}') AS DATETIME2)`;
}

public timeStampCast(value: string) {
Expand All @@ -106,21 +111,24 @@ export class MssqlQuery extends BaseQuery {
}

/**
* Returns sql for source expression floored to timestamps aligned with
* intervals relative to origin timestamp point.
* The formula operates with seconds diffs so it won't produce human-expected dates aligned with offset date parts.
* Returns SQL for source expression floored to timestamps aligned with
* intervals relative to the origin timestamp point.
* The formula operates with seconds diffs, so it won't produce human-expected dates aligned with offset date parts.
*/
public dateBin(interval: string, source: string, origin: string): string {
// Both source and origin are now DATETIME2 in the query timezone (naive timestamps),
// ensuring their in the same timezone context for correct date bin calculation.
const originAligned = this.dateTimeCast(`'${origin}'`);
const beginOfTime = this.dateTimeCast('DATEFROMPARTS(1970, 1, 1)');
const timeUnit = this.diffTimeUnitForInterval(interval);

// Need to explicitly cast one argument of floor to float to trigger correct sign logic
return `DATEADD(${timeUnit},
FLOOR(
CAST(DATEDIFF(${timeUnit}, ${this.dateTimeCast(`'${origin}'`)}, ${source}) AS FLOAT) /
CAST(DATEDIFF(${timeUnit}, ${originAligned}, ${source}) AS FLOAT) /
DATEDIFF(${timeUnit}, ${beginOfTime}, ${this.addInterval(beginOfTime, interval)})
) * DATEDIFF(${timeUnit}, ${beginOfTime}, ${this.addInterval(beginOfTime, interval)}),
${this.dateTimeCast(`'${origin}'`)}
${originAligned}
)`;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -158,8 +158,7 @@ describe('Custom Granularities', () => {
{ joinGraph, cubeEvaluator, compiler }
));

/// TODO: fix date bin calculation... for some reason it goes from 2023-12-31T23:00:00.000Z
xit('works with five_minutes_from_utc_origin custom granularity in Europe/Paris timezone', async () => dbRunner.runQueryTest(
it('works with five_minutes_from_utc_origin custom granularity in Europe/Paris timezone', async () => dbRunner.runQueryTest(
{
measures: ['orders.count'],
timeDimensions: [{
Expand Down Expand Up @@ -188,8 +187,7 @@ describe('Custom Granularities', () => {
{ joinGraph, cubeEvaluator, compiler }
));

/// TODO: fix date bin calculation... for some reason it goes from 2023-12-31T23:00:00.000Z
xit('works with five_minutes_from_local_origin custom granularity in Europe/Paris timezone', async () => dbRunner.runQueryTest(
it('works with five_minutes_from_local_origin custom granularity in Europe/Paris timezone', async () => dbRunner.runQueryTest(
{
measures: ['orders.count'],
timeDimensions: [{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -79,22 +79,22 @@ describe('MSSqlCumulativeMeasures', () => {
expect(await dbRunner.testQuery(query.buildSqlAndParams())).toEqual([
{
visitors__count: 1,
visitors__created_at_day: new Date('2017-01-03T00:00:00.000Z'),
visitors__created_at_day: new Date('2017-01-02T00:00:00.000Z'),
visitors__unbounded_count: 2,
},
{
visitors__count: 1,
visitors__created_at_day: new Date('2017-01-05T00:00:00.000Z'),
visitors__created_at_day: new Date('2017-01-04T00:00:00.000Z'),
visitors__unbounded_count: 3,
},
{
visitors__count: 1,
visitors__created_at_day: new Date('2017-01-06T00:00:00.000Z'),
visitors__created_at_day: new Date('2017-01-05T00:00:00.000Z'),
visitors__unbounded_count: 4,
},
{
visitors__count: 2,
visitors__created_at_day: new Date('2017-01-07T00:00:00.000Z'),
visitors__created_at_day: new Date('2017-01-06T00:00:00.000Z'),
visitors__unbounded_count: 6,
}
]);
Expand Down Expand Up @@ -128,25 +128,25 @@ describe('MSSqlCumulativeMeasures', () => {
expect(await dbRunner.testQuery(query.buildSqlAndParams())).toEqual([
{
visitors__count: 1,
visitors__created_at_day: new Date('2017-01-03T00:00:00.000Z'),
visitors__created_at_day: new Date('2017-01-02T00:00:00.000Z'),
visitors__source: 'some',
visitors__unbounded_count: 1
},
{
visitors__count: 1,
visitors__created_at_day: new Date('2017-01-05T00:00:00.000Z'),
visitors__created_at_day: new Date('2017-01-04T00:00:00.000Z'),
visitors__source: 'some',
visitors__unbounded_count: 2,
},
{
visitors__count: 1,
visitors__created_at_day: new Date('2017-01-06T00:00:00.000Z'),
visitors__created_at_day: new Date('2017-01-05T00:00:00.000Z'),
visitors__source: 'google',
visitors__unbounded_count: 1,
},
{
visitors__count: 2,
visitors__created_at_day: new Date('2017-01-07T00:00:00.000Z'),
visitors__created_at_day: new Date('2017-01-06T00:00:00.000Z'),
visitors__source: null,
visitors__unbounded_count: 3,
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -278,22 +278,22 @@ describe('MSSqlPreAggregations', () => {
expect(res)
.toEqual([
{
visitors__created_at_day: new Date('2017-01-03T00:00:00.000Z'),
visitors__created_at_day: new Date('2017-01-02T00:00:00.000Z'),
visitors__checkins_total: 3,
visitors__source: 'some',
},
{
visitors__created_at_day: new Date('2017-01-05T00:00:00.000Z'),
visitors__created_at_day: new Date('2017-01-04T00:00:00.000Z'),
visitors__checkins_total: 2,
visitors__source: 'some',
},
{
visitors__created_at_day: new Date('2017-01-06T00:00:00.000Z'),
visitors__created_at_day: new Date('2017-01-05T00:00:00.000Z'),
visitors__checkins_total: 1,
visitors__source: 'google',
},
{
visitors__created_at_day: new Date('2017-01-07T00:00:00.000Z'),
visitors__created_at_day: new Date('2017-01-06T00:00:00.000Z'),
visitors__checkins_total: 0,
visitors__source: null
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -366,12 +366,12 @@ describe('MSSqlUngrouped', () => {
ungrouped: true,
allowUngroupedWithoutPrimaryKey: true,
}, [
{ visitor_checkins__created_at_day: new Date('2017-01-02T00:00:00.000Z'), visitor_checkins__google_sourced_checkins: null },
{ visitor_checkins__created_at_day: new Date('2017-01-03T00:00:00.000Z'), visitor_checkins__google_sourced_checkins: null },
{ visitor_checkins__created_at_day: new Date('2017-01-04T00:00:00.000Z'), visitor_checkins__google_sourced_checkins: null },
{ visitor_checkins__created_at_day: new Date('2017-01-05T00:00:00.000Z'), visitor_checkins__google_sourced_checkins: null },
{ visitor_checkins__created_at_day: new Date('2017-01-05T00:00:00.000Z'), visitor_checkins__google_sourced_checkins: null },
{ visitor_checkins__created_at_day: new Date('2017-01-05T00:00:00.000Z'), visitor_checkins__google_sourced_checkins: null },
{ visitor_checkins__created_at_day: new Date('2017-01-06T00:00:00.000Z'), visitor_checkins__google_sourced_checkins: 1 },
{ visitor_checkins__created_at_day: new Date('2017-01-04T00:00:00.000Z'), visitor_checkins__google_sourced_checkins: null },
{ visitor_checkins__created_at_day: new Date('2017-01-04T00:00:00.000Z'), visitor_checkins__google_sourced_checkins: null },
{ visitor_checkins__created_at_day: new Date('2017-01-05T00:00:00.000Z'), visitor_checkins__google_sourced_checkins: 1 },
]));

it('ungrouped filtered distinct count', () => runQueryTest({
Expand All @@ -390,12 +390,12 @@ describe('MSSqlUngrouped', () => {
ungrouped: true,
allowUngroupedWithoutPrimaryKey: true,
}, [
{ visitor_checkins__created_at_day: new Date('2017-01-02T00:00:00.000Z'), visitor_checkins__unique_google_sourced_checkins: null },
{ visitor_checkins__created_at_day: new Date('2017-01-03T00:00:00.000Z'), visitor_checkins__unique_google_sourced_checkins: null },
{ visitor_checkins__created_at_day: new Date('2017-01-04T00:00:00.000Z'), visitor_checkins__unique_google_sourced_checkins: null },
{ visitor_checkins__created_at_day: new Date('2017-01-05T00:00:00.000Z'), visitor_checkins__unique_google_sourced_checkins: null },
{ visitor_checkins__created_at_day: new Date('2017-01-05T00:00:00.000Z'), visitor_checkins__unique_google_sourced_checkins: null },
{ visitor_checkins__created_at_day: new Date('2017-01-05T00:00:00.000Z'), visitor_checkins__unique_google_sourced_checkins: null },
{ visitor_checkins__created_at_day: new Date('2017-01-06T00:00:00.000Z'), visitor_checkins__unique_google_sourced_checkins: 1 },
{ visitor_checkins__created_at_day: new Date('2017-01-04T00:00:00.000Z'), visitor_checkins__unique_google_sourced_checkins: null },
{ visitor_checkins__created_at_day: new Date('2017-01-04T00:00:00.000Z'), visitor_checkins__unique_google_sourced_checkins: null },
{ visitor_checkins__created_at_day: new Date('2017-01-05T00:00:00.000Z'), visitor_checkins__unique_google_sourced_checkins: 1 },
]));

it('ungrouped ratio measure', () => runQueryTest({
Expand All @@ -414,12 +414,12 @@ describe('MSSqlUngrouped', () => {
ungrouped: true,
allowUngroupedWithoutPrimaryKey: true,
}, [
{ visitor_checkins__created_at_day: new Date('2017-01-02T00:00:00.000Z'), visitor_checkins__unique_sources_per_checking: 1 },
{ visitor_checkins__created_at_day: new Date('2017-01-03T00:00:00.000Z'), visitor_checkins__unique_sources_per_checking: 1 },
{ visitor_checkins__created_at_day: new Date('2017-01-04T00:00:00.000Z'), visitor_checkins__unique_sources_per_checking: 1 },
{ visitor_checkins__created_at_day: new Date('2017-01-04T00:00:00.000Z'), visitor_checkins__unique_sources_per_checking: 1 },
{ visitor_checkins__created_at_day: new Date('2017-01-04T00:00:00.000Z'), visitor_checkins__unique_sources_per_checking: 1 },
{ visitor_checkins__created_at_day: new Date('2017-01-05T00:00:00.000Z'), visitor_checkins__unique_sources_per_checking: 1 },
{ visitor_checkins__created_at_day: new Date('2017-01-05T00:00:00.000Z'), visitor_checkins__unique_sources_per_checking: 1 },
{ visitor_checkins__created_at_day: new Date('2017-01-05T00:00:00.000Z'), visitor_checkins__unique_sources_per_checking: 1 },
{ visitor_checkins__created_at_day: new Date('2017-01-06T00:00:00.000Z'), visitor_checkins__unique_sources_per_checking: 1 },
]));

it('ungrouped', () => runQueryTest({
Expand All @@ -439,22 +439,22 @@ describe('MSSqlUngrouped', () => {
ungrouped: true
}, [{
visitors__id: 6,
visitors__created_at_day: new Date('2016-09-07T00:00:00.000Z')
visitors__created_at_day: new Date('2016-09-06T00:00:00.000Z')
}, {
visitors__id: 1,
visitors__created_at_day: new Date('2017-01-03T00:00:00.000Z')
visitors__created_at_day: new Date('2017-01-02T00:00:00.000Z')
}, {
visitors__id: 2,
visitors__created_at_day: new Date('2017-01-05T00:00:00.000Z')
visitors__created_at_day: new Date('2017-01-04T00:00:00.000Z')
}, {
visitors__id: 3,
visitors__created_at_day: new Date('2017-01-06T00:00:00.000Z')
visitors__created_at_day: new Date('2017-01-05T00:00:00.000Z')
}, {
visitors__id: 4,
visitors__created_at_day: new Date('2017-01-07T00:00:00.000Z')
visitors__created_at_day: new Date('2017-01-06T00:00:00.000Z')
}, {
visitors__id: 5,
visitors__created_at_day: new Date('2017-01-07T00:00:00.000Z')
visitors__created_at_day: new Date('2017-01-06T00:00:00.000Z')
}]));

it('offset cache', () => runQueryTest({
Expand All @@ -475,7 +475,7 @@ describe('MSSqlUngrouped', () => {
offset: 5
}, [{
visitors__id: 5,
visitors__created_at_day: new Date('2017-01-07T00:00:00.000Z')
visitors__created_at_day: new Date('2017-01-06T00:00:00.000Z')
}]));

it('ungrouped without id', () => runQueryTest({
Expand All @@ -493,17 +493,17 @@ describe('MSSqlUngrouped', () => {
ungrouped: true,
allowUngroupedWithoutPrimaryKey: true
}, [{
visitors__created_at_day: new Date('2016-09-07T00:00:00.000Z')
visitors__created_at_day: new Date('2016-09-06T00:00:00.000Z')
}, {
visitors__created_at_day: new Date('2017-01-02T00:00:00.000Z')
}, {
visitors__created_at_day: new Date('2017-01-03T00:00:00.000Z')
visitors__created_at_day: new Date('2017-01-04T00:00:00.000Z')
}, {
visitors__created_at_day: new Date('2017-01-05T00:00:00.000Z')
}, {
visitors__created_at_day: new Date('2017-01-06T00:00:00.000Z')
}, {
visitors__created_at_day: new Date('2017-01-07T00:00:00.000Z')
}, {
visitors__created_at_day: new Date('2017-01-07T00:00:00.000Z')
visitors__created_at_day: new Date('2017-01-06T00:00:00.000Z')
}]));

it('ungrouped false without id', () => runQueryTest({
Expand All @@ -520,14 +520,14 @@ describe('MSSqlUngrouped', () => {
timezone: 'America/Los_Angeles',
ungrouped: false
}, [{
visitors__created_at_day: new Date('2016-09-07T00:00:00.000Z')
visitors__created_at_day: new Date('2016-09-06T00:00:00.000Z')
}, {
visitors__created_at_day: new Date('2017-01-03T00:00:00.000Z')
visitors__created_at_day: new Date('2017-01-02T00:00:00.000Z')
}, {
visitors__created_at_day: new Date('2017-01-04T00:00:00.000Z')
}, {
visitors__created_at_day: new Date('2017-01-05T00:00:00.000Z')
}, {
visitors__created_at_day: new Date('2017-01-06T00:00:00.000Z')
}, {
visitors__created_at_day: new Date('2017-01-07T00:00:00.000Z')
}]));
});
Loading
Loading