const pg = require('pg'); const fs = require('fs'); const path = require('path'); const config = require('src/config/index.js'); // schema file name format ######_name.sql const schemaDir = path.resolve(__dirname, '..', 'schemas'); const db = new pg.Client({ host: config.database.host, port: config.database.port, user: config.database.user, password: config.database.password, database: config.database.dbname, }); (async () => { await db.connect(); await db.query(`select now();`); let version = -1; // check migrate record table exists const checkTable = await db.query( ` select exists( select 1 from "information_schema"."tables" where "table_schema" = $1 and "table_name" = $2 ) as exists `, ['public', 'migrate_log'] ); if (checkTable.rowCount > 0 && checkTable.rows[0].exists === true) { // version table exists const maxVersion = await db.query(`select max("version")::integer as version from "public"."migrate_log"`); if (maxVersion.rowCount > 0 && maxVersion.rows[0] && maxVersion.rows[0].version !== null) version = maxVersion.rows[0].version; // eslint-disable-line } else { // create version table await db.query(`create table "public"."migrate_log" ( "version" integer not null primary key, "created_time" timestamptz not null default now() );`); } console.info(`Database Now Version: ${version}`); // read all schema files const schemaList = await fs.promises.readdir(schemaDir); /** * @type {{[x: number]: boolean}} */ const checkDuplicate = {}; /** * @type {{version: number, filename: string}[]} */ const versionList = schemaList .map(file => { const strs = file.split('_'); const v = parseInt(strs[0], 10); if (isNaN(version)) throw new Error(`schema filename format error (######_name.sql)`); // eslint-disable-line if (v in checkDuplicate) throw new Error(`schema file version (${v}) is duplicate`); checkDuplicate[v] = true; return { version: v, filename: file }; }) .filter(t => t && t.version > version) .sort((a, b) => a.version - b.version); // 沒有需要更新的檔案 if (versionList.length === 0) return; await db.query('begin'); try { const vers = []; // write all schema file for (const it of versionList) { vers.push(`(${it.version})`); console.info(`Write Version: ${it.version}`); const fileContent = await fs.promises.readFile(path.resolve(schemaDir, it.filename), 'utf-8'); await db.query(fileContent); } await db.query(`insert into "public"."migrate_log" ("version") values ${vers.join(',')}`); await db.query('commit'); } catch (err) { await db.query('rollback'); throw err; } })() .then(() => { console.info('Database Migrate Finish'); }) .catch(err => { console.error('Database Migrate Failed, ', err); process.exit(1); }) .finally(() => { db.end(); });