views:

1486

answers:

6

I'm adding a new, "NOT NULL" column to my Postgresql database using the following query (sanitized for the Internet):

ALTER TABLE mytable ADD COLUMN mycolumn character varying(50) NOT NULL;

Each time I run this query, I receive the following error message:

ERROR:  column "mycolumn" contains null values

I'm stumped. Where am I going wrong?

NOTE: I'm using pgAdmin III (1.8.4) primarily, but I received the same error when I ran the SQL from within Terminal.

+10  A: 

Since rows already exist in the table, the ALTER statement is trying to insert NULL into the newly created column for all of the existing rows. You would have to add the column as allowing NULL, then fill the column with the values you want, and then set it to NOT NULL afterwards.

Sean Bright
Ah...of course! Thanks!
Huuuze
+3  A: 

You either need to define a default, or do what Sean says and add it without the null constraint until you've filled it in on the existing rows.

Paul Tomblin
A: 

Or, create a new table as temp with the extra column, copy the data to this new table while manipulating it as necessary to fill the non-nullable new column, and then swap the table via a two-step name change.

Yes, it is more complicated, but you may need to do it this way if you don't want a big UPDATE on a live table.

alphadogg
I didn't -1 you, but I think there may be subtle difficulties with this -- e.g. I'm betting that existing indices, triggers and views will continue to refer to the original table even after the rename as I think they store the relid of the table (which doesn't change) rather than its name.
j_random_hacker
Yes, I should have stated that the new table should be an exact copy of the original, including adding indices and such. My bad for being too brief.The reason for this is that there are also subtle diffculties of performing an ALTER on a table that may be live, and sometimes you need to stage it.
alphadogg
For example, using the DEFAULT approach, you'll add that default value to each row. Not sure how Postgres locks up a table when doing this. Or, if column order matters, you can't just add a column with the ALTER command.
alphadogg
Fair enough, ALTER TABLE does lock the table according to the PostgreSQL docs, however your non-transactional approach risks losing changes if the table is indeed live. Also I'd suggest that any code that relies on column order is broken (that may be outside your control of course).
j_random_hacker
+9  A: 

You have to set a default value.

ALTER TABLE mytable ADD COLUMN mycolumn character varying(50) NOT NULL DEFAULT 'foo';

... some work (set real values as you want)...

ALTER TABLE mytable ALTER COLUMN mycolumn DROP DEFAULT;
Luc M
Nice solution. I couldn't get to the online postgres docs for some reason to see what the syntax would be for this.
Sean Bright
I had to do this recently... I took my code :-)
Luc M
+2  A: 

Specifying a default value would also work, assuming a default value is appropriate.

Ryan Graham
+2  A: 

As others have observed, you must either create a nullable column or provide a DEFAULT value. If that isn't flexible enough (e.g. if you need the new value to be computed for each row individually somehow), you can use that fact that in PostgreSQL, all DDL commands can be executed inside a transaction:

BEGIN;
ALTER TABLE mytable ADD COLUMN mycolumn character varying(50);
UPDATE mytable SET mycolumn = timeofday();    -- Just a silly example
ALTER TABLE mytable ALTER COLUMN mycolumn SET NOT NULL;
COMMIT;
j_random_hacker