Made addUnique/dropUnique migration utils idempodent

refs https://github.com/TryGhost/Ghost/pull/12598

- This changeset adds idepmotence to situations where unique contraint has to be dropped or added to the table
- Note '4.0/07-alter-unique-constraint-for-posts-slug.js` was migration that was effected by lack of idempotence
This commit is contained in:
Naz 2021-02-18 20:17:53 +13:00 committed by naz
parent ce50653f89
commit adebca422f

View File

@ -63,6 +63,46 @@ function dropColumn(tableName, column, transaction) {
}); });
} }
/**
* Checks if unique index exists in a table over the given columns.
*
* @param {string} tableName - name of the table to add unique constraint to
* @param {string|[string]} columns - column(s) to form unique constraint with
* @param {Object} transaction - connnection object containing knex reference
* @param {Object} transaction.knex - knex instance
*/
async function hasUnique(tableName, columns, transaction) {
const knex = (transaction || db.knex);
const client = knex.client.config.client;
const columnNames = _.isArray(columns) ? columns.join('_') : columns;
const constraintName = `${tableName}_${columnNames}_unique`;
if (client === 'mysql') {
const dbName = knex.client.config.connection.database;
const [rawConstraints] = await knex.raw(`
SELECT CONSTRAINT_NAME
FROM information_schema.TABLE_CONSTRAINTS
WHERE 1=1
AND CONSTRAINT_SCHEMA=:dbName
AND TABLE_NAME=:tableName
AND CONSTRAINT_TYPE='UNIQUE'`, {dbName, tableName});
const dbConstraints = rawConstraints.map(c => c.CONSTRAINT_NAME);
if (dbConstraints.includes(constraintName)) {
return true;
}
} else {
const rawConstraints = await knex.raw(`PRAGMA index_list('${tableName}');`);
const dbConstraints = rawConstraints.map(c => c.name);
if (dbConstraints.includes(constraintName)) {
return true;
}
}
return false;
}
/** /**
* Adds an unique index to a table over the given columns. * Adds an unique index to a table over the given columns.
* *
@ -71,10 +111,17 @@ function dropColumn(tableName, column, transaction) {
* @param {Object} transaction - connnection object containing knex reference * @param {Object} transaction - connnection object containing knex reference
* @param {Object} transaction.knex - knex instance * @param {Object} transaction.knex - knex instance
*/ */
function addUnique(tableName, columns, transaction) { async function addUnique(tableName, columns, transaction) {
return (transaction || db.knex).schema.table(tableName, function (table) { const hasUniqueConstraint = await hasUnique(tableName, columns, transaction);
table.unique(columns);
}); if (!hasUniqueConstraint) {
logging.info(`Adding unique constraint for: ${columns} in table ${tableName}`);
return (transaction || db.knex).schema.table(tableName, function (table) {
table.unique(columns);
});
} else {
logging.warn(`Constraint for: ${columns} already exists for table: ${tableName}`);
}
} }
/** /**
@ -85,10 +132,17 @@ function addUnique(tableName, columns, transaction) {
* @param {Object} transaction - connnection object containing knex reference * @param {Object} transaction - connnection object containing knex reference
* @param {Object} transaction.knex - knex instance * @param {Object} transaction.knex - knex instance
*/ */
function dropUnique(tableName, columns, transaction) { async function dropUnique(tableName, columns, transaction) {
return (transaction || db.knex).schema.table(tableName, function (table) { const hasUniqueConstraint = await hasUnique(tableName, columns, transaction);
table.dropUnique(columns);
}); if (hasUniqueConstraint) {
logging.info(`Dropping unique constraint for: ${columns} in table: ${tableName}`);
return (transaction || db.knex).schema.table(tableName, function (table) {
table.dropUnique(columns);
});
} else {
logging.warn(`Constraint for: ${columns} does not exist for table: ${tableName}`);
}
} }
/** /**