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