Settings in clickhouse are only set for the duration of the session so we need to keep the same client
108 lines
2.5 KiB
TypeScript
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);
|
|
});
|