graphile-migrate
Version:
Opinionated SQL-powered migration tool for PostgreSQL
141 lines • 4.92 kB
JavaScript
;
Object.defineProperty(exports, "__esModule", { value: true });
const pg_1 = require("pg");
const pg_connection_string_1 = require("pg-connection-string");
/**
* For efficiency, we keep pools around for a period of time after they were
* last "released" so we don't have to keep re-creating them. This value
* chooses this time (in milliseconds). Note: clean exit will be delayed by
* this duration.
*/
const POOL_KEEPALIVE = 200;
const poolDetailsByConnectionString = new Map();
function clearAllPools() {
for (const details of poolDetailsByConnectionString.values()) {
if (details.referenceCount === 0) {
details._reallyRelease();
}
}
}
exports.clearAllPools = clearAllPools;
function getPoolDetailsFromConnectionString({ logger }, connectionString) {
let details = poolDetailsByConnectionString.get(connectionString);
if (!details) {
const { database } = pg_connection_string_1.parse(connectionString);
if (!database) {
throw new Error("Connection string does not specify a database");
}
const pool = new pg_1.Pool({ connectionString });
pool.on("error", (error) => {
logger.error(`An error occurred in the PgPool: ${error.message}`, {
error,
});
process.exit(1);
});
// We don't want someone else ending our pool; delete the end method.
const end = pool.end;
pool.end = () => {
throw new Error("You must not call .end() on this pool! Release the pool detail instead");
};
details = {
pool,
database,
referenceCount: 0,
release() {
this.referenceCount--;
if (this.referenceCount === 0) {
this._timer = setTimeout(this._reallyRelease, POOL_KEEPALIVE);
}
},
_timer: null,
_reference() {
clearTimeout(this._timer);
this._timer = null;
this.referenceCount++;
},
_reallyRelease() {
clearTimeout(this._timer);
this._timer = null;
pool.end = end;
pool.end();
poolDetailsByConnectionString.delete(connectionString);
},
};
poolDetailsByConnectionString.set(connectionString, details);
}
details._reference();
return details;
}
async function withClient(connectionString, parsedSettings, callback) {
const details = getPoolDetailsFromConnectionString(parsedSettings, connectionString);
const { pool: pgPool, database } = details;
try {
const pgClient = await pgPool.connect();
try {
if (parsedSettings.pgSettings) {
const sqlFragments = [];
const sqlValues = [];
for (const [key, value] of Object.entries(parsedSettings.pgSettings)) {
sqlValues.push(key, value);
sqlFragments.push(`pg_catalog.set_config($${sqlValues.length - 1}::text, $${sqlValues.length}::text, false)`);
}
if (sqlFragments.length) {
await pgClient.query({
text: `select ${sqlFragments.join(", ")}`,
values: sqlValues,
});
}
}
const context = {
database,
};
return await callback(pgClient, context);
}
finally {
await Promise.resolve(pgClient.release());
}
}
finally {
details.release();
}
}
exports.withClient = withClient;
const ADVISORY_LOCK_MIGRATE = "4727445306447283"; /* `GRAPHILE MIGRATE` on phone keypad */
async function withAdvisoryLock(pgClient, callback) {
if (pgClient["__isMockClient"]) {
return callback(pgClient);
}
const { rows: [{ locked }], } = await pgClient.query("select pg_try_advisory_lock($1) as locked", [
ADVISORY_LOCK_MIGRATE,
]);
if (!locked) {
throw new Error("Failed to get exclusive lock");
}
try {
return await callback(pgClient);
}
finally {
await pgClient.query("select pg_advisory_unlock($1)", [
ADVISORY_LOCK_MIGRATE,
]);
}
}
exports.withAdvisoryLock = withAdvisoryLock;
async function withTransaction(pgClient, callback) {
await pgClient.query("begin");
try {
const result = await callback();
await pgClient.query("commit");
return result;
}
catch (e) {
await pgClient.query("rollback");
throw e;
}
}
exports.withTransaction = withTransaction;
function escapeIdentifier(str) {
return '"' + str.replace(/"/g, '""') + '"';
}
exports.escapeIdentifier = escapeIdentifier;
//# sourceMappingURL=pgReal.js.map