Adding a non-nullable column to existing table fails. Is the “value” attribute being ignored?

时光总嘲笑我的痴心妄想 提交于 2019-12-06 20:24:45

问题


Background: we have a Grails 1.3.7 app and are using Liquibase to manage our database migrations.

I am trying to add a new column to an existing table which is not empty.

My changeset looks like this:

    changeSet(author: "someCoolGuy (generated)", id: "1326842592275-1") {
        addColumn(tableName: "layer") {
            column(name: "abstract_trimmed", type: "VARCHAR(455)", value: "No text") {
                constraints(nullable: "false")
            }
        }
    }

Which should have inserted the value 'No text' into every existing row, and therefore satisfied the not null constraint. Liquibase "Add Column" docs.

But when the migrations changesets are being applied I get the following exception:

liquibase.exception.DatabaseException: Error executing SQL ALTER TABLE layer ADD abstract_trimmed VARCHAR(455) NOT NULL: ERROR: column "abstract_trimmed" contains null values

Which looks to me like it is not using the 'value' attribute.

If I change my changeset to work look like the following I can achieve the same thing. But I don't want to (and shouldn't have to) do this.

    changeSet(author: "someCoolGuy (generated)", id: "1326842592275-1") {
        addColumn(tableName: "layer") {
            column(name: "abstract_trimmed", type: "VARCHAR(455)")
        }

        addNotNullConstraint(tableName: "layer", columnName:"abstract_trimmed", defaultNullValue: "No text")
    }

Is Liquibase really ignoring my value attribute, or is there something else going on here that I can't see?

I am using Grails 1.3.7, Database-migration plugin 1.0, Postgres 9.0


回答1:


Short answer

The "value" attribute will not work if you are adding a not-null constraint at the time of the column creation (this is not mentioned in the documentation). The SQL generated will not be able to execute.

Workaround

The workaround described in the question is the way to go. The resulting SQL will be:

  1. Add the column

    ALTER TABLE layer ADD COLUMN abstract_trimmed varchar(455);
    
  2. Set it to a non-null value for every row

    UPDATE table SET abstract_trimmed = 'No text';
    
  3. Add the NOT NULL constraint

    ALTER TABLE layer ALTER COLUMN abstract_trimmed SET NOT NULL;
    

Why?

A column default is only inserted into the column with an INSERT. The "value" tag will do that for you, but after the column is added. Liquibase tries to add the column in one step, with the NOT NULL constraint in place:

ALTER TABLE layer ADD abstract_trimmed VARCHAR(455) NOT NULL;

... which is not possible when the table already contains rows. It just isn't smart enough.

Alternative solution

Since PostgreSQL 8.0 (so almost forever by now) an alternative would be to add the new column with a non-null DEFAULT clause:

ALTER TABLE layer ADD COLUMN abstract_trimmed varchar(455) DEFAULT 'No text';

The manual on ALTER TABLE:

When a column is added with ADD COLUMN, all existing rows in the table are initialized with the column's default value (NULL if no DEFAULT clause is specified).




回答2:


Use "defaultValue" instead of "value" to set a default value for the new column.



来源:https://stackoverflow.com/questions/8904316/adding-a-non-nullable-column-to-existing-table-fails-is-the-value-attribute-b

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!