Как предотвратить вставки в родительскую таблицу? - PullRequest
9 голосов
/ 03 марта 2012

Есть таблица t, которая унаследовала детей. Я хочу, чтобы только дети могли получать вкладыши. Каков наилучший способ заставить родительскую таблицу отклонять вставки?

create table t (c int);
create table t1 () inherits (t);

Это не должно быть возможно:

insert into t (c) values (1);

EDIT:

В дополнение к решению от @wildplasser, я обнаружил видимое решение для модели:

* * 1010

Теперь insert into t невозможно, если только это нулевое значение, и все еще возможно insert into его потомков. Это может быть хорошим решением, если этот столбец уже ограничен как not null, но в противном случае этого недостаточно. Или кто-то знает хитрость, чтобы заставить вышеуказанное работать для нулевых значений?

Новости

Я запросил новый синтаксис в списке postgresql, и это было сделано для 9.2:

Разрешить объявлять ограничения CHECK НЕТ НАСЛЕДОВАНИЯ (Нихил Сонтакке, Алекс Хансакер)

Это делает их принудительными только для родительской таблицы, но не для дочерних таблиц.

Ответы [ 4 ]

5 голосов
/ 30 мая 2013
create table t (c int, check (false) no inherit);

Это предотвратит вставки в таблицу t. Это добавляет ограничение, которое никогда не будет истинным, поэтому никакие данные не могут быть вставлены. no inherit предотвратит влияние этого ограничения на дочерние таблицы.

3 голосов
/ 03 марта 2012

Вы можете использовать триггер перед вставкой, чтобы вызвать ошибку или перенаправить на правильную таблицу.

1 голос
/ 03 марта 2012

Это некрасиво, но, похоже, работает:

--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;

Результат:

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)

ОБНОВЛЕНИЕ: так как ОП хочет, чтобы вставки не работали с большим шумом, у меня былодобавить канареечную таблицу с невозможным ограничением:

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)
        ;

Новый вывод:

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)

Следующая попытка: переместить (ТОЛЬКО) базовую таблицу в недоступную схему (так как я действительно ненавижу вызывает) ...

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;
0 голосов
/ 12 марта 2016

обновление: revoke update и т. Д. На самом деле плохо, так как это приведет к сбою обновлений и не будет автоматически направлять их на разделы :-( ... так что лучший способ кажетсябыть @ простым проверочным условием JoeVanDyk для меня.


как @ derobert , уже упомянутым в комментарии, кажется хорошиммне, с учетом накладных расходов, скорости и практичности , чтобы сделать это через права :

revoke insert, update, references on  my_master_table  from  my_upd_usr

(references, потому что он не будет работать, как предполагалось, если используется: предостережения о наследовании )

(Конечно, предоставление некоторого пользователя прав на случайное добавление в мастер кажется возможным, например, путем предоставления некоторых прав для всех таблиц,но я думаю, что чаще всего вряд ли кто-то «случайно» предоставит права на операции DML. И даже если у этого человека есть права, зачем ему начинать вставку в пустую таблицу, видя, что она не используется таким образом, а данные находятся в разделах.)

...