diff --git a/core/server/data/migrations/utils.js b/core/server/data/migrations/utils.js index d1b96fbe33..55b0c90fad 100644 --- a/core/server/data/migrations/utils.js +++ b/core/server/data/migrations/utils.js @@ -3,6 +3,7 @@ const logging = require('@tryghost/logging'); const errors = require('@tryghost/errors'); const tpl = require('@tryghost/tpl'); const commands = require('../schema').commands; +const DatabaseInfo = require('@tryghost/database-info'); const MIGRATION_USER = 1; @@ -443,18 +444,32 @@ function createDropColumnMigration(table, column, columnDefinition) { /** * @param {string} table * @param {string} column - * + * @param {Object} options + * @param {boolean} options.disableForeignKeyChecks Disable foreign key checks for the down operation (when dropping nullable) * @returns {Migration} */ -function createSetNullableMigration(table, column) { - return createNonTransactionalMigration( +function createSetNullableMigration(table, column, options = {}) { + return createTransactionalMigration( async function up(knex) { logging.info(`Setting nullable: ${table}.${column}`); await commands.setNullable(table, column, knex); }, async function down(knex) { - logging.info(`Dropping nullable: ${table}.${column}`); - await commands.dropNullable(table, column, knex); + if (DatabaseInfo.isSQLite(knex)) { + options.disableForeignKeyChecks = false; + } + logging.info(`Dropping nullable: ${table}.${column}${options.disableForeignKeyChecks ? ' with foreign keys disabled' : ''}`); + if (options.disableForeignKeyChecks) { + await knex.raw('SET FOREIGN_KEY_CHECKS=0;').transacting(knex); + } + + try { + await commands.dropNullable(table, column, knex); + } finally { + if (options.disableForeignKeyChecks) { + await knex.raw('SET FOREIGN_KEY_CHECKS=1;').transacting(knex); + } + } } ); } @@ -462,14 +477,29 @@ function createSetNullableMigration(table, column) { /** * @param {string} table * @param {string} column - * + * @param {Object} options + * @param {boolean} options.disableForeignKeyChecks Disable foreign key checks for the up operation (when dropping nullable) * @returns {Migration} */ -function createDropNullableMigration(table, column) { - return createNonTransactionalMigration( +function createDropNullableMigration(table, column, options = {}) { + return createTransactionalMigration( async function up(knex) { - logging.info(`Dropping nullable: ${table}.${column}`); - await commands.dropNullable(table, column, knex); + if (DatabaseInfo.isSQLite(knex)) { + options.disableForeignKeyChecks = false; + } + logging.info(`Dropping nullable: ${table}.${column}${options.disableForeignKeyChecks ? ' with foreign keys disabled' : ''}`); + + if (options.disableForeignKeyChecks) { + await knex.raw('SET FOREIGN_KEY_CHECKS=0;').transacting(knex); + } + + try { + await commands.dropNullable(table, column, knex); + } finally { + if (options.disableForeignKeyChecks) { + await knex.raw('SET FOREIGN_KEY_CHECKS=1;').transacting(knex); + } + } }, async function down(knex) { logging.info(`Setting nullable: ${table}.${column}`); diff --git a/core/server/data/migrations/versions/5.0/2022-05-03-09-39-drop-nullable-subscribe-event-newsletter-id.js b/core/server/data/migrations/versions/5.0/2022-05-03-09-39-drop-nullable-subscribe-event-newsletter-id.js new file mode 100644 index 0000000000..cd3272f0e4 --- /dev/null +++ b/core/server/data/migrations/versions/5.0/2022-05-03-09-39-drop-nullable-subscribe-event-newsletter-id.js @@ -0,0 +1,4 @@ +const {createDropNullableMigration} = require('../../utils'); + +// We need to disable foreign key checks because if MySQL is missing the STRICT_TRANS_TABLES mode, we cannot drop nullable from a foreign key +module.exports = createDropNullableMigration('members_subscribe_events', 'newsletter_id', {disableForeignKeyChecks: true});