Adding a non-nullable column to existing table fails. Is the value attribute being ignored?
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:
Add the column
ALTER TABLE layer ADD COLUMN abstract_trimmed varchar(455);
Set it to a non-null value for every row
UPDATE table SET abstract_trimmed = 'No text';
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
:
ALTER TABLE layer
ADD COLUMN abstract_trimmed varchar(455) NOT NULL DEFAULT 'No text';
The manual:
When a column is added with
ADD COLUMN
and a non-volatileDEFAULT
is
specified, the default is evaluated at the time of the statement and
the result stored in the table's metadata. That value will be used for
the column for all existing rows. If noDEFAULT
is specified, NULL is
used. In neither case is a rewrite of the table required.Adding a column with a volatile
DEFAULT
or changing the type of an
existing column will require the entire table and its indexes to be
rewritten. As an exception, when changing the type of an existing
column, if theUSING
clause does not change the column contents and
the old type is either binary coercible to the new type or an
unconstrained domain over the new type, a table rewrite is not needed;
but any indexes on the affected columns must still be rebuilt. Table
and/or index rebuilds may take a significant amount of time for a
large table; and will temporarily require as much as double the disk space.
Entity Framework Code first making a column non-nullable
That's because you allowed NULL
values in that column, then tried to make it non-nullable. It will subsequently try to migrate your existing data into that newly non-nullable column, which will break because you already have NULL
values in there.
Two solutions:
1) Change it back to nullable
2) Give it a default value for items that don't have a value.
How to add new column with default value from existing column in Liquibase
Since no one answered here I'm posting the way I handled it:
<changeSet id="Add MODIFY_USER_ID to ORDERS" author="Noam">
<addColumn tableName="ORDERS">
<column name="MODIFY_USER_ID" type="BIGINT">
<constraints foreignKeyName="ORDERS_MODIFY_FK" referencedTableName="USERS" referencedColumnNames="ID"/>
</column>
</addColumn>
</changeSet>
<changeSet id="update the new MODIFY_USER_ID column to get the CREATOR" author="Noam">
<sql>update ORDERS set MODIFY_USER_ID = CREATOR</sql>
</changeSet>
<changeSet id="Add not nullable constraint on MODIFY_USER_ID column" author="Noam">
<addNotNullConstraint tableName="ORDERS" columnName="MODIFY_USER_ID" columnDataType="BIGINT"/>
</changeSet>
I've done this in three different change-sets as the documentation recommends
Why is SQL server throwing this error: Cannot insert the value NULL into column 'id'?
I'm assuming that id
is supposed to be an incrementing value.
You need to set this, or else if you have a non-nullable column, with no default value, if you provide no value it will error.
To set up auto-increment in SQL Server Management Studio:
- Open your table in
Design
- Select your column and go to
Column Properties
- Under
Indentity Specification
, set(Is Identity)=Yes
andIndentity Increment=1
Grails Database Migration plugin silently fails to add not null constraint
While at lunch I was thinking about this and wondered why I hadn't taken an obvious debugging step. The groovy script changes were made from scratch. The update to add the column worked, which means that the only difference between my DB and my domain object was the not nullable constraint:
class Game {
String genre
static constraints = {
genre(nullable: false, blank: false)
}
}
...so why not run grails dbm-gorm-diff
and see what the plugin comes up with?
The result was this:
databaseChangeLog = {
changeSet(author: "Ryan (generated)", id: "1340897310305-1") {
addNotNullConstraint(columnDataType: "varchar(255)", columnName: "genre", tableName: "game")
}
}
The only difference between my created change script and the script that the plugin made was the:
columnDataType: "varchar(255)"
I added that to the script I made (just to be sure I hadn't done anything else wrong) and bingo, update worked.
Update columns if input values are not null otherwise ignore and keep the existing values of column in database
You are inserting the user name straight into the SQL without escaping or even quoting. I think you simply missed the apostrophes.
To prevent SQL injection issues, NEVER insert SQL string constants from dynamic data, ALWAYS use PreparedStatement and insert markers.
Alternatively, escape the values, but using markers is much safer, and improves SQL performance by allowing the database to cache the compiled SQL statement.
String updateQuery = "UPDATE " + USER_TABLE +
" SET " + USER_TABLE_FIRST_NAME + "=IFNULL(? ," + USER_TABLE_FIRST_NAME + ")," +
USER_TABLE_LAST_NAME + "=?," +
USER_TABLE_ABOUT_ME + "=?," +
USER_TABLE_CITY + "=?," +
USER_TABLE_DOB + "=?" +
" WHERE " + USER_TABLE_ID + "=?";
PreparedStatement stmt = conn.prepareStatement(updateQuery);
stmt.setString(1, user.getFirstName());
stmt.setString(2, user.getLastName());
stmt.setString(3, user.getAboutMe());
stmt.setString(4, user.getCity());
stmt.setString(5, user.getDateOfBirth());
stmt.setString(6, user.getUserId());
Note: Answer extended to cover the null check issue.
When you're using simple string injection, "A='" + name + "'"
becomes A='Joe'
for a non-null value but A='null'
for a null value, which is definitely not what you want.
By using parameter markers, the value of ?
can be null
, which means that IFNULL(?, Name)
will give the exact behavior needed, i.e. using the value of ?
when it's not null, and the value of NAME
when ?
is null.
Related Topics
Firstname, Lastname in Sql, Too Complex
Why Does This Oracle Drop Column Alter The Default Value of Another Column
Executing SQL Query on Multiple Databases
Is Using "Not Exists" Considered to Be Bad SQL Practise
How to Deal with Spark Udf Input/Output of Primitive Nullable Type
How to Execute SQL Statements in Command Prompt (Cmd)
How to List All Stored Procedures in Informix
How to Get Just The First Row in a Result Set After Ordering
Change Data Type Varchar to Varbinary(Max) in SQL Server
Haversine Formula Using SQL Server to Find Closest Venue - VB.NET
Unique Date Range Fields in SQL Server 2008
Comparing Comma Separated Values from Two Columns of Two Different Tables
Select Multiple Rows from Single Column into Single Row
Sql Server: Do I Need to Use Go Statements Between Batches
Best Way to Change Clustered Index (Pk) in SQL 2005
Need to Convert Text Field to Varchar Temporarily So That I Can Pass to a Stored Procedure