1 -- Creating an index on a partitioned table makes the partitions
2 -- automatically get the index
3 create table idxpart (a int, b int, c text) partition by range (a);
4 -- relhassubclass of a partitioned index is false before creating any partition.
5 -- It will be set after the first partition is created.
6 create index idxpart_idx on idxpart (a);
7 select relhassubclass from pg_class where relname = 'idxpart_idx';
13 -- Check that partitioned indexes are present in pg_indexes.
14 select indexdef from pg_indexes where indexname like 'idxpart_idx%';
16 -----------------------------------------------------------------
17 CREATE INDEX idxpart_idx ON ONLY public.idxpart USING btree (a)
20 drop index idxpart_idx;
21 create table idxpart1 partition of idxpart for values from (0) to (10);
22 create table idxpart2 partition of idxpart for values from (10) to (100)
23 partition by range (b);
24 create table idxpart21 partition of idxpart2 for values from (0) to (100);
25 -- Even with partitions, relhassubclass should not be set if a partitioned
26 -- index is created only on the parent.
27 create index idxpart_idx on only idxpart(a);
28 select relhassubclass from pg_class where relname = 'idxpart_idx';
34 drop index idxpart_idx;
35 create index on idxpart (a);
36 select relname, relkind, relhassubclass, inhparent::regclass
37 from pg_class left join pg_index ix on (indexrelid = oid)
38 left join pg_inherits on (ix.indexrelid = inhrelid)
39 where relname like 'idxpart%' order by relname;
40 relname | relkind | relhassubclass | inhparent
41 -----------------+---------+----------------+----------------
44 idxpart1_a_idx | i | f | idxpart_a_idx
47 idxpart21_a_idx | i | f | idxpart2_a_idx
48 idxpart2_a_idx | I | t | idxpart_a_idx
49 idxpart_a_idx | I | t |
53 -- Some unsupported features
54 create table idxpart (a int, b int, c text) partition by range (a);
55 create table idxpart1 partition of idxpart for values from (0) to (10);
56 create index concurrently on idxpart (a);
57 ERROR: cannot create index on partitioned table "idxpart" concurrently
59 -- Verify bugfix with query on indexed partitioned table with no partitions
60 -- https://postgr.es/m/20180124162006.pmapfiznhgngwtjf@alvherre.pgsql
61 CREATE TABLE idxpart (col1 INT) PARTITION BY RANGE (col1);
62 CREATE INDEX ON idxpart (col1);
63 CREATE TABLE idxpart_two (col2 INT);
64 SELECT col2 FROM idxpart_two fk LEFT OUTER JOIN idxpart pk ON (col1 = col2);
69 DROP table idxpart, idxpart_two;
70 -- Verify bugfix with index rewrite on ALTER TABLE / SET DATA TYPE
71 -- https://postgr.es/m/CAKcux6mxNCGsgATwf5CGMF8g4WSupCXicCVMeKUTuWbyxHOMsQ@mail.gmail.com
72 CREATE TABLE idxpart (a INT, b TEXT, c INT) PARTITION BY RANGE(a);
73 CREATE TABLE idxpart1 PARTITION OF idxpart FOR VALUES FROM (MINVALUE) TO (MAXVALUE);
74 CREATE INDEX partidx_abc_idx ON idxpart (a, b, c);
75 INSERT INTO idxpart (a, b, c) SELECT i, i, i FROM generate_series(1, 50) i;
76 ALTER TABLE idxpart ALTER COLUMN c TYPE numeric;
78 -- If a table without index is attached as partition to a table with
79 -- an index, the index is automatically created
80 create table idxpart (a int, b int, c text) partition by range (a);
81 create index idxparti on idxpart (a);
82 create index idxparti2 on idxpart (b, c);
83 create table idxpart1 (like idxpart);
85 Table "public.idxpart1"
86 Column | Type | Collation | Nullable | Default
87 --------+---------+-----------+----------+---------
92 alter table idxpart attach partition idxpart1 for values from (0) to (10);
94 Table "public.idxpart1"
95 Column | Type | Collation | Nullable | Default
96 --------+---------+-----------+----------+---------
100 Partition of: idxpart FOR VALUES FROM (0) TO (10)
102 "idxpart1_a_idx" btree (a)
103 "idxpart1_b_c_idx" btree (b, c)
106 Index "public.idxpart1_a_idx"
107 Column | Type | Key? | Definition | Storage | Stats target
108 --------+---------+------+------------+---------+--------------
109 a | integer | yes | a | plain |
110 Partition of: idxparti
111 No partition constraint
112 btree, for table "public.idxpart1"
115 Index "public.idxpart1_b_c_idx"
116 Column | Type | Key? | Definition | Storage | Stats target
117 --------+---------+------+------------+----------+--------------
118 b | integer | yes | b | plain |
119 c | text | yes | c | extended |
120 Partition of: idxparti2
121 No partition constraint
122 btree, for table "public.idxpart1"
124 -- Forbid ALTER TABLE when attaching or detaching an index to a partition.
125 create index idxpart_c on only idxpart (c);
126 create index idxpart1_c on idxpart1 (c);
127 alter table idxpart_c attach partition idxpart1_c for values from (10) to (20);
128 ERROR: "idxpart_c" is not a partitioned table
129 alter index idxpart_c attach partition idxpart1_c;
130 select relname, relpartbound from pg_class
131 where relname in ('idxpart_c', 'idxpart1_c')
133 relname | relpartbound
134 ------------+--------------
139 alter table idxpart_c detach partition idxpart1_c;
140 ERROR: ALTER action DETACH PARTITION cannot be performed on relation "idxpart_c"
141 DETAIL: This operation is not supported for partitioned indexes.
143 -- If a partition already has an index, don't create a duplicative one
144 create table idxpart (a int, b int) partition by range (a, b);
145 create table idxpart1 partition of idxpart for values from (0, 0) to (10, 10);
146 create index on idxpart1 (a, b);
147 create index on idxpart (a, b);
149 Table "public.idxpart1"
150 Column | Type | Collation | Nullable | Default
151 --------+---------+-----------+----------+---------
154 Partition of: idxpart FOR VALUES FROM (0, 0) TO (10, 10)
156 "idxpart1_a_b_idx" btree (a, b)
158 select relname, relkind, relhassubclass, inhparent::regclass
159 from pg_class left join pg_index ix on (indexrelid = oid)
160 left join pg_inherits on (ix.indexrelid = inhrelid)
161 where relname like 'idxpart%' order by relname;
162 relname | relkind | relhassubclass | inhparent
163 ------------------+---------+----------------+-----------------
166 idxpart1_a_b_idx | i | f | idxpart_a_b_idx
167 idxpart_a_b_idx | I | t |
171 -- DROP behavior for partitioned indexes
172 create table idxpart (a int) partition by range (a);
173 create index on idxpart (a);
174 create table idxpart1 partition of idxpart for values from (0) to (10);
175 drop index idxpart1_a_idx; -- no way
176 ERROR: cannot drop index idxpart1_a_idx because index idxpart_a_idx requires it
177 HINT: You can drop index idxpart_a_idx instead.
178 drop index concurrently idxpart_a_idx; -- unsupported
179 ERROR: cannot drop partitioned index "idxpart_a_idx" concurrently
180 drop index idxpart_a_idx; -- both indexes go away
181 select relname, relkind from pg_class
182 where relname like 'idxpart%' order by relname;
189 create index on idxpart (a);
190 drop table idxpart1; -- the index on partition goes away too
191 select relname, relkind from pg_class
192 where relname like 'idxpart%' order by relname;
194 ---------------+---------
200 -- DROP behavior with temporary partitioned indexes
201 create temp table idxpart_temp (a int) partition by range (a);
202 create index on idxpart_temp(a);
203 create temp table idxpart1_temp partition of idxpart_temp
204 for values from (0) to (10);
205 drop index idxpart1_temp_a_idx; -- error
206 ERROR: cannot drop index idxpart1_temp_a_idx because index idxpart_temp_a_idx requires it
207 HINT: You can drop index idxpart_temp_a_idx instead.
208 -- non-concurrent drop is enforced here, so it is a valid case.
209 drop index concurrently idxpart_temp_a_idx;
210 select relname, relkind from pg_class
211 where relname like 'idxpart_temp%' order by relname;
213 --------------+---------
217 drop table idxpart_temp;
218 -- ALTER INDEX .. ATTACH, error cases
219 create table idxpart (a int, b int) partition by range (a, b);
220 create table idxpart1 partition of idxpart for values from (0, 0) to (10, 10);
221 create index idxpart_a_b_idx on only idxpart (a, b);
222 create index idxpart1_a_b_idx on idxpart1 (a, b);
223 create index idxpart1_tst1 on idxpart1 (b, a);
224 create index idxpart1_tst2 on idxpart1 using hash (a);
225 create index idxpart1_tst3 on idxpart1 (a, b) where a > 10;
226 alter index idxpart attach partition idxpart1;
227 ERROR: "idxpart" is not an index
228 alter index idxpart_a_b_idx attach partition idxpart1;
229 ERROR: "idxpart1" is not an index
230 alter index idxpart_a_b_idx attach partition idxpart_a_b_idx;
231 ERROR: cannot attach index "idxpart_a_b_idx" as a partition of index "idxpart_a_b_idx"
232 DETAIL: Index "idxpart_a_b_idx" is not an index on any partition of table "idxpart".
233 alter index idxpart_a_b_idx attach partition idxpart1_b_idx;
234 ERROR: relation "idxpart1_b_idx" does not exist
235 alter index idxpart_a_b_idx attach partition idxpart1_tst1;
236 ERROR: cannot attach index "idxpart1_tst1" as a partition of index "idxpart_a_b_idx"
237 DETAIL: The index definitions do not match.
238 alter index idxpart_a_b_idx attach partition idxpart1_tst2;
239 ERROR: cannot attach index "idxpart1_tst2" as a partition of index "idxpart_a_b_idx"
240 DETAIL: The index definitions do not match.
241 alter index idxpart_a_b_idx attach partition idxpart1_tst3;
242 ERROR: cannot attach index "idxpart1_tst3" as a partition of index "idxpart_a_b_idx"
243 DETAIL: The index definitions do not match.
245 alter index idxpart_a_b_idx attach partition idxpart1_a_b_idx;
246 alter index idxpart_a_b_idx attach partition idxpart1_a_b_idx; -- quiet
248 create index idxpart1_2_a_b on idxpart1 (a, b);
249 alter index idxpart_a_b_idx attach partition idxpart1_2_a_b;
250 ERROR: cannot attach index "idxpart1_2_a_b" as a partition of index "idxpart_a_b_idx"
251 DETAIL: Another index is already attached for partition "idxpart1".
253 -- make sure everything's gone
254 select indexrelid::regclass, indrelid::regclass
255 from pg_index where indexrelid::regclass::text like 'idxpart%';
256 indexrelid | indrelid
257 ------------+----------
260 -- Don't auto-attach incompatible indexes
261 create table idxpart (a int, b int) partition by range (a);
262 create table idxpart1 (a int, b int);
263 create index on idxpart1 using hash (a);
264 create index on idxpart1 (a) where b > 1;
265 create index on idxpart1 ((a + 0));
266 create index on idxpart1 (a, a);
267 create index on idxpart (a);
268 alter table idxpart attach partition idxpart1 for values from (0) to (1000);
270 Table "public.idxpart1"
271 Column | Type | Collation | Nullable | Default
272 --------+---------+-----------+----------+---------
275 Partition of: idxpart FOR VALUES FROM (0) TO (1000)
277 "idxpart1_a_a1_idx" btree (a, a)
278 "idxpart1_a_idx" hash (a)
279 "idxpart1_a_idx1" btree (a) WHERE b > 1
280 "idxpart1_a_idx2" btree (a)
281 "idxpart1_expr_idx" btree ((a + 0))
284 -- If CREATE INDEX ONLY, don't create indexes on partitions; and existing
285 -- indexes on partitions don't change parent. ALTER INDEX ATTACH can change
286 -- the parent after the fact.
287 create table idxpart (a int) partition by range (a);
288 create table idxpart1 partition of idxpart for values from (0) to (100);
289 create table idxpart2 partition of idxpart for values from (100) to (1000)
290 partition by range (a);
291 create table idxpart21 partition of idxpart2 for values from (100) to (200);
292 create table idxpart22 partition of idxpart2 for values from (200) to (300);
293 create index on idxpart22 (a);
294 create index on only idxpart2 (a);
295 create index on idxpart (a);
296 -- Here we expect that idxpart1 and idxpart2 have a new index, but idxpart21
297 -- does not; also, idxpart22 is not attached.
299 Table "public.idxpart1"
300 Column | Type | Collation | Nullable | Default
301 --------+---------+-----------+----------+---------
303 Partition of: idxpart FOR VALUES FROM (0) TO (100)
305 "idxpart1_a_idx" btree (a)
308 Partitioned table "public.idxpart2"
309 Column | Type | Collation | Nullable | Default
310 --------+---------+-----------+----------+---------
312 Partition of: idxpart FOR VALUES FROM (100) TO (1000)
313 Partition key: RANGE (a)
315 "idxpart2_a_idx" btree (a) INVALID
316 Number of partitions: 2 (Use \d+ to list them.)
319 Table "public.idxpart21"
320 Column | Type | Collation | Nullable | Default
321 --------+---------+-----------+----------+---------
323 Partition of: idxpart2 FOR VALUES FROM (100) TO (200)
325 select indexrelid::regclass, indrelid::regclass, inhparent::regclass
326 from pg_index idx left join pg_inherits inh on (idx.indexrelid = inh.inhrelid)
327 where indexrelid::regclass::text like 'idxpart%'
328 order by indexrelid::regclass::text collate "C";
329 indexrelid | indrelid | inhparent
330 -----------------+-----------+---------------
331 idxpart1_a_idx | idxpart1 | idxpart_a_idx
332 idxpart22_a_idx | idxpart22 |
333 idxpart2_a_idx | idxpart2 | idxpart_a_idx
334 idxpart_a_idx | idxpart |
337 alter index idxpart2_a_idx attach partition idxpart22_a_idx;
338 select indexrelid::regclass, indrelid::regclass, inhparent::regclass
339 from pg_index idx left join pg_inherits inh on (idx.indexrelid = inh.inhrelid)
340 where indexrelid::regclass::text like 'idxpart%'
341 order by indexrelid::regclass::text collate "C";
342 indexrelid | indrelid | inhparent
343 -----------------+-----------+----------------
344 idxpart1_a_idx | idxpart1 | idxpart_a_idx
345 idxpart22_a_idx | idxpart22 | idxpart2_a_idx
346 idxpart2_a_idx | idxpart2 | idxpart_a_idx
347 idxpart_a_idx | idxpart |
350 -- attaching idxpart22 is not enough to set idxpart22_a_idx valid ...
351 alter index idxpart2_a_idx attach partition idxpart22_a_idx;
353 Partitioned table "public.idxpart2"
354 Column | Type | Collation | Nullable | Default
355 --------+---------+-----------+----------+---------
357 Partition of: idxpart FOR VALUES FROM (100) TO (1000)
358 Partition key: RANGE (a)
360 "idxpart2_a_idx" btree (a) INVALID
361 Number of partitions: 2 (Use \d+ to list them.)
363 -- ... but this one is.
364 create index on idxpart21 (a);
365 alter index idxpart2_a_idx attach partition idxpart21_a_idx;
367 Partitioned table "public.idxpart2"
368 Column | Type | Collation | Nullable | Default
369 --------+---------+-----------+----------+---------
371 Partition of: idxpart FOR VALUES FROM (100) TO (1000)
372 Partition key: RANGE (a)
374 "idxpart2_a_idx" btree (a)
375 Number of partitions: 2 (Use \d+ to list them.)
378 -- When a table is attached a partition and it already has an index, a
379 -- duplicate index should not get created, but rather the index becomes
380 -- attached to the parent's index.
381 create table idxpart (a int, b int, c text, d bool) partition by range (a);
382 create index idxparti on idxpart (a);
383 create index idxparti2 on idxpart (b, c);
384 create table idxpart1 (like idxpart including indexes);
386 Table "public.idxpart1"
387 Column | Type | Collation | Nullable | Default
388 --------+---------+-----------+----------+---------
394 "idxpart1_a_idx" btree (a)
395 "idxpart1_b_c_idx" btree (b, c)
397 select relname, relkind, inhparent::regclass
398 from pg_class left join pg_index ix on (indexrelid = oid)
399 left join pg_inherits on (ix.indexrelid = inhrelid)
400 where relname like 'idxpart%' order by relname;
401 relname | relkind | inhparent
402 ------------------+---------+-----------
406 idxpart1_b_c_idx | i |
411 alter table idxpart attach partition idxpart1 for values from (0) to (10);
413 Table "public.idxpart1"
414 Column | Type | Collation | Nullable | Default
415 --------+---------+-----------+----------+---------
420 Partition of: idxpart FOR VALUES FROM (0) TO (10)
422 "idxpart1_a_idx" btree (a)
423 "idxpart1_b_c_idx" btree (b, c)
425 select relname, relkind, inhparent::regclass
426 from pg_class left join pg_index ix on (indexrelid = oid)
427 left join pg_inherits on (ix.indexrelid = inhrelid)
428 where relname like 'idxpart%' order by relname;
429 relname | relkind | inhparent
430 ------------------+---------+-----------
433 idxpart1_a_idx | i | idxparti
434 idxpart1_b_c_idx | i | idxparti2
439 -- While here, also check matching when creating an index after the fact.
440 create index on idxpart1 ((a+b)) where d = true;
442 Table "public.idxpart1"
443 Column | Type | Collation | Nullable | Default
444 --------+---------+-----------+----------+---------
449 Partition of: idxpart FOR VALUES FROM (0) TO (10)
451 "idxpart1_a_idx" btree (a)
452 "idxpart1_b_c_idx" btree (b, c)
453 "idxpart1_expr_idx" btree ((a + b)) WHERE d = true
455 select relname, relkind, inhparent::regclass
456 from pg_class left join pg_index ix on (indexrelid = oid)
457 left join pg_inherits on (ix.indexrelid = inhrelid)
458 where relname like 'idxpart%' order by relname;
459 relname | relkind | inhparent
460 -------------------+---------+-----------
463 idxpart1_a_idx | i | idxparti
464 idxpart1_b_c_idx | i | idxparti2
465 idxpart1_expr_idx | i |
470 create index idxparti3 on idxpart ((a+b)) where d = true;
472 Table "public.idxpart1"
473 Column | Type | Collation | Nullable | Default
474 --------+---------+-----------+----------+---------
479 Partition of: idxpart FOR VALUES FROM (0) TO (10)
481 "idxpart1_a_idx" btree (a)
482 "idxpart1_b_c_idx" btree (b, c)
483 "idxpart1_expr_idx" btree ((a + b)) WHERE d = true
485 select relname, relkind, inhparent::regclass
486 from pg_class left join pg_index ix on (indexrelid = oid)
487 left join pg_inherits on (ix.indexrelid = inhrelid)
488 where relname like 'idxpart%' order by relname;
489 relname | relkind | inhparent
490 -------------------+---------+-----------
493 idxpart1_a_idx | i | idxparti
494 idxpart1_b_c_idx | i | idxparti2
495 idxpart1_expr_idx | i | idxparti3
502 -- Verify that attaching an invalid index does not mark the parent index valid.
503 -- On the other hand, attaching a valid index marks not only its direct
504 -- ancestor valid, but also any indirect ancestor that was only missing the one
505 -- that was just made valid
506 create table idxpart (a int, b int) partition by range (a);
507 create table idxpart1 partition of idxpart for values from (1) to (1000) partition by range (a);
508 create table idxpart11 partition of idxpart1 for values from (1) to (100);
509 create index on only idxpart1 (a);
510 create index on only idxpart (a);
511 -- this results in two invalid indexes:
512 select relname, indisvalid from pg_class join pg_index on indexrelid = oid
513 where relname like 'idxpart%' order by relname;
515 ----------------+------------
520 -- idxpart1_a_idx is not valid, so idxpart_a_idx should not become valid:
521 alter index idxpart_a_idx attach partition idxpart1_a_idx;
522 select relname, indisvalid from pg_class join pg_index on indexrelid = oid
523 where relname like 'idxpart%' order by relname;
525 ----------------+------------
530 -- after creating and attaching this, both idxpart1_a_idx and idxpart_a_idx
531 -- should become valid
532 create index on idxpart11 (a);
533 alter index idxpart1_a_idx attach partition idxpart11_a_idx;
534 select relname, indisvalid from pg_class join pg_index on indexrelid = oid
535 where relname like 'idxpart%' order by relname;
537 -----------------+------------
544 -- verify dependency handling during ALTER TABLE DETACH PARTITION
545 create table idxpart (a int) partition by range (a);
546 create table idxpart1 (like idxpart);
547 create index on idxpart1 (a);
548 create index on idxpart (a);
549 create table idxpart2 (like idxpart);
550 alter table idxpart attach partition idxpart1 for values from (0000) to (1000);
551 alter table idxpart attach partition idxpart2 for values from (1000) to (2000);
552 create table idxpart3 partition of idxpart for values from (2000) to (3000);
553 select relname, relkind from pg_class where relname like 'idxpart%' order by relname;
555 ----------------+---------
566 -- a) after detaching partitions, the indexes can be dropped independently
567 alter table idxpart detach partition idxpart1;
568 alter table idxpart detach partition idxpart2;
569 alter table idxpart detach partition idxpart3;
570 drop index idxpart1_a_idx;
571 drop index idxpart2_a_idx;
572 drop index idxpart3_a_idx;
573 select relname, relkind from pg_class where relname like 'idxpart%' order by relname;
575 ---------------+---------
583 drop table idxpart, idxpart1, idxpart2, idxpart3;
584 select relname, relkind from pg_class where relname like 'idxpart%' order by relname;
589 create table idxpart (a int) partition by range (a);
590 create table idxpart1 (like idxpart);
591 create index on idxpart1 (a);
592 create index on idxpart (a);
593 create table idxpart2 (like idxpart);
594 alter table idxpart attach partition idxpart1 for values from (0000) to (1000);
595 alter table idxpart attach partition idxpart2 for values from (1000) to (2000);
596 create table idxpart3 partition of idxpart for values from (2000) to (3000);
597 -- b) after detaching, dropping the index on parent does not remove the others
598 select relname, relkind from pg_class where relname like 'idxpart%' order by relname;
600 ----------------+---------
611 alter table idxpart detach partition idxpart1;
612 alter table idxpart detach partition idxpart2;
613 alter table idxpart detach partition idxpart3;
614 drop index idxpart_a_idx;
615 select relname, relkind from pg_class where relname like 'idxpart%' order by relname;
617 ----------------+---------
627 drop table idxpart, idxpart1, idxpart2, idxpart3;
628 select relname, relkind from pg_class where relname like 'idxpart%' order by relname;
633 create table idxpart (a int, b int, c int) partition by range(a);
634 create index on idxpart(c);
635 create table idxpart1 partition of idxpart for values from (0) to (250);
636 create table idxpart2 partition of idxpart for values from (250) to (500);
637 alter table idxpart detach partition idxpart2;
639 Table "public.idxpart2"
640 Column | Type | Collation | Nullable | Default
641 --------+---------+-----------+----------+---------
646 "idxpart2_c_idx" btree (c)
648 alter table idxpart2 drop column c;
650 Table "public.idxpart2"
651 Column | Type | Collation | Nullable | Default
652 --------+---------+-----------+----------+---------
656 drop table idxpart, idxpart2;
657 -- Verify that expression indexes inherit correctly
658 create table idxpart (a int, b int) partition by range (a);
659 create table idxpart1 (like idxpart);
660 create index on idxpart1 ((a + b));
661 create index on idxpart ((a + b));
662 create table idxpart2 (like idxpart);
663 alter table idxpart attach partition idxpart1 for values from (0000) to (1000);
664 alter table idxpart attach partition idxpart2 for values from (1000) to (2000);
665 create table idxpart3 partition of idxpart for values from (2000) to (3000);
666 select relname as child, inhparent::regclass as parent, pg_get_indexdef as childdef
667 from pg_class join pg_inherits on inhrelid = oid,
668 lateral pg_get_indexdef(pg_class.oid)
669 where relkind in ('i', 'I') and relname like 'idxpart%' order by relname;
670 child | parent | childdef
671 -------------------+------------------+---------------------------------------------------------------------------
672 idxpart1_expr_idx | idxpart_expr_idx | CREATE INDEX idxpart1_expr_idx ON public.idxpart1 USING btree (((a + b)))
673 idxpart2_expr_idx | idxpart_expr_idx | CREATE INDEX idxpart2_expr_idx ON public.idxpart2 USING btree (((a + b)))
674 idxpart3_expr_idx | idxpart_expr_idx | CREATE INDEX idxpart3_expr_idx ON public.idxpart3 USING btree (((a + b)))
678 -- Verify behavior for collation (mis)matches
679 create table idxpart (a text) partition by range (a);
680 create table idxpart1 (like idxpart);
681 create table idxpart2 (like idxpart);
682 create index on idxpart2 (a collate "POSIX");
683 create index on idxpart2 (a);
684 create index on idxpart2 (a collate "C");
685 alter table idxpart attach partition idxpart1 for values from ('aaa') to ('bbb');
686 alter table idxpart attach partition idxpart2 for values from ('bbb') to ('ccc');
687 create table idxpart3 partition of idxpart for values from ('ccc') to ('ddd');
688 create index on idxpart (a collate "C");
689 create table idxpart4 partition of idxpart for values from ('ddd') to ('eee');
690 select relname as child, inhparent::regclass as parent, pg_get_indexdef as childdef
691 from pg_class left join pg_inherits on inhrelid = oid,
692 lateral pg_get_indexdef(pg_class.oid)
693 where relkind in ('i', 'I') and relname like 'idxpart%' order by relname;
694 child | parent | childdef
695 -----------------+---------------+--------------------------------------------------------------------------------
696 idxpart1_a_idx | idxpart_a_idx | CREATE INDEX idxpart1_a_idx ON public.idxpart1 USING btree (a COLLATE "C")
697 idxpart2_a_idx | | CREATE INDEX idxpart2_a_idx ON public.idxpart2 USING btree (a COLLATE "POSIX")
698 idxpart2_a_idx1 | | CREATE INDEX idxpart2_a_idx1 ON public.idxpart2 USING btree (a)
699 idxpart2_a_idx2 | idxpart_a_idx | CREATE INDEX idxpart2_a_idx2 ON public.idxpart2 USING btree (a COLLATE "C")
700 idxpart3_a_idx | idxpart_a_idx | CREATE INDEX idxpart3_a_idx ON public.idxpart3 USING btree (a COLLATE "C")
701 idxpart4_a_idx | idxpart_a_idx | CREATE INDEX idxpart4_a_idx ON public.idxpart4 USING btree (a COLLATE "C")
702 idxpart_a_idx | | CREATE INDEX idxpart_a_idx ON ONLY public.idxpart USING btree (a COLLATE "C")
706 -- Verify behavior for opclass (mis)matches
707 create table idxpart (a text) partition by range (a);
708 create table idxpart1 (like idxpart);
709 create table idxpart2 (like idxpart);
710 create index on idxpart2 (a);
711 alter table idxpart attach partition idxpart1 for values from ('aaa') to ('bbb');
712 alter table idxpart attach partition idxpart2 for values from ('bbb') to ('ccc');
713 create table idxpart3 partition of idxpart for values from ('ccc') to ('ddd');
714 create index on idxpart (a text_pattern_ops);
715 create table idxpart4 partition of idxpart for values from ('ddd') to ('eee');
716 -- must *not* have attached the index we created on idxpart2
717 select relname as child, inhparent::regclass as parent, pg_get_indexdef as childdef
718 from pg_class left join pg_inherits on inhrelid = oid,
719 lateral pg_get_indexdef(pg_class.oid)
720 where relkind in ('i', 'I') and relname like 'idxpart%' order by relname;
721 child | parent | childdef
722 -----------------+---------------+------------------------------------------------------------------------------------
723 idxpart1_a_idx | idxpart_a_idx | CREATE INDEX idxpart1_a_idx ON public.idxpart1 USING btree (a text_pattern_ops)
724 idxpart2_a_idx | | CREATE INDEX idxpart2_a_idx ON public.idxpart2 USING btree (a)
725 idxpart2_a_idx1 | idxpart_a_idx | CREATE INDEX idxpart2_a_idx1 ON public.idxpart2 USING btree (a text_pattern_ops)
726 idxpart3_a_idx | idxpart_a_idx | CREATE INDEX idxpart3_a_idx ON public.idxpart3 USING btree (a text_pattern_ops)
727 idxpart4_a_idx | idxpart_a_idx | CREATE INDEX idxpart4_a_idx ON public.idxpart4 USING btree (a text_pattern_ops)
728 idxpart_a_idx | | CREATE INDEX idxpart_a_idx ON ONLY public.idxpart USING btree (a text_pattern_ops)
731 drop index idxpart_a_idx;
732 create index on only idxpart (a text_pattern_ops);
734 alter index idxpart_a_idx attach partition idxpart2_a_idx;
735 ERROR: cannot attach index "idxpart2_a_idx" as a partition of index "idxpart_a_idx"
736 DETAIL: The index definitions do not match.
738 -- Verify that attaching indexes maps attribute numbers correctly
739 create table idxpart (col1 int, a int, col2 int, b int) partition by range (a);
740 create table idxpart1 (b int, col1 int, col2 int, col3 int, a int);
741 alter table idxpart drop column col1, drop column col2;
742 alter table idxpart1 drop column col1, drop column col2, drop column col3;
743 alter table idxpart attach partition idxpart1 for values from (0) to (1000);
744 create index idxpart_1_idx on only idxpart (b, a);
745 create index idxpart1_1_idx on idxpart1 (b, a);
746 create index idxpart1_1b_idx on idxpart1 (b);
747 -- test expressions and partial-index predicate, too
748 create index idxpart_2_idx on only idxpart ((b + a)) where a > 1;
749 create index idxpart1_2_idx on idxpart1 ((b + a)) where a > 1;
750 create index idxpart1_2b_idx on idxpart1 ((a + b)) where a > 1;
751 create index idxpart1_2c_idx on idxpart1 ((b + a)) where b > 1;
752 alter index idxpart_1_idx attach partition idxpart1_1b_idx; -- fail
753 ERROR: cannot attach index "idxpart1_1b_idx" as a partition of index "idxpart_1_idx"
754 DETAIL: The index definitions do not match.
755 alter index idxpart_1_idx attach partition idxpart1_1_idx;
756 alter index idxpart_2_idx attach partition idxpart1_2b_idx; -- fail
757 ERROR: cannot attach index "idxpart1_2b_idx" as a partition of index "idxpart_2_idx"
758 DETAIL: The index definitions do not match.
759 alter index idxpart_2_idx attach partition idxpart1_2c_idx; -- fail
760 ERROR: cannot attach index "idxpart1_2c_idx" as a partition of index "idxpart_2_idx"
761 DETAIL: The index definitions do not match.
762 alter index idxpart_2_idx attach partition idxpart1_2_idx; -- ok
763 select relname as child, inhparent::regclass as parent, pg_get_indexdef as childdef
764 from pg_class left join pg_inherits on inhrelid = oid,
765 lateral pg_get_indexdef(pg_class.oid)
766 where relkind in ('i', 'I') and relname like 'idxpart%' order by relname;
767 child | parent | childdef
768 -----------------+---------------+-----------------------------------------------------------------------------------------
769 idxpart1_1_idx | idxpart_1_idx | CREATE INDEX idxpart1_1_idx ON public.idxpart1 USING btree (b, a)
770 idxpart1_1b_idx | | CREATE INDEX idxpart1_1b_idx ON public.idxpart1 USING btree (b)
771 idxpart1_2_idx | idxpart_2_idx | CREATE INDEX idxpart1_2_idx ON public.idxpart1 USING btree (((b + a))) WHERE (a > 1)
772 idxpart1_2b_idx | | CREATE INDEX idxpart1_2b_idx ON public.idxpart1 USING btree (((a + b))) WHERE (a > 1)
773 idxpart1_2c_idx | | CREATE INDEX idxpart1_2c_idx ON public.idxpart1 USING btree (((b + a))) WHERE (b > 1)
774 idxpart_1_idx | | CREATE INDEX idxpart_1_idx ON ONLY public.idxpart USING btree (b, a)
775 idxpart_2_idx | | CREATE INDEX idxpart_2_idx ON ONLY public.idxpart USING btree (((b + a))) WHERE (a > 1)
779 -- Make sure the partition columns are mapped correctly
780 create table idxpart (a int, b int, c text) partition by range (a);
781 create index idxparti on idxpart (a);
782 create index idxparti2 on idxpart (c, b);
783 create table idxpart1 (c text, a int, b int);
784 alter table idxpart attach partition idxpart1 for values from (0) to (10);
785 create table idxpart2 (c text, a int, b int);
786 create index on idxpart2 (a);
787 create index on idxpart2 (c, b);
788 alter table idxpart attach partition idxpart2 for values from (10) to (20);
789 select c.relname, pg_get_indexdef(indexrelid)
790 from pg_class c join pg_index i on c.oid = i.indexrelid
791 where indrelid::regclass::text like 'idxpart%'
792 order by indexrelid::regclass::text collate "C";
793 relname | pg_get_indexdef
794 ------------------+---------------------------------------------------------------------
795 idxpart1_a_idx | CREATE INDEX idxpart1_a_idx ON public.idxpart1 USING btree (a)
796 idxpart1_c_b_idx | CREATE INDEX idxpart1_c_b_idx ON public.idxpart1 USING btree (c, b)
797 idxpart2_a_idx | CREATE INDEX idxpart2_a_idx ON public.idxpart2 USING btree (a)
798 idxpart2_c_b_idx | CREATE INDEX idxpart2_c_b_idx ON public.idxpart2 USING btree (c, b)
799 idxparti | CREATE INDEX idxparti ON ONLY public.idxpart USING btree (a)
800 idxparti2 | CREATE INDEX idxparti2 ON ONLY public.idxpart USING btree (c, b)
804 -- Verify that columns are mapped correctly in expression indexes
805 create table idxpart (col1 int, col2 int, a int, b int) partition by range (a);
806 create table idxpart1 (col2 int, b int, col1 int, a int);
807 create table idxpart2 (col1 int, col2 int, b int, a int);
808 alter table idxpart drop column col1, drop column col2;
809 alter table idxpart1 drop column col1, drop column col2;
810 alter table idxpart2 drop column col1, drop column col2;
811 create index on idxpart2 (abs(b));
812 alter table idxpart attach partition idxpart2 for values from (0) to (1);
813 create index on idxpart (abs(b));
814 create index on idxpart ((b + 1));
815 alter table idxpart attach partition idxpart1 for values from (1) to (2);
816 select c.relname, pg_get_indexdef(indexrelid)
817 from pg_class c join pg_index i on c.oid = i.indexrelid
818 where indrelid::regclass::text like 'idxpart%'
819 order by indexrelid::regclass::text collate "C";
820 relname | pg_get_indexdef
821 -------------------+------------------------------------------------------------------------------
822 idxpart1_abs_idx | CREATE INDEX idxpart1_abs_idx ON public.idxpart1 USING btree (abs(b))
823 idxpart1_expr_idx | CREATE INDEX idxpart1_expr_idx ON public.idxpart1 USING btree (((b + 1)))
824 idxpart2_abs_idx | CREATE INDEX idxpart2_abs_idx ON public.idxpart2 USING btree (abs(b))
825 idxpart2_expr_idx | CREATE INDEX idxpart2_expr_idx ON public.idxpart2 USING btree (((b + 1)))
826 idxpart_abs_idx | CREATE INDEX idxpart_abs_idx ON ONLY public.idxpart USING btree (abs(b))
827 idxpart_expr_idx | CREATE INDEX idxpart_expr_idx ON ONLY public.idxpart USING btree (((b + 1)))
831 -- Verify that columns are mapped correctly for WHERE in a partial index
832 create table idxpart (col1 int, a int, col3 int, b int) partition by range (a);
833 alter table idxpart drop column col1, drop column col3;
834 create table idxpart1 (col1 int, col2 int, col3 int, col4 int, b int, a int);
835 alter table idxpart1 drop column col1, drop column col2, drop column col3, drop column col4;
836 alter table idxpart attach partition idxpart1 for values from (0) to (1000);
837 create table idxpart2 (col1 int, col2 int, b int, a int);
838 create index on idxpart2 (a) where b > 1000;
839 alter table idxpart2 drop column col1, drop column col2;
840 alter table idxpart attach partition idxpart2 for values from (1000) to (2000);
841 create index on idxpart (a) where b > 1000;
842 select c.relname, pg_get_indexdef(indexrelid)
843 from pg_class c join pg_index i on c.oid = i.indexrelid
844 where indrelid::regclass::text like 'idxpart%'
845 order by indexrelid::regclass::text collate "C";
846 relname | pg_get_indexdef
847 ----------------+------------------------------------------------------------------------------------
848 idxpart1_a_idx | CREATE INDEX idxpart1_a_idx ON public.idxpart1 USING btree (a) WHERE (b > 1000)
849 idxpart2_a_idx | CREATE INDEX idxpart2_a_idx ON public.idxpart2 USING btree (a) WHERE (b > 1000)
850 idxpart_a_idx | CREATE INDEX idxpart_a_idx ON ONLY public.idxpart USING btree (a) WHERE (b > 1000)
854 -- Column number mapping: dropped columns in the partition
855 create table idxpart1 (drop_1 int, drop_2 int, col_keep int, drop_3 int);
856 alter table idxpart1 drop column drop_1;
857 alter table idxpart1 drop column drop_2;
858 alter table idxpart1 drop column drop_3;
859 create index on idxpart1 (col_keep);
860 create table idxpart (col_keep int) partition by range (col_keep);
861 create index on idxpart (col_keep);
862 alter table idxpart attach partition idxpart1 for values from (0) to (1000);
864 Partitioned table "public.idxpart"
865 Column | Type | Collation | Nullable | Default
866 ----------+---------+-----------+----------+---------
867 col_keep | integer | | |
868 Partition key: RANGE (col_keep)
870 "idxpart_col_keep_idx" btree (col_keep)
871 Number of partitions: 1 (Use \d+ to list them.)
874 Table "public.idxpart1"
875 Column | Type | Collation | Nullable | Default
876 ----------+---------+-----------+----------+---------
877 col_keep | integer | | |
878 Partition of: idxpart FOR VALUES FROM (0) TO (1000)
880 "idxpart1_col_keep_idx" btree (col_keep)
882 select attrelid::regclass, attname, attnum from pg_attribute
883 where attrelid::regclass::text like 'idxpart%' and attnum > 0
884 order by attrelid::regclass, attnum;
885 attrelid | attname | attnum
886 -----------------------+------------------------------+--------
887 idxpart1 | ........pg.dropped.1........ | 1
888 idxpart1 | ........pg.dropped.2........ | 2
889 idxpart1 | col_keep | 3
890 idxpart1 | ........pg.dropped.4........ | 4
891 idxpart1_col_keep_idx | col_keep | 1
892 idxpart | col_keep | 1
893 idxpart_col_keep_idx | col_keep | 1
897 -- Column number mapping: dropped columns in the parent table
898 create table idxpart(drop_1 int, drop_2 int, col_keep int, drop_3 int) partition by range (col_keep);
899 alter table idxpart drop column drop_1;
900 alter table idxpart drop column drop_2;
901 alter table idxpart drop column drop_3;
902 create table idxpart1 (col_keep int);
903 create index on idxpart1 (col_keep);
904 create index on idxpart (col_keep);
905 alter table idxpart attach partition idxpart1 for values from (0) to (1000);
907 Partitioned table "public.idxpart"
908 Column | Type | Collation | Nullable | Default
909 ----------+---------+-----------+----------+---------
910 col_keep | integer | | |
911 Partition key: RANGE (col_keep)
913 "idxpart_col_keep_idx" btree (col_keep)
914 Number of partitions: 1 (Use \d+ to list them.)
917 Table "public.idxpart1"
918 Column | Type | Collation | Nullable | Default
919 ----------+---------+-----------+----------+---------
920 col_keep | integer | | |
921 Partition of: idxpart FOR VALUES FROM (0) TO (1000)
923 "idxpart1_col_keep_idx" btree (col_keep)
925 select attrelid::regclass, attname, attnum from pg_attribute
926 where attrelid::regclass::text like 'idxpart%' and attnum > 0
927 order by attrelid::regclass, attnum;
928 attrelid | attname | attnum
929 -----------------------+------------------------------+--------
930 idxpart | ........pg.dropped.1........ | 1
931 idxpart | ........pg.dropped.2........ | 2
932 idxpart | col_keep | 3
933 idxpart | ........pg.dropped.4........ | 4
934 idxpart1 | col_keep | 1
935 idxpart1_col_keep_idx | col_keep | 1
936 idxpart_col_keep_idx | col_keep | 1
941 -- Constraint-related indexes
943 -- Verify that it works to add primary key / unique to partitioned tables
944 create table idxpart (a int primary key, b int) partition by range (a);
946 Partitioned table "public.idxpart"
947 Column | Type | Collation | Nullable | Default
948 --------+---------+-----------+----------+---------
949 a | integer | | not null |
951 Partition key: RANGE (a)
953 "idxpart_pkey" PRIMARY KEY, btree (a)
954 Number of partitions: 0
956 -- multiple primary key on child should fail
957 create table failpart partition of idxpart (b primary key) for values from (0) to (100);
958 ERROR: multiple primary keys for table "failpart" are not allowed
960 -- primary key on child is okay if there's no PK in the parent, though
961 create table idxpart (a int) partition by range (a);
962 create table idxpart1pk partition of idxpart (a primary key) for values from (0) to (100);
964 Table "public.idxpart1pk"
965 Column | Type | Collation | Nullable | Default
966 --------+---------+-----------+----------+---------
967 a | integer | | not null |
968 Partition of: idxpart FOR VALUES FROM (0) TO (100)
970 "idxpart1pk_pkey" PRIMARY KEY, btree (a)
973 -- Failing to use the full partition key is not allowed
974 create table idxpart (a int unique, b int) partition by range (a, b);
975 ERROR: unique constraint on partitioned table must include all partitioning columns
976 DETAIL: UNIQUE constraint on table "idxpart" lacks column "b" which is part of the partition key.
977 create table idxpart (a int, b int unique) partition by range (a, b);
978 ERROR: unique constraint on partitioned table must include all partitioning columns
979 DETAIL: UNIQUE constraint on table "idxpart" lacks column "a" which is part of the partition key.
980 create table idxpart (a int primary key, b int) partition by range (b, a);
981 ERROR: unique constraint on partitioned table must include all partitioning columns
982 DETAIL: PRIMARY KEY constraint on table "idxpart" lacks column "b" which is part of the partition key.
983 create table idxpart (a int, b int primary key) partition by range (b, a);
984 ERROR: unique constraint on partitioned table must include all partitioning columns
985 DETAIL: PRIMARY KEY constraint on table "idxpart" lacks column "a" which is part of the partition key.
986 -- OK if you use them in some other order
987 create table idxpart (a int, b int, c text, primary key (a, b, c)) partition by range (b, c, a);
989 -- OK to add an exclusion constraint if partitioning by its equal column
990 create table idxpart (a int4range, exclude USING GIST (a with = )) partition by range (a);
992 -- OK more than one equal column
993 create table idxpart (a int4range, b int4range, exclude USING GIST (a with =, b with =)) partition by range (a, b);
995 -- OK with more than one equal column: constraint is a proper superset of partition key
996 create table idxpart (a int4range, b int4range, exclude USING GIST (a with =, b with =)) partition by range (a);
998 -- Not OK more than one equal column: partition keys are a proper superset of constraint
999 create table idxpart (a int4range, b int4range, exclude USING GIST (a with = )) partition by range (a, b);
1000 ERROR: unique constraint on partitioned table must include all partitioning columns
1001 DETAIL: EXCLUDE constraint on table "idxpart" lacks column "b" which is part of the partition key.
1002 -- Not OK with just -|-
1003 create table idxpart (a int4range, exclude USING GIST (a with -|- )) partition by range (a);
1004 ERROR: cannot match partition key to index on column "a" using non-equal operator "-|-"
1005 -- OK with equals and &&, and equals is the partition key
1006 create table idxpart (a int4range, b int4range, exclude USING GIST (a with =, b with &&)) partition by range (a);
1008 -- Not OK with equals and &&, and equals is not the partition key
1009 create table idxpart (a int4range, b int4range, c int4range, exclude USING GIST (b with =, c with &&)) partition by range (a);
1010 ERROR: unique constraint on partitioned table must include all partitioning columns
1011 DETAIL: EXCLUDE constraint on table "idxpart" lacks column "a" which is part of the partition key.
1012 -- OK more than one equal column and a && column
1013 create table idxpart (a int4range, b int4range, c int4range, exclude USING GIST (a with =, b with =, c with &&)) partition by range (a, b);
1015 -- no expressions in partition key for PK/UNIQUE
1016 create table idxpart (a int primary key, b int) partition by range ((b + a));
1017 ERROR: unsupported PRIMARY KEY constraint with partition key definition
1018 DETAIL: PRIMARY KEY constraints cannot be used when partition keys include expressions.
1019 create table idxpart (a int unique, b int) partition by range ((b + a));
1020 ERROR: unsupported UNIQUE constraint with partition key definition
1021 DETAIL: UNIQUE constraints cannot be used when partition keys include expressions.
1022 -- use ALTER TABLE to add a primary key
1023 create table idxpart (a int, b int, c text) partition by range (a, b);
1024 alter table idxpart add primary key (a); -- not an incomplete one though
1025 ERROR: unique constraint on partitioned table must include all partitioning columns
1026 DETAIL: PRIMARY KEY constraint on table "idxpart" lacks column "b" which is part of the partition key.
1027 alter table idxpart add primary key (a, b); -- this works
1029 Partitioned table "public.idxpart"
1030 Column | Type | Collation | Nullable | Default
1031 --------+---------+-----------+----------+---------
1032 a | integer | | not null |
1033 b | integer | | not null |
1035 Partition key: RANGE (a, b)
1037 "idxpart_pkey" PRIMARY KEY, btree (a, b)
1038 Number of partitions: 0
1040 create table idxpart1 partition of idxpart for values from (0, 0) to (1000, 1000);
1042 Table "public.idxpart1"
1043 Column | Type | Collation | Nullable | Default
1044 --------+---------+-----------+----------+---------
1045 a | integer | | not null |
1046 b | integer | | not null |
1048 Partition of: idxpart FOR VALUES FROM (0, 0) TO (1000, 1000)
1050 "idxpart1_pkey" PRIMARY KEY, btree (a, b)
1053 -- use ALTER TABLE to add a unique constraint
1054 create table idxpart (a int, b int) partition by range (a, b);
1055 alter table idxpart add unique (a); -- not an incomplete one though
1056 ERROR: unique constraint on partitioned table must include all partitioning columns
1057 DETAIL: UNIQUE constraint on table "idxpart" lacks column "b" which is part of the partition key.
1058 alter table idxpart add unique (b, a); -- this works
1060 Partitioned table "public.idxpart"
1061 Column | Type | Collation | Nullable | Default
1062 --------+---------+-----------+----------+---------
1065 Partition key: RANGE (a, b)
1067 "idxpart_b_a_key" UNIQUE CONSTRAINT, btree (b, a)
1068 Number of partitions: 0
1071 -- Exclusion constraints can be added if partitioning by their equal column
1072 create table idxpart (a int4range, b int4range) partition by range (a);
1073 alter table idxpart add exclude USING GIST (a with =);
1075 -- OK more than one equal column
1076 create table idxpart (a int4range, b int4range) partition by range (a, b);
1077 alter table idxpart add exclude USING GIST (a with =, b with =);
1079 -- OK with more than one equal column: constraint is a proper superset of partition key
1080 create table idxpart (a int4range, b int4range) partition by range (a);
1081 alter table idxpart add exclude USING GIST (a with =, b with =);
1083 -- Not OK more than one equal column: partition keys are a proper superset of constraint
1084 create table idxpart (a int4range, b int4range) partition by range (a, b);
1085 alter table idxpart add exclude USING GIST (a with =);
1086 ERROR: unique constraint on partitioned table must include all partitioning columns
1087 DETAIL: EXCLUDE constraint on table "idxpart" lacks column "b" which is part of the partition key.
1089 -- Not OK with just -|-
1090 create table idxpart (a int4range, b int4range) partition by range (a, b);
1091 alter table idxpart add exclude USING GIST (a with -|-);
1092 ERROR: cannot match partition key to index on column "a" using non-equal operator "-|-"
1094 -- OK with equals and &&, and equals is the partition key
1095 create table idxpart (a int4range, b int4range) partition by range (a);
1096 alter table idxpart add exclude USING GIST (a with =, b with &&);
1098 -- Not OK with equals and &&, and equals is not the partition key
1099 create table idxpart (a int4range, b int4range, c int4range) partition by range (a);
1100 alter table idxpart add exclude USING GIST (b with =, c with &&);
1101 ERROR: unique constraint on partitioned table must include all partitioning columns
1102 DETAIL: EXCLUDE constraint on table "idxpart" lacks column "a" which is part of the partition key.
1104 -- OK more than one equal column and a && column
1105 create table idxpart (a int4range, b int4range, c int4range) partition by range (a, b);
1106 alter table idxpart add exclude USING GIST (a with =, b with =, c with &&);
1108 -- When (sub)partitions are created, they also contain the constraint
1109 create table idxpart (a int, b int, primary key (a, b)) partition by range (a, b);
1110 create table idxpart1 partition of idxpart for values from (1, 1) to (10, 10);
1111 create table idxpart2 partition of idxpart for values from (10, 10) to (20, 20)
1112 partition by range (b);
1113 create table idxpart21 partition of idxpart2 for values from (10) to (15);
1114 create table idxpart22 partition of idxpart2 for values from (15) to (20);
1115 create table idxpart3 (b int not null, a int not null);
1116 alter table idxpart attach partition idxpart3 for values from (20, 20) to (30, 30);
1117 select conname, contype, conrelid::regclass, conindid::regclass, conkey
1118 from pg_constraint where conrelid::regclass::text like 'idxpart%'
1119 order by conrelid::regclass::text, conname;
1120 conname | contype | conrelid | conindid | conkey
1121 ---------------------+---------+-----------+----------------+--------
1122 idxpart_a_not_null | n | idxpart | - | {1}
1123 idxpart_b_not_null | n | idxpart | - | {2}
1124 idxpart_pkey | p | idxpart | idxpart_pkey | {1,2}
1125 idxpart1_pkey | p | idxpart1 | idxpart1_pkey | {1,2}
1126 idxpart_a_not_null | n | idxpart1 | - | {1}
1127 idxpart_b_not_null | n | idxpart1 | - | {2}
1128 idxpart2_pkey | p | idxpart2 | idxpart2_pkey | {1,2}
1129 idxpart_a_not_null | n | idxpart2 | - | {1}
1130 idxpart_b_not_null | n | idxpart2 | - | {2}
1131 idxpart21_pkey | p | idxpart21 | idxpart21_pkey | {1,2}
1132 idxpart_a_not_null | n | idxpart21 | - | {1}
1133 idxpart_b_not_null | n | idxpart21 | - | {2}
1134 idxpart22_pkey | p | idxpart22 | idxpart22_pkey | {1,2}
1135 idxpart_a_not_null | n | idxpart22 | - | {1}
1136 idxpart_b_not_null | n | idxpart22 | - | {2}
1137 idxpart3_a_not_null | n | idxpart3 | - | {2}
1138 idxpart3_b_not_null | n | idxpart3 | - | {1}
1139 idxpart3_pkey | p | idxpart3 | idxpart3_pkey | {2,1}
1143 -- Verify that multi-layer partitioning honors the requirement that all
1144 -- columns in the partition key must appear in primary/unique key
1145 create table idxpart (a int, b int, primary key (a)) partition by range (a);
1146 create table idxpart2 partition of idxpart
1147 for values from (0) to (1000) partition by range (b); -- fail
1148 ERROR: unique constraint on partitioned table must include all partitioning columns
1149 DETAIL: PRIMARY KEY constraint on table "idxpart2" lacks column "b" which is part of the partition key.
1151 -- Ditto for the ATTACH PARTITION case
1152 create table idxpart (a int unique, b int) partition by range (a);
1153 create table idxpart1 (a int not null, b int, unique (a, b))
1154 partition by range (a, b);
1155 alter table idxpart attach partition idxpart1 for values from (1) to (1000);
1156 ERROR: unique constraint on partitioned table must include all partitioning columns
1157 DETAIL: UNIQUE constraint on table "idxpart1" lacks column "b" which is part of the partition key.
1158 DROP TABLE idxpart, idxpart1;
1159 -- Multi-layer partitioning works correctly in this case:
1160 create table idxpart (a int, b int, primary key (a, b)) partition by range (a);
1161 create table idxpart2 partition of idxpart for values from (0) to (1000) partition by range (b);
1162 create table idxpart21 partition of idxpart2 for values from (0) to (1000);
1163 select conname, contype, conrelid::regclass, conindid::regclass, conkey
1164 from pg_constraint where conrelid::regclass::text like 'idxpart%'
1165 order by conrelid::regclass::text, conname;
1166 conname | contype | conrelid | conindid | conkey
1167 --------------------+---------+-----------+----------------+--------
1168 idxpart_a_not_null | n | idxpart | - | {1}
1169 idxpart_b_not_null | n | idxpart | - | {2}
1170 idxpart_pkey | p | idxpart | idxpart_pkey | {1,2}
1171 idxpart2_pkey | p | idxpart2 | idxpart2_pkey | {1,2}
1172 idxpart_a_not_null | n | idxpart2 | - | {1}
1173 idxpart_b_not_null | n | idxpart2 | - | {2}
1174 idxpart21_pkey | p | idxpart21 | idxpart21_pkey | {1,2}
1175 idxpart_a_not_null | n | idxpart21 | - | {1}
1176 idxpart_b_not_null | n | idxpart21 | - | {2}
1180 -- If a partitioned table has a unique/PK constraint, then it's not possible
1181 -- to drop the corresponding constraint in the children; nor it's possible
1182 -- to drop the indexes individually. Dropping the constraint in the parent
1183 -- gets rid of the lot.
1184 create table idxpart (i int) partition by hash (i);
1185 create table idxpart0 partition of idxpart (i) for values with (modulus 2, remainder 0);
1186 create table idxpart1 partition of idxpart (i) for values with (modulus 2, remainder 1);
1187 alter table idxpart0 add primary key(i);
1188 alter table idxpart add primary key(i);
1189 select indrelid::regclass, indexrelid::regclass, inhparent::regclass, indisvalid,
1190 conname, conislocal, coninhcount, connoinherit, convalidated
1191 from pg_index idx left join pg_inherits inh on (idx.indexrelid = inh.inhrelid)
1192 left join pg_constraint con on (idx.indexrelid = con.conindid)
1193 where indrelid::regclass::text like 'idxpart%'
1194 order by indexrelid::regclass::text collate "C";
1195 indrelid | indexrelid | inhparent | indisvalid | conname | conislocal | coninhcount | connoinherit | convalidated
1196 ----------+---------------+--------------+------------+---------------+------------+-------------+--------------+--------------
1197 idxpart0 | idxpart0_pkey | idxpart_pkey | t | idxpart0_pkey | f | 1 | t | t
1198 idxpart1 | idxpart1_pkey | idxpart_pkey | t | idxpart1_pkey | f | 1 | f | t
1199 idxpart | idxpart_pkey | | t | idxpart_pkey | t | 0 | t | t
1202 drop index idxpart0_pkey; -- fail
1203 ERROR: cannot drop index idxpart0_pkey because index idxpart_pkey requires it
1204 HINT: You can drop index idxpart_pkey instead.
1205 drop index idxpart1_pkey; -- fail
1206 ERROR: cannot drop index idxpart1_pkey because index idxpart_pkey requires it
1207 HINT: You can drop index idxpart_pkey instead.
1208 alter table idxpart0 drop constraint idxpart0_pkey; -- fail
1209 ERROR: cannot drop inherited constraint "idxpart0_pkey" of relation "idxpart0"
1210 alter table idxpart1 drop constraint idxpart1_pkey; -- fail
1211 ERROR: cannot drop inherited constraint "idxpart1_pkey" of relation "idxpart1"
1212 alter table idxpart drop constraint idxpart_pkey; -- ok
1213 select indrelid::regclass, indexrelid::regclass, inhparent::regclass, indisvalid,
1214 conname, conislocal, coninhcount, connoinherit, convalidated
1215 from pg_index idx left join pg_inherits inh on (idx.indexrelid = inh.inhrelid)
1216 left join pg_constraint con on (idx.indexrelid = con.conindid)
1217 where indrelid::regclass::text like 'idxpart%'
1218 order by indexrelid::regclass::text collate "C";
1219 indrelid | indexrelid | inhparent | indisvalid | conname | conislocal | coninhcount | connoinherit | convalidated
1220 ----------+------------+-----------+------------+---------+------------+-------------+--------------+--------------
1224 -- If the partition to be attached already has a primary key, fail if
1225 -- it doesn't match the parent's PK.
1226 CREATE TABLE idxpart (c1 INT PRIMARY KEY, c2 INT, c3 VARCHAR(10)) PARTITION BY RANGE(c1);
1227 CREATE TABLE idxpart1 (LIKE idxpart);
1228 ALTER TABLE idxpart1 ADD PRIMARY KEY (c1, c2);
1229 ALTER TABLE idxpart ATTACH PARTITION idxpart1 FOR VALUES FROM (100) TO (200);
1230 ERROR: multiple primary keys for table "idxpart1" are not allowed
1231 DROP TABLE idxpart, idxpart1;
1232 -- Ditto if there is some distance between the PKs (subpartitioning)
1233 create table idxpart (a int, b int, primary key (a)) partition by range (a);
1234 create table idxpart1 (a int not null, b int) partition by range (a);
1235 create table idxpart11 (a int not null, b int primary key);
1236 alter table idxpart1 attach partition idxpart11 for values from (0) to (1000);
1237 alter table idxpart attach partition idxpart1 for values from (0) to (10000);
1238 ERROR: multiple primary keys for table "idxpart11" are not allowed
1239 drop table idxpart, idxpart1, idxpart11;
1240 -- If a partitioned table has a constraint whose index is not valid,
1241 -- attaching a missing partition makes it valid.
1242 create table idxpart (a int) partition by range (a);
1243 create table idxpart0 (like idxpart);
1244 alter table idxpart0 add primary key (a);
1245 alter table idxpart attach partition idxpart0 for values from (0) to (1000);
1246 alter table only idxpart add primary key (a);
1247 select indrelid::regclass, indexrelid::regclass, inhparent::regclass, indisvalid,
1248 conname, conislocal, coninhcount, connoinherit, convalidated
1249 from pg_index idx left join pg_inherits inh on (idx.indexrelid = inh.inhrelid)
1250 left join pg_constraint con on (idx.indexrelid = con.conindid)
1251 where indrelid::regclass::text like 'idxpart%'
1252 order by indexrelid::regclass::text collate "C";
1253 indrelid | indexrelid | inhparent | indisvalid | conname | conislocal | coninhcount | connoinherit | convalidated
1254 ----------+---------------+-----------+------------+---------------+------------+-------------+--------------+--------------
1255 idxpart0 | idxpart0_pkey | | t | idxpart0_pkey | t | 0 | t | t
1256 idxpart | idxpart_pkey | | f | idxpart_pkey | t | 0 | t | t
1259 alter index idxpart_pkey attach partition idxpart0_pkey;
1260 select indrelid::regclass, indexrelid::regclass, inhparent::regclass, indisvalid,
1261 conname, conislocal, coninhcount, connoinherit, convalidated
1262 from pg_index idx left join pg_inherits inh on (idx.indexrelid = inh.inhrelid)
1263 left join pg_constraint con on (idx.indexrelid = con.conindid)
1264 where indrelid::regclass::text like 'idxpart%'
1265 order by indexrelid::regclass::text collate "C";
1266 indrelid | indexrelid | inhparent | indisvalid | conname | conislocal | coninhcount | connoinherit | convalidated
1267 ----------+---------------+--------------+------------+---------------+------------+-------------+--------------+--------------
1268 idxpart0 | idxpart0_pkey | idxpart_pkey | t | idxpart0_pkey | f | 1 | t | t
1269 idxpart | idxpart_pkey | | t | idxpart_pkey | t | 0 | t | t
1273 -- Related to the above scenario: ADD PRIMARY KEY on the parent mustn't
1274 -- automatically propagate NOT NULL to child columns.
1275 create table idxpart (a int) partition by range (a);
1276 create table idxpart0 (like idxpart);
1277 alter table idxpart0 add unique (a);
1278 alter table idxpart attach partition idxpart0 default;
1279 alter table only idxpart add primary key (a); -- fail, no not-null constraint
1280 ERROR: column "a" of table "idxpart0" is not marked NOT NULL
1281 alter table idxpart0 alter column a set not null;
1282 alter table only idxpart add primary key (a); -- now it works
1283 alter index idxpart_pkey attach partition idxpart0_a_key;
1285 -- if a partition has a unique index without a constraint, does not attach
1286 -- automatically; creates a new index instead.
1287 create table idxpart (a int, b int) partition by range (a);
1288 create table idxpart1 (a int not null, b int);
1289 create unique index on idxpart1 (a);
1290 alter table idxpart add primary key (a);
1291 alter table idxpart attach partition idxpart1 for values from (1) to (1000);
1292 select indrelid::regclass, indexrelid::regclass, inhparent::regclass, indisvalid,
1293 conname, conislocal, coninhcount, connoinherit, convalidated
1294 from pg_index idx left join pg_inherits inh on (idx.indexrelid = inh.inhrelid)
1295 left join pg_constraint con on (idx.indexrelid = con.conindid)
1296 where indrelid::regclass::text like 'idxpart%'
1297 order by indexrelid::regclass::text collate "C";
1298 indrelid | indexrelid | inhparent | indisvalid | conname | conislocal | coninhcount | connoinherit | convalidated
1299 ----------+----------------+--------------+------------+---------------+------------+-------------+--------------+--------------
1300 idxpart1 | idxpart1_a_idx | | t | | | | |
1301 idxpart1 | idxpart1_pkey | idxpart_pkey | t | idxpart1_pkey | f | 1 | f | t
1302 idxpart | idxpart_pkey | | t | idxpart_pkey | t | 0 | t | t
1306 -- Can't attach an index without a corresponding constraint
1307 create table idxpart (a int, b int) partition by range (a);
1308 create table idxpart1 (a int not null, b int);
1309 create unique index on idxpart1 (a);
1310 alter table idxpart attach partition idxpart1 for values from (1) to (1000);
1311 alter table only idxpart add primary key (a);
1312 alter index idxpart_pkey attach partition idxpart1_a_idx; -- fail
1313 ERROR: cannot attach index "idxpart1_a_idx" as a partition of index "idxpart_pkey"
1314 DETAIL: The index "idxpart_pkey" belongs to a constraint in table "idxpart" but no constraint exists for index "idxpart1_a_idx".
1316 -- Test that unique constraints are working
1317 create table idxpart (a int, b text, primary key (a, b)) partition by range (a);
1318 create table idxpart1 partition of idxpart for values from (0) to (100000);
1319 create table idxpart2 (c int, like idxpart);
1320 insert into idxpart2 (c, a, b) values (42, 572814, 'inserted first');
1321 alter table idxpart2 drop column c;
1322 create unique index on idxpart (a);
1323 alter table idxpart attach partition idxpart2 for values from (100000) to (1000000);
1324 insert into idxpart values (0, 'zero'), (42, 'life'), (2^16, 'sixteen');
1325 insert into idxpart select 2^g, format('two to power of %s', g) from generate_series(15, 17) g;
1326 ERROR: duplicate key value violates unique constraint "idxpart1_a_idx"
1327 DETAIL: Key (a)=(65536) already exists.
1328 insert into idxpart values (16, 'sixteen');
1329 insert into idxpart (b, a) values ('one', 142857), ('two', 285714);
1330 insert into idxpart select a * 2, b || b from idxpart where a between 2^16 and 2^19;
1331 ERROR: duplicate key value violates unique constraint "idxpart2_a_idx"
1332 DETAIL: Key (a)=(285714) already exists.
1333 insert into idxpart values (572814, 'five');
1334 ERROR: duplicate key value violates unique constraint "idxpart2_a_idx"
1335 DETAIL: Key (a)=(572814) already exists.
1336 insert into idxpart values (857142, 'six');
1337 select tableoid::regclass, * from idxpart order by a;
1339 ----------+--------+----------------
1341 idxpart1 | 16 | sixteen
1342 idxpart1 | 42 | life
1343 idxpart1 | 65536 | sixteen
1344 idxpart2 | 142857 | one
1345 idxpart2 | 285714 | two
1346 idxpart2 | 572814 | inserted first
1347 idxpart2 | 857142 | six
1351 -- Test some other non-btree index types
1352 create table idxpart (a int, b text, c int[]) partition by range (a);
1353 create table idxpart1 partition of idxpart for values from (0) to (100000);
1354 set enable_seqscan to off;
1355 create index idxpart_brin on idxpart using brin(b);
1356 explain (costs off) select * from idxpart where b = 'abcd';
1358 -------------------------------------------
1359 Bitmap Heap Scan on idxpart1 idxpart
1360 Recheck Cond: (b = 'abcd'::text)
1361 -> Bitmap Index Scan on idxpart1_b_idx
1362 Index Cond: (b = 'abcd'::text)
1365 drop index idxpart_brin;
1366 create index idxpart_spgist on idxpart using spgist(b);
1367 explain (costs off) select * from idxpart where b = 'abcd';
1369 -------------------------------------------
1370 Bitmap Heap Scan on idxpart1 idxpart
1371 Recheck Cond: (b = 'abcd'::text)
1372 -> Bitmap Index Scan on idxpart1_b_idx
1373 Index Cond: (b = 'abcd'::text)
1376 drop index idxpart_spgist;
1377 create index idxpart_gin on idxpart using gin(c);
1378 explain (costs off) select * from idxpart where c @> array[42];
1380 ----------------------------------------------
1381 Bitmap Heap Scan on idxpart1 idxpart
1382 Recheck Cond: (c @> '{42}'::integer[])
1383 -> Bitmap Index Scan on idxpart1_c_idx
1384 Index Cond: (c @> '{42}'::integer[])
1387 drop index idxpart_gin;
1388 reset enable_seqscan;
1390 -- intentionally leave some objects around
1391 create table idxpart (a int) partition by range (a);
1392 create table idxpart1 partition of idxpart for values from (0) to (100);
1393 create table idxpart2 partition of idxpart for values from (100) to (1000)
1394 partition by range (a);
1395 create table idxpart21 partition of idxpart2 for values from (100) to (200);
1396 create table idxpart22 partition of idxpart2 for values from (200) to (300);
1397 create index on idxpart22 (a);
1398 create index on only idxpart2 (a);
1399 alter index idxpart2_a_idx attach partition idxpart22_a_idx;
1400 create index on idxpart (a);
1401 create table idxpart_another (a int, b int, primary key (a, b)) partition by range (a);
1402 create table idxpart_another_1 partition of idxpart_another for values from (0) to (100);
1403 create table idxpart3 (c int, b int, a int) partition by range (a);
1404 alter table idxpart3 drop column b, drop column c;
1405 create table idxpart31 partition of idxpart3 for values from (1000) to (1200);
1406 create table idxpart32 partition of idxpart3 for values from (1200) to (1400);
1407 alter table idxpart attach partition idxpart3 for values from (1000) to (2000);
1408 -- More objects intentionally left behind, to verify some pg_dump/pg_upgrade
1409 -- behavior; see https://postgr.es/m/20190321204928.GA17535@alvherre.pgsql
1410 create schema regress_indexing;
1411 set search_path to regress_indexing;
1412 create table pk (a int primary key) partition by range (a);
1413 create table pk1 partition of pk for values from (0) to (1000);
1414 create table pk2 (b int, a int);
1415 alter table pk2 drop column b;
1416 alter table pk2 alter a set not null;
1417 alter table pk attach partition pk2 for values from (1000) to (2000);
1418 create table pk3 partition of pk for values from (2000) to (3000);
1419 create table pk4 (like pk);
1420 alter table pk attach partition pk4 for values from (3000) to (4000);
1421 create table pk5 (like pk) partition by range (a);
1422 create table pk51 partition of pk5 for values from (4000) to (4500);
1423 create table pk52 partition of pk5 for values from (4500) to (5000);
1424 alter table pk attach partition pk5 for values from (4000) to (5000);
1426 -- Test that covering partitioned indexes work in various cases
1427 create table covidxpart (a int, b int) partition by list (a);
1428 create unique index on covidxpart (a) include (b);
1429 create table covidxpart1 partition of covidxpart for values in (1);
1430 create table covidxpart2 partition of covidxpart for values in (2);
1431 insert into covidxpart values (1, 1);
1432 insert into covidxpart values (1, 1);
1433 ERROR: duplicate key value violates unique constraint "covidxpart1_a_b_idx"
1434 DETAIL: Key (a)=(1) already exists.
1435 create table covidxpart3 (b int, c int, a int);
1436 alter table covidxpart3 drop c;
1437 alter table covidxpart attach partition covidxpart3 for values in (3);
1438 insert into covidxpart values (3, 1);
1439 insert into covidxpart values (3, 1);
1440 ERROR: duplicate key value violates unique constraint "covidxpart3_a_b_idx"
1441 DETAIL: Key (a)=(3) already exists.
1442 create table covidxpart4 (b int, a int);
1443 create unique index on covidxpart4 (a) include (b);
1444 create unique index on covidxpart4 (a);
1445 alter table covidxpart attach partition covidxpart4 for values in (4);
1446 insert into covidxpart values (4, 1);
1447 insert into covidxpart values (4, 1);
1448 ERROR: duplicate key value violates unique constraint "covidxpart4_a_b_idx"
1449 DETAIL: Key (a)=(4) already exists.
1450 create unique index on covidxpart (b) include (a); -- should fail
1451 ERROR: unique constraint on partitioned table must include all partitioning columns
1452 DETAIL: UNIQUE constraint on table "covidxpart" lacks column "a" which is part of the partition key.
1453 -- check that detaching a partition also detaches the primary key constraint
1454 create table parted_pk_detach_test (a int primary key) partition by list (a);
1455 create table parted_pk_detach_test1 partition of parted_pk_detach_test for values in (1);
1456 alter table parted_pk_detach_test1 drop constraint parted_pk_detach_test1_pkey; -- should fail
1457 ERROR: cannot drop inherited constraint "parted_pk_detach_test1_pkey" of relation "parted_pk_detach_test1"
1458 alter table parted_pk_detach_test detach partition parted_pk_detach_test1;
1459 alter table parted_pk_detach_test1 drop constraint parted_pk_detach_test1_pkey;
1460 drop table parted_pk_detach_test, parted_pk_detach_test1;
1461 create table parted_uniq_detach_test (a int unique) partition by list (a);
1462 create table parted_uniq_detach_test1 partition of parted_uniq_detach_test for values in (1);
1463 alter table parted_uniq_detach_test1 drop constraint parted_uniq_detach_test1_a_key; -- should fail
1464 ERROR: cannot drop inherited constraint "parted_uniq_detach_test1_a_key" of relation "parted_uniq_detach_test1"
1465 alter table parted_uniq_detach_test detach partition parted_uniq_detach_test1;
1466 alter table parted_uniq_detach_test1 drop constraint parted_uniq_detach_test1_a_key;
1467 drop table parted_uniq_detach_test, parted_uniq_detach_test1;
1468 -- check that dropping a column takes with it any partitioned indexes
1470 create table parted_index_col_drop(a int, b int, c int)
1471 partition by list (a);
1472 create table parted_index_col_drop1 partition of parted_index_col_drop
1473 for values in (1) partition by list (a);
1474 -- leave this partition without children.
1475 create table parted_index_col_drop2 partition of parted_index_col_drop
1476 for values in (2) partition by list (a);
1477 create table parted_index_col_drop11 partition of parted_index_col_drop1
1479 create index on parted_index_col_drop (b);
1480 create index on parted_index_col_drop (c);
1481 create index on parted_index_col_drop (b, c);
1482 alter table parted_index_col_drop drop column c;
1483 \d parted_index_col_drop
1484 Partitioned table "public.parted_index_col_drop"
1485 Column | Type | Collation | Nullable | Default
1486 --------+---------+-----------+----------+---------
1489 Partition key: LIST (a)
1491 "parted_index_col_drop_b_idx" btree (b)
1492 Number of partitions: 2 (Use \d+ to list them.)
1494 \d parted_index_col_drop1
1495 Partitioned table "public.parted_index_col_drop1"
1496 Column | Type | Collation | Nullable | Default
1497 --------+---------+-----------+----------+---------
1500 Partition of: parted_index_col_drop FOR VALUES IN (1)
1501 Partition key: LIST (a)
1503 "parted_index_col_drop1_b_idx" btree (b)
1504 Number of partitions: 1 (Use \d+ to list them.)
1506 \d parted_index_col_drop2
1507 Partitioned table "public.parted_index_col_drop2"
1508 Column | Type | Collation | Nullable | Default
1509 --------+---------+-----------+----------+---------
1512 Partition of: parted_index_col_drop FOR VALUES IN (2)
1513 Partition key: LIST (a)
1515 "parted_index_col_drop2_b_idx" btree (b)
1516 Number of partitions: 0
1518 \d parted_index_col_drop11
1519 Table "public.parted_index_col_drop11"
1520 Column | Type | Collation | Nullable | Default
1521 --------+---------+-----------+----------+---------
1524 Partition of: parted_index_col_drop1 FOR VALUES IN (1)
1526 "parted_index_col_drop11_b_idx" btree (b)
1528 drop table parted_index_col_drop;
1529 -- Check that invalid indexes are not selected when attaching a partition.
1530 create table parted_inval_tab (a int) partition by range (a);
1531 create index parted_inval_idx on parted_inval_tab (a);
1532 create table parted_inval_tab_1 (a int) partition by range (a);
1533 create table parted_inval_tab_1_1 partition of parted_inval_tab_1
1534 for values from (0) to (10);
1535 create table parted_inval_tab_1_2 partition of parted_inval_tab_1
1536 for values from (10) to (20);
1537 -- this creates an invalid index.
1538 create index parted_inval_ixd_1 on only parted_inval_tab_1 (a);
1539 -- this creates new indexes for all the partitions of parted_inval_tab_1,
1540 -- discarding the invalid index created previously as what is chosen.
1541 alter table parted_inval_tab attach partition parted_inval_tab_1
1542 for values from (1) to (100);
1543 select indexrelid::regclass, indisvalid,
1544 indrelid::regclass, inhparent::regclass
1545 from pg_index idx left join
1546 pg_inherits inh on (idx.indexrelid = inh.inhrelid)
1547 where indexrelid::regclass::text like 'parted_inval%'
1548 order by indexrelid::regclass::text collate "C";
1549 indexrelid | indisvalid | indrelid | inhparent
1550 ----------------------------+------------+----------------------+--------------------------
1551 parted_inval_idx | t | parted_inval_tab |
1552 parted_inval_ixd_1 | f | parted_inval_tab_1 |
1553 parted_inval_tab_1_1_a_idx | t | parted_inval_tab_1_1 | parted_inval_tab_1_a_idx
1554 parted_inval_tab_1_2_a_idx | t | parted_inval_tab_1_2 | parted_inval_tab_1_a_idx
1555 parted_inval_tab_1_a_idx | t | parted_inval_tab_1 | parted_inval_idx
1558 drop table parted_inval_tab;
1559 -- Check setup of indisvalid across a complex partition tree on index
1560 -- creation. If one index in a partition index is invalid, so should its
1561 -- partitioned index.
1562 create table parted_isvalid_tab (a int, b int) partition by range (a);
1563 create table parted_isvalid_tab_1 partition of parted_isvalid_tab
1564 for values from (1) to (10) partition by range (a);
1565 create table parted_isvalid_tab_2 partition of parted_isvalid_tab
1566 for values from (10) to (20) partition by range (a);
1567 create table parted_isvalid_tab_11 partition of parted_isvalid_tab_1
1568 for values from (1) to (5);
1569 create table parted_isvalid_tab_12 partition of parted_isvalid_tab_1
1570 for values from (5) to (10);
1571 -- create an invalid index on one of the partitions.
1572 insert into parted_isvalid_tab_11 values (1, 0);
1573 create index concurrently parted_isvalid_idx_11 on parted_isvalid_tab_11 ((a/b));
1574 ERROR: division by zero
1575 -- The previous invalid index is selected, invalidating all the indexes up to
1576 -- the top-most parent.
1577 create index parted_isvalid_idx on parted_isvalid_tab ((a/b));
1578 select indexrelid::regclass, indisvalid,
1579 indrelid::regclass, inhparent::regclass
1580 from pg_index idx left join
1581 pg_inherits inh on (idx.indexrelid = inh.inhrelid)
1582 where indexrelid::regclass::text like 'parted_isvalid%'
1583 order by indexrelid::regclass::text collate "C";
1584 indexrelid | indisvalid | indrelid | inhparent
1585 --------------------------------+------------+-----------------------+-------------------------------
1586 parted_isvalid_idx | f | parted_isvalid_tab |
1587 parted_isvalid_idx_11 | f | parted_isvalid_tab_11 | parted_isvalid_tab_1_expr_idx
1588 parted_isvalid_tab_12_expr_idx | t | parted_isvalid_tab_12 | parted_isvalid_tab_1_expr_idx
1589 parted_isvalid_tab_1_expr_idx | f | parted_isvalid_tab_1 | parted_isvalid_idx
1590 parted_isvalid_tab_2_expr_idx | t | parted_isvalid_tab_2 | parted_isvalid_idx
1593 drop table parted_isvalid_tab;
1594 -- Check state of replica indexes when attaching a partition.
1596 create table parted_replica_tab (id int not null) partition by range (id);
1597 create table parted_replica_tab_1 partition of parted_replica_tab
1598 for values from (1) to (10) partition by range (id);
1599 create table parted_replica_tab_11 partition of parted_replica_tab_1
1600 for values from (1) to (5);
1601 create unique index parted_replica_idx
1602 on only parted_replica_tab using btree (id);
1603 create unique index parted_replica_idx_1
1604 on only parted_replica_tab_1 using btree (id);
1605 -- This triggers an update of pg_index.indisreplident for parted_replica_idx.
1606 alter table only parted_replica_tab_1 replica identity
1607 using index parted_replica_idx_1;
1608 create unique index parted_replica_idx_11 on parted_replica_tab_11 USING btree (id);
1609 select indexrelid::regclass, indisvalid, indisreplident,
1610 indrelid::regclass, inhparent::regclass
1611 from pg_index idx left join
1612 pg_inherits inh on (idx.indexrelid = inh.inhrelid)
1613 where indexrelid::regclass::text like 'parted_replica%'
1614 order by indexrelid::regclass::text collate "C";
1615 indexrelid | indisvalid | indisreplident | indrelid | inhparent
1616 -----------------------+------------+----------------+-----------------------+-----------
1617 parted_replica_idx | f | f | parted_replica_tab |
1618 parted_replica_idx_1 | f | t | parted_replica_tab_1 |
1619 parted_replica_idx_11 | t | f | parted_replica_tab_11 |
1622 -- parted_replica_idx is not valid yet here, because parted_replica_idx_1
1624 alter index parted_replica_idx ATTACH PARTITION parted_replica_idx_1;
1625 select indexrelid::regclass, indisvalid, indisreplident,
1626 indrelid::regclass, inhparent::regclass
1627 from pg_index idx left join
1628 pg_inherits inh on (idx.indexrelid = inh.inhrelid)
1629 where indexrelid::regclass::text like 'parted_replica%'
1630 order by indexrelid::regclass::text collate "C";
1631 indexrelid | indisvalid | indisreplident | indrelid | inhparent
1632 -----------------------+------------+----------------+-----------------------+--------------------
1633 parted_replica_idx | f | f | parted_replica_tab |
1634 parted_replica_idx_1 | f | t | parted_replica_tab_1 | parted_replica_idx
1635 parted_replica_idx_11 | t | f | parted_replica_tab_11 |
1638 -- parted_replica_idx becomes valid here.
1639 alter index parted_replica_idx_1 ATTACH PARTITION parted_replica_idx_11;
1640 alter table only parted_replica_tab_1 replica identity
1641 using index parted_replica_idx_1;
1643 select indexrelid::regclass, indisvalid, indisreplident,
1644 indrelid::regclass, inhparent::regclass
1645 from pg_index idx left join
1646 pg_inherits inh on (idx.indexrelid = inh.inhrelid)
1647 where indexrelid::regclass::text like 'parted_replica%'
1648 order by indexrelid::regclass::text collate "C";
1649 indexrelid | indisvalid | indisreplident | indrelid | inhparent
1650 -----------------------+------------+----------------+-----------------------+----------------------
1651 parted_replica_idx | t | f | parted_replica_tab |
1652 parted_replica_idx_1 | t | t | parted_replica_tab_1 | parted_replica_idx
1653 parted_replica_idx_11 | t | f | parted_replica_tab_11 | parted_replica_idx_1
1656 drop table parted_replica_tab;
1657 -- test that indexing commands work with TOASTed values in pg_index
1658 create table test_pg_index_toast_table (a int);
1659 create or replace function test_pg_index_toast_func (a int, b int[])
1660 returns bool as $$ select true $$ language sql immutable;
1661 select array_agg(n) b from generate_series(1, 10000) n \gset
1662 create index concurrently test_pg_index_toast_index
1663 on test_pg_index_toast_table (test_pg_index_toast_func(a, :'b'));
1664 reindex index concurrently test_pg_index_toast_index;
1665 drop index concurrently test_pg_index_toast_index;
1666 create index test_pg_index_toast_index
1667 on test_pg_index_toast_table (test_pg_index_toast_func(a, :'b'));
1668 reindex index test_pg_index_toast_index;
1669 drop index test_pg_index_toast_index;
1670 drop function test_pg_index_toast_func;
1671 drop table test_pg_index_toast_table;