7

I recently upgraded from PostgreSQL 9.5 to PostgreSQL 10. One of the nifty features in PostgreSQL 10 is the new identity column type, an alternative to PostgreSQL' serial pseudo-type. Official documentation for identity column can be found one the CREATE TABLE page.

However, when inserting multiple rows into a table with a GENERATED BY DEFAULT AS IDENTITY column and using the keyword DEFAULT to get the next ID value, the default value is coming back as null.

For example, let's say I have a table

CREATE TABLE test (
  id int GENERATED BY DEFAULT AS IDENTITY,
  t text
);
CREATE TABLE

Inserting a single row with the DEFAULT keyword seems to work fine.

INSERT INTO test (id, t) VALUES (DEFAULT, 'a');
INSERT 0 1

Inserting multiple rows does not.

INSERT INTO test (id, t) VALUES (DEFAULT, 'b'), (DEFAULT, 'c');
ERROR:  null value in column "id" violates not-null constraint
DETAIL:  Failing row contains (null, b).

Inserting multiple rows using an implicit default also works.

INSERT INTO test (t) VALUES ('d'), ('e');
INSERT 0 2

The problem specified above does not appear to be present when using the SERIAL column pseudo-type.

CREATE TABLE test2 (
  id SERIAL,
  t text
);
CREATE TABLE

INSERT INTO test2 (id, t) VALUES (DEFAULT, 'a'), (DEFAULT, 'b');
INSERT 0 2

So my question is: am I missing something? Is the DEFAULT keyword just not expected to work with the new identity column? Or is this a bug?

4

This is in fact a bug. I verified it. I went to go see if it was filed and it seems it already is. It's not just filed, the commit is there.

You can see their test, exactly like yours

+-- VALUES RTEs
+INSERT INTO itest3 VALUES (DEFAULT, 'a');
+INSERT INTO itest3 VALUES (DEFAULT, 'b'), (DEFAULT, 'c');
+SELECT * FROM itest3;

So just wait, it's there for PostgreSQL 10.2.

Possible work around for PostgreSQL < 10.2

If you absolutely must have this, and using the implicit column isn't acceptable. One easy solution would be to retrieve the sequence with the catalog info function

pg_get_serial_sequence(table_name, column_name) 

Which I believe should work, and to set that as a default.

ALTER TABLE ONLY test
  ALTER COLUMN id
  DEFAULT nextval('seqname');
  • 2
    Thanks for checking that it's already been filed (and committed). My plan is to continue using sequences where necessary until 10.2 – danwoz Dec 30 '17 at 20:21
4

It's a bug as @Evan discovered, so until it is fixed we have to work around it.

As you probably know, it's easy to work around the case for multiple column inserts where not all values are default — just omit the column(s) where you want the default value inserted entirely, default is the default:

INSERT INTO test (t) VALUES ('b'), ('c');
SELECT * FROM test;
id | t 
-: | :-
 1 | b 
 2 | c 

dbfiddle here

If there are multiple columns and you want them all to be default, you can still use a CTE:

WITH w AS (INSERT INTO test (t) VALUES (DEFAULT) RETURNING t )
INSERT INTO test (t) SELECT w.t FROM w CROSS JOIN generate_series(1, 1);
SELECT * FROM test;
id | t  
-: | :--
 1 | foo
 2 | foo

dbfiddle here

Unfortunately, there is no easy workaround if you need all columns to be populated with default:

For the case when there is only one column and it's a serial, I see no way to use the default.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.