liquibase change primary key granularity

9.4k Views Asked by At

I need to change a table to add a column and include it to the Primary Key. So I have a luiqbase changeset:

I wonder how to separate concerns and implement rollback correctly.

  1. addColumn and PK changes in one changeset
<changeSet author="rahul" id="change_pk">
        <addColumn tableName="posts">
            <column name="aux_id" type="INT" defaultValue="0"/>
        </addColumn>
        <dropPrimaryKey tableName="posts"/>
        <addPrimaryKey tableName="posts" columnNames="id,aux_id"/>
        <rollback>
          <dropPrimaryKey tableName="posts"/>
          <addPrimaryKey tableName="posts" columnNames="id"/>
        </rollback>
    </changeSet>

What concerns me in this approach is that if I am unable to create a column, I will drop PK and recreate it, which may affect DB response time, assuming the table is quite big. However, this makes all these changes atomic.

  1. Move PK changes to a different changeset
<changeSet author="rahul" id="add_col">
        <addColumn tableName="posts">
            <column name="aux_id" type="INT" defaultValue="0"/>
        </addColumn>
</changeSet>
<changeSet author="rahul" id="change_pk">
        <dropPrimaryKey tableName="posts"/>
        <addPrimaryKey tableName="posts" columnNames="id,aux_id"/>
        <rollback>
          <dropPrimaryKey tableName="posts"/>
          <addPrimaryKey tableName="posts" columnNames="id"/>
        </rollback>
</changeSet>

Thus I will be able to have more control over rollback for PK changes, which will drop and recreate the old one. However, there is a problem, when change_pk changeset is not applied - that I have a column, which should be included to a PK, but it is not, making system vulnerable to unique constraint violations.

1

There are 1 best solutions below

2
htshame On BEST ANSWER

You can do the following, using runAlways attribute. Also, check out different onFail options

<changeSet author="rahul" id="add_col" runAlways="true">
    <preConditions (perhaps, some non-default onFail option) >
        <not>
            <columnExists tableName="posts" columnName="aux_id"/>
        </not>
    </preConditions>
    <addColumn tableName="posts">
        <column name="aux_id" type="INT" defaultValue="0"/>
    </addColumn>
</changeSet>

<changeSet author="rahul" id="change_pk">
    <preConditions>
        <and>
            <columnExists tableName="posts" columnName="aux_id"/>
            <!-- perhaps some other precondition --> 
            <sqlCheck expectedResult="id">
                SELECT key_column_usage.column_name
                FROM   information_schema.key_column_usage
                WHERE  table_schema = SCHEMA()     
                AND    constraint_name = 'PRIMARY' 
                AND    table_name = 'posts' 
            </sqlCheck>
        </and>
    </preConditions>
    <dropPrimaryKey tableName="posts"/>
    <addPrimaryKey tableName="posts" columnNames="id,aux_id"/>
    <rollback>
        <dropPrimaryKey tableName="posts"/>
        <addPrimaryKey tableName="posts" columnNames="id"/>
        <dropColumn tableName="posts" columnName="aux_id">
    </rollback>
</changeSet>