Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

renameColumn fails when composite foreign keys are present #2909

Open
Zakini opened this issue Nov 15, 2018 · 0 comments
Open

renameColumn fails when composite foreign keys are present #2909

Zakini opened this issue Nov 15, 2018 · 0 comments

Comments

@Zakini
Copy link

Zakini commented Nov 15, 2018

Environment

Knex version: 0.14.6
Database + version: MySQL 5.7.21
OS: macOS 10.13.6

Bug

Renaming a column in a table that has composite foreign keys fails with the following message:

Knex:warning - migration file "20181115140157_plugin-core-publish-schedule.js" failed
Knex:warning - migration failed with error: alter table `page` drop foreign key `page_id_content_type_id_foreign` - ER_CANT_DROP_FIELD_OR_KEY: Can't DROP 'page_id_content_type_id_foreign'; check thatcolumn/key exists
Warning: alter table `page` drop foreign key `page_id_content_type_id_foreign` - ER_CANT_DROP_FIELD_OR_KEY: Can't DROP 'page_id_content_type_id_foreign'; check that column/key exists Use --force to continue.

Here are the simplified definitions of the tables causing this:

CREATE TABLE `content` (
  `id` int(10) unsigned NOT NULL AUTO_INCREMENT,
  `content_type_id` int(10) unsigned NOT NULL,
  `published_at` timestamp NULL DEFAULT NULL,
  PRIMARY KEY (`id`),
  KEY `content_id_content_type_id_index` (`id`,`content_type_id`),
) ENGINE=InnoDB AUTO_INCREMENT=11 DEFAULT CHARSET=utf8mb4

CREATE TABLE `page` (
  `id` int(10) unsigned NOT NULL,
  `content_type_id` int(10) unsigned NOT NULL,
  `body` text NOT NULL,
  PRIMARY KEY (`id`),
  KEY `page_id_content_type_id_foreign` (`id`,`content_type_id`),
  CONSTRAINT `page_id_content_type_id_foreign` FOREIGN KEY (`id`, `content_type_id`) REFERENCES `content` (`id`, `content_type_id`) ON DELETE CASCADE ON UPDATE CASCADE
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4

Setup these table, then create and run a migration containing:

exports.up = async (knex) => {
  await knex.schema.alterTable('content', (table) => {
    table.renameColumn('published_at', 'publish_from');
  });
};

exports.down = async (knex) => {
  await knex.schema.alterTable('content', (table) => {
    table.renameColumn('publish_from', 'published_at');
  });
};

The problem here seems to be twofold:

  • renameColumn drops all foreign keys on a table, it should only drop foreign keys that reference the column being renamed
  • when renameColumn drops foreign keys, it attempts to drop composite foreign keys multiple times (once for each column in the key)

In my case since there are no foreign keys that reference the column I'm renaming, I can just do:

const renameColumn = async (knex, table, columnOld, columnNew) => {
  const [[columnCurrent]] = await knex.raw(`show fields from content where field = '${columnOld}'`);
  let sql = `alter table \`${table}\` change \`${columnOld}\` \`${columnNew}\` ${columnCurrent.Type}`;

  if (String(columnCurrent.Null).toUpperCase() !== 'YES') {
    sql += ' NOT NULL';
  }
  if (columnCurrent.Default !== undefined && columnCurrent.Default !== null) {
    sql += ` DEFAULT '${columnCurrent.Default}'`;
  }

  await knex.raw(sql);
};

exports.up = async (knex) => {
  await renameColumn(knex, 'content', 'published_at', 'publish_from');
};

exports.down = async (knex) => {
  await renameColumn(knex, 'content', 'publish_from', 'published_at');
};
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

2 participants