There is a table t which has inherited children. I want that only the children can receive inserts. What is the best way to force that the parent table rejects inserts?
create table t (c int);
create table t1 () inherits (t);
This should not be possible:
insert into t (c) values (1);
EDIT:
I found a model visible solution in addition to the one from @wildplasser:
create table tfk (c integer unique check(false));
create table t (c integer, foreign key (c) references tfk(c));
Now it is not possible to insert into t
UNLESS it is a null value, and still possible to insert into
its children. It can be a good solution if that columnn is already constrained as not null
but not enough otherwise. Or does someone know a trick to make the above work for null values?
I asked for a new syntax in the postgresql list and it was done for 9.2:
Allow CHECK constraints to be declared NO INHERIT (Nikhil Sontakke, Alex Hunsaker)
This makes them enforceable only on the parent table, not on child tables.
create table t (c int, check (false) no inherit);
This will prevent inserts into table t
. It adds a constraint that will never be true, so no data can be inserted. no inherit
will prevent that constraint from affecting child tables.
You could use a before insert trigger to raise an error or redirect to the correct table.
This is ugly, but it appears to work:
--SET search_path='tmp';
DROP TABLE dontinsert CASCADE;
CREATE TABLE dontinsert
( id INTEGER NOT NULL PRIMARY KEY
);
DROP TABLE doinsert CASCADE;
CREATE TABLE doinsert ()
INHERITS (dontinsert)
;
CREATE RULE dont_do_it AS
ON INSERT TO dontinsert
DO INSTEAD NOTHING
;
INSERT INTO dontinsert(id) VALUES( 13) ;
INSERT INTO doinsert(id) VALUES( 42) ;
SELECT id AS id_from_dont FROM dontinsert;
SELECT id AS id_from_do FROM doinsert;
Result:
SET
NOTICE: drop cascades to table doinsert
DROP TABLE
NOTICE: CREATE TABLE / PRIMARY KEY will create implicit index "dontinsert_pkey" for table "dontinsert"
CREATE TABLE
ERROR: table "doinsert" does not exist
CREATE TABLE
CREATE RULE
INSERT 0 0
INSERT 0 1
id_from_dont
--------------
42
(1 row)
id_from_do
------------
42
(1 row)
UPDATE: since the OP wants INSERTSs to fail with a lot of noise, I had to add a canary-table with an impossible constraint imposed on it:
DROP TABLE alwaysempty CASCADE;
CREATE TABLE alwaysempty
( id INTEGER NOT NULL
);
ALTER TABLE alwaysempty
ADD CONSTRAINT dont_insert_you_sucker CHECK (id > 0 AND id < 0)
;
CREATE RULE dont_do_it AS
ON INSERT TO dontinsert
DO INSTEAD -- NOTHING
INSERT INTO alwaysempty (id)
VALUES (NEW.id)
;
The new output:
SET
NOTICE: drop cascades to table doinsert
DROP TABLE
NOTICE: CREATE TABLE / PRIMARY KEY will create implicit index "dontinsert_pkey" for table "dontinsert"
CREATE TABLE
ERROR: table "doinsert" does not exist
CREATE TABLE
DROP TABLE
CREATE TABLE
ALTER TABLE
CREATE RULE
ERROR: new row for relation "alwaysempty" violates check constraint "dont_insert_you_sucker"
INSERT 0 1
id_from_dont
--------------
42
(1 row)
id_from_do
------------
42
(1 row)
Next attempt: move (ONLY) the basetable into an unreacheable schema (since I really hate triggers) ...
SET search_path='tmp';
DROP SCHEMA hidden CASCADE;
CREATE SCHEMA hidden;
REVOKE ALL ON SCHEMA hidden FROM PUBLIC;
DROP TABLE dontinsert CASCADE;
CREATE TABLE dontinsert
( id INTEGER NOT NULL PRIMARY KEY
);
DROP TABLE doinsert CASCADE;
CREATE TABLE doinsert ()
INHERITS (dontinsert)
;
ALTER TABLE ONLY dontinsert SET SCHEMA hidden;
INSERT INTO alwaysempty (id) VALUES (NEW.id) ;
INSERT INTO dontinsert(id) VALUES( 13) ;
INSERT INTO doinsert(id) VALUES( 42) ;
SELECT id AS id_from_dont FROM hidden.dontinsert;
SELECT id AS id_from_do FROM doinsert;
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With