Postgresql 允许不存在的外键
Postgresql allows nonexistent foreign keys
我有一个使用 postgresql 12 的 rails 应用程序。最近,我编写了一些测试并看到了一些奇怪的行为。
我有一个 countries
table。它的架构如下所示:
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
字段上都有外键约束。
当我 运行 我的测试时,我看到 table:
中有记录
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)
我有两份测试记录,它们有语言和货币参考。但是他们的 table 是空的:
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
table 中不存在的引用?
- Ruby 2.7.1 核磁共振
- Rails: 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,'foo',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
) 什么可能会禁用您的外键约束检查,然后相应地解决您的发现。
我有一个使用 postgresql 12 的 rails 应用程序。最近,我编写了一些测试并看到了一些奇怪的行为。
我有一个 countries
table。它的架构如下所示:
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
字段上都有外键约束。
当我 运行 我的测试时,我看到 table:
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)
我有两份测试记录,它们有语言和货币参考。但是他们的 table 是空的:
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
table 中不存在的引用?
- Ruby 2.7.1 核磁共振
- Rails: 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,'foo',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
) 什么可能会禁用您的外键约束检查,然后相应地解决您的发现。