1-- Test basic TRUNCATE functionality. 2CREATE TABLE truncate_a (col1 integer primary key); 3INSERT INTO truncate_a VALUES (1); 4INSERT INTO truncate_a VALUES (2); 5SELECT * FROM truncate_a; 6 col1 7------ 8 1 9 2 10(2 rows) 11 12-- Roll truncate back 13BEGIN; 14TRUNCATE truncate_a; 15ROLLBACK; 16SELECT * FROM truncate_a; 17 col1 18------ 19 1 20 2 21(2 rows) 22 23-- Commit the truncate this time 24BEGIN; 25TRUNCATE truncate_a; 26COMMIT; 27SELECT * FROM truncate_a; 28 col1 29------ 30(0 rows) 31 32-- Test foreign-key checks 33CREATE TABLE trunc_b (a int REFERENCES truncate_a); 34CREATE TABLE trunc_c (a serial PRIMARY KEY); 35CREATE TABLE trunc_d (a int REFERENCES trunc_c); 36CREATE TABLE trunc_e (a int REFERENCES truncate_a, b int REFERENCES trunc_c); 37TRUNCATE TABLE truncate_a; -- fail 38ERROR: cannot truncate a table referenced in a foreign key constraint 39DETAIL: Table "trunc_b" references "truncate_a". 40HINT: Truncate table "trunc_b" at the same time, or use TRUNCATE ... CASCADE. 41TRUNCATE TABLE truncate_a,trunc_b; -- fail 42ERROR: cannot truncate a table referenced in a foreign key constraint 43DETAIL: Table "trunc_e" references "truncate_a". 44HINT: Truncate table "trunc_e" at the same time, or use TRUNCATE ... CASCADE. 45TRUNCATE TABLE truncate_a,trunc_b,trunc_e; -- ok 46TRUNCATE TABLE truncate_a,trunc_e; -- fail 47ERROR: cannot truncate a table referenced in a foreign key constraint 48DETAIL: Table "trunc_b" references "truncate_a". 49HINT: Truncate table "trunc_b" at the same time, or use TRUNCATE ... CASCADE. 50TRUNCATE TABLE trunc_c; -- fail 51ERROR: cannot truncate a table referenced in a foreign key constraint 52DETAIL: Table "trunc_d" references "trunc_c". 53HINT: Truncate table "trunc_d" at the same time, or use TRUNCATE ... CASCADE. 54TRUNCATE TABLE trunc_c,trunc_d; -- fail 55ERROR: cannot truncate a table referenced in a foreign key constraint 56DETAIL: Table "trunc_e" references "trunc_c". 57HINT: Truncate table "trunc_e" at the same time, or use TRUNCATE ... CASCADE. 58TRUNCATE TABLE trunc_c,trunc_d,trunc_e; -- ok 59TRUNCATE TABLE trunc_c,trunc_d,trunc_e,truncate_a; -- fail 60ERROR: cannot truncate a table referenced in a foreign key constraint 61DETAIL: Table "trunc_b" references "truncate_a". 62HINT: Truncate table "trunc_b" at the same time, or use TRUNCATE ... CASCADE. 63TRUNCATE TABLE trunc_c,trunc_d,trunc_e,truncate_a,trunc_b; -- ok 64TRUNCATE TABLE truncate_a RESTRICT; -- fail 65ERROR: cannot truncate a table referenced in a foreign key constraint 66DETAIL: Table "trunc_b" references "truncate_a". 67HINT: Truncate table "trunc_b" at the same time, or use TRUNCATE ... CASCADE. 68TRUNCATE TABLE truncate_a CASCADE; -- ok 69NOTICE: truncate cascades to table "trunc_b" 70NOTICE: truncate cascades to table "trunc_e" 71-- circular references 72ALTER TABLE truncate_a ADD FOREIGN KEY (col1) REFERENCES trunc_c; 73-- Add some data to verify that truncating actually works ... 74INSERT INTO trunc_c VALUES (1); 75INSERT INTO truncate_a VALUES (1); 76INSERT INTO trunc_b VALUES (1); 77INSERT INTO trunc_d VALUES (1); 78INSERT INTO trunc_e VALUES (1,1); 79TRUNCATE TABLE trunc_c; 80ERROR: cannot truncate a table referenced in a foreign key constraint 81DETAIL: Table "truncate_a" references "trunc_c". 82HINT: Truncate table "truncate_a" at the same time, or use TRUNCATE ... CASCADE. 83TRUNCATE TABLE trunc_c,truncate_a; 84ERROR: cannot truncate a table referenced in a foreign key constraint 85DETAIL: Table "trunc_d" references "trunc_c". 86HINT: Truncate table "trunc_d" at the same time, or use TRUNCATE ... CASCADE. 87TRUNCATE TABLE trunc_c,truncate_a,trunc_d; 88ERROR: cannot truncate a table referenced in a foreign key constraint 89DETAIL: Table "trunc_e" references "trunc_c". 90HINT: Truncate table "trunc_e" at the same time, or use TRUNCATE ... CASCADE. 91TRUNCATE TABLE trunc_c,truncate_a,trunc_d,trunc_e; 92ERROR: cannot truncate a table referenced in a foreign key constraint 93DETAIL: Table "trunc_b" references "truncate_a". 94HINT: Truncate table "trunc_b" at the same time, or use TRUNCATE ... CASCADE. 95TRUNCATE TABLE trunc_c,truncate_a,trunc_d,trunc_e,trunc_b; 96-- Verify that truncating did actually work 97SELECT * FROM truncate_a 98 UNION ALL 99 SELECT * FROM trunc_c 100 UNION ALL 101 SELECT * FROM trunc_b 102 UNION ALL 103 SELECT * FROM trunc_d; 104 col1 105------ 106(0 rows) 107 108SELECT * FROM trunc_e; 109 a | b 110---+--- 111(0 rows) 112 113-- Add data again to test TRUNCATE ... CASCADE 114INSERT INTO trunc_c VALUES (1); 115INSERT INTO truncate_a VALUES (1); 116INSERT INTO trunc_b VALUES (1); 117INSERT INTO trunc_d VALUES (1); 118INSERT INTO trunc_e VALUES (1,1); 119TRUNCATE TABLE trunc_c CASCADE; -- ok 120NOTICE: truncate cascades to table "truncate_a" 121NOTICE: truncate cascades to table "trunc_d" 122NOTICE: truncate cascades to table "trunc_e" 123NOTICE: truncate cascades to table "trunc_b" 124SELECT * FROM truncate_a 125 UNION ALL 126 SELECT * FROM trunc_c 127 UNION ALL 128 SELECT * FROM trunc_b 129 UNION ALL 130 SELECT * FROM trunc_d; 131 col1 132------ 133(0 rows) 134 135SELECT * FROM trunc_e; 136 a | b 137---+--- 138(0 rows) 139 140DROP TABLE truncate_a,trunc_c,trunc_b,trunc_d,trunc_e CASCADE; 141-- Test TRUNCATE with inheritance 142CREATE TABLE trunc_f (col1 integer primary key); 143INSERT INTO trunc_f VALUES (1); 144INSERT INTO trunc_f VALUES (2); 145CREATE TABLE trunc_fa (col2a text) INHERITS (trunc_f); 146INSERT INTO trunc_fa VALUES (3, 'three'); 147CREATE TABLE trunc_fb (col2b int) INHERITS (trunc_f); 148INSERT INTO trunc_fb VALUES (4, 444); 149CREATE TABLE trunc_faa (col3 text) INHERITS (trunc_fa); 150INSERT INTO trunc_faa VALUES (5, 'five', 'FIVE'); 151BEGIN; 152SELECT * FROM trunc_f; 153 col1 154------ 155 1 156 2 157 3 158 4 159 5 160(5 rows) 161 162TRUNCATE trunc_f; 163SELECT * FROM trunc_f; 164 col1 165------ 166(0 rows) 167 168ROLLBACK; 169BEGIN; 170SELECT * FROM trunc_f; 171 col1 172------ 173 1 174 2 175 3 176 4 177 5 178(5 rows) 179 180TRUNCATE ONLY trunc_f; 181SELECT * FROM trunc_f; 182 col1 183------ 184 3 185 4 186 5 187(3 rows) 188 189ROLLBACK; 190BEGIN; 191SELECT * FROM trunc_f; 192 col1 193------ 194 1 195 2 196 3 197 4 198 5 199(5 rows) 200 201SELECT * FROM trunc_fa; 202 col1 | col2a 203------+------- 204 3 | three 205 5 | five 206(2 rows) 207 208SELECT * FROM trunc_faa; 209 col1 | col2a | col3 210------+-------+------ 211 5 | five | FIVE 212(1 row) 213 214TRUNCATE ONLY trunc_fb, ONLY trunc_fa; 215SELECT * FROM trunc_f; 216 col1 217------ 218 1 219 2 220 5 221(3 rows) 222 223SELECT * FROM trunc_fa; 224 col1 | col2a 225------+------- 226 5 | five 227(1 row) 228 229SELECT * FROM trunc_faa; 230 col1 | col2a | col3 231------+-------+------ 232 5 | five | FIVE 233(1 row) 234 235ROLLBACK; 236BEGIN; 237SELECT * FROM trunc_f; 238 col1 239------ 240 1 241 2 242 3 243 4 244 5 245(5 rows) 246 247SELECT * FROM trunc_fa; 248 col1 | col2a 249------+------- 250 3 | three 251 5 | five 252(2 rows) 253 254SELECT * FROM trunc_faa; 255 col1 | col2a | col3 256------+-------+------ 257 5 | five | FIVE 258(1 row) 259 260TRUNCATE ONLY trunc_fb, trunc_fa; 261SELECT * FROM trunc_f; 262 col1 263------ 264 1 265 2 266(2 rows) 267 268SELECT * FROM trunc_fa; 269 col1 | col2a 270------+------- 271(0 rows) 272 273SELECT * FROM trunc_faa; 274 col1 | col2a | col3 275------+-------+------ 276(0 rows) 277 278ROLLBACK; 279DROP TABLE trunc_f CASCADE; 280NOTICE: drop cascades to 3 other objects 281DETAIL: drop cascades to table trunc_fa 282drop cascades to table trunc_faa 283drop cascades to table trunc_fb 284-- Test ON TRUNCATE triggers 285CREATE TABLE trunc_trigger_test (f1 int, f2 text, f3 text); 286CREATE TABLE trunc_trigger_log (tgop text, tglevel text, tgwhen text, 287 tgargv text, tgtable name, rowcount bigint); 288CREATE FUNCTION trunctrigger() RETURNS trigger as $$ 289declare c bigint; 290begin 291 execute 'select count(*) from ' || quote_ident(tg_table_name) into c; 292 insert into trunc_trigger_log values 293 (TG_OP, TG_LEVEL, TG_WHEN, TG_ARGV[0], tg_table_name, c); 294 return null; 295end; 296$$ LANGUAGE plpgsql; 297-- basic before trigger 298INSERT INTO trunc_trigger_test VALUES(1, 'foo', 'bar'), (2, 'baz', 'quux'); 299CREATE TRIGGER t 300BEFORE TRUNCATE ON trunc_trigger_test 301FOR EACH STATEMENT 302EXECUTE PROCEDURE trunctrigger('before trigger truncate'); 303SELECT count(*) as "Row count in test table" FROM trunc_trigger_test; 304 Row count in test table 305------------------------- 306 2 307(1 row) 308 309SELECT * FROM trunc_trigger_log; 310 tgop | tglevel | tgwhen | tgargv | tgtable | rowcount 311------+---------+--------+--------+---------+---------- 312(0 rows) 313 314TRUNCATE trunc_trigger_test; 315SELECT count(*) as "Row count in test table" FROM trunc_trigger_test; 316 Row count in test table 317------------------------- 318 0 319(1 row) 320 321SELECT * FROM trunc_trigger_log; 322 tgop | tglevel | tgwhen | tgargv | tgtable | rowcount 323----------+-----------+--------+-------------------------+--------------------+---------- 324 TRUNCATE | STATEMENT | BEFORE | before trigger truncate | trunc_trigger_test | 2 325(1 row) 326 327DROP TRIGGER t ON trunc_trigger_test; 328truncate trunc_trigger_log; 329-- same test with an after trigger 330INSERT INTO trunc_trigger_test VALUES(1, 'foo', 'bar'), (2, 'baz', 'quux'); 331CREATE TRIGGER tt 332AFTER TRUNCATE ON trunc_trigger_test 333FOR EACH STATEMENT 334EXECUTE PROCEDURE trunctrigger('after trigger truncate'); 335SELECT count(*) as "Row count in test table" FROM trunc_trigger_test; 336 Row count in test table 337------------------------- 338 2 339(1 row) 340 341SELECT * FROM trunc_trigger_log; 342 tgop | tglevel | tgwhen | tgargv | tgtable | rowcount 343------+---------+--------+--------+---------+---------- 344(0 rows) 345 346TRUNCATE trunc_trigger_test; 347SELECT count(*) as "Row count in test table" FROM trunc_trigger_test; 348 Row count in test table 349------------------------- 350 0 351(1 row) 352 353SELECT * FROM trunc_trigger_log; 354 tgop | tglevel | tgwhen | tgargv | tgtable | rowcount 355----------+-----------+--------+------------------------+--------------------+---------- 356 TRUNCATE | STATEMENT | AFTER | after trigger truncate | trunc_trigger_test | 0 357(1 row) 358 359DROP TABLE trunc_trigger_test; 360DROP TABLE trunc_trigger_log; 361DROP FUNCTION trunctrigger(); 362-- test TRUNCATE ... RESTART IDENTITY 363CREATE SEQUENCE truncate_a_id1 START WITH 33; 364CREATE TABLE truncate_a (id serial, 365 id1 integer default nextval('truncate_a_id1')); 366ALTER SEQUENCE truncate_a_id1 OWNED BY truncate_a.id1; 367INSERT INTO truncate_a DEFAULT VALUES; 368INSERT INTO truncate_a DEFAULT VALUES; 369SELECT * FROM truncate_a; 370 id | id1 371----+----- 372 1 | 33 373 2 | 34 374(2 rows) 375 376TRUNCATE truncate_a; 377INSERT INTO truncate_a DEFAULT VALUES; 378INSERT INTO truncate_a DEFAULT VALUES; 379SELECT * FROM truncate_a; 380 id | id1 381----+----- 382 3 | 35 383 4 | 36 384(2 rows) 385 386TRUNCATE truncate_a RESTART IDENTITY; 387INSERT INTO truncate_a DEFAULT VALUES; 388INSERT INTO truncate_a DEFAULT VALUES; 389SELECT * FROM truncate_a; 390 id | id1 391----+----- 392 1 | 33 393 2 | 34 394(2 rows) 395 396CREATE TABLE truncate_b (id int GENERATED ALWAYS AS IDENTITY (START WITH 44)); 397INSERT INTO truncate_b DEFAULT VALUES; 398INSERT INTO truncate_b DEFAULT VALUES; 399SELECT * FROM truncate_b; 400 id 401---- 402 44 403 45 404(2 rows) 405 406TRUNCATE truncate_b; 407INSERT INTO truncate_b DEFAULT VALUES; 408INSERT INTO truncate_b DEFAULT VALUES; 409SELECT * FROM truncate_b; 410 id 411---- 412 46 413 47 414(2 rows) 415 416TRUNCATE truncate_b RESTART IDENTITY; 417INSERT INTO truncate_b DEFAULT VALUES; 418INSERT INTO truncate_b DEFAULT VALUES; 419SELECT * FROM truncate_b; 420 id 421---- 422 44 423 45 424(2 rows) 425 426-- check rollback of a RESTART IDENTITY operation 427BEGIN; 428TRUNCATE truncate_a RESTART IDENTITY; 429INSERT INTO truncate_a DEFAULT VALUES; 430SELECT * FROM truncate_a; 431 id | id1 432----+----- 433 1 | 33 434(1 row) 435 436ROLLBACK; 437INSERT INTO truncate_a DEFAULT VALUES; 438INSERT INTO truncate_a DEFAULT VALUES; 439SELECT * FROM truncate_a; 440 id | id1 441----+----- 442 1 | 33 443 2 | 34 444 3 | 35 445 4 | 36 446(4 rows) 447 448DROP TABLE truncate_a; 449SELECT nextval('truncate_a_id1'); -- fail, seq should have been dropped 450ERROR: relation "truncate_a_id1" does not exist 451LINE 1: SELECT nextval('truncate_a_id1'); 452 ^ 453-- partitioned table 454CREATE TABLE truncparted (a int, b char) PARTITION BY LIST (a); 455-- error, can't truncate a partitioned table 456TRUNCATE ONLY truncparted; 457ERROR: cannot truncate only a partitioned table 458HINT: Do not specify the ONLY keyword, or use TRUNCATE ONLY on the partitions directly. 459CREATE TABLE truncparted1 PARTITION OF truncparted FOR VALUES IN (1); 460INSERT INTO truncparted VALUES (1, 'a'); 461-- error, must truncate partitions 462TRUNCATE ONLY truncparted; 463ERROR: cannot truncate only a partitioned table 464HINT: Do not specify the ONLY keyword, or use TRUNCATE ONLY on the partitions directly. 465TRUNCATE truncparted; 466DROP TABLE truncparted; 467-- foreign key on partitioned table: partition key is referencing column. 468-- Make sure truncate did execute on all tables 469CREATE FUNCTION tp_ins_data() RETURNS void LANGUAGE plpgsql AS $$ 470 BEGIN 471 INSERT INTO truncprim VALUES (1), (100), (150); 472 INSERT INTO truncpart VALUES (1), (100), (150); 473 END 474$$; 475CREATE FUNCTION tp_chk_data(OUT pktb regclass, OUT pkval int, OUT fktb regclass, OUT fkval int) 476 RETURNS SETOF record LANGUAGE plpgsql AS $$ 477 BEGIN 478 RETURN QUERY SELECT 479 pk.tableoid::regclass, pk.a, fk.tableoid::regclass, fk.a 480 FROM truncprim pk FULL JOIN truncpart fk USING (a) 481 ORDER BY 2, 4; 482 END 483$$; 484CREATE TABLE truncprim (a int PRIMARY KEY); 485CREATE TABLE truncpart (a int REFERENCES truncprim) 486 PARTITION BY RANGE (a); 487CREATE TABLE truncpart_1 PARTITION OF truncpart FOR VALUES FROM (0) TO (100); 488CREATE TABLE truncpart_2 PARTITION OF truncpart FOR VALUES FROM (100) TO (200) 489 PARTITION BY RANGE (a); 490CREATE TABLE truncpart_2_1 PARTITION OF truncpart_2 FOR VALUES FROM (100) TO (150); 491CREATE TABLE truncpart_2_d PARTITION OF truncpart_2 DEFAULT; 492TRUNCATE TABLE truncprim; -- should fail 493ERROR: cannot truncate a table referenced in a foreign key constraint 494DETAIL: Table "truncpart" references "truncprim". 495HINT: Truncate table "truncpart" at the same time, or use TRUNCATE ... CASCADE. 496select tp_ins_data(); 497 tp_ins_data 498------------- 499 500(1 row) 501 502-- should truncate everything 503TRUNCATE TABLE truncprim, truncpart; 504select * from tp_chk_data(); 505 pktb | pkval | fktb | fkval 506------+-------+------+------- 507(0 rows) 508 509select tp_ins_data(); 510 tp_ins_data 511------------- 512 513(1 row) 514 515-- should truncate everything 516TRUNCATE TABLE truncprim CASCADE; 517NOTICE: truncate cascades to table "truncpart" 518NOTICE: truncate cascades to table "truncpart_1" 519NOTICE: truncate cascades to table "truncpart_2" 520NOTICE: truncate cascades to table "truncpart_2_1" 521NOTICE: truncate cascades to table "truncpart_2_d" 522SELECT * FROM tp_chk_data(); 523 pktb | pkval | fktb | fkval 524------+-------+------+------- 525(0 rows) 526 527SELECT tp_ins_data(); 528 tp_ins_data 529------------- 530 531(1 row) 532 533-- should truncate all partitions 534TRUNCATE TABLE truncpart; 535SELECT * FROM tp_chk_data(); 536 pktb | pkval | fktb | fkval 537-----------+-------+------+------- 538 truncprim | 1 | | 539 truncprim | 100 | | 540 truncprim | 150 | | 541(3 rows) 542 543DROP TABLE truncprim, truncpart; 544DROP FUNCTION tp_ins_data(), tp_chk_data(); 545-- test cascade when referencing a partitioned table 546CREATE TABLE trunc_a (a INT PRIMARY KEY) PARTITION BY RANGE (a); 547CREATE TABLE trunc_a1 PARTITION OF trunc_a FOR VALUES FROM (0) TO (10); 548CREATE TABLE trunc_a2 PARTITION OF trunc_a FOR VALUES FROM (10) TO (20) 549 PARTITION BY RANGE (a); 550CREATE TABLE trunc_a21 PARTITION OF trunc_a2 FOR VALUES FROM (10) TO (12); 551CREATE TABLE trunc_a22 PARTITION OF trunc_a2 FOR VALUES FROM (12) TO (16); 552CREATE TABLE trunc_a2d PARTITION OF trunc_a2 DEFAULT; 553CREATE TABLE trunc_a3 PARTITION OF trunc_a FOR VALUES FROM (20) TO (30); 554INSERT INTO trunc_a VALUES (0), (5), (10), (15), (20), (25); 555-- truncate a partition cascading to a table 556CREATE TABLE ref_b ( 557 b INT PRIMARY KEY, 558 a INT REFERENCES trunc_a(a) ON DELETE CASCADE 559); 560INSERT INTO ref_b VALUES (10, 0), (50, 5), (100, 10), (150, 15); 561TRUNCATE TABLE trunc_a1 CASCADE; 562NOTICE: truncate cascades to table "ref_b" 563SELECT a FROM ref_b; 564 a 565--- 566(0 rows) 567 568DROP TABLE ref_b; 569-- truncate a partition cascading to a partitioned table 570CREATE TABLE ref_c ( 571 c INT PRIMARY KEY, 572 a INT REFERENCES trunc_a(a) ON DELETE CASCADE 573) PARTITION BY RANGE (c); 574CREATE TABLE ref_c1 PARTITION OF ref_c FOR VALUES FROM (100) TO (200); 575CREATE TABLE ref_c2 PARTITION OF ref_c FOR VALUES FROM (200) TO (300); 576INSERT INTO ref_c VALUES (100, 10), (150, 15), (200, 20), (250, 25); 577TRUNCATE TABLE trunc_a21 CASCADE; 578NOTICE: truncate cascades to table "ref_c" 579NOTICE: truncate cascades to table "ref_c1" 580NOTICE: truncate cascades to table "ref_c2" 581SELECT a as "from table ref_c" FROM ref_c; 582 from table ref_c 583------------------ 584(0 rows) 585 586SELECT a as "from table trunc_a" FROM trunc_a ORDER BY a; 587 from table trunc_a 588-------------------- 589 15 590 20 591 25 592(3 rows) 593 594DROP TABLE trunc_a, ref_c; 595