diff --git a/src/lib/clickhouse.test.ts b/src/lib/clickhouse.test.ts new file mode 100644 index 000000000..1fcdedd5f --- /dev/null +++ b/src/lib/clickhouse.test.ts @@ -0,0 +1,12 @@ +import { describe, expect, test } from 'vitest'; +import { CLICKHOUSE_DATE_FORMATS } from './clickhouse'; + +describe('CLICKHOUSE_DATE_FORMATS', () => { + test('uses date format tokens compatible with ClickHouse 22.8 and newer', () => { + expect(CLICKHOUSE_DATE_FORMATS).toMatchObject({ + utc: '%Y-%m-%dT%TZ', + second: '%Y-%m-%dT%T', + minute: '%Y-%m-%d %R:00', + }); + }); +}); diff --git a/src/lib/clickhouse.ts b/src/lib/clickhouse.ts index 1e26d4354..bfce9ba62 100644 --- a/src/lib/clickhouse.ts +++ b/src/lib/clickhouse.ts @@ -7,9 +7,11 @@ import { filtersObjectToArray } from './params'; import type { Operator, PropertyFilter, QueryFilters, QueryOptions } from './types'; export const CLICKHOUSE_DATE_FORMATS = { - utc: '%Y-%m-%dT%H:%i:%SZ', - second: '%Y-%m-%dT%H:%i:%S', - minute: '%Y-%m-%d %H:%i:00', + // %i is unsupported by ClickHouse before v23.4. %T and %R work on both + // older and newer versions while retaining the intended ISO time format. + utc: '%Y-%m-%dT%TZ', + second: '%Y-%m-%dT%T', + minute: '%Y-%m-%d %R:00', hour: '%Y-%m-%d %H:00:00', day: '%Y-%m-%d', month: '%Y-%m-01', diff --git a/src/queries/sql/replays/getSessionReplays.ts b/src/queries/sql/replays/getSessionReplays.ts index 22f59973c..4cff4103c 100644 --- a/src/queries/sql/replays/getSessionReplays.ts +++ b/src/queries/sql/replays/getSessionReplays.ts @@ -114,7 +114,7 @@ async function clickhouseQuery(websiteId: string, filters: QueryFilters, session : ''; const havingQuery = minDurationMs - ? `having toInt64(sum(dateDiff('millisecond', session_replay.started_at, session_replay.ended_at))) >= {minDurationMs:Int64}` + ? `having toInt64(sum(toUnixTimestamp64Milli(session_replay.ended_at) - toUnixTimestamp64Milli(session_replay.started_at))) >= {minDurationMs:Int64}` : ''; return pagedRawQuery( @@ -132,7 +132,7 @@ async function clickhouseQuery(websiteId: string, filters: QueryFilters, session count(session_replay.replay_id) as chunkCount, min(session_replay.started_at) as startedAt, max(session_replay.ended_at) as endedAt, - toInt64(sum(dateDiff('millisecond', session_replay.started_at, session_replay.ended_at))) as duration, + toInt64(sum(toUnixTimestamp64Milli(session_replay.ended_at) - toUnixTimestamp64Milli(session_replay.started_at))) as duration, max(session_replay.created_at) as createdAt from session_replay join (