UNPKG

@clickup/pg-mig

Version:

PostgreSQL schema migration tool with microsharding and clustering support

133 lines 5.92 kB
"use strict"; var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); exports.Patch = void 0; const flatten_1 = __importDefault(require("lodash/flatten")); const sortBy_1 = __importDefault(require("lodash/sortBy")); const isSchemaValid_1 = require("./helpers/isSchemaValid"); const promiseAllMap_1 = require("./helpers/promiseAllMap"); /** * A set of chains to apply to the database. */ class Patch { constructor(hosts, registry, mode) { this.hosts = hosts; this.registry = registry; this.mode = mode; } async getChains() { // The algorithm is not perfect: we treat schemas independently, so even if // e.g. a migration for schema=public precedes a migration for some other // schema=sh1234 in the repository, there is a chance that sh1234 will be // processed earlier than public. It's a trade-off to not implement a much // more complicated algorithm with explicit cross-schema dependencies. if (this.mode.undo) { const undoVersion = this.registry.extractVersion(this.mode.undo); if (!this.registry.hasVersion(undoVersion)) { throw `No such version on disk: ${undoVersion} (in ${this.registry.dir})`; } } const chains = await (0, promiseAllMap_1.promiseAllMap)(this.hosts, async (hostDest) => this.getHostChains(hostDest)); return (0, flatten_1.default)(chains); } async getHostChains(hostDest) { const allSchemas = await hostDest.loadSchemas(); const reEntries = this.registry.groupBySchema(allSchemas); const validShards = await hostDest.loadValidShards(this.mode.validShardSchemasSql); const schemas = Array.from(reEntries.keys()).filter((schema) => (0, isSchemaValid_1.isSchemaValid)(schema, reEntries.get(schema)[0].schemaPrefix, validShards)); const dbVersions = await hostDest.loadVersionsBySchema(schemas); const chains = schemas.map((schema) => this.getSchemaChain(hostDest.createSchemaDest(schema), dbVersions.get(schema), reEntries.get(schema))); return (0, sortBy_1.default)(chains.filter((chain) => chain && chain.migrations.length > 0), (chain) => chain.dest.toString()); } getSchemaChain(dest, dbVersions, reEntries) { try { if (!this.mode.undo) { return this.getChainUp(dest, dbVersions, reEntries); } else { return this.getChainDn(dest, dbVersions, reEntries, this.mode.undo); } } catch (e) { throw typeof e === "string" ? dest.toString() + ": " + e : e; } } getChainUp(dest, dbVersions, reEntries) { for (let i = 0; i < reEntries.length; i++) { if (i >= dbVersions.length) { // db: a b c d e // dir: a b c d e F G // ^i const entriesToApply = reEntries.slice(i); return { type: "up", dest, // F: a b c d e F // G: a b c d e F G migrations: entriesToApply.map((entry, pos) => ({ version: entry.name, file: entry.up, newVersions: [ ...dbVersions, ...entriesToApply.slice(0, pos + 1).map((ver) => ver.name), ], })), }; } else if (dbVersions[i] !== reEntries[i].name) { throw ("Migration timeline violation: you're asking to apply version " + reEntries[i].name + ", although version " + dbVersions[i] + " has already been applied. Hint: make sure that you've rebased on top of the main branch, and new migration versions are still the most recent."); } } if (dbVersions.length > reEntries.length) { throw ("Version " + dbVersions[reEntries.length] + " exists in the DB, but is missing on disk. Hint: make sure you've rebased on top of the main branch."); } return { type: "up", dest, migrations: [] }; } getChainDn(dest, dbVersions, reEntries, undoVersion) { undoVersion = this.registry.extractVersion(undoVersion); if (dbVersions[dbVersions.length - 1] === undoVersion) { // Undo the exactly latest version. // db: a b c d e // undo: e const undoEntry = reEntries.find((entry) => entry.name === undoVersion); if (!undoEntry) { throw `No such version on disk: ${undoVersion} (in ${this.registry.dir})`; } return { type: "dn", dest, migrations: [ { version: undoVersion, file: undoEntry.dn, newVersions: dbVersions.slice(0, -1), }, ], }; } const pos = dbVersions.indexOf(undoVersion); if (pos >= 0) { // Can't undo in the middle. // db: a b c d e // undo: c throw ("We can undo to only the latest version, and there are versions in the DB after " + undoVersion + ": " + dbVersions.slice(pos + 1).join(", ")); } // Just skip, undoVersion was never applied to the dest. // db: a b c d e // undo: f return null; } } exports.Patch = Patch; //# sourceMappingURL=Patch.js.map