2015-08-20 8 views
2

Мои PostgreSQL (9.2) база данных содержит две таблицы registrations и attributes с ограничением внешнего ключа:Причина нарушения внешнего ключа PostgreSQL?

postgres=# \d+ registrations; 
       Table "public.registrations" 
Column | Type | Modifiers | Storage | Stats target | Description 
---------+-------+-----------+----------+--------------+------------- 
name | text | not null | extended |    | 
parent | text |   | extended |    | 
storage | bytea |   | extended |    | 
Indexes: 
    "registrations_pkey" PRIMARY KEY, btree (name) 
Referenced by: 
    TABLE "attributes" CONSTRAINT "attributes_cname_fkey" FOREIGN KEY (cname) REFERENCES registrations(name) ON DELETE CASCADE 
Has OIDs: no 

postgres=# \d+ attributes; 
       Table "public.attributes" 
Column | Type | Modifiers | Storage | Stats target | Description 
--------+-------+-----------+----------+--------------+------------- 
cname | text | not null | extended |    | 
aname | text | not null | extended |    | 
tags | text |   | extended |    | 
value | bytea |   | extended |    | 
Indexes: 
    "attributes_pkey" PRIMARY KEY, btree (cname, aname) 
Foreign-key constraints: 
    "attributes_cname_fkey" FOREIGN KEY (cname) REFERENCES registrations(name) ON DELETE CASCADE 
Has OIDs: no 

В какой-то момент я понял, что некоторые строки нарушают ограничение внешнего ключа:

postgres=# SELECT COUNT(*) FROM attributes LEFT JOIN registrations ON attributes.cname=registrations.name WHERE registrations.name IS NULL; 
count 
------- 
    71 
(1 row) 

Могли бы вы помогите мне понять, как может произойти эта коррупция?

+0

X отправленного в http://dba.stackexchange.com/q/ 111655/7788 –

+0

@ CraigRinger X-post удален. – fiddler

ответ

1

Ограничение помечена как NOT VALID это один случай, можно было бы ожидать, чтобы увидеть нарушения, но NOT VALID будет отображаться в выводе psql \d+. (Я считаю, что можно вручную обновить этот флаг в каталоге, но я надеюсь, что это не проблема ...)

Насколько я знаю, единственный поддерживаемый способ обхода внешнего ключа перед изменением данных отметьте SET session_replication_role TO replica. Это происходит в интересах процессов репликации, работающих в предположении, что ограничение уже было проверено мастером, хотя это может пойти не так, если ваш репликатор ошибочен или неправильно настроен.

Это также возможно, чтобы суперпользователь вручную отключил триггеры ограничения ограничения (и часто возникает соблазн для кого-то, кто пытается ускорить массовый импорт). Ниже будет сообщать вам, если триггера в настоящее время активны (tgenabled должны быть 'O'):

SELECT * 
FROM pg_trigger 
WHERE tgname ~ '^RI_ConstraintTrigger' 
    AND tgrelid IN ('registrations'::regclass, 'attributes'::regclass) 

Я не думаю, что есть какой-либо способ узнать, было ли это временно изменить в прошлом, хотя, если у вас есть протоколирование заявления вы можете найти оператор ALTER TABLE ... DISABLE TRIGGER где-то там.

Существует также в ключевых органах внешних at least one loophole, и, конечно, это всегда возможно, что вы нашли ошибку ...

+0

Наша система фактически включает в себя репликацию между двумя базами данных, как вы думаете, это может быть причиной проблемы? – fiddler

+0

@fiddler: Предполагая, что вы используете какую-то репликацию на основе триггеров, а не репликацию в реестре Postgres, тогда это не может быть и речи. Хотя, предположительно, процесс репликации никогда не будет записываться мастеру, поэтому данные будут ошибочными только на подчиненном устройстве. –

+0

Ну, мы используем двоичную репликацию (потоковая репликация) – fiddler

0

Это может произойти, если contraint FK была создана с пунктом NOT VALID (не делайте этого):

CREATE TABLE one 
     (one_id INTEGER NOT NULL PRIMARY KEY 
     ); 

CREATE TABLE two 
     (two_id INTEGER NOT NULL PRIMARY KEY 
     , one_id INTEGER NOT NULL 
     ); 

INSERT INTO one(one_id) 
SELECT gs FROM generate_series(0,12,2) gs; 

INSERT INTO two(two_id,one_id) 
SELECT gs, gs FROM generate_series(0,12,3) gs; 

ALTER TABLE two 
     ADD CONSTRAINT omg FOREIGN KEY (one_id) references one(one_id) 
     -- DEFERRABLE INITIALLY DEFERRED 
     NOT VALID 
     ; 
+0

Есть ли какая-нибудь команда, чтобы проверить, было ли это ограничение создано с этим предложением? – fiddler

+0

Не знаю. Может быть, проверить журналы, если они есть? Примечание: pg_dump также испускает фразу 'NOT VALID', поэтому она должна быть в каталогах где-то. – wildplasser

+0

Не похоже на то, что в журналах я вижу следующее: 'create table attributes (cname text not null, aname text not null, теги text, value bytea, первичный ключ (cname, aname), внешний ключ (cname) ссылки регистрации (имя) на удаление каскада) ' – fiddler