Files
twenty/packages/twenty-server/src/database/clickhouse/migrations/run-migrations.ts
Félix Malfait 0b729cb000 Fix json type in clickhouse migrations (#11710)
Settings in clickhouse are only set for the duration of the session so
we need to keep the same client
2025-04-23 22:08:52 +02:00

108 lines
2.5 KiB
TypeScript

/* eslint-disable no-console */
import fs from 'fs';
import path from 'path';
import { ClickHouseClient, createClient } from '@clickhouse/client';
import { config } from 'dotenv';
config({
path: process.env.NODE_ENV === 'test' ? '.env.test' : '.env',
override: true,
});
const clickhouseUrl = () => {
const url = process.env.CLICKHOUSE_URL;
if (url) return url;
throw new Error(
'CLICKHOUSE_URL environment variable is not set. Please set it to the ClickHouse URL.',
);
};
async function ensureDatabaseExists() {
const [url, database] = clickhouseUrl().split(/\/(?=[^/]*$)/);
const client = createClient({
url,
});
await client.command({
query: `CREATE DATABASE IF NOT EXISTS "${database}"`,
});
await client.close();
}
async function ensureMigrationTable(client: ClickHouseClient) {
await client.command({
query: `
CREATE TABLE IF NOT EXISTS migrations (
filename String,
applied_at DateTime DEFAULT now()
) ENGINE = MergeTree()
ORDER BY filename;
`,
});
}
async function hasMigrationBeenRun(
filename: string,
client: ClickHouseClient,
): Promise<boolean> {
const resultSet = await client.query({
query: `SELECT count() as count FROM migrations WHERE filename = {filename:String}`,
query_params: { filename },
format: 'JSON',
});
const result = await resultSet.json<{ count: number }>();
return result.data[0].count > 0;
}
async function recordMigration(filename: string, client: ClickHouseClient) {
await client.insert({
table: 'migrations',
values: [{ filename }],
format: 'JSONEachRow',
});
}
async function runMigrations() {
const dir = path.join(__dirname);
const files = fs.readdirSync(dir).filter((f) => f.endsWith('.sql'));
await ensureDatabaseExists();
const client = createClient({
url: clickhouseUrl(),
clickhouse_settings: {
allow_experimental_json_type: 1,
},
});
await ensureMigrationTable(client);
for (const file of files) {
const alreadyRun = await hasMigrationBeenRun(file, client);
if (alreadyRun) {
console.log(`✔︎ Skipping already applied migration: ${file}`);
continue;
}
const sql = fs.readFileSync(path.join(dir, file), 'utf8');
console.log(`⚡ Running ${file}...`);
await client.command({ query: sql });
await recordMigration(file, client);
}
console.log('✅ All migrations applied.');
await client.close();
}
runMigrations().catch((err) => {
console.error('Migration error:', err);
process.exit(1);
});