-- Creating an index on a partitioned table makes the partitions -- automatically get the index createtable idxpart (a int, b int, c text) partition by range (a);
-- relhassubclass of a partitioned index is false before creating any partition. -- It will be set after the first partition is created. createindex idxpart_idx on idxpart (a); select relhassubclass from pg_class where relname = 'idxpart_idx';
-- Check that partitioned indexes are present in pg_indexes. select indexdef from pg_indexes where indexname like'idxpart_idx%'; dropindex idxpart_idx;
createtable idxpart1 partition of idxpart forvaluesfrom (0) to (10); createtable idxpart2 partition of idxpart forvaluesfrom (10) to (100)
partition by range (b); createtable idxpart21 partition of idxpart2 forvaluesfrom (0) to (100);
-- Even with partitions, relhassubclass should not be set if a partitioned -- index is created only on the parent. createindex idxpart_idx on only idxpart(a); select relhassubclass from pg_class where relname = 'idxpart_idx'; dropindex idxpart_idx;
createindexon idxpart (a); select relname, relkind, relhassubclass, inhparent::regclass from pg_class leftjoin pg_index ix on (indexrelid = oid) leftjoin pg_inherits on (ix.indexrelid = inhrelid) where relname like'idxpart%'orderby relname; droptable idxpart;
-- Some unsupported features createtable idxpart (a int, b int, c text) partition by range (a); createtable idxpart1 partition of idxpart forvaluesfrom (0) to (10); createindex concurrently on idxpart (a); droptable idxpart;
-- Verify bugfix with query on indexed partitioned table with no partitions -- https://postgr.es/m/20180124162006.pmapfiznhgngwtjf@alvherre.pgsql CREATETABLE idxpart (col1 INT) PARTITION BY RANGE (col1); CREATEINDEXON idxpart (col1); CREATETABLE idxpart_two (col2 INT); SELECT col2 FROM idxpart_two fk LEFTOUTERJOIN idxpart pk ON (col1 = col2); DROPtable idxpart, idxpart_two;
-- Verify bugfix with index rewrite on ALTER TABLE / SET DATA TYPE -- https://postgr.es/m/CAKcux6mxNCGsgATwf5CGMF8g4WSupCXicCVMeKUTuWbyxHOMsQ@mail.gmail.com CREATETABLE idxpart (a INT, b TEXT, c INT) PARTITION BY RANGE(a); CREATETABLE idxpart1 PARTITION OF idxpart FORVALUESFROM (MINVALUE) TO (MAXVALUE); CREATEINDEX partidx_abc_idx ON idxpart (a, b, c); INSERTINTO idxpart (a, b, c) SELECT i, i, i FROM generate_series(1, 50) i; ALTERTABLE idxpart ALTERCOLUMN c TYPE numeric; DROPTABLE idxpart;
-- If a table without index is attached as partition to a table with -- an index, the index is automatically created createtable idxpart (a int, b int, c text) partition by range (a); createindex idxparti on idxpart (a); createindex idxparti2 on idxpart (b, c); createtable idxpart1 (like idxpart);
\d idxpart1 altertable idxpart attach partition idxpart1 forvaluesfrom (0) to (10);
\d idxpart1
\d+ idxpart1_a_idx
\d+ idxpart1_b_c_idx
-- Forbid ALTER TABLE when attaching or detaching an index to a partition. createindex idxpart_c on only idxpart (c); createindex idxpart1_c on idxpart1 (c); altertable idxpart_c attach partition idxpart1_c forvaluesfrom (10) to (20); alterindex idxpart_c attach partition idxpart1_c; select relname, relpartbound from pg_class where relname in ('idxpart_c', 'idxpart1_c') orderby relname; altertable idxpart_c detach partition idxpart1_c; droptable idxpart;
-- If a partition already has an index, don't create a duplicative one createtable idxpart (a int, b int) partition by range (a, b); createtable idxpart1 partition of idxpart forvaluesfrom (0, 0) to (10, 10); createindexon idxpart1 (a, b); createindexon idxpart (a, b);
\d idxpart1 select relname, relkind, relhassubclass, inhparent::regclass from pg_class leftjoin pg_index ix on (indexrelid = oid) leftjoin pg_inherits on (ix.indexrelid = inhrelid) where relname like'idxpart%'orderby relname; droptable idxpart;
-- DROP behavior for partitioned indexes createtable idxpart (a int) partition by range (a); createindexon idxpart (a); createtable idxpart1 partition of idxpart forvaluesfrom (0) to (10); dropindex idxpart1_a_idx; -- no way dropindex concurrently idxpart_a_idx; -- unsupported dropindex idxpart_a_idx; -- both indexes go away select relname, relkind from pg_class where relname like'idxpart%'orderby relname; createindexon idxpart (a); droptable idxpart1; -- the index on partition goes away too select relname, relkind from pg_class where relname like'idxpart%'orderby relname; droptable idxpart;
-- DROP behavior with temporary partitioned indexes create temp table idxpart_temp (a int) partition by range (a); createindexon idxpart_temp(a); create temp table idxpart1_temp partition of idxpart_temp forvaluesfrom (0) to (10); dropindex idxpart1_temp_a_idx; -- error -- non-concurrent drop is enforced here, so it is a valid case. dropindex concurrently idxpart_temp_a_idx; select relname, relkind from pg_class where relname like'idxpart_temp%'orderby relname; droptable idxpart_temp;
-- ALTER INDEX .. ATTACH, error cases createtable idxpart (a int, b int) partition by range (a, b); createtable idxpart1 partition of idxpart forvaluesfrom (0, 0) to (10, 10); createindex idxpart_a_b_idx on only idxpart (a, b); createindex idxpart1_a_b_idx on idxpart1 (a, b); createindex idxpart1_tst1 on idxpart1 (b, a); createindex idxpart1_tst2 on idxpart1 using hash (a); createindex idxpart1_tst3 on idxpart1 (a, b) where a > 10;
-- reject dupe createindex idxpart1_2_a_b on idxpart1 (a, b); alterindex idxpart_a_b_idx attach partition idxpart1_2_a_b; droptable idxpart; -- make sure everything's gone select indexrelid::regclass, indrelid::regclass from pg_index where indexrelid::regclass::text like'idxpart%';
-- Don't auto-attach incompatible indexes createtable idxpart (a int, b int) partition by range (a); createtable idxpart1 (a int, b int); createindexon idxpart1 using hash (a); createindexon idxpart1 (a) where b > 1; createindexon idxpart1 ((a + 0)); createindexon idxpart1 (a, a); createindexon idxpart (a); altertable idxpart attach partition idxpart1 forvaluesfrom (0) to (1000);
\d idxpart1 droptable idxpart;
-- If CREATE INDEX ONLY, don't create indexes on partitions; and existing -- indexes on partitions don't change parent. ALTER INDEX ATTACH can change -- the parent after the fact. createtable idxpart (a int) partition by range (a); createtable idxpart1 partition of idxpart forvaluesfrom (0) to (100); createtable idxpart2 partition of idxpart forvaluesfrom (100) to (1000)
partition by range (a); createtable idxpart21 partition of idxpart2 forvaluesfrom (100) to (200); createtable idxpart22 partition of idxpart2 forvaluesfrom (200) to (300); createindexon idxpart22 (a); createindexon only idxpart2 (a); createindexon idxpart (a); -- Here we expect that idxpart1 and idxpart2 have a new index, but idxpart21 -- does not; also, idxpart22 is not attached.
\d idxpart1
\d idxpart2
\d idxpart21 select indexrelid::regclass, indrelid::regclass, inhparent::regclass from pg_index idx leftjoin pg_inherits inh on (idx.indexrelid = inh.inhrelid) where indexrelid::regclass::text like'idxpart%' orderby indexrelid::regclass::text collate"C"; alterindex idxpart2_a_idx attach partition idxpart22_a_idx; select indexrelid::regclass, indrelid::regclass, inhparent::regclass from pg_index idx leftjoin pg_inherits inh on (idx.indexrelid = inh.inhrelid) where indexrelid::regclass::text like'idxpart%' orderby indexrelid::regclass::text collate"C"; -- attaching idxpart22 is not enough to set idxpart22_a_idx valid ... alterindex idxpart2_a_idx attach partition idxpart22_a_idx;
\d idxpart2 -- ... but this one is. createindexon idxpart21 (a); alterindex idxpart2_a_idx attach partition idxpart21_a_idx;
\d idxpart2 droptable idxpart;
-- When a table is attached a partition and it already has an index, a -- duplicate index should not get created, but rather the index becomes -- attached to the parent's index. createtable idxpart (a int, b int, c text, d bool) partition by range (a); createindex idxparti on idxpart (a); createindex idxparti2 on idxpart (b, c); createtable idxpart1 (like idxpart including indexes);
\d idxpart1 select relname, relkind, inhparent::regclass from pg_class leftjoin pg_index ix on (indexrelid = oid) leftjoin pg_inherits on (ix.indexrelid = inhrelid) where relname like'idxpart%'orderby relname; altertable idxpart attach partition idxpart1 forvaluesfrom (0) to (10);
\d idxpart1 select relname, relkind, inhparent::regclass from pg_class leftjoin pg_index ix on (indexrelid = oid) leftjoin pg_inherits on (ix.indexrelid = inhrelid) where relname like'idxpart%'orderby relname; -- While here, also check matching when creating an index after the fact. createindexon idxpart1 ((a+b)) where d = true;
\d idxpart1 select relname, relkind, inhparent::regclass from pg_class leftjoin pg_index ix on (indexrelid = oid) leftjoin pg_inherits on (ix.indexrelid = inhrelid) where relname like'idxpart%'orderby relname; createindex idxparti3 on idxpart ((a+b)) where d = true;
\d idxpart1 select relname, relkind, inhparent::regclass from pg_class leftjoin pg_index ix on (indexrelid = oid) leftjoin pg_inherits on (ix.indexrelid = inhrelid) where relname like'idxpart%'orderby relname; droptable idxpart;
-- Verify that attaching an invalid index does not mark the parent index valid. -- On the other hand, attaching a valid index marks not only its direct -- ancestor valid, but also any indirect ancestor that was only missing the one -- that was just made valid createtable idxpart (a int, b int) partition by range (a); createtable idxpart1 partition of idxpart forvaluesfrom (1) to (1000) partition by range (a); createtable idxpart11 partition of idxpart1 forvaluesfrom (1) to (100); createindexon only idxpart1 (a); createindexon only idxpart (a); -- this results in two invalid indexes: select relname, indisvalid from pg_class join pg_index on indexrelid = oid where relname like'idxpart%'orderby relname; -- idxpart1_a_idx is not valid, so idxpart_a_idx should not become valid: alterindex idxpart_a_idx attach partition idxpart1_a_idx; select relname, indisvalid from pg_class join pg_index on indexrelid = oid where relname like'idxpart%'orderby relname; -- after creating and attaching this, both idxpart1_a_idx and idxpart_a_idx -- should become valid createindexon idxpart11 (a); alterindex idxpart1_a_idx attach partition idxpart11_a_idx; select relname, indisvalid from pg_class join pg_index on indexrelid = oid where relname like'idxpart%'orderby relname; droptable idxpart;
-- Verify that re-attaching an already-attached partition index can -- validate the parent index if it was still invalid, including -- indirect ancestors in subpartitions. createtable idxpart (a int, b int) partition by range (a); createtable idxpart1 partition of idxpart forvaluesfrom (0) to (1000) partition by range (a); createtable idxpart11 partition of idxpart1 forvaluesfrom (0) to (500); -- Partitioned table with no partitions createtable idxpart2 partition of idxpart forvaluesfrom (1000) to (2000) partition by range (a); -- create parent indexes createindexon only idxpart ((a/b)); createindexon only idxpart1 ((a/b)); createindexon only idxpart2 ((a/b)); -- fail, leaves behind an invalid index on the leaf partition insertinto idxpart11 values (1, 0); createindex concurrently on idxpart11 ((a/b)); select relname, indisvalid from pg_class join pg_index on indexrelid = oid where relname like'idxpart%'orderby relname; -- attach the indexes; parents stay invalid alterindex idxpart1_expr_idx attach partition idxpart11_expr_idx; alterindex idxpart_expr_idx attach partition idxpart1_expr_idx; alterindex idxpart_expr_idx attach partition idxpart2_expr_idx; select relname, indisvalid from pg_class join pg_index on indexrelid = oid where relname like'idxpart%'orderby relname; -- fix the index on the leaf partition deletefrom idxpart11 where b = 0;
reindex index concurrently idxpart11_expr_idx; -- reattach the leaf partition index; parents should now be valid alterindex idxpart1_expr_idx attach partition idxpart11_expr_idx; select relname, indisvalid from pg_class join pg_index on indexrelid = oid where relname like'idxpart%'orderby relname; droptable idxpart;
-- Verify that re-attaching does not validate the parent when another -- child index is still invalid. createtable idxpart (a int, b int) partition by range (a); createtable idxpart1 partition of idxpart forvaluesfrom (0) to (500); createtable idxpart2 partition of idxpart forvaluesfrom (500) to (1000); createindexon only idxpart ((a/b)); -- create invalid indexes on both children insertinto idxpart1 values (1, 0); insertinto idxpart2 values (501, 0); createindex concurrently on idxpart1 ((a/b)); createindex concurrently on idxpart2 ((a/b)); select relname, indisvalid from pg_class join pg_index on indexrelid = oid where relname like'idxpart%'orderby relname; -- attach both; parent stays invalid alterindex idxpart_expr_idx attach partition idxpart1_expr_idx; alterindex idxpart_expr_idx attach partition idxpart2_expr_idx; select relname, indisvalid from pg_class join pg_index on indexrelid = oid where relname like'idxpart%'orderby relname; -- fix only idxpart1's index, leave idxpart2's still invalid deletefrom idxpart1 where b = 0;
reindex index concurrently idxpart1_expr_idx; -- re-attach the fixed child; parent should stay invalid alterindex idxpart_expr_idx attach partition idxpart1_expr_idx; select relname, indisvalid from pg_class join pg_index on indexrelid = oid where relname like'idxpart%'orderby relname; droptable idxpart;
-- verify dependency handling during ALTER TABLE DETACH PARTITION createtable idxpart (a int) partition by range (a); createtable idxpart1 (like idxpart); createindexon idxpart1 (a); createindexon idxpart (a); createtable idxpart2 (like idxpart); altertable idxpart attach partition idxpart1 forvaluesfrom (0000) to (1000); altertable idxpart attach partition idxpart2 forvaluesfrom (1000) to (2000); createtable idxpart3 partition of idxpart forvaluesfrom (2000) to (3000); select relname, relkind from pg_class where relname like'idxpart%'orderby relname; -- a) after detaching partitions, the indexes can be dropped independently altertable idxpart detach partition idxpart1; altertable idxpart detach partition idxpart2; altertable idxpart detach partition idxpart3; dropindex idxpart1_a_idx; dropindex idxpart2_a_idx; dropindex idxpart3_a_idx; select relname, relkind from pg_class where relname like'idxpart%'orderby relname; droptable idxpart, idxpart1, idxpart2, idxpart3; select relname, relkind from pg_class where relname like'idxpart%'orderby relname;
createtable idxpart (a int) partition by range (a); createtable idxpart1 (like idxpart); createindexon idxpart1 (a); createindexon idxpart (a); createtable idxpart2 (like idxpart); altertable idxpart attach partition idxpart1 forvaluesfrom (0000) to (1000); altertable idxpart attach partition idxpart2 forvaluesfrom (1000) to (2000); createtable idxpart3 partition of idxpart forvaluesfrom (2000) to (3000); -- b) after detaching, dropping the index on parent does not remove the others select relname, relkind from pg_class where relname like'idxpart%'orderby relname; altertable idxpart detach partition idxpart1; altertable idxpart detach partition idxpart2; altertable idxpart detach partition idxpart3; dropindex idxpart_a_idx; select relname, relkind from pg_class where relname like'idxpart%'orderby relname; droptable idxpart, idxpart1, idxpart2, idxpart3; select relname, relkind from pg_class where relname like'idxpart%'orderby relname;
createtable idxpart (a int, b int, c int) partition by range(a); createindexon idxpart(c); createtable idxpart1 partition of idxpart forvaluesfrom (0) to (250); createtable idxpart2 partition of idxpart forvaluesfrom (250) to (500); altertable idxpart detach partition idxpart2;
\d idxpart2 altertable idxpart2 dropcolumn c;
\d idxpart2 droptable idxpart, idxpart2;
-- Verify that expression indexes inherit correctly createtable idxpart (a int, b int) partition by range (a); createtable idxpart1 (like idxpart); createindexon idxpart1 ((a + b)); createindexon idxpart ((a + b)); createtable idxpart2 (like idxpart); altertable idxpart attach partition idxpart1 forvaluesfrom (0000) to (1000); altertable idxpart attach partition idxpart2 forvaluesfrom (1000) to (2000); createtable idxpart3 partition of idxpart forvaluesfrom (2000) to (3000); select relname as child, inhparent::regclass as parent, pg_get_indexdef as childdef from pg_class join pg_inherits on inhrelid = oid,
lateral pg_get_indexdef(pg_class.oid) where relkind in ('i', 'I') and relname like'idxpart%'orderby relname; droptable idxpart;
-- Verify behavior for collation (mis)matches createtable idxpart (a text) partition by range (a); createtable idxpart1 (like idxpart); createtable idxpart2 (like idxpart); createindexon idxpart2 (a collate"POSIX"); createindexon idxpart2 (a); createindexon idxpart2 (a collate"C"); altertable idxpart attach partition idxpart1 forvaluesfrom ('aaa') to ('bbb'); altertable idxpart attach partition idxpart2 forvaluesfrom ('bbb') to ('ccc'); createtable idxpart3 partition of idxpart forvaluesfrom ('ccc') to ('ddd'); createindexon idxpart (a collate"C"); createtable idxpart4 partition of idxpart forvaluesfrom ('ddd') to ('eee'); select relname as child, inhparent::regclass as parent, pg_get_indexdef as childdef from pg_class leftjoin pg_inherits on inhrelid = oid,
lateral pg_get_indexdef(pg_class.oid) where relkind in ('i', 'I') and relname like'idxpart%'orderby relname; droptable idxpart;
-- Verify behavior for opclass (mis)matches createtable idxpart (a text) partition by range (a); createtable idxpart1 (like idxpart); createtable idxpart2 (like idxpart); createindexon idxpart2 (a); altertable idxpart attach partition idxpart1 forvaluesfrom ('aaa') to ('bbb'); altertable idxpart attach partition idxpart2 forvaluesfrom ('bbb') to ('ccc'); createtable idxpart3 partition of idxpart forvaluesfrom ('ccc') to ('ddd'); createindexon idxpart (a text_pattern_ops); createtable idxpart4 partition of idxpart forvaluesfrom ('ddd') to ('eee'); -- must *not* have attached the index we created on idxpart2 select relname as child, inhparent::regclass as parent, pg_get_indexdef as childdef from pg_class leftjoin pg_inherits on inhrelid = oid,
lateral pg_get_indexdef(pg_class.oid) where relkind in ('i', 'I') and relname like'idxpart%'orderby relname; dropindex idxpart_a_idx; createindexon only idxpart (a text_pattern_ops); -- must reject alterindex idxpart_a_idx attach partition idxpart2_a_idx; droptable idxpart;
-- Verify that attaching indexes maps attribute numbers correctly createtable idxpart (col1 int, a int, col2 int, b int) partition by range (a); createtable idxpart1 (b int, col1 int, col2 int, col3 int, a int); altertable idxpart dropcolumn col1, dropcolumn col2; altertable idxpart1 dropcolumn col1, dropcolumn col2, dropcolumn col3; altertable idxpart attach partition idxpart1 forvaluesfrom (0) to (1000); createindex idxpart_1_idx on only idxpart (b, a); createindex idxpart1_1_idx on idxpart1 (b, a); createindex idxpart1_1b_idx on idxpart1 (b); -- test expressions and partial-index predicate, too createindex idxpart_2_idx on only idxpart ((b + a)) where a > 1; createindex idxpart1_2_idx on idxpart1 ((b + a)) where a > 1; createindex idxpart1_2b_idx on idxpart1 ((a + b)) where a > 1; createindex idxpart1_2c_idx on idxpart1 ((b + a)) where b > 1; alterindex idxpart_1_idx attach partition idxpart1_1b_idx; -- fail alterindex idxpart_1_idx attach partition idxpart1_1_idx; alterindex idxpart_2_idx attach partition idxpart1_2b_idx; -- fail alterindex idxpart_2_idx attach partition idxpart1_2c_idx; -- fail alterindex idxpart_2_idx attach partition idxpart1_2_idx; -- ok select relname as child, inhparent::regclass as parent, pg_get_indexdef as childdef from pg_class leftjoin pg_inherits on inhrelid = oid,
lateral pg_get_indexdef(pg_class.oid) where relkind in ('i', 'I') and relname like'idxpart%'orderby relname; droptable idxpart;
-- Make sure the partition columns are mapped correctly createtable idxpart (a int, b int, c text) partition by range (a); createindex idxparti on idxpart (a); createindex idxparti2 on idxpart (c, b); createtable idxpart1 (c text, a int, b int); altertable idxpart attach partition idxpart1 forvaluesfrom (0) to (10); createtable idxpart2 (c text, a int, b int); createindexon idxpart2 (a); createindexon idxpart2 (c, b); altertable idxpart attach partition idxpart2 forvaluesfrom (10) to (20); select c.relname, pg_get_indexdef(indexrelid) from pg_class c join pg_index i on c.oid = i.indexrelid where indrelid::regclass::text like'idxpart%' orderby indexrelid::regclass::text collate"C"; droptable idxpart;
-- Verify that columns are mapped correctly in expression indexes createtable idxpart (col1 int, col2 int, a int, b int) partition by range (a); createtable idxpart1 (col2 int, b int, col1 int, a int); createtable idxpart2 (col1 int, col2 int, b int, a int); altertable idxpart dropcolumn col1, dropcolumn col2; altertable idxpart1 dropcolumn col1, dropcolumn col2; altertable idxpart2 dropcolumn col1, dropcolumn col2; createindexon idxpart2 (abs(b)); altertable idxpart attach partition idxpart2 forvaluesfrom (0) to (1); createindexon idxpart (abs(b)); createindexon idxpart ((b + 1)); altertable idxpart attach partition idxpart1 forvaluesfrom (1) to (2); select c.relname, pg_get_indexdef(indexrelid) from pg_class c join pg_index i on c.oid = i.indexrelid where indrelid::regclass::text like'idxpart%' orderby indexrelid::regclass::text collate"C"; droptable idxpart;
-- Verify that columns are mapped correctly for WHERE in a partial index createtable idxpart (col1 int, a int, col3 int, b int) partition by range (a); altertable idxpart dropcolumn col1, dropcolumn col3; createtable idxpart1 (col1 int, col2 int, col3 int, col4 int, b int, a int); altertable idxpart1 dropcolumn col1, dropcolumn col2, dropcolumn col3, dropcolumn col4; altertable idxpart attach partition idxpart1 forvaluesfrom (0) to (1000); createtable idxpart2 (col1 int, col2 int, b int, a int); createindexon idxpart2 (a) where b > 1000; altertable idxpart2 dropcolumn col1, dropcolumn col2; altertable idxpart attach partition idxpart2 forvaluesfrom (1000) to (2000); createindexon idxpart (a) where b > 1000; select c.relname, pg_get_indexdef(indexrelid) from pg_class c join pg_index i on c.oid = i.indexrelid where indrelid::regclass::text like'idxpart%' orderby indexrelid::regclass::text collate"C"; droptable idxpart;
-- Column number mapping: dropped columns in the partition createtable idxpart1 (drop_1 int, drop_2 int, col_keep int, drop_3 int); altertable idxpart1 dropcolumn drop_1; altertable idxpart1 dropcolumn drop_2; altertable idxpart1 dropcolumn drop_3; createindexon idxpart1 (col_keep); createtable idxpart (col_keep int) partition by range (col_keep); createindexon idxpart (col_keep); altertable idxpart attach partition idxpart1 forvaluesfrom (0) to (1000);
\d idxpart
\d idxpart1 select attrelid::regclass, attname, attnum from pg_attribute where attrelid::regclass::text like'idxpart%'and attnum > 0 orderby attrelid::regclass, attnum; droptable idxpart;
-- Column number mapping: dropped columns in the parent table createtable idxpart(drop_1 int, drop_2 int, col_keep int, drop_3 int) partition by range (col_keep); altertable idxpart dropcolumn drop_1; altertable idxpart dropcolumn drop_2; altertable idxpart dropcolumn drop_3; createtable idxpart1 (col_keep int); createindexon idxpart1 (col_keep); createindexon idxpart (col_keep); altertable idxpart attach partition idxpart1 forvaluesfrom (0) to (1000);
\d idxpart
\d idxpart1 select attrelid::regclass, attname, attnum from pg_attribute where attrelid::regclass::text like'idxpart%'and attnum > 0 orderby attrelid::regclass, attnum; droptable idxpart;
-- -- Constraint-related indexes --
-- Verify that it works to add primary key / unique to partitioned tables createtable idxpart (a intprimarykey, b int) partition by range (a);
\d idxpart -- multiple primary key on child should fail createtable failpart partition of idxpart (b primarykey) forvaluesfrom (0) to (100); droptable idxpart; -- primary key on child is okay if there's no PK in the parent, though createtable idxpart (a int) partition by range (a); createtable idxpart1pk partition of idxpart (a primarykey) forvaluesfrom (0) to (100);
\d idxpart1pk droptable idxpart;
-- Failing to use the full partition key is not allowed createtable idxpart (a intunique, b int) partition by range (a, b); createtable idxpart (a int, b intunique) partition by range (a, b); createtable idxpart (a intprimarykey, b int) partition by range (b, a); createtable idxpart (a int, b intprimarykey) partition by range (b, a);
-- OK if you use them in some other order createtable idxpart (a int, b int, c text, primarykey (a, b, c)) partition by range (b, c, a); droptable idxpart;
-- OK to add an exclusion constraint if partitioning by its equal column createtable idxpart (a int4range, exclude USING GIST (a with = )) partition by range (a); droptable idxpart; -- OK more than one equal column createtable idxpart (a int4range, b int4range, exclude USING GIST (a with =, b with =)) partition by range (a, b); droptable idxpart; -- OK with more than one equal column: constraint is a proper superset of partition key createtable idxpart (a int4range, b int4range, exclude USING GIST (a with =, b with =)) partition by range (a); droptable idxpart; -- Not OK more than one equal column: partition keys are a proper superset of constraint createtable idxpart (a int4range, b int4range, exclude USING GIST (a with = )) partition by range (a, b); -- Not OK with just -|- createtable idxpart (a int4range, exclude USING GIST (a with -|- )) partition by range (a); -- OK with equals and &&, and equals is the partition key createtable idxpart (a int4range, b int4range, exclude USING GIST (a with =, b with &&)) partition by range (a); droptable idxpart; -- Not OK with equals and &&, and equals is not the partition key createtable idxpart (a int4range, b int4range, c int4range, exclude USING GIST (b with =, c with &&)) partition by range (a); -- OK more than one equal column and a && column createtable idxpart (a int4range, b int4range, c int4range, exclude USING GIST (a with =, b with =, c with &&)) partition by range (a, b); droptable idxpart;
-- no expressions in partition key for PK/UNIQUE createtable idxpart (a intprimarykey, b int) partition by range ((b + a)); createtable idxpart (a intunique, b int) partition by range ((b + a));
-- use ALTER TABLE to add a primary key createtable idxpart (a int, b int, c text) partition by range (a, b); altertable idxpart addprimarykey (a); -- not an incomplete one though altertable idxpart addprimarykey (a, b); -- this works
\d idxpart createtable idxpart1 partition of idxpart forvaluesfrom (0, 0) to (1000, 1000);
\d idxpart1 droptable idxpart;
-- use ALTER TABLE to add a unique constraint createtable idxpart (a int, b int) partition by range (a, b); altertable idxpart addunique (a); -- not an incomplete one though altertable idxpart addunique (b, a); -- this works
\d idxpart droptable idxpart;
-- Exclusion constraints can be added if partitioning by their equal column createtable idxpart (a int4range, b int4range) partition by range (a); altertable idxpart add exclude USING GIST (a with =); droptable idxpart; -- OK more than one equal column createtable idxpart (a int4range, b int4range) partition by range (a, b); altertable idxpart add exclude USING GIST (a with =, b with =); droptable idxpart; -- OK with more than one equal column: constraint is a proper superset of partition key createtable idxpart (a int4range, b int4range) partition by range (a); altertable idxpart add exclude USING GIST (a with =, b with =); droptable idxpart; -- Not OK more than one equal column: partition keys are a proper superset of constraint createtable idxpart (a int4range, b int4range) partition by range (a, b); altertable idxpart add exclude USING GIST (a with =); droptable idxpart; -- Not OK with just -|- createtable idxpart (a int4range, b int4range) partition by range (a, b); altertable idxpart add exclude USING GIST (a with -|-); droptable idxpart; -- OK with equals and &&, and equals is the partition key createtable idxpart (a int4range, b int4range) partition by range (a); altertable idxpart add exclude USING GIST (a with =, b with &&); droptable idxpart; -- Not OK with equals and &&, and equals is not the partition key createtable idxpart (a int4range, b int4range, c int4range) partition by range (a); altertable idxpart add exclude USING GIST (b with =, c with &&); droptable idxpart; -- OK more than one equal column and a && column createtable idxpart (a int4range, b int4range, c int4range) partition by range (a, b); altertable idxpart add exclude USING GIST (a with =, b with =, c with &&); droptable idxpart;
-- When (sub)partitions are created, they also contain the constraint createtable idxpart (a int, b int, primarykey (a, b)) partition by range (a, b); createtable idxpart1 partition of idxpart forvaluesfrom (1, 1) to (10, 10); createtable idxpart2 partition of idxpart forvaluesfrom (10, 10) to (20, 20)
partition by range (b); createtable idxpart21 partition of idxpart2 forvaluesfrom (10) to (15); createtable idxpart22 partition of idxpart2 forvaluesfrom (15) to (20); createtable idxpart3 (b intnotnull, a intnotnull); altertable idxpart attach partition idxpart3 forvaluesfrom (20, 20) to (30, 30); select conname, contype, conrelid::regclass, conindid::regclass, conkey from pg_constraint where conrelid::regclass::text like'idxpart%' orderby conrelid::regclass::text, conname; droptable idxpart;
-- Verify that multi-layer partitioning honors the requirement that all -- columns in the partition key must appear in primary/unique key createtable idxpart (a int, b int, primarykey (a)) partition by range (a); createtable idxpart2 partition of idxpart forvaluesfrom (0) to (1000) partition by range (b); -- fail droptable idxpart;
-- Ditto for the ATTACH PARTITION case createtable idxpart (a intunique, b int) partition by range (a); createtable idxpart1 (a intnotnull, b int, unique (a, b))
partition by range (a, b); altertable idxpart attach partition idxpart1 forvaluesfrom (1) to (1000); DROPTABLE idxpart, idxpart1;
-- Multi-layer partitioning works correctly in this case: createtable idxpart (a int, b int, primarykey (a, b)) partition by range (a); createtable idxpart2 partition of idxpart forvaluesfrom (0) to (1000) partition by range (b); createtable idxpart21 partition of idxpart2 forvaluesfrom (0) to (1000); select conname, contype, conrelid::regclass, conindid::regclass, conkey from pg_constraint where conrelid::regclass::text like'idxpart%' orderby conrelid::regclass::text, conname; droptable idxpart;
-- If a partitioned table has a unique/PK constraint, then it's not possible -- to drop the corresponding constraint in the children; nor it's possible -- to drop the indexes individually. Dropping the constraint in the parent -- gets rid of the lot. createtable idxpart (i int) partition by hash (i); createtable idxpart0 partition of idxpart (i) forvalueswith (modulus 2, remainder 0); createtable idxpart1 partition of idxpart (i) forvalueswith (modulus 2, remainder 1); altertable idxpart0 addprimarykey(i); altertable idxpart addprimarykey(i); select indrelid::regclass, indexrelid::regclass, inhparent::regclass, indisvalid,
conname, conislocal, coninhcount, connoinherit, convalidated from pg_index idx leftjoin pg_inherits inh on (idx.indexrelid = inh.inhrelid) leftjoin pg_constraint con on (idx.indexrelid = con.conindid) where indrelid::regclass::text like'idxpart%' orderby indexrelid::regclass::text collate"C"; dropindex idxpart0_pkey; -- fail dropindex idxpart1_pkey; -- fail altertable idxpart0 dropconstraint idxpart0_pkey; -- fail altertable idxpart1 dropconstraint idxpart1_pkey; -- fail altertable idxpart dropconstraint idxpart_pkey; -- ok select indrelid::regclass, indexrelid::regclass, inhparent::regclass, indisvalid,
conname, conislocal, coninhcount, connoinherit, convalidated from pg_index idx leftjoin pg_inherits inh on (idx.indexrelid = inh.inhrelid) leftjoin pg_constraint con on (idx.indexrelid = con.conindid) where indrelid::regclass::text like'idxpart%' orderby indexrelid::regclass::text collate"C"; droptable idxpart;
-- If the partition to be attached already has a primary key, fail if -- it doesn't match the parent's PK. CREATETABLE idxpart (c1 INTPRIMARYKEY, c2 INT, c3 VARCHAR(10)) PARTITION BY RANGE(c1); CREATETABLE idxpart1 (LIKE idxpart); ALTERTABLE idxpart1 ADDPRIMARYKEY (c1, c2); ALTERTABLE idxpart ATTACH PARTITION idxpart1 FORVALUESFROM (100) TO (200); DROPTABLE idxpart, idxpart1;
-- Ditto if there is some distance between the PKs (subpartitioning) createtable idxpart (a int, b int, primarykey (a)) partition by range (a); createtable idxpart1 (a intnotnull, b int) partition by range (a); createtable idxpart11 (a intnotnull, b intprimarykey); altertable idxpart1 attach partition idxpart11 forvaluesfrom (0) to (1000); altertable idxpart attach partition idxpart1 forvaluesfrom (0) to (10000); droptable idxpart, idxpart1, idxpart11;
-- If a partitioned table has a constraint whose index is not valid, -- attaching a missing partition makes it valid. createtable idxpart (a int) partition by range (a); createtable idxpart0 (like idxpart); altertable idxpart0 addprimarykey (a); altertable idxpart attach partition idxpart0 forvaluesfrom (0) to (1000); altertable only idxpart addprimarykey (a); select indrelid::regclass, indexrelid::regclass, inhparent::regclass, indisvalid,
conname, conislocal, coninhcount, connoinherit, convalidated from pg_index idx leftjoin pg_inherits inh on (idx.indexrelid = inh.inhrelid) leftjoin pg_constraint con on (idx.indexrelid = con.conindid) where indrelid::regclass::text like'idxpart%' orderby indexrelid::regclass::text collate"C"; alterindex idxpart_pkey attach partition idxpart0_pkey; select indrelid::regclass, indexrelid::regclass, inhparent::regclass, indisvalid,
conname, conislocal, coninhcount, connoinherit, convalidated from pg_index idx leftjoin pg_inherits inh on (idx.indexrelid = inh.inhrelid) leftjoin pg_constraint con on (idx.indexrelid = con.conindid) where indrelid::regclass::text like'idxpart%' orderby indexrelid::regclass::text collate"C"; droptable idxpart;
-- Related to the above scenario: ADD PRIMARY KEY on the parent mustn't -- automatically propagate NOT NULL to child columns. createtable idxpart (a int) partition by range (a); createtable idxpart0 (like idxpart); altertable idxpart0 addunique (a); altertable idxpart attach partition idxpart0 default; altertable only idxpart addprimarykey (a); -- fail, no not-null constraint altertable idxpart0 altercolumn a setnotnull; altertable only idxpart addprimarykey (a); -- now it works alterindex idxpart_pkey attach partition idxpart0_a_key; droptable idxpart;
-- if a partition has a unique index without a constraint, does not attach -- automatically; creates a new index instead. createtable idxpart (a int, b int) partition by range (a); createtable idxpart1 (a intnotnull, b int); createuniqueindexon idxpart1 (a); altertable idxpart addprimarykey (a); altertable idxpart attach partition idxpart1 forvaluesfrom (1) to (1000); select indrelid::regclass, indexrelid::regclass, inhparent::regclass, indisvalid,
conname, conislocal, coninhcount, connoinherit, convalidated from pg_index idx leftjoin pg_inherits inh on (idx.indexrelid = inh.inhrelid) leftjoin pg_constraint con on (idx.indexrelid = con.conindid) where indrelid::regclass::text like'idxpart%' orderby indexrelid::regclass::text collate"C"; droptable idxpart;
-- Can't attach an index without a corresponding constraint createtable idxpart (a int, b int) partition by range (a); createtable idxpart1 (a intnotnull, b int); createuniqueindexon idxpart1 (a); altertable idxpart attach partition idxpart1 forvaluesfrom (1) to (1000); altertable only idxpart addprimarykey (a); alterindex idxpart_pkey attach partition idxpart1_a_idx; -- fail droptable idxpart;
-- Test that unique constraints are working createtable idxpart (a int, b text, primarykey (a, b)) partition by range (a); createtable idxpart1 partition of idxpart forvaluesfrom (0) to (100000); createtable idxpart2 (c int, like idxpart); insertinto idxpart2 (c, a, b) values (42, 572814, 'inserted first'); altertable idxpart2 dropcolumn c; createuniqueindexon idxpart (a); altertable idxpart attach partition idxpart2 forvaluesfrom (100000) to (1000000); insertinto idxpart values (0, 'zero'), (42, 'life'), (2^16, 'sixteen'); insertinto idxpart select2^g, format('two to power of %s', g) from generate_series(15, 17) g; insertinto idxpart values (16, 'sixteen'); insertinto idxpart (b, a) values ('one', 142857), ('two', 285714); insertinto idxpart select a * 2, b || b from idxpart where a between2^16and2^19; insertinto idxpart values (572814, 'five'); insertinto idxpart values (857142, 'six'); select tableoid::regclass, * from idxpart orderby a; droptable idxpart;
-- Test some other non-btree index types createtable idxpart (a int, b text, c int[]) partition by range (a); createtable idxpart1 partition of idxpart forvaluesfrom (0) to (100000); set enable_seqscan to off;
createindex idxpart_brin on idxpart using brin(b); explain (costs off) select * from idxpart where b = 'abcd'; dropindex idxpart_brin;
createindex idxpart_spgist on idxpart using spgist(b); explain (costs off) select * from idxpart where b = 'abcd'; dropindex idxpart_spgist;
createindex idxpart_gin on idxpart using gin(c); explain (costs off) select * from idxpart where c @> array[42]; dropindex idxpart_gin;
reset enable_seqscan; droptable idxpart;
-- intentionally leave some objects around createtable idxpart (a int) partition by range (a); createtable idxpart1 partition of idxpart forvaluesfrom (0) to (100); createtable idxpart2 partition of idxpart forvaluesfrom (100) to (1000)
partition by range (a); createtable idxpart21 partition of idxpart2 forvaluesfrom (100) to (200); createtable idxpart22 partition of idxpart2 forvaluesfrom (200) to (300); createindexon idxpart22 (a); createindexon only idxpart2 (a); alterindex idxpart2_a_idx attach partition idxpart22_a_idx; createindexon idxpart (a); createtable idxpart_another (a int, b int, primarykey (a, b)) partition by range (a); createtable idxpart_another_1 partition of idxpart_another forvaluesfrom (0) to (100); createtable idxpart3 (c int, b int, a int) partition by range (a); altertable idxpart3 dropcolumn b, dropcolumn c; createtable idxpart31 partition of idxpart3 forvaluesfrom (1000) to (1200); createtable idxpart32 partition of idxpart3 forvaluesfrom (1200) to (1400); altertable idxpart attach partition idxpart3 forvaluesfrom (1000) to (2000);
-- More objects intentionally left behind, to verify some pg_dump/pg_upgrade -- behavior; see https://postgr.es/m/20190321204928.GA17535@alvherre.pgsql createschema regress_indexing; set search_path to regress_indexing; createtable pk (a intprimarykey) partition by range (a); createtable pk1 partition of pk forvaluesfrom (0) to (1000); createtable pk2 (b int, a int); altertable pk2 dropcolumn b; altertable pk2 alter a setnotnull; altertable pk attach partition pk2 forvaluesfrom (1000) to (2000); createtable pk3 partition of pk forvaluesfrom (2000) to (3000); createtable pk4 (like pk); altertable pk attach partition pk4 forvaluesfrom (3000) to (4000); createtable pk5 (like pk) partition by range (a); createtable pk51 partition of pk5 forvaluesfrom (4000) to (4500); createtable pk52 partition of pk5 forvaluesfrom (4500) to (5000); altertable pk attach partition pk5 forvaluesfrom (4000) to (5000);
reset search_path;
-- Test that covering partitioned indexes work in various cases createtable covidxpart (a int, b int) partition by list (a); createuniqueindexon covidxpart (a) include (b); createtable covidxpart1 partition of covidxpart forvaluesin (1); createtable covidxpart2 partition of covidxpart forvaluesin (2); insertinto covidxpart values (1, 1); insertinto covidxpart values (1, 1); createtable covidxpart3 (b int, c int, a int); altertable covidxpart3 drop c; altertable covidxpart attach partition covidxpart3 forvaluesin (3); insertinto covidxpart values (3, 1); insertinto covidxpart values (3, 1); createtable covidxpart4 (b int, a int); createuniqueindexon covidxpart4 (a) include (b); createuniqueindexon covidxpart4 (a); altertable covidxpart attach partition covidxpart4 forvaluesin (4); insertinto covidxpart values (4, 1); insertinto covidxpart values (4, 1); createuniqueindexon covidxpart (b) include (a); -- should fail
-- check that detaching a partition also detaches the primary key constraint createtable parted_pk_detach_test (a intprimarykey) partition by list (a); createtable parted_pk_detach_test1 partition of parted_pk_detach_test forvaluesin (1); altertable parted_pk_detach_test1 dropconstraint parted_pk_detach_test1_pkey; -- should fail altertable parted_pk_detach_test detach partition parted_pk_detach_test1; altertable parted_pk_detach_test1 dropconstraint parted_pk_detach_test1_pkey; droptable parted_pk_detach_test, parted_pk_detach_test1; createtable parted_uniq_detach_test (a intunique) partition by list (a); createtable parted_uniq_detach_test1 partition of parted_uniq_detach_test forvaluesin(1); altertable parted_uniq_detach_test1 dropconstraint parted_uniq_detach_test1_a_key; -- should fail altertable parted_uniq_detach_test detach partition parted_uniq_detach_test1; altertable parted_uniq_detach_test1 dropconstraint parted_uniq_detach_test1_a_key; droptable parted_uniq_detach_test, parted_uniq_detach_test1;
-- check that dropping a column takes with it any partitioned indexes -- depending on it. createtable parted_index_col_drop(a int, b int, c int)
partition by list (a); createtable parted_index_col_drop1 partition of parted_index_col_drop forvaluesin (1) partition by list (a); -- leave this partition without children. createtable parted_index_col_drop2 partition of parted_index_col_drop forvaluesin (2) partition by list (a); createtable parted_index_col_drop11 partition of parted_index_col_drop1 forvaluesin (1); createindexon parted_index_col_drop (b); createindexon parted_index_col_drop (c); createindexon parted_index_col_drop (b, c); altertable parted_index_col_drop dropcolumn c;
\d parted_index_col_drop
\d parted_index_col_drop1
\d parted_index_col_drop2
\d parted_index_col_drop11 droptable parted_index_col_drop;
-- Check that invalid indexes are not selected when attaching a partition. createtable parted_inval_tab (a int) partition by range (a); createindex parted_inval_idx on parted_inval_tab (a); createtable parted_inval_tab_1 (a int) partition by range (a); createtable parted_inval_tab_1_1 partition of parted_inval_tab_1 forvaluesfrom (0) to (10); createtable parted_inval_tab_1_2 partition of parted_inval_tab_1 forvaluesfrom (10) to (20); -- this creates an invalid index. createindex parted_inval_ixd_1 on only parted_inval_tab_1 (a); -- this creates new indexes for all the partitions of parted_inval_tab_1, -- discarding the invalid index created previously as what is chosen. altertable parted_inval_tab attach partition parted_inval_tab_1 forvaluesfrom (1) to (100); select indexrelid::regclass, indisvalid,
indrelid::regclass, inhparent::regclass from pg_index idx leftjoin
pg_inherits inh on (idx.indexrelid = inh.inhrelid) where indexrelid::regclass::text like'parted_inval%' orderby indexrelid::regclass::text collate"C"; droptable parted_inval_tab;
-- Check setup of indisvalid across a complex partition tree on index -- creation. If one index in a partition index is invalid, so should its -- partitioned index. createtable parted_isvalid_tab (a int, b int) partition by range (a); createtable parted_isvalid_tab_1 partition of parted_isvalid_tab forvaluesfrom (1) to (10) partition by range (a); createtable parted_isvalid_tab_2 partition of parted_isvalid_tab forvaluesfrom (10) to (20) partition by range (a); createtable parted_isvalid_tab_11 partition of parted_isvalid_tab_1 forvaluesfrom (1) to (5); createtable parted_isvalid_tab_12 partition of parted_isvalid_tab_1 forvaluesfrom (5) to (10); -- create an invalid index on one of the partitions. insertinto parted_isvalid_tab_11 values (1, 0); createindex concurrently parted_isvalid_idx_11 on parted_isvalid_tab_11 ((a/b)); -- The previous invalid index is selected, invalidating all the indexes up to -- the top-most parent. createindex parted_isvalid_idx on parted_isvalid_tab ((a/b)); select indexrelid::regclass, indisvalid,
indrelid::regclass, inhparent::regclass from pg_index idx leftjoin
pg_inherits inh on (idx.indexrelid = inh.inhrelid) where indexrelid::regclass::text like'parted_isvalid%' orderby indexrelid::regclass::text collate"C"; droptable parted_isvalid_tab;
-- Check state of replica indexes when attaching a partition.
begin; createtable parted_replica_tab (id intnotnull) partition by range (id); createtable parted_replica_tab_1 partition of parted_replica_tab forvaluesfrom (1) to (10) partition by range (id); createtable parted_replica_tab_11 partition of parted_replica_tab_1 forvaluesfrom (1) to (5); createuniqueindex parted_replica_idx on only parted_replica_tab using btree (id); createuniqueindex parted_replica_idx_1 on only parted_replica_tab_1 using btree (id); -- This triggers an update of pg_index.indisreplident for parted_replica_idx. altertable only parted_replica_tab_1 replica identity usingindex parted_replica_idx_1; createuniqueindex parted_replica_idx_11 on parted_replica_tab_11 USING btree (id); select indexrelid::regclass, indisvalid, indisreplident,
indrelid::regclass, inhparent::regclass from pg_index idx leftjoin
pg_inherits inh on (idx.indexrelid = inh.inhrelid) where indexrelid::regclass::text like'parted_replica%' orderby indexrelid::regclass::text collate"C"; -- parted_replica_idx is not valid yet here, because parted_replica_idx_1 -- is not valid. alterindex parted_replica_idx ATTACH PARTITION parted_replica_idx_1; select indexrelid::regclass, indisvalid, indisreplident,
indrelid::regclass, inhparent::regclass from pg_index idx leftjoin
pg_inherits inh on (idx.indexrelid = inh.inhrelid) where indexrelid::regclass::text like'parted_replica%' orderby indexrelid::regclass::text collate"C"; -- parted_replica_idx becomes valid here. alterindex parted_replica_idx_1 ATTACH PARTITION parted_replica_idx_11; altertable only parted_replica_tab_1 replica identity usingindex parted_replica_idx_1; commit; select indexrelid::regclass, indisvalid, indisreplident,
indrelid::regclass, inhparent::regclass from pg_index idx leftjoin
pg_inherits inh on (idx.indexrelid = inh.inhrelid) where indexrelid::regclass::text like'parted_replica%' orderby indexrelid::regclass::text collate"C"; droptable parted_replica_tab;
-- test that indexing commands work with TOASTed values in pg_index createtable test_pg_index_toast_table (a int); createorreplace function test_pg_index_toast_func (a int, b int[])
returns bool as $$ selecttrue $$ language sql immutable; select array_agg(n) b from generate_series(1, 10000) n \gset createindex concurrently test_pg_index_toast_index on test_pg_index_toast_table (test_pg_index_toast_func(a, :'b'));
reindex index concurrently test_pg_index_toast_index; dropindex concurrently test_pg_index_toast_index; createindex test_pg_index_toast_index on test_pg_index_toast_table (test_pg_index_toast_func(a, :'b'));
reindex index test_pg_index_toast_index; dropindex test_pg_index_toast_index; drop function test_pg_index_toast_func; droptable test_pg_index_toast_table;
Messung V0.5 in Prozent
¤ Dauer der Verarbeitung: 0.38 Sekunden
(vorverarbeitet am 2026-08-08)
¤
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.