Отключить проверку внешнего ключа PostgreSQL для миграции

92

Я создаю много миграций с внешними ключами в PostgreSQL 9.4.

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

В MySQL я могу упростить это, просто добавив SET FOREIGN_KEY_CHECKS = 0;в начало файла миграции. Как я могу сделать это временно в PostgresSQL только на время кода миграции?

Кстати, используя для этого Laravel Schema Builder.

eComEvo
источник

Ответы:

83

PostgreSQL не поддерживает никаких параметров конфигурации, но есть и другая возможность.

postgres=# \d b
        Table "public.b"
┌────────┬─────────┬───────────┐
│ Column │  Type   │ Modifiers │
╞════════╪═════════╪═══════════╡
│ id     │ integer │           │
└────────┴─────────┴───────────┘
Foreign-key constraints:
    "b_id_fkey" FOREIGN KEY (id) REFERENCES a(id) DEFERRABLE

Ссылочная целостность в Postgres реализуется с помощью триггеров, и вы можете отключить триггеры для таблицы. С помощью этого метода вы можете загружать любые данные (риск), но это значительно быстрее, потому что проверка больших данных стоит дорого. И если ваша загрузка безопасна, вы можете это сделать.

BEGIN;
ALTER TABLE b DISABLE TRIGGER ALL;
-- now the RI over table b is disabled
ALTER TABLE b ENABLE TRIGGER ALL;
COMMIT;

Следующая возможность - использование отложенных ограничений. Это проверка ограничения перемещения для фиксации времени. Поэтому не следует соблюдать порядок в INSERTкомандах:

ALTER TABLE b ALTER CONSTRAINT b_id_fkey DEFERRABLE;

BEGIN
postgres=# SET CONSTRAINTS b_id_fkey DEFERRED;
SET CONSTRAINTS
postgres=# INSERT INTO b VALUES(100); -- this is not in a table
INSERT 0 1
postgres=# INSERT INTO b VALUES(10);
INSERT 0 1 
postgres=# COMMIT;
ERROR:  insert or update on table "b" violates foreign key constraint "b_id_fkey"
DETAIL:  Key (id)=(100) is not present in table "a".

Этот метод должен быть предпочтительным для вас, потому что вставленные данные будут проверяться.

Павел Стехуле
источник
2
По какой-то причине у меня это сработало один раз, а потом совсем нет. Я использую aws aurora postgres, где они блокируют super userроль, чтобы клиенты не могли испортить настройки репликации. Похоже, мне нужно быть суперпользователем, чтобы отключить некоторые системные триггеры. (В настоящее время я использую свою учетную запись администратора, которая также является владельцем - я не уверен, почему она сработала однажды.) Установка параметра репликации также не является жизнеспособным вариантом, поскольку для этого также требуется super userроль. Мой единственный вариант - отбросить и воссоздать внешние ключи ...
ps2goat
Тоже самое. Делает DISABLE TRIGGER ALLчто-то, но это не имеет никакого эффекта. Я даже не получаю предупреждения. Это просто игнорируется.
jayarjo,
В Amazon RDS возникает следующая ошибка:> доступ запрещен: «RI_ConstraintTrigger_a_23031» - системный триггер, поэтому этот рецепт, к сожалению, не для всех случаев :)
kolypto
Ошибка и на локальной БД. Пользователь, имеющий все права доступа к базе данных, получил ** разрешение отклонено: «RI_ConstraintTrigger_a_16564» - системный триггер **
Solo.dmitry
154

Для миграции проще отключить все триггеры с помощью:

SET session_replication_role = 'replica';

И после миграции снова включите все с помощью

SET session_replication_role = 'origin';
andro83
источник
3
Святая корова, это и проще, и больше подходит для конкретной задачи. (Да.)
ijoseph
10
Предупреждение: для этого требуются привилегии суперпользователя. Попробуйте "УСТАНОВИТЬ ВСЕ ОТЛОЖЕННЫЕ ОГРАНИЧЕНИЯ".
JJC
9
Я в деле, 10.4и это утверждение, похоже, не работает.
Стефан
2
Сможет ли кто-нибудь описать опасности / риски этого метода, и в каких сценариях его следует использовать и как снизить риски? Что является наилучшей практикой, если это считается плохой практикой?
karns
6
Кстати, этот параметр можно установить в AWS RDS в группе параметров базы данных и применить без перезапуска db! Очень полезно, если вы используете DMS в пустой базе данных с существующей схемой и созданными ограничениями.
Майк Атлас,