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;