Skip to main content
edited title
Link
s.k
  • 384
  • 1
  • 7
  • 25

How to correctly deal with IDENTITY fields on parent/child tabletables when using inheritance in PostgreSQL

Source Link
s.k
  • 384
  • 1
  • 7
  • 25

How to correctly deal with IDENTITY fields on parent/child table when using inheritance in PostgreSQL

I have a rather simple question when playing with a PG 15.1 database.

I've tried to set up a simple inheritance case:

DROP TABLE IF EXISTS cities CASCADE;

CREATE TABLE IF NOT EXISTS cities (
  id INT UNIQUE PRIMARY KEY GENERATED ALWAYS AS IDENTITY,
  "name" TEXT,
  population REAL,
  elevation INT
);

CREATE TABLE IF NOT EXISTS capitals (
  "state" CHAR(2) UNIQUE NOT NULL
) INHERITS (cities);

INSERT INTO cities (name, population, elevation) 
VALUES ('City One', 25000, 430), ('Town Two', 18000, 380), ('Urban 3', 30000, 400), ('Metropolitan 4', 50000, 450);

INSERT INTO capitals (name, population, elevation, state)
VALUES ('Capital 1', 1200000, 550, 'AK'), ('Capital 2', 1030000, 540, 'ZA');

But this raises the following error:

NOTICE:  drop cascades to table capitals
ERROR:  Failing row contains (null, Capital 1, 1.2e+06, 550, AK).null value in column "id" of relation "capitals" violates not-null constraint 

ERROR:  null value in column "id" of relation "capitals" violates not-null constraint
SQL state: 23502
Detail: Failing row contains (null, Capital 1, 1.2e+06, 550, AK).

Which is OK I guess, because the doc says:

If a column in the parent table is an identity column, that property is not inherited. A column in the child table can be declared identity column if desired.

Other people met the same issue and a frequent suggestion is to use the SERIAL type instead. Which I don't really want to do.

So I also added the identity field on the child table:

DROP TABLE IF EXISTS cities CASCADE;

CREATE TABLE IF NOT EXISTS cities (
  id INT UNIQUE PRIMARY KEY GENERATED ALWAYS AS IDENTITY,
  "name" TEXT,
  population REAL,
  elevation INT
);

CREATE TABLE IF NOT EXISTS capitals (
  id INT PRIMARY KEY GENERATED ALWAYS AS IDENTITY, -- added
  "state" CHAR(2) UNIQUE NOT NULL
) INHERITS (cities);

INSERT INTO cities (name, population, elevation) 
VALUES ('City One', 25000, 430), ('Town Two', 18000, 380), ('Urban 3', 30000, 400), ('Metropolitan 4', 50000, 450);

INSERT INTO capitals (name, population, elevation, state)
VALUES ('Capital 1', 1200000, 550, 'AK'), ('Capital 2', 1030000, 540, 'ZA');

This runs fine. But there is something strange now.

Indeed, this first SELECT statement seems to be OK:


SELECT * FROM capitals;
 id |   name    | population | elevation | state 
----+-----------+------------+-----------+-------
  1 | Capital 1 |    1.2e+06 |       550 | AK
  2 | Capital 2 |   1.03e+06 |       540 | ZA
(2 rows)

But this one returns multiple times the same id in the first column:

SELECT * FROM cities;
 id |      name      | population | elevation 
----+----------------+------------+-----------
  1 | City One       |      25000 |       430
  2 | Town Two       |      18000 |       380
  3 | Urban 3        |      30000 |       400
  4 | Metropolitan 4 |      50000 |       450
  1 | Capital 1      |    1.2e+06 |       550
  2 | Capital 2      |   1.03e+06 |       540
(6 rows)

So it seems that the uniqueness of the identity field of the parent table is no more respected.

I'm wondering why I met this behavior, and how could I properly deal with ids in case of inheritance in PostgreSQL? Especially, how can I keep a unique identifier on the parent table (if this makes sense)?

An underlying target for me is to have the parent table having a FK on itself, e.g. a field called twined_with to be able to link two cities as twin cities.