principlesqlMinor
Truncation vs Deletion When `session_replication_role = replica`
Viewed 0 times
replicasession_replication_roletruncationwhendeletion
Problem
When I first run
And then run
It successfully deletes the row in
However, when I instead run
I get this output:
For performance reasons, I really want to be able to use TRUNCATE and not DELETE. How can I get around this issue without using TRUNCATE users CASCADE? I can not use cascading deletes because my DB may have other foreign keys that reference the users table that I do not want to delete.
SET session_replication_role = DEFAULT;
CREATE TABLE users (
id serial PRIMARY KEY
);
CREATE TABLE posts(
id serial PRIMARY KEY,
user_id INTEGER REFERENCES users(id)
);And then run
INSERT INTO users(id) VALUES (1);
INSERT INTO posts(id, user_id) VALUES (1, 1);
SET session_replication_role = replica;
DELETE FROM users;It successfully deletes the row in
users even though this violates referential integrity because the session_replication_role = replica. So far so good. However, when I instead run
INSERT INTO users(id) VALUES (1);
INSERT INTO posts(id, user_id) VALUES (1, 1);
SET session_replication_role = replica;
TRUNCATE users;I get this output:
ERROR: cannot truncate a table referenced in a foreign key constraint
DETAIL: Table "posts" references "users".
HINT: Truncate table "posts" at the same time, or use TRUNCATE ... CASCADE.For performance reasons, I really want to be able to use TRUNCATE and not DELETE. How can I get around this issue without using TRUNCATE users CASCADE? I can not use cascading deletes because my DB may have other foreign keys that reference the users table that I do not want to delete.
Solution
First and foremost you should know the name of the FOREIGN KEY constraint:
Then you can DROP FOREIGN KEY and after that, TRUNCATE the table.
Now,
db<>fiddle here
Quoted from Postgres docs:
ADD table_constraint [ NOT VALID ]
This form adds a new constraint to a table using the same syntax as CREATE TABLE, plus the option NOT VALID, which is currently only allowed for foreign key and CHECK constraints. If the constraint is marked NOT VALID, the potentially-lengthy initial check to verify that all rows in the table satisfy the constraint is skipped. The constraint will still be enforced against subsequent inserts or updates (that is, they'll fail unless there is a matching row in the referenced table, in the case of foreign keys; and they'll fail unless the new row matches the specified check constraints). But the database will not assume that the constraint holds for all rows in the table, until it is validated by using the VALIDATE CONSTRAINT option. Foreign key constraints on partitioned tables may not be declared NOT VALID at present.
The addition of a foreign key constraint requires a SHARE ROW EXCLUSIVE lock on the referenced table.
Additional restrictions apply when unique or primary key constraints are added to partitioned tables; see CREATE TABLE.
CREATE TABLE users (
id serial PRIMARY KEY
);
CREATE TABLE posts(
id serial PRIMARY KEY,
user_id INTEGER,
CONSTRAINT fk_post_users
FOREIGN KEY (user_id) REFERENCES users(id)
);
INSERT INTO users(id) VALUES (1);
INSERT INTO posts(id, user_id) VALUES (1, 1);Then you can DROP FOREIGN KEY and after that, TRUNCATE the table.
ALTER TABLE posts DROP CONSTRAINT fk_post_users;
TRUNCATE users;Now,
posts table remains in an inconsistent state, but you can add the foreign key again using NOT VALID option, that does not check current values.ALTER TABLE posts ADD CONSTRAINT fk_post_users
FOREIGN KEY (user_id) REFERENCES users(id) NOT VALID;db<>fiddle here
Quoted from Postgres docs:
ADD table_constraint [ NOT VALID ]
This form adds a new constraint to a table using the same syntax as CREATE TABLE, plus the option NOT VALID, which is currently only allowed for foreign key and CHECK constraints. If the constraint is marked NOT VALID, the potentially-lengthy initial check to verify that all rows in the table satisfy the constraint is skipped. The constraint will still be enforced against subsequent inserts or updates (that is, they'll fail unless there is a matching row in the referenced table, in the case of foreign keys; and they'll fail unless the new row matches the specified check constraints). But the database will not assume that the constraint holds for all rows in the table, until it is validated by using the VALIDATE CONSTRAINT option. Foreign key constraints on partitioned tables may not be declared NOT VALID at present.
The addition of a foreign key constraint requires a SHARE ROW EXCLUSIVE lock on the referenced table.
Additional restrictions apply when unique or primary key constraints are added to partitioned tables; see CREATE TABLE.
Code Snippets
CREATE TABLE users (
id serial PRIMARY KEY
);
CREATE TABLE posts(
id serial PRIMARY KEY,
user_id INTEGER,
CONSTRAINT fk_post_users
FOREIGN KEY (user_id) REFERENCES users(id)
);
INSERT INTO users(id) VALUES (1);
INSERT INTO posts(id, user_id) VALUES (1, 1);ALTER TABLE posts DROP CONSTRAINT fk_post_users;
TRUNCATE users;ALTER TABLE posts ADD CONSTRAINT fk_post_users
FOREIGN KEY (user_id) REFERENCES users(id) NOT VALID;Context
StackExchange Database Administrators Q#223296, answer score: 3
Revisions (0)
No revisions yet.