Switched to util for retrieving DB info in migrations

refs https://github.com/TryGhost/Toolbox/issues/174

- right now, our migrations manually check the client of the knex
  instance to see whether we're running on MySQL or SQLite
- that's been working fine, but the problem is that we're due to switch
  to the mysql2 driver soon, so all these checks will be faulty
- i've altered the functionality of `@tryghost/database-info` to accept
  a knex instance, and it'll return if the DB is MySQL or SQLite in some
  helper functions
- this commit bumps the package and switches to that format
- originally I used a shared instance of the class within
  `@tryghost/database-info` but there's a chance that the knex instance
  inside migrations actually comes from knex-migrator, and not Ghost, so
  that wouldn't work
This commit is contained in:
Daniel Lockyer 2022-03-01 15:33:31 +01:00
parent 91d5fa0fc5
commit d0e71524ca
18 changed files with 51 additions and 43 deletions

View File

@ -1,4 +1,5 @@
const logging = require('@tryghost/logging');
const DatabaseInfo = require('@tryghost/database-info');
module.exports = {
config: {
@ -6,7 +7,7 @@ module.exports = {
},
async up({transacting: knex}) {
if (knex.client.config.client !== 'mysql') {
if (!DatabaseInfo.isMySQL(knex)) {
logging.warn('Skipping cleanup of duplicate subscriptions - database is not MySQL');
return;
}

View File

@ -1,4 +1,5 @@
const logging = require('@tryghost/logging');
const DatabaseInfo = require('@tryghost/database-info');
module.exports = {
config: {
@ -6,7 +7,7 @@ module.exports = {
},
async up({transacting: knex}) {
if (knex.client.config.client !== 'mysql') {
if (!DatabaseInfo.isMySQL(knex)) {
logging.warn('Skipping cleanup of duplicate customers - database is not MySQL');
return;
}

View File

@ -1,4 +1,5 @@
const logging = require('@tryghost/logging');
const DatabaseInfo = require('@tryghost/database-info');
module.exports = {
config: {
@ -6,7 +7,7 @@ module.exports = {
},
async up({transacting: knex}) {
if (knex.client.config.client !== 'mysql') {
if (!DatabaseInfo.isMySQL(knex)) {
logging.warn('Skipping cleanup of orphaned customers - database is not MySQL');
return;
}

View File

@ -1,4 +1,5 @@
const logging = require('@tryghost/logging');
const DatabaseInfo = require('@tryghost/database-info');
module.exports = {
config: {
@ -6,7 +7,7 @@ module.exports = {
},
async up({transacting: knex}) {
if (knex.client.config.client !== 'mysql') {
if (!DatabaseInfo.isMySQL(knex)) {
logging.warn('Skipping cleanup of orphaned subscriptions - database is not MySQL');
return;
}

View File

@ -1,4 +1,5 @@
const logging = require('@tryghost/logging');
const DatabaseInfo = require('@tryghost/database-info');
module.exports = {
config: {
@ -6,7 +7,7 @@ module.exports = {
},
async up({transacting: knex}) {
if (knex.client.config.client !== 'mysql') {
if (!DatabaseInfo.isMySQL(knex)) {
return logging.warn('Skipping member tables index creation - database is not MySQL');
}
@ -91,7 +92,7 @@ module.exports = {
},
async down({transacting: knex}) {
if (knex.client.config.client !== 'mysql') {
if (!DatabaseInfo.isMySQL(knex)) {
return logging.warn('Skipping member tables index removal - database is not MySQL');
}

View File

@ -1,11 +1,12 @@
const logging = require('@tryghost/logging');
const {createNonTransactionalMigration} = require('../../utils');
const DatabaseInfo = require('@tryghost/database-info');
module.exports = createNonTransactionalMigration(
async function up(knex) {
let hasIndex = false;
if (knex.client.config.client === 'sqlite3') {
if (DatabaseInfo.isSQLite(knex)) {
const result = await knex.raw(`select * from sqlite_master where type = 'index' and tbl_name = 'email_recipients' and name = 'email_recipients_email_id_member_email_index'`);
hasIndex = result.length !== 0;
} else {
@ -27,7 +28,7 @@ module.exports = createNonTransactionalMigration(
async function down(knex) {
let missingIndex = false;
if (knex.client.config.client === 'sqlite3') {
if (DatabaseInfo.isSQLite(knex)) {
const result = await knex.raw(`select * from sqlite_master where type = 'index' and tbl_name = 'email_recipients' and name = 'email_recipients_email_id_member_email_index'`);
missingIndex = result.length === 0;
} else {
@ -42,7 +43,7 @@ module.exports = createNonTransactionalMigration(
logging.info('Dropping composite index on email_recipients for [email_id, member_email]');
if (knex.client.config.client === 'mysql') {
if (DatabaseInfo.isMySQL(knex)) {
await knex.schema.table('email_recipients', (table) => {
table.dropForeign('email_id');
table.dropIndex(['email_id', 'member_email']);

View File

@ -1,8 +1,9 @@
const {createIrreversibleMigration} = require('../../utils');
const logging = require('@tryghost/logging');
const DatabaseInfo = require('@tryghost/database-info');
module.exports = createIrreversibleMigration(async function up(connection) {
if (connection.client.config.client === 'mysql') {
if (DatabaseInfo.isMySQL(connection)) {
logging.info('Skipping removal of orphaned stripe records for MySQL');
return;
}

View File

@ -1,9 +1,10 @@
const logging = require('@tryghost/logging');
const {createIrreversibleMigration} = require('../../utils');
const {addForeign, dropForeign} = require('../../../schema/commands');
const DatabaseInfo = require('@tryghost/database-info');
module.exports = createIrreversibleMigration(async (knex) => {
if (knex.client.config.client !== 'sqlite3') {
if (!DatabaseInfo.isSQLite(knex)) {
return logging.warn('Skipping adding "on delete cascade" - database is not SQLite3');
}

View File

@ -1,9 +1,10 @@
const logging = require('@tryghost/logging');
const {createIrreversibleMigration} = require('../../utils');
const {addForeign, dropForeign} = require('../../../schema/commands');
const DatabaseInfo = require('@tryghost/database-info');
module.exports = createIrreversibleMigration(async (knex) => {
if (knex.client.config.client !== 'sqlite3') {
if (!DatabaseInfo.isSQLite(knex)) {
return logging.warn('Skipping fixing foreign key for members_stripe_customers_subscriptions - database is not SQLite3');
}

View File

@ -1,10 +1,11 @@
const logging = require('@tryghost/logging');
const {createTransactionalMigration} = require('../../utils');
const {addUnique} = require('../../../schema/commands');
const DatabaseInfo = require('@tryghost/database-info');
module.exports = createTransactionalMigration(
async function up(connection) {
if (connection.client.config.client !== 'sqlite3') {
if (!DatabaseInfo.isSQLite(connection)) {
return logging.warn('Skipping adding unique constraint for members_stripe_customers_subscriptions and members_stripe_customers - database is not SQLite3');
}

View File

@ -1,6 +1,7 @@
const logging = require('@tryghost/logging');
const {createNonTransactionalMigration} = require('../../utils');
const {addUnique} = require('../../../schema/commands');
const DatabaseInfo = require('@tryghost/database-info');
module.exports = createNonTransactionalMigration(
async function up(knex) {
@ -14,7 +15,7 @@ module.exports = createNonTransactionalMigration(
table.string('portal_title', 191).nullable();
});
if (knex.client.config.client === 'sqlite3') {
if (DatabaseInfo.isSQLite(knex)) {
// eslint-disable-next-line no-restricted-syntax
for (const column of ['name', 'code', 'stripe_coupon_id']) {
await addUnique('offers', column, knex);
@ -32,7 +33,7 @@ module.exports = createNonTransactionalMigration(
table.string('portal_title', 191).notNullable();
});
if (knex.client.config.client === 'sqlite3') {
if (DatabaseInfo.isSQLite(knex)) {
// eslint-disable-next-line no-restricted-syntax
for (const column of ['name', 'code', 'stripe_coupon_id']) {
await addUnique('offers', column, knex);

View File

@ -1,10 +1,10 @@
const logging = require('@tryghost/logging');
const {createTransactionalMigration} = require('../../utils');
const DatabaseInfo = require('@tryghost/database-info');
module.exports = createTransactionalMigration(
async function up(knex) {
if (knex.client.config.client !== 'mysql') {
if (!DatabaseInfo.isMySQL(knex)) {
logging.warn('Skipping cleanup of duplicate offer redemptions - database is not MySQL');
return;
}

View File

@ -1,9 +1,10 @@
const logging = require('@tryghost/logging');
const {createNonTransactionalMigration} = require('../../utils');
const DatabaseInfo = require('@tryghost/database-info');
module.exports = createNonTransactionalMigration(
async function up(knex) {
if (knex.client.config.client === 'sqlite3') {
if (DatabaseInfo.isSQLite(knex)) {
logging.warn('Skipping migration for SQLite3');
return;
}

View File

@ -1,9 +1,10 @@
const logging = require('@tryghost/logging');
const {createNonTransactionalMigration} = require('../../utils');
const DatabaseInfo = require('@tryghost/database-info');
module.exports = createNonTransactionalMigration(
async function up(knex) {
if (knex.client.config.client === 'sqlite3') {
if (DatabaseInfo.isSQLite(knex)) {
logging.warn('Skipping migration for SQLite3');
return;
}

View File

@ -1,9 +1,10 @@
const logging = require('@tryghost/logging');
const {createTransactionalMigration} = require('../../utils');
const DatabaseInfo = require('@tryghost/database-info');
module.exports = createTransactionalMigration(
async function up(knex) {
if (knex.client.config.client === 'sqlite3') {
if (DatabaseInfo.isSQLite(knex)) {
logging.warn('Skipping migration for SQLite3');
return;
}
@ -21,7 +22,7 @@ module.exports = createTransactionalMigration(
`);
},
async function down(knex) {
if (knex.client.config.client === 'sqlite3') {
if (DatabaseInfo.isSQLite(knex)) {
logging.warn('Skipping migration for SQLite3');
return;
}

View File

@ -4,6 +4,7 @@ const logging = require('@tryghost/logging');
const errors = require('@tryghost/errors');
const tpl = require('@tryghost/tpl');
const db = require('../db');
const DatabaseInfo = require('@tryghost/database-info');
const schema = require('./schema');
const clients = require('./clients');
@ -135,9 +136,7 @@ async function dropUnique(tableName, columns, transaction = db.knex) {
* @param {import('knex')} configuration.transaction - connection object containing knex reference
*/
async function hasForeignSQLite({fromTable, fromColumn, toTable, toColumn, transaction = db.knex}) {
const client = transaction.client.config.client;
if (client !== 'sqlite3') {
if (!DatabaseInfo.isSQLite(transaction)) {
throw new errors.InternalServerError({
message: tpl(messages.hasForeignSQLite3)
});
@ -162,8 +161,7 @@ async function hasForeignSQLite({fromTable, fromColumn, toTable, toColumn, trans
* @param {import('knex')} configuration.transaction - connection object containing knex reference
*/
async function addForeign({fromTable, fromColumn, toTable, toColumn, cascadeDelete = false, transaction = db.knex}) {
const isSQLite = transaction.client.config.client === 'sqlite3';
if (isSQLite) {
if (DatabaseInfo.isSQLite(transaction)) {
const foreignKeyExists = await hasForeignSQLite({fromTable, fromColumn, toTable, toColumn, transaction});
if (foreignKeyExists) {
logging.warn(`Skipped adding foreign key from ${fromTable}.${fromColumn} to ${toTable}.${toColumn} - foreign key already exists`);
@ -175,7 +173,7 @@ async function addForeign({fromTable, fromColumn, toTable, toColumn, cascadeDele
//disable and re-enable foreign key checks on sqlite because of https://github.com/knex/knex/issues/4155
let foreignKeysEnabled;
if (isSQLite) {
if (DatabaseInfo.isSQLite(transaction)) {
foreignKeysEnabled = await db.knex.raw('PRAGMA foreign_keys;');
if (foreignKeysEnabled[0].foreign_keys) {
await db.knex.raw('PRAGMA foreign_keys = OFF;');
@ -190,7 +188,7 @@ async function addForeign({fromTable, fromColumn, toTable, toColumn, cascadeDele
}
});
if (isSQLite) {
if (DatabaseInfo.isSQLite(transaction)) {
if (foreignKeysEnabled[0].foreign_keys) {
await db.knex.raw('PRAGMA foreign_keys = ON;');
}
@ -215,8 +213,7 @@ async function addForeign({fromTable, fromColumn, toTable, toColumn, cascadeDele
* @param {import('knex')} configuration.transaction - connection object containing knex reference
*/
async function dropForeign({fromTable, fromColumn, toTable, toColumn, transaction = db.knex}) {
const isSQLite = transaction.client.config.client === 'sqlite3';
if (isSQLite) {
if (DatabaseInfo.isSQLite(transaction)) {
const foreignKeyExists = await hasForeignSQLite({fromTable, fromColumn, toTable, toColumn, transaction});
if (!foreignKeyExists) {
logging.warn(`Skipped dropping foreign key from ${fromTable}.${fromColumn} to ${toTable}.${toColumn} - foreign key does not exist`);
@ -228,7 +225,7 @@ async function dropForeign({fromTable, fromColumn, toTable, toColumn, transactio
//disable and re-enable foreign key checks on sqlite because of https://github.com/knex/knex/issues/4155
let foreignKeysEnabled;
if (isSQLite) {
if (DatabaseInfo.isSQLite(transaction)) {
foreignKeysEnabled = await db.knex.raw('PRAGMA foreign_keys;');
if (foreignKeysEnabled[0].foreign_keys) {
await db.knex.raw('PRAGMA foreign_keys = OFF;');
@ -239,7 +236,7 @@ async function dropForeign({fromTable, fromColumn, toTable, toColumn, transactio
table.dropForeign(fromColumn);
});
if (isSQLite) {
if (DatabaseInfo.isSQLite(transaction)) {
if (foreignKeysEnabled[0].foreign_keys) {
await db.knex.raw('PRAGMA foreign_keys = ON;');
}
@ -260,9 +257,7 @@ async function dropForeign({fromTable, fromColumn, toTable, toColumn, transactio
* @param {import('knex')} transaction - connection object containing knex reference
*/
async function hasPrimaryKeySQLite(tableName, transaction = db.knex) {
const client = transaction.client.config.client;
if (client !== 'sqlite3') {
if (!DatabaseInfo.isSQLite(transaction)){
throw new errors.InternalServerError({
message: tpl(messages.hasPrimaryKeySQLiteError)
});
@ -282,8 +277,7 @@ async function hasPrimaryKeySQLite(tableName, transaction = db.knex) {
* @param {import('knex')} transaction - connection object containing knex reference
*/
async function addPrimaryKey(tableName, columns, transaction = db.knex) {
const isSQLite = transaction.client.config.client === 'sqlite3';
if (isSQLite) {
if (DatabaseInfo.isSQLite(transaction)) {
const primaryKeyExists = await hasPrimaryKeySQLite(tableName, transaction);
if (primaryKeyExists) {
logging.warn(`Primary key constraint for: ${columns} already exists for table: ${tableName}`);
@ -366,7 +360,7 @@ function getColumns(table, transaction = db.knex) {
function checkTables(transaction = db.knex) {
const client = transaction.client.config.client;
if (client === 'mysql') {
if (DatabaseInfo.isMySQL(transaction)) {
return clients[client].checkPostTable();
}
}

View File

@ -64,7 +64,7 @@
"@tryghost/config-url-helpers": "0.1.4",
"@tryghost/constants": "1.0.1",
"@tryghost/custom-theme-settings-service": "0.3.1",
"@tryghost/database-info": "0.1.0",
"@tryghost/database-info": "0.2.4",
"@tryghost/debug": "0.1.13",
"@tryghost/domain-events": "0.1.8",
"@tryghost/email-analytics-provider-mailgun": "1.0.7",

View File

@ -1891,10 +1891,10 @@
"@tryghost/tpl" "^0.1.4"
lodash "^4.17.21"
"@tryghost/database-info@0.1.0":
version "0.1.0"
resolved "https://registry.yarnpkg.com/@tryghost/database-info/-/database-info-0.1.0.tgz#881e47e802fc2ae321d8c42d17a2a1cb8e4da143"
integrity sha512-3Nwo6pGR8yXrhSky2Uikz8oEKJsBLuaG68rcwIsMB6kFoWR3q7uAMWPvw3jIzVNnkZE1FQPb36w4z1t/v7dujw==
"@tryghost/database-info@0.2.4":
version "0.2.4"
resolved "https://registry.yarnpkg.com/@tryghost/database-info/-/database-info-0.2.4.tgz#83ca8976878f5a8eb8b1b3b229294252932bcbe0"
integrity sha512-zIV0IPpHXvH8ThuMpO9JhbUy8tntBnlF+UIon5KtryZq6F91n0lf/ZGpvvm+7X4kduTQQsIk26/S3UzrI88LeA==
"@tryghost/debug@0.1.10":
version "0.1.10"