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

Migration on ENUM with postgres dialect: type already exists #2554

Closed
ghost opened this issue Nov 10, 2014 · 34 comments
Closed

Migration on ENUM with postgres dialect: type already exists #2554

ghost opened this issue Nov 10, 2014 · 34 comments
Labels
dialect: postgres For issues and PRs. Things that involve PostgreSQL (and do not involve all dialects). type: bug

Comments

@ghost
Copy link

ghost commented Nov 10, 2014

Deleting previously existing ENUM column and re-creating it but with a different ENUM results in:

Executing (default): ALTER TABLE "Trackers" DROP COLUMN "type";
Executing (default): CREATE TYPE "enum_Trackers_type" AS ENUM('image', 'javascript', 'snippet'); ALTER TABLE "Trackers" ADD COLUMN "type" "enum_Trackers_type";
Possibly unhandled SequelizeDatabaseError: error: type "enum_Trackers_type" already exists
@janmeier
Copy link
Member

That's a postgres issue. Dropping an enum column does not drop the enum itself (since an enum might be used in several tables as I understand it)

@ghost
Copy link
Author

ghost commented Nov 10, 2014

@janmeier: Any idea how to correctly edit enum values through migration then?

@ghost
Copy link
Author

ghost commented Nov 14, 2014

Using raw query meanwhile, unless someone comes up with a better suggestion.

@mickhansen mickhansen added type: feature For issues and PRs. For new features. Never breaking changes. type: bug labels Dec 4, 2014
@laser
Copy link

laser commented Jan 9, 2015

@kytwb

Any idea how to correctly edit enum values through migration then?

I was able to modify an ENUM by:

  1. Modifying the array assigned to the values property of the configuration object passed as the second argument to sequelize.define to include a new value
  2. Create a migration in which the up simply calls ModelNameHere.sync()

That generated the SQL statements required to modify the ENUM-type to match what I'd expressed in JavaScript code.

Erin

@ghost
Copy link
Author

ghost commented Mar 22, 2015

@laser Tried what you said, it does work when you add only one new value at a time.

Executing (default): ALTER TYPE "enum_warehouses_type" ADD VALUE 'postmaster' BEFORE 'amazon'
Executing (default): ALTER TYPE "enum_warehouses_type" ADD VALUE 'amazon' AFTER 'postmaster'
Possibly unhandled SequelizeDatabaseError: "amazon" is not an existing enum label

With amazon and postmaster being the two new values here.

@Victor08
Copy link

Victor08 commented Feb 6, 2016

I am facing the same issue. I was able to bypass it with using raw query

module.exports = {
  up: function (queryInterface, Sequelize) {
    return queryInterface.sequelize.query("CREATE TYPE \"enum_users_sellerAccountType\" AS ENUM(\'sa\', \'sm\'); ALTER TABLE \"users\" ADD COLUMN \"sellerAccountType\" \"enum_users_sellerAccountType\";");
  },

  down: function (queryInterface, Sequelize) {
    return queryInterface.sequelize.query("DROP TYPE \"enum_users_sellerAccountType\"; ALTER TABLE \"users\" DROP COLUMN \"sellerAccountType\";");
  }
};

Is it a bug in sequelize, that causes enum migration to fail?

@delveintechnolabs
Copy link

Facing same issue. To add to the problem, sequelize only allows changing default value, allowance of null or the data type. If you want to change some other attribute of a column, you will have to find a hack for it.

@thealmightygrant
Copy link

thealmightygrant commented Oct 4, 2016

Heads up that if you decide to do raw queries like @Victor08 points out above, the down migration should drop the column first so that nothing depends on the data type, like this:

 down: function (queryInterface, Sequelize) {
    return queryInterface.sequelize.query("ALTER TABLE \"users\" DROP COLUMN \"userType\"; DROP TYPE \"enum_users_userType\";");
  }

@felixfbecker
Copy link
Contributor

We have QueryInterface.dropAllEnums(), I think we should add a simple dropEnum() method.

@kgalli
Copy link

kgalli commented Feb 21, 2017

@felixfbecker is there an update on the dropEnum() method? This would really be very handy. What is the context of QueryInterface.dropAllEnums()? Is it the whole database or maybe a table?

@felixfbecker
Copy link
Contributor

No, there is no update. Can't answer your second question.

@stale
Copy link

stale bot commented Jun 29, 2017

This issue has been automatically marked as stale because it has not had recent activity. It will be closed if no further activity occurs. If this is still an issue, just leave a comment 🙂

@markelog
Copy link

This is still an issue

@felixfbecker felixfbecker reopened this Jul 18, 2017
@stale stale bot removed the stale label Jul 18, 2017
@mariano-aguero
Copy link

mariano-aguero commented Sep 8, 2017

This is how I solved it, not the best way but it allows adding a new field with an existing enum without it throwing an error.

queryInterface.addColumn(
            'Users',
            'language', {
                type: Sequelize.ENUM('nl', 'en'),
                allowNull: false,
                defaultValue: 'nl'
            }).then(function (response) {
                console.log(response);
                done();
            }).catch(function (err) {
                return queryInterface.sequelize.query(`ALTER TABLE "Users" ADD COLUMN "language" "public"."enum_Users_language" NOT NULL DEFAULT 'nl';`)
                    .then(function (response) {
                        console.log(response);
                        done();
                    }).catch(function (err) {
                        done();
                    });
            });

@Edudjr
Copy link

Edudjr commented Sep 15, 2017

finally got it working:

'use strict';

module.exports = {
  up: (queryInterface, Sequelize) => {
    return queryInterface.sequelize.query("ALTER TYPE enum_type_name ADD VALUE 'new_value'");
  },

  down: (queryInterface, Sequelize) => {
    var query = 'DELETE FROM pg_enum ' +
      'WHERE enumlabel = \'new_value\' ' +
      'AND enumtypid = ( SELECT oid FROM pg_type WHERE typname = \'enum_type_name\')';
    return queryInterface.sequelize.query(query);
  }
};

change enum_type_name and new_value to fit your needs.

@JustBlackBird
Copy link

Any plans for adding QueryInterface.dropEnum() method?

@ajmueller
Copy link

ajmueller commented Oct 20, 2017

@Edudjr that almost worked for me, but I had to make an ever so slight change to your up function:

up: (queryInterface, Sequelize) => {
    return queryInterface.sequelize.query(`ALTER TYPE "enum_type_name" ADD VALUE 'new_value'`);
}

As you can see, I had to wrap my enum name in double quotes; for whatever reason, that's how Sequelize created my enum, so without the double quotes I got a "type does not exist" error. I'm not sure if this is a standard for Sequelize, but I figured I'd post my findings here in case anyone else ran into a similar issue. Also note that due to the use of both single and double quotes, I wrapped the query in tick marks since I'm using a new enough version of Node that supports template strings, thereby avoiding the need to escape any strings.

And for the down function, I made similar changes for readability:

down: (queryInterface, Sequelize) => {
    return queryInterface.sequelize.query(`
        DELETE 
        FROM
            pg_enum
        WHERE
            enumlabel = 'new_value' AND
            enumtypid = (
                SELECT
                    oid
                FROM
                    pg_type
                WHERE
                    typname = 'enum_type_name'
            )
    `);
}

Strangely enough, enum_type_name here in the pg_type table did not have double quotes in its actual value, unlike the type that was created.

@abelosorio
Copy link

See my comment #7151 (comment).

I've written a function to entirely replace the ENUM. It works inside a transaction and supports default value.

Hope you find it helpul.

@theSuiGenerisAakash
Copy link

theSuiGenerisAakash commented Feb 13, 2018

I asked this on SO here and this is something sweet and simple that seems to be doing it

down: (queryInterface, Sequelize) => queryInterface.removeColumn('TableName', 'ColumnName') .then(() => queryInterface.sequelize.query('DROP TYPE "enum_TableName_ColumnName";')); };

@tomprogers
Copy link

The best way to modify enum values is to queryInterface.changeColumn:

module.exports = {
  up  : function (queryInterface, Sequelize) {
    return queryInterface
      .changeColumn(tableName, 'status', {
        type: Sequelize.ENUM('a', 'b', 'c', 'd'),
        allowNull: false
      });
  },
  down: function (queryInterface, Sequelize) {
    return queryInterface
      .changeColumn(tableName, 'status', {
        type: Sequelize.ENUM('a', 'b', 'c'),
        allowNull: false
      });
  }
};

@naartjie
Copy link

The best way to modify enum values is to queryInterface.changeColumn:

@tomprogers does it work with Postgres?

@jraut
Copy link

jraut commented Jun 18, 2018

@naartjie @tomprogers No, it does not seem to work on Postgres.
ERROR: type "tableName_status" already exists

@abelosorio
Copy link

@naartjie @tomprogers @jraut is not possible to "change" an enum in Postgres. You have to destroy it and create it again with the new values.

Checkout this issue #7151 and this comment #7151 (comment).

I created this npm package to change enums in Postgres: https://www.npmjs.com/package/sequelize-replace-enum-postgres. Hope it helps.

Good luck!

@robcresswell
Copy link

@abelosorio It seems like you've been able to alter ENUMs since at least Postgres 9.1https://www.postgresql.org/docs/9.1/static/sql-altertype.html so I'm not sure why you are saying it isn't possible. Can we get this issue reopened @sushantdhiman ?

@rostismal
Copy link

is not fixed

@DaddyWarbucks
Copy link

This may be a bit off topic to this particular thread, but I wanted to post my solution to a similar problem incase anyone ends up here like I did.

I was trying to set an ENUM along with allowNull: true, then do a bulk update, and then changeColumn to allowNull: false

await queryInterface.addColumn(
        'payments',
        'entity_type',
        {
          type: Sequelize.ENUM,
          allowNull: true // set to true to allow bulk update
          values: [SHIPPER, CARRIER]
        },
        { transaction }
      );

await queryInterface.bulkUpdate(
        'payments',
        {
          entity_id: 1,
          entity_type: SHIPPER
        },
        {},
        { transaction }
      );

await queryInterface.changeColumn(
        'payments',
        'entity_type',
        {
          type: Sequelize.ENUM,
          allowNull: false,
          values: [SHIPPER, CARRIER]
        },
        { transaction }
      );

^^ This code threw the error enum_payments_entity_type already exists. Even when trying to manually drop it, alter the column, etc, I could never get it to work.
My solution was to set the column as a STRING first, then come back and set it as an ENUM.

await queryInterface.addColumn(
        'payments',
        'entity_type',
        {
          type: Sequelize.STRING, // NOTE THIS IS A STRING
          allowNull: true // set to true to allow bulk update
        },
        { transaction }
      );

await queryInterface.bulkUpdate(
        'payments',
        {
          entity_id: 1,
          entity_type: SHIPPER
        },
        {},
        { transaction }
      );

await queryInterface.changeColumn(
        'payments',
        'entity_type',
        {
          type: Sequelize.ENUM,
          allowNull: false,
          values: [SHIPPER, CARRIER]
        },
        { transaction }
      );

I am not very well versed in Sequelize (or in RDMS in general) but this bug kicked my butt. Hopefully if this is helpful to someone. And if this is bad practice, feel free to downvote it and tell me why.

@Dakkers
Copy link

Dakkers commented Jul 16, 2019

still not fixed 😭

@Otavioensa
Copy link

I'm still running on the same problem

@Otavioensa
Copy link

This issue should definitely be reopened

@papb
Copy link
Member

papb commented Jul 23, 2019

Hello, this thread is very long and was closed twice already... Can someone please provide an up-to-date MVCE for this? Something that I can just copy+paste and run and see the problem. If I can easily reproduce it, I will reopen this issue. Thanks!!

@papb papb added dialect: postgres For issues and PRs. Things that involve PostgreSQL (and do not involve all dialects). and removed type: feature For issues and PRs. For new features. Never breaking changes. labels Jul 23, 2019
@MaxMls
Copy link

MaxMls commented Dec 14, 2019

i have this problem with code:

module.exports = (sequelize, DataTypes) => {
	const tag = sequelize.define('tag', {
		name: {allowNull: false, type: DataTypes.TEXT},
		type: {allowNull: false, type: DataTypes.ENUM('lang', 'category', 'author')}
	}, {});
	tag.associate = function (models) {
		tag.belongsToMany(models.chapter, {through: 'titlesTags'});

		tag.belongsToMany(models.user, {through: 'tagBlackList'});

	};
	return tag;
};

error:
(node:24616) UnhandledPromiseRejectionWarning: SequelizeDatabaseError: тип "enum_tags_type" уже существует

@papb
Copy link
Member

papb commented Jan 16, 2020

I think issues #2577 and #7649 cover everything mentioned in this issue already.

@navsaini53

This comment was marked as spam.

@RasenGUY
Copy link

@kytwb

Any idea how to correctly edit enum values through migration then?

I was able to modify an ENUM by:

1. Modifying the array assigned to the `values` property of the configuration object passed as the second argument to `sequelize.define` to include a new value

2. Create a migration in which the `up` simply calls `ModelNameHere.sync()`

That generated the SQL statements required to modify the ENUM-type to match what I'd expressed in JavaScript code.

Erin

this works thanks for sharing.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
dialect: postgres For issues and PRs. Things that involve PostgreSQL (and do not involve all dialects). type: bug
Projects
None yet
Development

No branches or pull requests