1-- 2-- insert with DEFAULT in the target_list 3-- 4CREATE TABLE inserttest ( 5 col1 int4, 6 col2 int4 NOT NULL, 7 col3 text DEFAULT 'testing' 8); 9 10INSERT INTO inserttest (col1, col2, col3) 11 VALUES (DEFAULT, DEFAULT, DEFAULT); 12 13INSERT INTO inserttest (col2, col3) 14 VALUES (3, DEFAULT); 15 16INSERT INTO inserttest (col1, col2, col3) 17 VALUES (DEFAULT, 5, DEFAULT); 18 19INSERT INTO inserttest 20 VALUES (DEFAULT, 5, 'test'); 21 22INSERT INTO inserttest 23 VALUES (DEFAULT, 7); 24 25SELECT 26 * 27FROM 28 inserttest; 29 30-- 31-- insert with similar expression / target_list values (all fail) 32-- 33INSERT INTO inserttest (col1, col2, col3) 34 VALUES (DEFAULT, DEFAULT); 35 36INSERT INTO inserttest (col1, col2, col3) 37 VALUES (1, 2); 38 39INSERT INTO inserttest (col1) 40 VALUES (1, 2); 41 42INSERT INTO inserttest (col1) 43 VALUES (DEFAULT, DEFAULT); 44 45SELECT 46 * 47FROM 48 inserttest; 49 50-- 51-- VALUES test 52-- 53INSERT INTO inserttest 54 VALUES (10, 20, '40'), (-1, 2, DEFAULT), (( 55 SELECT 56 2), ( 57 SELECT 58 i 59 FROM ( 60 VALUES (3)) AS foo (i)), 'values are fun!'); 61 62SELECT 63 * 64FROM 65 inserttest; 66 67-- 68-- TOASTed value test 69-- 70INSERT INTO inserttest 71 VALUES (30, 50, repeat('x', 10000)); 72 73SELECT 74 col1, 75 col2, 76 char_length(col3) 77FROM 78 inserttest; 79 80DROP TABLE inserttest; 81 82-- 83-- check indirection (field/array assignment), cf bug #14265 84-- 85-- these tests are aware that transformInsertStmt has 3 separate code paths 86-- 87CREATE TYPE insert_test_type AS ( 88 if1 int, 89 if2 text[] 90); 91 92CREATE TABLE inserttest ( 93 f1 int, 94 f2 int[], 95 f3 insert_test_type, 96 f4 insert_test_type[] 97); 98 99INSERT INTO inserttest (f2[1], f2[2]) 100 VALUES (1, 2); 101 102INSERT INTO inserttest (f2[1], f2[2]) 103 VALUES (3, 4), (5, 6); 104 105INSERT INTO inserttest (f2[1], f2[2]) 106SELECT 107 7, 108 8; 109 110INSERT INTO inserttest (f2[1], f2[2]) 111 VALUES (1, DEFAULT); 112 113-- not supported 114INSERT INTO inserttest (f3.if1, f3.if2) 115 VALUES (1, ARRAY['foo']); 116 117INSERT INTO inserttest (f3.if1, f3.if2) 118 VALUES (1, '{foo}'), (2, '{bar}'); 119 120INSERT INTO inserttest (f3.if1, f3.if2) 121SELECT 122 3, 123 '{baz,quux}'; 124 125INSERT INTO inserttest (f3.if1, f3.if2) 126 VALUES (1, DEFAULT); 127 128-- not supported 129INSERT INTO inserttest (f3.if2[1], f3.if2[2]) 130 VALUES ('foo', 'bar'); 131 132INSERT INTO inserttest (f3.if2[1], f3.if2[2]) 133 VALUES ('foo', 'bar'), ('baz', 'quux'); 134 135INSERT INTO inserttest (f3.if2[1], f3.if2[2]) 136SELECT 137 'bear', 138 'beer'; 139 140INSERT INTO inserttest (f4[1].if2[1], f4[1].if2[2]) 141 VALUES ('foo', 'bar'); 142 143INSERT INTO inserttest (f4[1].if2[1], f4[1].if2[2]) 144 VALUES ('foo', 'bar'), ('baz', 'quux'); 145 146INSERT INTO inserttest (f4[1].if2[1], f4[1].if2[2]) 147SELECT 148 'bear', 149 'beer'; 150 151SELECT 152 * 153FROM 154 inserttest; 155 156-- also check reverse-listing 157CREATE TABLE inserttest2 ( 158 f1 bigint, 159 f2 text 160); 161 162CREATE RULE irule1 AS ON INSERT TO inserttest2 163 DO ALSO 164 INSERT INTO inserttest (f3.if2[1], f3.if2[2]) 165 VALUES (NEW.f1, NEW.f2); 166 167CREATE RULE irule2 AS ON INSERT TO inserttest2 168 DO ALSO 169 INSERT INTO inserttest (f4[1].if1, f4[1].if2[2]) 170 VALUES (1, 'fool'), (NEW.f1, NEW.f2); 171 172CREATE RULE irule3 AS ON INSERT TO inserttest2 173 DO ALSO 174 INSERT INTO inserttest (f4[1].if1, f4[1].if2[2]) 175 SELECT 176 NEW.f1, 177 NEW.f2; 178 179\d+ inserttest2 180DROP TABLE inserttest2; 181 182DROP TABLE inserttest; 183 184DROP TYPE insert_test_type; 185 186-- direct partition inserts should check partition bound constraint 187CREATE TABLE range_parted ( 188 a text, 189 b int 190) 191PARTITION BY RANGE (a, (b + 0)); 192 193-- no partitions, so fail 194INSERT INTO range_parted 195 VALUES ('a', 11); 196 197CREATE TABLE part1 PARTITION OF range_parted 198FOR VALUES FROM ('a', 1) TO ('a', 10); 199 200CREATE TABLE part2 PARTITION OF range_parted 201FOR VALUES FROM ('a', 10) TO ('a', 20); 202 203CREATE TABLE part3 PARTITION OF range_parted 204FOR VALUES FROM ('b', 1) TO ('b', 10); 205 206CREATE TABLE part4 PARTITION OF range_parted 207FOR VALUES FROM ('b', 10) TO ('b', 20); 208 209-- fail 210INSERT INTO part1 211 VALUES ('a', 11); 212 213INSERT INTO part1 214 VALUES ('b', 1); 215 216-- ok 217INSERT INTO part1 218 VALUES ('a', 1); 219 220-- fail 221INSERT INTO part4 222 VALUES ('b', 21); 223 224INSERT INTO part4 225 VALUES ('a', 10); 226 227-- ok 228INSERT INTO part4 229 VALUES ('b', 10); 230 231-- fail (partition key a has a NOT NULL constraint) 232INSERT INTO part1 233 VALUES (NULL); 234 235-- fail (expression key (b+0) cannot be null either) 236INSERT INTO part1 237 VALUES (1); 238 239CREATE TABLE list_parted ( 240 a text, 241 b int 242) 243PARTITION BY LIST (lower(a)); 244 245CREATE TABLE part_aa_bb PARTITION OF list_parted 246FOR VALUES IN ('aa', 'bb'); 247 248CREATE TABLE part_cc_dd PARTITION OF list_parted 249FOR VALUES IN ('cc', 'dd'); 250 251CREATE TABLE part_null PARTITION OF list_parted 252FOR VALUES IN (NULL); 253 254-- fail 255INSERT INTO part_aa_bb 256 VALUES ('cc', 1); 257 258INSERT INTO part_aa_bb 259 VALUES ('AAa', 1); 260 261INSERT INTO part_aa_bb 262 VALUES (NULL); 263 264-- ok 265INSERT INTO part_cc_dd 266 VALUES ('cC', 1); 267 268INSERT INTO part_null 269 VALUES (NULL, 0); 270 271-- check in case of multi-level partitioned table 272CREATE TABLE part_ee_ff PARTITION OF list_parted 273FOR VALUES IN ('ee', 'ff') 274PARTITION BY RANGE (b); 275 276CREATE TABLE part_ee_ff1 PARTITION OF part_ee_ff 277FOR VALUES FROM (1) TO (10); 278 279CREATE TABLE part_ee_ff2 PARTITION OF part_ee_ff 280FOR VALUES FROM (10) TO (20); 281 282-- test default partition 283CREATE TABLE part_default PARTITION OF list_parted DEFAULT; 284 285-- Negative test: a row, which would fit in other partition, does not fit 286-- default partition, even when inserted directly 287INSERT INTO part_default 288 VALUES ('aa', 2); 289 290INSERT INTO part_default 291 VALUES (NULL, 2); 292 293-- ok 294INSERT INTO part_default 295 VALUES ('Zz', 2); 296 297-- test if default partition works as expected for multi-level partitioned 298-- table as well as when default partition itself is further partitioned 299DROP TABLE part_default; 300 301CREATE TABLE part_xx_yy PARTITION OF list_parted 302FOR VALUES IN ('xx', 'yy') 303PARTITION BY LIST (a); 304 305CREATE TABLE part_xx_yy_p1 PARTITION OF part_xx_yy 306FOR VALUES IN ('xx'); 307 308CREATE TABLE part_xx_yy_defpart PARTITION OF part_xx_yy DEFAULT; 309 310CREATE TABLE part_default PARTITION OF list_parted DEFAULT PARTITION BY RANGE (b); 311 312CREATE TABLE part_default_p1 PARTITION OF part_default 313FOR VALUES FROM (20) TO (30); 314 315CREATE TABLE part_default_p2 PARTITION OF part_default 316FOR VALUES FROM (30) TO (40); 317 318-- fail 319INSERT INTO part_ee_ff1 320 VALUES ('EE', 11); 321 322INSERT INTO part_default_p2 323 VALUES ('gg', 43); 324 325-- fail (even the parent's, ie, part_ee_ff's partition constraint applies) 326INSERT INTO part_ee_ff1 327 VALUES ('cc', 1); 328 329INSERT INTO part_default 330 VALUES ('gg', 43); 331 332-- ok 333INSERT INTO part_ee_ff1 334 VALUES ('ff', 1); 335 336INSERT INTO part_ee_ff2 337 VALUES ('ff', 11); 338 339INSERT INTO part_default_p1 340 VALUES ('cd', 25); 341 342INSERT INTO part_default_p2 343 VALUES ('de', 35); 344 345INSERT INTO list_parted 346 VALUES ('ab', 21); 347 348INSERT INTO list_parted 349 VALUES ('xx', 1); 350 351INSERT INTO list_parted 352 VALUES ('yy', 2); 353 354SELECT 355 tableoid::regclass, 356 * 357FROM 358 list_parted; 359 360-- Check tuple routing for partitioned tables 361-- fail 362INSERT INTO range_parted 363 VALUES ('a', 0); 364 365-- ok 366INSERT INTO range_parted 367 VALUES ('a', 1); 368 369INSERT INTO range_parted 370 VALUES ('a', 10); 371 372-- fail 373INSERT INTO range_parted 374 VALUES ('a', 20); 375 376-- ok 377INSERT INTO range_parted 378 VALUES ('b', 1); 379 380INSERT INTO range_parted 381 VALUES ('b', 10); 382 383-- fail (partition key (b+0) is null) 384INSERT INTO range_parted 385 VALUES ('a'); 386 387-- Check default partition 388CREATE TABLE part_def PARTITION OF range_parted DEFAULT; 389 390-- fail 391INSERT INTO part_def 392 VALUES ('b', 10); 393 394-- ok 395INSERT INTO part_def 396 VALUES ('c', 10); 397 398INSERT INTO range_parted 399 VALUES (NULL, NULL); 400 401INSERT INTO range_parted 402 VALUES ('a', NULL); 403 404INSERT INTO range_parted 405 VALUES (NULL, 19); 406 407INSERT INTO range_parted 408 VALUES ('b', 20); 409 410SELECT 411 tableoid::regclass, 412 * 413FROM 414 range_parted; 415 416-- ok 417INSERT INTO list_parted 418 VALUES (NULL, 1); 419 420INSERT INTO list_parted (a) 421 VALUES ('aA'); 422 423-- fail (partition of part_ee_ff not found in both cases) 424INSERT INTO list_parted 425 VALUES ('EE', 0); 426 427INSERT INTO part_ee_ff 428 VALUES ('EE', 0); 429 430-- ok 431INSERT INTO list_parted 432 VALUES ('EE', 1); 433 434INSERT INTO part_ee_ff 435 VALUES ('EE', 10); 436 437SELECT 438 tableoid::regclass, 439 * 440FROM 441 list_parted; 442 443-- some more tests to exercise tuple-routing with multi-level partitioning 444CREATE TABLE part_gg PARTITION OF list_parted 445FOR VALUES IN ('gg') 446PARTITION BY RANGE (b); 447 448CREATE TABLE part_gg1 PARTITION OF part_gg 449FOR VALUES FROM (MINVALUE) TO (1); 450 451CREATE TABLE part_gg2 PARTITION OF part_gg 452FOR VALUES FROM (1) TO (10) 453PARTITION BY RANGE (b); 454 455CREATE TABLE part_gg2_1 PARTITION OF part_gg2 456FOR VALUES FROM (1) TO (5); 457 458CREATE TABLE part_gg2_2 PARTITION OF part_gg2 459FOR VALUES FROM (5) TO (10); 460 461CREATE TABLE part_ee_ff3 PARTITION OF part_ee_ff 462FOR VALUES FROM (20) TO (30) 463PARTITION BY RANGE (b); 464 465CREATE TABLE part_ee_ff3_1 PARTITION OF part_ee_ff3 466FOR VALUES FROM (20) TO (25); 467 468CREATE TABLE part_ee_ff3_2 PARTITION OF part_ee_ff3 469FOR VALUES FROM (25) TO (30); 470 471TRUNCATE list_parted; 472 473INSERT INTO list_parted 474 VALUES ('aa'), ('cc'); 475 476INSERT INTO list_parted 477SELECT 478 'Ff', 479 s.a 480FROM 481 generate_series(1, 29) s (a); 482 483INSERT INTO list_parted 484SELECT 485 'gg', 486 s.a 487FROM 488 generate_series(1, 9) s (a); 489 490INSERT INTO list_parted (b) 491 VALUES (1); 492 493SELECT 494 tableoid::regclass::text, 495 a, 496 min(b) AS min_b, 497 max(b) AS max_b 498FROM 499 list_parted 500GROUP BY 501 1, 502 2 503ORDER BY 504 1; 505 506-- direct partition inserts should check hash partition bound constraint 507-- Use hand-rolled hash functions and operator classes to get predictable 508-- result on different matchines. The hash function for int4 simply returns 509-- the sum of the values passed to it and the one for text returns the length 510-- of the non-empty string value passed to it or 0. 511CREATE OR REPLACE FUNCTION part_hashint4_noop (value int4, seed int8) 512 RETURNS int8 513 AS $$ 514 SELECT 515 value + seed; 516 517$$ 518LANGUAGE sql 519IMMUTABLE; 520 521CREATE OPERATOR class part_test_int4_ops FOR TYPE int4 522 USING HASH AS 523 OPERATOR 1 =, 524 FUNCTION 2 part_hashint4_noop (int4, int8 525); 526 527CREATE OR REPLACE FUNCTION part_hashtext_length (value text, seed int8) 528 RETURNS int8 529 AS $$ 530 SELECT 531 length(coalesce(value, ''))::int8 532$$ 533LANGUAGE sql 534IMMUTABLE; 535 536CREATE OPERATOR class part_test_text_ops FOR TYPE text 537 USING HASH AS 538 OPERATOR 1 =, 539 FUNCTION 2 part_hashtext_length (text, int8 540); 541 542CREATE TABLE hash_parted ( 543 a int 544) 545PARTITION BY HASH (a part_test_int4_ops); 546 547CREATE TABLE hpart0 PARTITION OF hash_parted 548FOR VALUES WITH (MODULUS 4, REMAINDER 0); 549 550CREATE TABLE hpart1 PARTITION OF hash_parted 551FOR VALUES WITH (MODULUS 4, REMAINDER 1); 552 553CREATE TABLE hpart2 PARTITION OF hash_parted 554FOR VALUES WITH (MODULUS 4, REMAINDER 2); 555 556CREATE TABLE hpart3 PARTITION OF hash_parted 557FOR VALUES WITH (MODULUS 4, REMAINDER 3); 558 559INSERT INTO hash_parted 560 VALUES (generate_series(1, 10)); 561 562-- direct insert of values divisible by 4 - ok; 563INSERT INTO hpart0 564 VALUES (12), (16); 565 566-- fail; 567INSERT INTO hpart0 568 VALUES (11); 569 570-- 11 % 4 -> 3 remainder i.e. valid data for hpart3 partition 571INSERT INTO hpart3 572 VALUES (11); 573 574-- view data 575SELECT 576 tableoid::regclass AS part, 577 a, 578 a % 4 AS "remainder = a % 4" 579FROM 580 hash_parted 581ORDER BY 582 part; 583 584-- test \d+ output on a table which has both partitioned and unpartitioned 585-- partitions 586\d+ list_parted 587-- cleanup 588DROP TABLE range_parted, list_parted; 589 590DROP TABLE hash_parted; 591 592-- test that a default partition added as the first partition accepts any value 593-- including null 594CREATE TABLE list_parted ( 595 a int 596) 597PARTITION BY LIST (a); 598 599CREATE TABLE part_default PARTITION OF list_parted DEFAULT; 600 601\d+ part_default 602INSERT INTO part_default 603 VALUES (NULL); 604 605INSERT INTO part_default 606 VALUES (1); 607 608INSERT INTO part_default 609 VALUES (-1); 610 611SELECT 612 tableoid::regclass, 613 a 614FROM 615 list_parted; 616 617-- cleanup 618DROP TABLE list_parted; 619 620-- more tests for certain multi-level partitioning scenarios 621CREATE TABLE mlparted ( 622 a int, 623 b int 624) 625PARTITION BY RANGE (a, b); 626 627CREATE TABLE mlparted1 ( 628 b int NOT NULL, 629 a int NOT NULL 630) 631PARTITION BY RANGE ((b + 0)); 632 633CREATE TABLE mlparted11 ( 634 LIKE mlparted1 635); 636 637ALTER TABLE mlparted11 638 DROP a; 639 640ALTER TABLE mlparted11 641 ADD a int; 642 643ALTER TABLE mlparted11 644 DROP a; 645 646ALTER TABLE mlparted11 647 ADD a int NOT NULL; 648 649-- attnum for key attribute 'a' is different in mlparted, mlparted1, and mlparted11 650SELECT 651 attrelid::regclass, 652 attname, 653 attnum 654FROM 655 pg_attribute 656WHERE 657 attname = 'a' 658 AND (attrelid = 'mlparted'::regclass 659 OR attrelid = 'mlparted1'::regclass 660 OR attrelid = 'mlparted11'::regclass) 661ORDER BY 662 attrelid::regclass::text; 663 664ALTER TABLE mlparted1 ATTACH PARTITION mlparted11 665FOR VALUES FROM (2) TO (5); 666 667ALTER TABLE mlparted ATTACH PARTITION mlparted1 668FOR VALUES FROM (1, 2) TO (1, 10); 669 670-- check that "(1, 2)" is correctly routed to mlparted11. 671INSERT INTO mlparted 672 VALUES (1, 2); 673 674SELECT 675 tableoid::regclass, 676 * 677FROM 678 mlparted; 679 680-- check that proper message is shown after failure to route through mlparted1 681INSERT INTO mlparted (a, b) 682 VALUES (1, 5); 683 684TRUNCATE mlparted; 685 686ALTER TABLE mlparted 687 ADD CONSTRAINT check_b CHECK (b = 3); 688 689-- have a BR trigger modify the row such that the check_b is violated 690CREATE FUNCTION mlparted11_trig_fn () 691 RETURNS TRIGGER 692 AS $$ 693BEGIN 694 NEW.b := 4; 695 RETURN NEW; 696END; 697$$ 698LANGUAGE plpgsql; 699 700CREATE TRIGGER mlparted11_trig 701 BEFORE INSERT ON mlparted11 FOR EACH ROW 702 EXECUTE PROCEDURE mlparted11_trig_fn (); 703 704-- check that the correct row is shown when constraint check_b fails after 705-- "(1, 2)" is routed to mlparted11 (actually "(1, 4)" would be shown due 706-- to the BR trigger mlparted11_trig_fn) 707INSERT INTO mlparted 708 VALUES (1, 2); 709 710DROP TRIGGER mlparted11_trig ON mlparted11; 711 712DROP FUNCTION mlparted11_trig_fn (); 713 714-- check that inserting into an internal partition successfully results in 715-- checking its partition constraint before inserting into the leaf partition 716-- selected by tuple-routing 717INSERT INTO mlparted1 (a, b) 718 VALUES (2, 3); 719 720-- check routing error through a list partitioned table when the key is null 721CREATE TABLE lparted_nonullpart ( 722 a int, 723 b char 724) 725PARTITION BY LIST (b); 726 727CREATE TABLE lparted_nonullpart_a PARTITION OF lparted_nonullpart 728FOR VALUES IN ('a'); 729 730INSERT INTO lparted_nonullpart 731 VALUES (1); 732 733DROP TABLE lparted_nonullpart; 734 735-- check that RETURNING works correctly with tuple-routing 736ALTER TABLE mlparted 737 DROP CONSTRAINT check_b; 738 739CREATE TABLE mlparted12 PARTITION OF mlparted1 740FOR VALUES FROM (5) TO (10); 741 742CREATE TABLE mlparted2 ( 743 b int NOT NULL, 744 a int NOT NULL 745); 746 747ALTER TABLE mlparted ATTACH PARTITION mlparted2 748FOR VALUES FROM (1, 10) TO (1, 20); 749 750CREATE TABLE mlparted3 PARTITION OF mlparted 751FOR VALUES FROM (1, 20) TO (1, 30); 752 753CREATE TABLE mlparted4 ( 754 LIKE mlparted 755); 756 757ALTER TABLE mlparted4 758 DROP a; 759 760ALTER TABLE mlparted4 761 ADD a int NOT NULL; 762 763ALTER TABLE mlparted ATTACH PARTITION mlparted4 764FOR VALUES FROM (1, 30) TO (1, 40); 765 766WITH ins ( 767 a, 768 b, 769 c 770) AS ( 771INSERT INTO mlparted (b, a) 772 SELECT 773 s.a, 774 1 775 FROM 776 generate_series(2, 39) s (a) 777 RETURNING 778 tableoid::regclass, 779 * 780) 781SELECT 782 a, 783 b, 784 min(c), 785 max(c) 786FROM 787 ins 788GROUP BY 789 a, 790 b 791ORDER BY 792 1; 793 794ALTER TABLE mlparted 795 ADD c text; 796 797CREATE TABLE mlparted5 ( 798 c text, 799 a int NOT NULL, 800 b int NOT NULL 801) 802PARTITION BY LIST (c); 803 804CREATE TABLE mlparted5a ( 805 a int NOT NULL, 806 c text, 807 b int NOT NULL 808); 809 810ALTER TABLE mlparted5 ATTACH PARTITION mlparted5a 811FOR VALUES IN ('a'); 812 813ALTER TABLE mlparted ATTACH PARTITION mlparted5 814FOR VALUES FROM (1, 40) TO (1, 50); 815 816ALTER TABLE mlparted 817 ADD CONSTRAINT check_b CHECK (a = 1 AND b < 45); 818 819INSERT INTO mlparted 820 VALUES (1, 45, 'a'); 821 822CREATE FUNCTION mlparted5abrtrig_func () 823 RETURNS TRIGGER 824 AS $$ 825BEGIN 826 NEW.c = 'b'; 827 RETURN new; 828END; 829$$ 830LANGUAGE plpgsql; 831 832CREATE TRIGGER mlparted5abrtrig 833 BEFORE INSERT ON mlparted5a FOR EACH ROW 834 EXECUTE PROCEDURE mlparted5abrtrig_func (); 835 836INSERT INTO mlparted5 (a, b, c) 837 VALUES (1, 40, 'a'); 838 839DROP TABLE mlparted5; 840 841ALTER TABLE mlparted 842 DROP CONSTRAINT check_b; 843 844-- Check multi-level default partition 845CREATE TABLE mlparted_def PARTITION OF mlparted DEFAULT PARTITION BY RANGE (a); 846 847CREATE TABLE mlparted_def1 PARTITION OF mlparted_def 848FOR VALUES FROM (40) TO (50); 849 850CREATE TABLE mlparted_def2 PARTITION OF mlparted_def 851FOR VALUES FROM (50) TO (60); 852 853INSERT INTO mlparted 854 VALUES (40, 100); 855 856INSERT INTO mlparted_def1 857 VALUES (42, 100); 858 859INSERT INTO mlparted_def2 860 VALUES (54, 50); 861 862-- fail 863INSERT INTO mlparted 864 VALUES (70, 100); 865 866INSERT INTO mlparted_def1 867 VALUES (52, 50); 868 869INSERT INTO mlparted_def2 870 VALUES (34, 50); 871 872-- ok 873CREATE TABLE mlparted_defd PARTITION OF mlparted_def DEFAULT; 874 875INSERT INTO mlparted 876 VALUES (70, 100); 877 878SELECT 879 tableoid::regclass, 880 * 881FROM 882 mlparted_def; 883 884-- Check multi-level tuple routing with attributes dropped from the 885-- top-most parent. First remove the last attribute. 886ALTER TABLE mlparted 887 ADD d int, 888 ADD e int; 889 890ALTER TABLE mlparted 891 DROP e; 892 893CREATE TABLE mlparted5 PARTITION OF mlparted 894FOR VALUES FROM (1, 40) TO (1, 50) 895PARTITION BY RANGE (c); 896 897CREATE TABLE mlparted5_ab PARTITION OF mlparted5 898FOR VALUES FROM ('a') TO ('c') 899PARTITION BY LIST (c); 900 901CREATE TABLE mlparted5_a PARTITION OF mlparted5_ab 902FOR VALUES IN ('a'); 903 904CREATE TABLE mlparted5_b ( 905 d int, 906 b int, 907 c text, 908 a int 909); 910 911ALTER TABLE mlparted5_ab ATTACH PARTITION mlparted5_b 912FOR VALUES IN ('b'); 913 914TRUNCATE mlparted; 915 916INSERT INTO mlparted 917 VALUES (1, 2, 'a', 1); 918 919INSERT INTO mlparted 920 VALUES (1, 40, 'a', 1); 921 922-- goes to mlparted5_a 923INSERT INTO mlparted 924 VALUES (1, 45, 'b', 1); 925 926-- goes to mlparted5_b 927SELECT 928 tableoid::regclass, 929 * 930FROM 931 mlparted 932ORDER BY 933 a, 934 b, 935 c, 936 d; 937 938ALTER TABLE mlparted 939 DROP d; 940 941TRUNCATE mlparted; 942 943-- Remove the before last attribute. 944ALTER TABLE mlparted 945 ADD e int, 946 ADD d int; 947 948ALTER TABLE mlparted 949 DROP e; 950 951INSERT INTO mlparted 952 VALUES (1, 2, 'a', 1); 953 954INSERT INTO mlparted 955 VALUES (1, 40, 'a', 1); 956 957-- goes to mlparted5_a 958INSERT INTO mlparted 959 VALUES (1, 45, 'b', 1); 960 961-- goes to mlparted5_b 962SELECT 963 tableoid::regclass, 964 * 965FROM 966 mlparted 967ORDER BY 968 a, 969 b, 970 c, 971 d; 972 973ALTER TABLE mlparted 974 DROP d; 975 976DROP TABLE mlparted5; 977 978-- check that message shown after failure to find a partition shows the 979-- appropriate key description (or none) in various situations 980CREATE TABLE key_desc ( 981 a int, 982 b int 983) 984PARTITION BY LIST ((a + 0)); 985 986CREATE TABLE key_desc_1 PARTITION OF key_desc 987FOR VALUES IN (1) 988PARTITION BY RANGE (b); 989 990CREATE USER regress_insert_other_user; 991 992GRANT SELECT (a) ON key_desc_1 TO regress_insert_other_user; 993 994GRANT INSERT ON key_desc TO regress_insert_other_user; 995 996SET ROLE regress_insert_other_user; 997 998-- no key description is shown 999INSERT INTO key_desc 1000 VALUES (1, 1); 1001 1002RESET ROLE; 1003 1004GRANT SELECT (b) ON key_desc_1 TO regress_insert_other_user; 1005 1006SET ROLE regress_insert_other_user; 1007 1008-- key description (b)=(1) is now shown 1009INSERT INTO key_desc 1010 VALUES (1, 1); 1011 1012-- key description is not shown if key contains expression 1013INSERT INTO key_desc 1014 VALUES (2, 1); 1015 1016RESET ROLE; 1017 1018REVOKE ALL ON key_desc FROM regress_insert_other_user; 1019 1020REVOKE ALL ON key_desc_1 FROM regress_insert_other_user; 1021 1022DROP ROLE regress_insert_other_user; 1023 1024DROP TABLE key_desc, key_desc_1; 1025 1026-- test minvalue/maxvalue restrictions 1027CREATE TABLE mcrparted ( 1028 a int, 1029 b int, 1030 c int 1031) 1032PARTITION BY RANGE (a, abs(b), c); 1033 1034CREATE TABLE mcrparted0 PARTITION OF mcrparted 1035FOR VALUES FROM (MINVALUE, 0, 0) TO (1, 1036MAXVALUE, 1037MAXVALUE); 1038 1039CREATE TABLE mcrparted2 PARTITION OF mcrparted 1040FOR VALUES FROM (10, 6, 1041MINVALUE) TO (10, 1042MAXVALUE, 1043MINVALUE); 1044 1045CREATE TABLE mcrparted4 PARTITION OF mcrparted 1046FOR VALUES FROM (21, 1047MINVALUE, 0) TO (30, 20, 1048MINVALUE); 1049 1050-- check multi-column range partitioning expression enforces the same 1051-- constraint as what tuple-routing would determine it to be 1052CREATE TABLE mcrparted0 PARTITION OF mcrparted 1053FOR VALUES FROM (MINVALUE, 1054MINVALUE, 1055MINVALUE) TO (1, 1056MAXVALUE, 1057MAXVALUE); 1058 1059CREATE TABLE mcrparted1 PARTITION OF mcrparted 1060FOR VALUES FROM (2, 1, 1061MINVALUE) TO (10, 5, 10); 1062 1063CREATE TABLE mcrparted2 PARTITION OF mcrparted 1064FOR VALUES FROM (10, 6, 1065MINVALUE) TO (10, 1066MAXVALUE, 1067MAXVALUE); 1068 1069CREATE TABLE mcrparted3 PARTITION OF mcrparted 1070FOR VALUES FROM (11, 1, 1) TO (20, 10, 10); 1071 1072CREATE TABLE mcrparted4 PARTITION OF mcrparted 1073FOR VALUES FROM (21, 1074MINVALUE, 1075MINVALUE) TO (30, 20, 1076MAXVALUE); 1077 1078CREATE TABLE mcrparted5 PARTITION OF mcrparted 1079FOR VALUES FROM (30, 21, 20) TO (MAXVALUE, 1080MAXVALUE, 1081MAXVALUE); 1082 1083-- null not allowed in range partition 1084INSERT INTO mcrparted 1085 VALUES (NULL, NULL, NULL); 1086 1087-- routed to mcrparted0 1088INSERT INTO mcrparted 1089 VALUES (0, 1, 1); 1090 1091INSERT INTO mcrparted0 1092 VALUES (0, 1, 1); 1093 1094-- routed to mcparted1 1095INSERT INTO mcrparted 1096 VALUES (9, 1000, 1); 1097 1098INSERT INTO mcrparted1 1099 VALUES (9, 1000, 1); 1100 1101INSERT INTO mcrparted 1102 VALUES (10, 5, -1); 1103 1104INSERT INTO mcrparted1 1105 VALUES (10, 5, -1); 1106 1107INSERT INTO mcrparted 1108 VALUES (2, 1, 0); 1109 1110INSERT INTO mcrparted1 1111 VALUES (2, 1, 0); 1112 1113-- routed to mcparted2 1114INSERT INTO mcrparted 1115 VALUES (10, 6, 1000); 1116 1117INSERT INTO mcrparted2 1118 VALUES (10, 6, 1000); 1119 1120INSERT INTO mcrparted 1121 VALUES (10, 1000, 1000); 1122 1123INSERT INTO mcrparted2 1124 VALUES (10, 1000, 1000); 1125 1126-- no partition exists, nor does mcrparted3 accept it 1127INSERT INTO mcrparted 1128 VALUES (11, 1, -1); 1129 1130INSERT INTO mcrparted3 1131 VALUES (11, 1, -1); 1132 1133-- routed to mcrparted5 1134INSERT INTO mcrparted 1135 VALUES (30, 21, 20); 1136 1137INSERT INTO mcrparted5 1138 VALUES (30, 21, 20); 1139 1140INSERT INTO mcrparted4 1141 VALUES (30, 21, 20); 1142 1143-- error 1144-- check rows 1145SELECT 1146 tableoid::regclass::text, 1147 * 1148FROM 1149 mcrparted 1150ORDER BY 1151 1; 1152 1153-- cleanup 1154DROP TABLE mcrparted; 1155 1156-- check that a BR constraint can't make partition contain violating rows 1157CREATE TABLE brtrigpartcon ( 1158 a int, 1159 b text 1160) 1161PARTITION BY LIST (a); 1162 1163CREATE TABLE brtrigpartcon1 PARTITION OF brtrigpartcon 1164FOR VALUES IN (1); 1165 1166CREATE OR REPLACE FUNCTION brtrigpartcon1trigf () 1167 RETURNS TRIGGER 1168 AS $$ 1169BEGIN 1170 NEW.a := 2; 1171 RETURN new; 1172END 1173$$ 1174LANGUAGE plpgsql; 1175 1176CREATE TRIGGER brtrigpartcon1trig 1177 BEFORE INSERT ON brtrigpartcon1 FOR EACH ROW 1178 EXECUTE PROCEDURE brtrigpartcon1trigf (); 1179 1180INSERT INTO brtrigpartcon 1181 VALUES (1, 'hi there'); 1182 1183INSERT INTO brtrigpartcon1 1184 VALUES (1, 'hi there'); 1185 1186-- check that the message shows the appropriate column description in a 1187-- situation where the partitioned table is not the primary ModifyTable node 1188CREATE TABLE inserttest3 ( 1189 f1 text DEFAULT 'foo', 1190 f2 text DEFAULT 'bar', 1191 f3 int 1192); 1193 1194CREATE ROLE regress_coldesc_role; 1195 1196GRANT INSERT ON inserttest3 TO regress_coldesc_role; 1197 1198GRANT INSERT ON brtrigpartcon TO regress_coldesc_role; 1199 1200REVOKE SELECT ON brtrigpartcon FROM regress_coldesc_role; 1201 1202SET ROLE regress_coldesc_role; 1203 1204WITH result AS ( 1205INSERT INTO brtrigpartcon 1206 VALUES (1, 'hi there') 1207 RETURNING 1208 1) 1209 INSERT INTO inserttest3 (f3) 1210 SELECT 1211 * 1212 FROM 1213 result; 1214 1215RESET ROLE; 1216 1217-- cleanup 1218REVOKE ALL ON inserttest3 FROM regress_coldesc_role; 1219 1220REVOKE ALL ON brtrigpartcon FROM regress_coldesc_role; 1221 1222DROP ROLE regress_coldesc_role; 1223 1224DROP TABLE inserttest3; 1225 1226DROP TABLE brtrigpartcon; 1227 1228DROP FUNCTION brtrigpartcon1trigf (); 1229 1230-- check that "do nothing" BR triggers work with tuple-routing (this checks 1231-- that estate->es_result_relation_info is appropriately set/reset for each 1232-- routed tuple) 1233CREATE TABLE donothingbrtrig_test ( 1234 a int, 1235 b text 1236) 1237PARTITION BY LIST (a); 1238 1239CREATE TABLE donothingbrtrig_test1 ( 1240 b text, 1241 a int 1242); 1243 1244CREATE TABLE donothingbrtrig_test2 ( 1245 c text, 1246 b text, 1247 a int 1248); 1249 1250ALTER TABLE donothingbrtrig_test2 1251 DROP COLUMN c; 1252 1253CREATE OR REPLACE FUNCTION donothingbrtrig_func () 1254 RETURNS TRIGGER 1255 AS $$ 1256BEGIN 1257 RAISE NOTICE 'b: %', NEW.b; 1258 RETURN NULL; 1259END 1260$$ 1261LANGUAGE plpgsql; 1262 1263CREATE TRIGGER donothingbrtrig1 1264 BEFORE INSERT ON donothingbrtrig_test1 FOR EACH ROW 1265 EXECUTE PROCEDURE donothingbrtrig_func (); 1266 1267CREATE TRIGGER donothingbrtrig2 1268 BEFORE INSERT ON donothingbrtrig_test2 FOR EACH ROW 1269 EXECUTE PROCEDURE donothingbrtrig_func (); 1270 1271ALTER TABLE donothingbrtrig_test ATTACH PARTITION donothingbrtrig_test1 1272FOR VALUES IN (1); 1273 1274ALTER TABLE donothingbrtrig_test ATTACH PARTITION donothingbrtrig_test2 1275FOR VALUES IN (2); 1276 1277INSERT INTO donothingbrtrig_test 1278 VALUES (1, 'foo'), (2, 'bar'); 1279 1280SELECT 1281 tableoid::regclass, 1282 * 1283FROM 1284 donothingbrtrig_test; 1285 1286-- cleanup 1287DROP TABLE donothingbrtrig_test; 1288 1289DROP FUNCTION donothingbrtrig_func (); 1290 1291-- check multi-column range partitioning with minvalue/maxvalue constraints 1292CREATE TABLE mcrparted ( 1293 a text, 1294 b int 1295) 1296PARTITION BY RANGE (a, b); 1297 1298CREATE TABLE mcrparted1_lt_b PARTITION OF mcrparted 1299FOR VALUES FROM (MINVALUE, 1300MINVALUE) TO ('b', 1301MINVALUE); 1302 1303CREATE TABLE mcrparted2_b PARTITION OF mcrparted 1304FOR VALUES FROM ('b', 1305MINVALUE) TO ('c', 1306MINVALUE); 1307 1308CREATE TABLE mcrparted3_c_to_common PARTITION OF mcrparted 1309FOR VALUES FROM ('c', 1310MINVALUE) TO ('common', 1311MINVALUE); 1312 1313CREATE TABLE mcrparted4_common_lt_0 PARTITION OF mcrparted 1314FOR VALUES FROM ('common', 1315MINVALUE) TO ('common', 0); 1316 1317CREATE TABLE mcrparted5_common_0_to_10 PARTITION OF mcrparted 1318FOR VALUES FROM ('common', 0) TO ('common', 10); 1319 1320CREATE TABLE mcrparted6_common_ge_10 PARTITION OF mcrparted 1321FOR VALUES FROM ('common', 10) TO ('common', 1322MAXVALUE); 1323 1324CREATE TABLE mcrparted7_gt_common_lt_d PARTITION OF mcrparted 1325FOR VALUES FROM ('common', 1326MAXVALUE) TO ('d', 1327MINVALUE); 1328 1329CREATE TABLE mcrparted8_ge_d PARTITION OF mcrparted 1330FOR VALUES FROM ('d', 1331MINVALUE) TO (MAXVALUE, 1332MAXVALUE); 1333 1334\d+ mcrparted 1335\d+ mcrparted1_lt_b 1336\d+ mcrparted2_b 1337\d+ mcrparted3_c_to_common 1338\d+ mcrparted4_common_lt_0 1339\d+ mcrparted5_common_0_to_10 1340\d+ mcrparted6_common_ge_10 1341\d+ mcrparted7_gt_common_lt_d 1342\d+ mcrparted8_ge_d 1343INSERT INTO mcrparted 1344 VALUES ('aaa', 0), ('b', 0), ('bz', 10), ('c', -10), ('comm', -10), ('common', -10), ('common', 0), ('common', 10), ('commons', 0), ('d', -10), ('e', 0); 1345 1346SELECT 1347 tableoid::regclass, 1348 * 1349FROM 1350 mcrparted 1351ORDER BY 1352 a, 1353 b; 1354 1355DROP TABLE mcrparted; 1356 1357-- check that wholerow vars in the RETURNING list work with partitioned tables 1358CREATE TABLE returningwrtest ( 1359 a int 1360) 1361PARTITION BY LIST (a); 1362 1363CREATE TABLE returningwrtest1 PARTITION OF returningwrtest 1364FOR VALUES IN (1); 1365 1366INSERT INTO returningwrtest 1367 VALUES (1) 1368RETURNING 1369 returningwrtest; 1370 1371-- check also that the wholerow vars in RETURNING list are converted as needed 1372ALTER TABLE returningwrtest 1373 ADD b text; 1374 1375CREATE TABLE returningwrtest2 ( 1376 b text, 1377 c int, 1378 a int 1379); 1380 1381ALTER TABLE returningwrtest2 1382 DROP c; 1383 1384ALTER TABLE returningwrtest ATTACH PARTITION returningwrtest2 1385FOR VALUES IN (2); 1386 1387INSERT INTO returningwrtest 1388 VALUES (2, 'foo') 1389RETURNING 1390 returningwrtest; 1391 1392DROP TABLE returningwrtest; 1393 1394