From d22467d4c2498e85adc5ff00bb2240c78e6001e1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E0=A4=95=E0=A4=BE=E0=A4=B0=E0=A4=A4=E0=A5=8B=E0=A4=AB?= =?UTF-8?q?=E0=A5=8D=E0=A4=AB=E0=A5=87=E0=A4=B2=E0=A4=B8=E0=A5=8D=E0=A4=95?= =?UTF-8?q?=E0=A5=8D=E0=A4=B0=E0=A4=BF=E0=A4=AA=E0=A5=8D=E0=A4=9F=E2=84=A2?= Date: Wed, 16 Aug 2023 20:17:40 +0200 Subject: [PATCH] fix(core): Use consistent timezone-aware timestamps in postgres Fixes: * ENG-51 / N8N-2490 * PAY-397 * #2178 * #2810 * #3855 Supersedes #2813 --- .../src/databases/entities/AbstractEntity.ts | 14 ++---- .../1694091729095-MigrateToTimestampTz.ts | 44 +++++++++++++++++++ .../databases/migrations/postgresdb/index.ts | 2 + 3 files changed, 50 insertions(+), 10 deletions(-) create mode 100644 packages/cli/src/databases/migrations/postgresdb/1694091729095-MigrateToTimestampTz.ts diff --git a/packages/cli/src/databases/entities/AbstractEntity.ts b/packages/cli/src/databases/entities/AbstractEntity.ts index 9125694619820..ca1881c2be5fa 100644 --- a/packages/cli/src/databases/entities/AbstractEntity.ts +++ b/packages/cli/src/databases/entities/AbstractEntity.ts @@ -1,3 +1,4 @@ +import type { ColumnOptions } from 'typeorm'; import { BeforeInsert, BeforeUpdate, @@ -5,7 +6,6 @@ import { PrimaryColumn, UpdateDateColumn, } from 'typeorm'; -import { IsDate, IsOptional } from 'class-validator'; import config from '@/config'; import { generateNanoId } from '../utils/generators'; @@ -21,9 +21,10 @@ const timestampSyntax = { export const jsonColumnType = dbType === 'sqlite' ? 'simple-json' : 'json'; export const datetimeColumnType = dbType === 'postgresdb' ? 'timestamptz' : 'datetime'; -const tsColumnOptions = { +const tsColumnOptions: ColumnOptions = { precision: 3, default: () => timestampSyntax, + type: datetimeColumnType, }; type Constructor = new (...args: any[]) => T; @@ -46,16 +47,9 @@ function mixinStringId>(base: T) { function mixinTimestamps>(base: T) { class Derived extends base { @CreateDateColumn(tsColumnOptions) - @IsOptional() // ignored by validation because set at DB level - @IsDate() createdAt: Date; - @UpdateDateColumn({ - ...tsColumnOptions, - onUpdate: timestampSyntax, - }) - @IsOptional() // ignored by validation because set at DB level - @IsDate() + @UpdateDateColumn(tsColumnOptions) updatedAt: Date; @BeforeUpdate() diff --git a/packages/cli/src/databases/migrations/postgresdb/1694091729095-MigrateToTimestampTz.ts b/packages/cli/src/databases/migrations/postgresdb/1694091729095-MigrateToTimestampTz.ts new file mode 100644 index 0000000000000..34f12bd457cad --- /dev/null +++ b/packages/cli/src/databases/migrations/postgresdb/1694091729095-MigrateToTimestampTz.ts @@ -0,0 +1,44 @@ +import type { IrreversibleMigration, MigrationContext } from '@/databases/types'; + +const defaultTimestampColumns = ['createdAt', 'updatedAt']; +const tablesWithDefaultTimestamps = [ + 'auth_identity', + 'credentials_entity', + 'event_destinations', + 'installed_packages', + 'role', + 'shared_credentials', + 'shared_workflow', + 'tag_entity', + 'user', + 'workflow_entity', +]; + +const additionalColumns = { + auth_provider_sync_history: ['endedAt', 'startedAt'], + execution_entity: ['startedAt', 'stoppedAt', 'waitTill'], + workflow_statistics: ['latestEvent'], +}; + +export class MigrateToTimestampTz1694091729095 implements IrreversibleMigration { + async up({ queryRunner, tablePrefix }: MigrationContext) { + const changeColumnType = async (tableName: string, columnName: string, setDefault: boolean) => { + const alterColumnQuery = `ALTER TABLE "${tablePrefix}${tableName}" ALTER COLUMN "${columnName}"`; + await queryRunner.query(`${alterColumnQuery} TYPE TIMESTAMP(3) WITH TIME ZONE`); + if (setDefault) + await queryRunner.query(`${alterColumnQuery} SET DEFAULT CURRENT_TIMESTAMP(3)`); + }; + + for (const tableName of tablesWithDefaultTimestamps) { + for (const columnName of defaultTimestampColumns) { + await changeColumnType(tableName, columnName, true); + } + } + + for (const [tableName, columnNames] of Object.entries(additionalColumns)) { + for (const columnName of columnNames) { + await changeColumnType(tableName, columnName, false); + } + } + } +} diff --git a/packages/cli/src/databases/migrations/postgresdb/index.ts b/packages/cli/src/databases/migrations/postgresdb/index.ts index aa4906c6b2d6b..b2e3a187d2733 100644 --- a/packages/cli/src/databases/migrations/postgresdb/index.ts +++ b/packages/cli/src/databases/migrations/postgresdb/index.ts @@ -46,6 +46,7 @@ import { AddMfaColumns1690000000030 } from './../common/1690000000040-AddMfaColu import { CreateWorkflowHistoryTable1692967111175 } from '../common/1692967111175-CreateWorkflowHistoryTable'; import { DisallowOrphanExecutions1693554410387 } from '../common/1693554410387-DisallowOrphanExecutions'; import { ExecutionSoftDelete1693491613982 } from '../common/1693491613982-ExecutionSoftDelete'; +import { MigrateToTimestampTz1694091729095 } from './1694091729095-MigrateToTimestampTz'; export const postgresMigrations: Migration[] = [ InitialMigration1587669153312, @@ -95,4 +96,5 @@ export const postgresMigrations: Migration[] = [ CreateWorkflowHistoryTable1692967111175, DisallowOrphanExecutions1693554410387, ExecutionSoftDelete1693491613982, + MigrateToTimestampTz1694091729095, ];