Improve nbtree unsatisfiable RowCompare detection.
[pgsql.git] / src / test / regress / expected / indexing.out
blobbcf1db11d731d0062ae8d6ef45e49d5ab2452939
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';
8  relhassubclass 
9 ----------------
10  f
11 (1 row)
13 -- Check that partitioned indexes are present in pg_indexes.
14 select indexdef from pg_indexes where indexname like 'idxpart_idx%';
15                             indexdef                             
16 -----------------------------------------------------------------
17  CREATE INDEX idxpart_idx ON ONLY public.idxpart USING btree (a)
18 (1 row)
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';
29  relhassubclass 
30 ----------------
31  f
32 (1 row)
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 -----------------+---------+----------------+----------------
42  idxpart         | p       | t              | 
43  idxpart1        | r       | f              | 
44  idxpart1_a_idx  | i       | f              | idxpart_a_idx
45  idxpart2        | p       | t              | 
46  idxpart21       | r       | f              | 
47  idxpart21_a_idx | i       | f              | idxpart2_a_idx
48  idxpart2_a_idx  | I       | t              | idxpart_a_idx
49  idxpart_a_idx   | I       | t              | 
50 (8 rows)
52 drop table idxpart;
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
58 drop table idxpart;
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);
65  col2 
66 ------
67 (0 rows)
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;
77 DROP TABLE idxpart;
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);
84 \d idxpart1
85               Table "public.idxpart1"
86  Column |  Type   | Collation | Nullable | Default 
87 --------+---------+-----------+----------+---------
88  a      | integer |           |          | 
89  b      | integer |           |          | 
90  c      | text    |           |          | 
92 alter table idxpart attach partition idxpart1 for values from (0) to (10);
93 \d idxpart1
94               Table "public.idxpart1"
95  Column |  Type   | Collation | Nullable | Default 
96 --------+---------+-----------+----------+---------
97  a      | integer |           |          | 
98  b      | integer |           |          | 
99  c      | text    |           |          | 
100 Partition of: idxpart FOR VALUES FROM (0) TO (10)
101 Indexes:
102     "idxpart1_a_idx" btree (a)
103     "idxpart1_b_c_idx" btree (b, c)
105 \d+ idxpart1_a_idx
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"
114 \d+ idxpart1_b_c_idx
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')
132   order by relname;
133   relname   | relpartbound 
134 ------------+--------------
135  idxpart1_c | 
136  idxpart_c  | 
137 (2 rows)
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.
142 drop table idxpart;
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);
148 \d idxpart1
149               Table "public.idxpart1"
150  Column |  Type   | Collation | Nullable | Default 
151 --------+---------+-----------+----------+---------
152  a      | integer |           |          | 
153  b      | integer |           |          | 
154 Partition of: idxpart FOR VALUES FROM (0, 0) TO (10, 10)
155 Indexes:
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 ------------------+---------+----------------+-----------------
164  idxpart          | p       | t              | 
165  idxpart1         | r       | f              | 
166  idxpart1_a_b_idx | i       | f              | idxpart_a_b_idx
167  idxpart_a_b_idx  | I       | t              | 
168 (4 rows)
170 drop table idxpart;
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;
183  relname  | relkind 
184 ----------+---------
185  idxpart  | p
186  idxpart1 | r
187 (2 rows)
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;
193     relname    | relkind 
194 ---------------+---------
195  idxpart       | p
196  idxpart_a_idx | I
197 (2 rows)
199 drop table idxpart;
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;
212    relname    | relkind 
213 --------------+---------
214  idxpart_temp | p
215 (1 row)
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.
244 -- OK
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
247 -- reject dupe
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".
252 drop table idxpart;
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 ------------+----------
258 (0 rows)
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);
269 \d idxpart1
270               Table "public.idxpart1"
271  Column |  Type   | Collation | Nullable | Default 
272 --------+---------+-----------+----------+---------
273  a      | integer |           |          | 
274  b      | integer |           |          | 
275 Partition of: idxpart FOR VALUES FROM (0) TO (1000)
276 Indexes:
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))
283 drop table idxpart;
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.
298 \d idxpart1
299               Table "public.idxpart1"
300  Column |  Type   | Collation | Nullable | Default 
301 --------+---------+-----------+----------+---------
302  a      | integer |           |          | 
303 Partition of: idxpart FOR VALUES FROM (0) TO (100)
304 Indexes:
305     "idxpart1_a_idx" btree (a)
307 \d idxpart2
308         Partitioned table "public.idxpart2"
309  Column |  Type   | Collation | Nullable | Default 
310 --------+---------+-----------+----------+---------
311  a      | integer |           |          | 
312 Partition of: idxpart FOR VALUES FROM (100) TO (1000)
313 Partition key: RANGE (a)
314 Indexes:
315     "idxpart2_a_idx" btree (a) INVALID
316 Number of partitions: 2 (Use \d+ to list them.)
318 \d idxpart21
319              Table "public.idxpart21"
320  Column |  Type   | Collation | Nullable | Default 
321 --------+---------+-----------+----------+---------
322  a      | integer |           |          | 
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   | 
335 (4 rows)
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   | 
348 (4 rows)
350 -- attaching idxpart22 is not enough to set idxpart22_a_idx valid ...
351 alter index idxpart2_a_idx attach partition idxpart22_a_idx;
352 \d idxpart2
353         Partitioned table "public.idxpart2"
354  Column |  Type   | Collation | Nullable | Default 
355 --------+---------+-----------+----------+---------
356  a      | integer |           |          | 
357 Partition of: idxpart FOR VALUES FROM (100) TO (1000)
358 Partition key: RANGE (a)
359 Indexes:
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;
366 \d idxpart2
367         Partitioned table "public.idxpart2"
368  Column |  Type   | Collation | Nullable | Default 
369 --------+---------+-----------+----------+---------
370  a      | integer |           |          | 
371 Partition of: idxpart FOR VALUES FROM (100) TO (1000)
372 Partition key: RANGE (a)
373 Indexes:
374     "idxpart2_a_idx" btree (a)
375 Number of partitions: 2 (Use \d+ to list them.)
377 drop table idxpart;
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);
385 \d idxpart1
386               Table "public.idxpart1"
387  Column |  Type   | Collation | Nullable | Default 
388 --------+---------+-----------+----------+---------
389  a      | integer |           |          | 
390  b      | integer |           |          | 
391  c      | text    |           |          | 
392  d      | boolean |           |          | 
393 Indexes:
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 ------------------+---------+-----------
403  idxpart          | p       | 
404  idxpart1         | r       | 
405  idxpart1_a_idx   | i       | 
406  idxpart1_b_c_idx | i       | 
407  idxparti         | I       | 
408  idxparti2        | I       | 
409 (6 rows)
411 alter table idxpart attach partition idxpart1 for values from (0) to (10);
412 \d idxpart1
413               Table "public.idxpart1"
414  Column |  Type   | Collation | Nullable | Default 
415 --------+---------+-----------+----------+---------
416  a      | integer |           |          | 
417  b      | integer |           |          | 
418  c      | text    |           |          | 
419  d      | boolean |           |          | 
420 Partition of: idxpart FOR VALUES FROM (0) TO (10)
421 Indexes:
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 ------------------+---------+-----------
431  idxpart          | p       | 
432  idxpart1         | r       | 
433  idxpart1_a_idx   | i       | idxparti
434  idxpart1_b_c_idx | i       | idxparti2
435  idxparti         | I       | 
436  idxparti2        | I       | 
437 (6 rows)
439 -- While here, also check matching when creating an index after the fact.
440 create index on idxpart1 ((a+b)) where d = true;
441 \d idxpart1
442               Table "public.idxpart1"
443  Column |  Type   | Collation | Nullable | Default 
444 --------+---------+-----------+----------+---------
445  a      | integer |           |          | 
446  b      | integer |           |          | 
447  c      | text    |           |          | 
448  d      | boolean |           |          | 
449 Partition of: idxpart FOR VALUES FROM (0) TO (10)
450 Indexes:
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 -------------------+---------+-----------
461  idxpart           | p       | 
462  idxpart1          | r       | 
463  idxpart1_a_idx    | i       | idxparti
464  idxpart1_b_c_idx  | i       | idxparti2
465  idxpart1_expr_idx | i       | 
466  idxparti          | I       | 
467  idxparti2         | I       | 
468 (7 rows)
470 create index idxparti3 on idxpart ((a+b)) where d = true;
471 \d idxpart1
472               Table "public.idxpart1"
473  Column |  Type   | Collation | Nullable | Default 
474 --------+---------+-----------+----------+---------
475  a      | integer |           |          | 
476  b      | integer |           |          | 
477  c      | text    |           |          | 
478  d      | boolean |           |          | 
479 Partition of: idxpart FOR VALUES FROM (0) TO (10)
480 Indexes:
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 -------------------+---------+-----------
491  idxpart           | p       | 
492  idxpart1          | r       | 
493  idxpart1_a_idx    | i       | idxparti
494  idxpart1_b_c_idx  | i       | idxparti2
495  idxpart1_expr_idx | i       | idxparti3
496  idxparti          | I       | 
497  idxparti2         | I       | 
498  idxparti3         | I       | 
499 (8 rows)
501 drop table idxpart;
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;
514     relname     | indisvalid 
515 ----------------+------------
516  idxpart1_a_idx | f
517  idxpart_a_idx  | f
518 (2 rows)
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;
524     relname     | indisvalid 
525 ----------------+------------
526  idxpart1_a_idx | f
527  idxpart_a_idx  | f
528 (2 rows)
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;
536      relname     | indisvalid 
537 -----------------+------------
538  idxpart11_a_idx | t
539  idxpart1_a_idx  | t
540  idxpart_a_idx   | t
541 (3 rows)
543 drop table idxpart;
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;
554     relname     | relkind 
555 ----------------+---------
556  idxpart        | p
557  idxpart1       | r
558  idxpart1_a_idx | i
559  idxpart2       | r
560  idxpart2_a_idx | i
561  idxpart3       | r
562  idxpart3_a_idx | i
563  idxpart_a_idx  | I
564 (8 rows)
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;
574     relname    | relkind 
575 ---------------+---------
576  idxpart       | p
577  idxpart1      | r
578  idxpart2      | r
579  idxpart3      | r
580  idxpart_a_idx | I
581 (5 rows)
583 drop table idxpart, idxpart1, idxpart2, idxpart3;
584 select relname, relkind from pg_class where relname like 'idxpart%' order by relname;
585  relname | relkind 
586 ---------+---------
587 (0 rows)
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;
599     relname     | relkind 
600 ----------------+---------
601  idxpart        | p
602  idxpart1       | r
603  idxpart1_a_idx | i
604  idxpart2       | r
605  idxpart2_a_idx | i
606  idxpart3       | r
607  idxpart3_a_idx | i
608  idxpart_a_idx  | I
609 (8 rows)
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;
616     relname     | relkind 
617 ----------------+---------
618  idxpart        | p
619  idxpart1       | r
620  idxpart1_a_idx | i
621  idxpart2       | r
622  idxpart2_a_idx | i
623  idxpart3       | r
624  idxpart3_a_idx | i
625 (7 rows)
627 drop table idxpart, idxpart1, idxpart2, idxpart3;
628 select relname, relkind from pg_class where relname like 'idxpart%' order by relname;
629  relname | relkind 
630 ---------+---------
631 (0 rows)
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;
638 \d idxpart2
639               Table "public.idxpart2"
640  Column |  Type   | Collation | Nullable | Default 
641 --------+---------+-----------+----------+---------
642  a      | integer |           |          | 
643  b      | integer |           |          | 
644  c      | integer |           |          | 
645 Indexes:
646     "idxpart2_c_idx" btree (c)
648 alter table idxpart2 drop column c;
649 \d idxpart2
650               Table "public.idxpart2"
651  Column |  Type   | Collation | Nullable | Default 
652 --------+---------+-----------+----------+---------
653  a      | integer |           |          | 
654  b      | integer |           |          | 
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)))
675 (3 rows)
677 drop table idxpart;
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")
703 (7 rows)
705 drop table idxpart;
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)
729 (6 rows)
731 drop index idxpart_a_idx;
732 create index on only idxpart (a text_pattern_ops);
733 -- must reject
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.
737 drop table idxpart;
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)
776 (7 rows)
778 drop table idxpart;
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)
801 (6 rows)
803 drop table idxpart;
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)))
828 (6 rows)
830 drop table idxpart;
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)
851 (3 rows)
853 drop table idxpart;
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);
863 \d idxpart
864          Partitioned table "public.idxpart"
865   Column  |  Type   | Collation | Nullable | Default 
866 ----------+---------+-----------+----------+---------
867  col_keep | integer |           |          | 
868 Partition key: RANGE (col_keep)
869 Indexes:
870     "idxpart_col_keep_idx" btree (col_keep)
871 Number of partitions: 1 (Use \d+ to list them.)
873 \d idxpart1
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)
879 Indexes:
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
894 (7 rows)
896 drop table idxpart;
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);
906 \d idxpart
907          Partitioned table "public.idxpart"
908   Column  |  Type   | Collation | Nullable | Default 
909 ----------+---------+-----------+----------+---------
910  col_keep | integer |           |          | 
911 Partition key: RANGE (col_keep)
912 Indexes:
913     "idxpart_col_keep_idx" btree (col_keep)
914 Number of partitions: 1 (Use \d+ to list them.)
916 \d idxpart1
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)
922 Indexes:
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
937 (7 rows)
939 drop table idxpart;
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);
945 \d idxpart
946         Partitioned table "public.idxpart"
947  Column |  Type   | Collation | Nullable | Default 
948 --------+---------+-----------+----------+---------
949  a      | integer |           | not null | 
950  b      | integer |           |          | 
951 Partition key: RANGE (a)
952 Indexes:
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
959 drop table idxpart;
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);
963 \d idxpart1pk
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)
969 Indexes:
970     "idxpart1pk_pkey" PRIMARY KEY, btree (a)
972 drop table idxpart;
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);
988 drop table idxpart;
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);
991 drop table idxpart;
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);
994 drop table idxpart;
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);
997 drop table idxpart;
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);
1007 drop table idxpart;
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);
1014 drop table idxpart;
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
1028 \d idxpart
1029         Partitioned table "public.idxpart"
1030  Column |  Type   | Collation | Nullable | Default 
1031 --------+---------+-----------+----------+---------
1032  a      | integer |           | not null | 
1033  b      | integer |           | not null | 
1034  c      | text    |           |          | 
1035 Partition key: RANGE (a, b)
1036 Indexes:
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);
1041 \d idxpart1
1042               Table "public.idxpart1"
1043  Column |  Type   | Collation | Nullable | Default 
1044 --------+---------+-----------+----------+---------
1045  a      | integer |           | not null | 
1046  b      | integer |           | not null | 
1047  c      | text    |           |          | 
1048 Partition of: idxpart FOR VALUES FROM (0, 0) TO (1000, 1000)
1049 Indexes:
1050     "idxpart1_pkey" PRIMARY KEY, btree (a, b)
1052 drop table idxpart;
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
1059 \d idxpart
1060         Partitioned table "public.idxpart"
1061  Column |  Type   | Collation | Nullable | Default 
1062 --------+---------+-----------+----------+---------
1063  a      | integer |           |          | 
1064  b      | integer |           |          | 
1065 Partition key: RANGE (a, b)
1066 Indexes:
1067     "idxpart_b_a_key" UNIQUE CONSTRAINT, btree (b, a)
1068 Number of partitions: 0
1070 drop table idxpart;
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 =);
1074 drop table idxpart;
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 =);
1078 drop table idxpart;
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 =);
1082 drop table idxpart;
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.
1088 drop table idxpart;
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 "-|-"
1093 drop table idxpart;
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 &&);
1097 drop table idxpart;
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.
1103 drop table idxpart;
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 &&);
1107 drop table idxpart;
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}
1140 (18 rows)
1142 drop table idxpart;
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.
1150 drop table idxpart;
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}
1177 (9 rows)
1179 drop table idxpart;
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
1200 (3 rows)
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 ----------+------------+-----------+------------+---------+------------+-------------+--------------+--------------
1221 (0 rows)
1223 drop table idxpart;
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
1257 (2 rows)
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
1270 (2 rows)
1272 drop table idxpart;
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;
1284 drop table idxpart;
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
1303 (3 rows)
1305 drop table idxpart;
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".
1315 drop table idxpart;
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;
1338  tableoid |   a    |       b        
1339 ----------+--------+----------------
1340  idxpart1 |      0 | zero
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
1348 (8 rows)
1350 drop table idxpart;
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';
1357                 QUERY PLAN                 
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)
1363 (4 rows)
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';
1368                 QUERY PLAN                 
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)
1374 (4 rows)
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];
1379                   QUERY PLAN                  
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[])
1385 (4 rows)
1387 drop index idxpart_gin;
1388 reset enable_seqscan;
1389 drop table idxpart;
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);
1425 reset search_path;
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
1469 -- depending on it.
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
1478   for values in (1);
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 --------+---------+-----------+----------+---------
1487  a      | integer |           |          | 
1488  b      | integer |           |          | 
1489 Partition key: LIST (a)
1490 Indexes:
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 --------+---------+-----------+----------+---------
1498  a      | integer |           |          | 
1499  b      | integer |           |          | 
1500 Partition of: parted_index_col_drop FOR VALUES IN (1)
1501 Partition key: LIST (a)
1502 Indexes:
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 --------+---------+-----------+----------+---------
1510  a      | integer |           |          | 
1511  b      | integer |           |          | 
1512 Partition of: parted_index_col_drop FOR VALUES IN (2)
1513 Partition key: LIST (a)
1514 Indexes:
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 --------+---------+-----------+----------+---------
1522  a      | integer |           |          | 
1523  b      | integer |           |          | 
1524 Partition of: parted_index_col_drop1 FOR VALUES IN (1)
1525 Indexes:
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
1556 (5 rows)
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
1591 (5 rows)
1593 drop table parted_isvalid_tab;
1594 -- Check state of replica indexes when attaching a partition.
1595 begin;
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 | 
1620 (3 rows)
1622 -- parted_replica_idx is not valid yet here, because parted_replica_idx_1
1623 -- is not valid.
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 | 
1636 (3 rows)
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;
1642 commit;
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
1654 (3 rows)
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;