如何解决PostgreSQL允许不存在的外键
我有一个使用postgresql 12的Rails应用程序。最近,我编写了一些测试并看到了一些奇怪的行为。
我有一个countries
表。其模式如下所示:
qq2_test=# \d countries;
Table "public.countries"
Column | Type | Collation | Nullable | Default
-----------------------+--------------------------------+-----------+----------+---------------------------------------
id | bigint | | not null | nextval('countries_id_seq'::regclass)
domain | character varying | | not null | ''::character varying
root_city_id | bigint | | |
language_id | bigint | | not null |
currency_id | bigint | | not null |
google_tag_manager_id | character varying | | not null | ''::character varying
created_at | timestamp(6) without time zone | | not null |
updated_at | timestamp(6) without time zone | | not null |
Indexes:
"countries_pkey" PRIMARY KEY,btree (id)
"index_countries_on_domain" UNIQUE,btree (domain)
"index_countries_on_currency_id" btree (currency_id)
"index_countries_on_language_id" btree (language_id)
Foreign-key constraints:
"fk_rails_6f479b409c" FOREIGN KEY (language_id) REFERENCES languages(id)
"fk_rails_7cac1212c7" FOREIGN KEY (currency_id) REFERENCES currencies(id)
"fk_rails_beac36a0bd" FOREIGN KEY (root_city_id) REFERENCES cities(id)
Referenced by:
TABLE "country_translations" CONSTRAINT "fk_rails_0c4ee35f26" FOREIGN KEY (country_id) REFERENCES countries(id) ON DELETE CASCADE
TABLE "countries_languages" CONSTRAINT "fk_rails_556e7398aa" FOREIGN KEY (country_id) REFERENCES countries(id) ON DELETE CASCADE
TABLE "cities" CONSTRAINT "fk_rails_996e05be41" FOREIGN KEY (country_id) REFERENCES countries(id)
如您所见,我在currency_id
和language_id
字段上都具有外键约束。
运行测试时,我看到该表中有记录:
qq2_test=# select * from countries;
id | domain | root_city_id | language_id | currency_id | google_tag_manager_id | created_at | updated_at
-----------+--------+--------------+-------------+-------------+-----------------------+----------------------------+----------------------------
665664142 | com | | 1019186233 | 432072940 | | 2020-10-23 06:20:49.288637 | 2020-10-23 06:20:49.288637
169150333 | by | | 1019186233 | 432072940 | | 2020-10-23 06:20:49.288637 | 2020-10-23 06:20:49.288637
(2 rows)
我的测试记录有两个,它们具有语言和货币参考。但是他们的表是空的:
qq2_test=# select * from currencies;
id | name | symbol | created_at | updated_at
----+------+--------+------------+------------
(0 rows)
qq2_test=# select * from languages;
id | name | locale | image | created_at | updated_at
----+------+--------+-------+------------+------------
(0 rows)
为什么Postgresql允许countries
表中不存在引用?
- Ruby 2.7.1 MRI
- 路轨:6.0.3.4
- Postgresql 12.4
- Ubuntu 20.04
解决方法
只有两个选项:
-
外键约束为
NOT VALID
。为新条目限制了此类约束,但是现有条目会违反它们。
但这会显示在您的
\d
输出中,事实并非如此。 -
您的数据已损坏。
除了硬件问题或软件错误外,可能的解释是:
-
有人设置了
session_replication_role = replica
,以使触发器不会触发。 -
超级用户已运行
ALTER TABLE countries DISABLE TRIGGER ALL;
-
假设您是唯一使用数据库的人(因为您正在谈论的是小型的1-2行测试),那么我猜您的Rails应用程序(或相应的驱动程序)正在禁用触发器或外键检查。完全有可能像这样绕过外键检查:
edb=# show session_replication_role ;
session_replication_role
--------------------------
origin
(1 row)
edb=# create table city (id int primary key,name text);
CREATE TABLE
edb=# select * from city;
id | name
----+------
(0 rows)
edb=# create table person (id int,name text,city int references city(id));
CREATE TABLE
edb=# insert into person values (1,'foo',1);
ERROR: insert or update on table "person" violates foreign key constraint "person_city_fkey"
DETAIL: Key (city)=(1) is not present in table "city".
edb=# set session_replication_role to replica;
SET
edb=# insert into person values (1,1);
INSERT 0 1
edb=# select * from person;
id | name | city
----+------+------
1 | foo | 1
(1 row)
edb=# select * from city;
id | name
----+------
(0 rows)
我建议您暂时设置log_statement = all
并再次运行测试-然后在Postgres服务器日志中查看(对于Ubuntu,默认值应为/var/log/postgresql/postgresql-12-main.log
),这可能会禁用外键约束检查,然后相应地解决您的发现。
版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。