@@ -98,6 +98,11 @@ static char *ChooseIndexName(const char *tabname, Oid namespaceId,
98
98
bool primary , bool isconstraint );
99
99
static char * ChooseIndexNameAddition (const List * colnames );
100
100
static List * ChooseIndexColumnNames (const List * indexElems );
101
+ static void DefineIndexConcurrentInternal (Oid relationId ,
102
+ Oid indexRelationId ,
103
+ IndexInfo * indexInfo ,
104
+ LOCKTAG heaplocktag ,
105
+ LockRelId heaprelid );
101
106
static void ReindexIndex (const ReindexStmt * stmt , const ReindexParams * params ,
102
107
bool isTopLevel );
103
108
static void RangeVarCallbackForReindexIndex (const RangeVar * relation ,
@@ -573,20 +578,17 @@ DefineIndex(Oid tableId,
573
578
amoptions_function amoptions ;
574
579
bool exclusion ;
575
580
bool partitioned ;
576
- bool safe_index ;
577
581
Datum reloptions ;
578
582
int16 * coloptions ;
579
583
IndexInfo * indexInfo ;
580
584
bits16 flags ;
581
585
bits16 constr_flags ;
582
586
int numberOfAttributes ;
583
587
int numberOfKeyAttributes ;
584
- TransactionId limitXmin ;
585
588
ObjectAddress address ;
586
589
LockRelId heaprelid ;
587
590
LOCKTAG heaplocktag ;
588
591
LOCKMODE lockmode ;
589
- Snapshot snapshot ;
590
592
Oid root_save_userid ;
591
593
int root_save_sec_context ;
592
594
int root_save_nestlevel ;
@@ -724,20 +726,6 @@ DefineIndex(Oid tableId,
724
726
* partition.
725
727
*/
726
728
partitioned = rel -> rd_rel -> relkind == RELKIND_PARTITIONED_TABLE ;
727
- if (partitioned )
728
- {
729
- /*
730
- * Note: we check 'stmt->concurrent' rather than 'concurrent', so that
731
- * the error is thrown also for temporary tables. Seems better to be
732
- * consistent, even though we could do it on temporary table because
733
- * we're not actually doing it concurrently.
734
- */
735
- if (stmt -> concurrent )
736
- ereport (ERROR ,
737
- (errcode (ERRCODE_FEATURE_NOT_SUPPORTED ),
738
- errmsg ("cannot create index on partitioned table \"%s\" concurrently" ,
739
- RelationGetRelationName (rel ))));
740
- }
741
729
742
730
/*
743
731
* Don't try to CREATE INDEX on temp tables of other backends.
@@ -1172,10 +1160,6 @@ DefineIndex(Oid tableId,
1172
1160
}
1173
1161
}
1174
1162
1175
- /* Is index safe for others to ignore? See set_indexsafe_procflags() */
1176
- safe_index = indexInfo -> ii_Expressions == NIL &&
1177
- indexInfo -> ii_Predicate == NIL ;
1178
-
1179
1163
/*
1180
1164
* Report index creation if appropriate (delay this till after most of the
1181
1165
* error checks)
@@ -1240,6 +1224,11 @@ DefineIndex(Oid tableId,
1240
1224
if (pd -> nparts != 0 )
1241
1225
flags |= INDEX_CREATE_INVALID ;
1242
1226
}
1227
+ else if (concurrent && OidIsValid (parentIndexId ))
1228
+ {
1229
+ /* If concurrent, initially build index partitions as "invalid" */
1230
+ flags |= INDEX_CREATE_INVALID ;
1231
+ }
1243
1232
1244
1233
if (stmt -> deferrable )
1245
1234
constr_flags |= INDEX_CONSTR_CREATE_DEFERRABLE ;
@@ -1557,21 +1546,7 @@ DefineIndex(Oid tableId,
1557
1546
*/
1558
1547
if (invalidate_parent )
1559
1548
{
1560
- Relation pg_index = table_open (IndexRelationId , RowExclusiveLock );
1561
- HeapTuple tup ,
1562
- newtup ;
1563
-
1564
- tup = SearchSysCache1 (INDEXRELID ,
1565
- ObjectIdGetDatum (indexRelationId ));
1566
- if (!HeapTupleIsValid (tup ))
1567
- elog (ERROR , "cache lookup failed for index %u" ,
1568
- indexRelationId );
1569
- newtup = heap_copytuple (tup );
1570
- ((Form_pg_index ) GETSTRUCT (newtup ))-> indisvalid = false;
1571
- CatalogTupleUpdate (pg_index , & tup -> t_self , newtup );
1572
- ReleaseSysCache (tup );
1573
- table_close (pg_index , RowExclusiveLock );
1574
- heap_freetuple (newtup );
1549
+ index_set_state_flags (indexRelationId , INDEX_DROP_CLEAR_VALID );
1575
1550
1576
1551
/*
1577
1552
* CCI here to make this update visible, in case this recurses
@@ -1583,37 +1558,49 @@ DefineIndex(Oid tableId,
1583
1558
1584
1559
/*
1585
1560
* Indexes on partitioned tables are not themselves built, so we're
1586
- * done here.
1561
+ * done here in the non-concurrent case .
1587
1562
*/
1588
- AtEOXact_GUC (false, root_save_nestlevel );
1589
- SetUserIdAndSecContext (root_save_userid , root_save_sec_context );
1590
- table_close (rel , NoLock );
1591
- if (!OidIsValid (parentIndexId ))
1592
- pgstat_progress_end_command ();
1593
- else
1563
+ if (!concurrent )
1594
1564
{
1595
- /* Update progress for an intermediate partitioned index itself */
1596
- pgstat_progress_incr_param ( PROGRESS_CREATEIDX_PARTITIONS_DONE , 1 );
1597
- }
1565
+ AtEOXact_GUC (false, root_save_nestlevel );
1566
+ SetUserIdAndSecContext ( root_save_userid , root_save_sec_context );
1567
+ table_close ( rel , NoLock );
1598
1568
1599
- return address ;
1569
+ if (!OidIsValid (parentIndexId ))
1570
+ pgstat_progress_end_command ();
1571
+ else
1572
+ {
1573
+ /*
1574
+ * Update progress for an intermediate partitioned index
1575
+ * itself
1576
+ */
1577
+ pgstat_progress_incr_param (PROGRESS_CREATEIDX_PARTITIONS_DONE , 1 );
1578
+ }
1579
+
1580
+ return address ;
1581
+ }
1600
1582
}
1601
1583
1602
1584
AtEOXact_GUC (false, root_save_nestlevel );
1603
1585
SetUserIdAndSecContext (root_save_userid , root_save_sec_context );
1604
1586
1605
- if (!concurrent )
1587
+ /*
1588
+ * All done in the non-concurrent case, and when building catalog entries
1589
+ * of partitions for CIC.
1590
+ */
1591
+ if (!concurrent || OidIsValid (parentIndexId ))
1606
1592
{
1607
- /* Close the heap and we're done, in the non-concurrent case */
1608
1593
table_close (rel , NoLock );
1609
1594
1610
1595
/*
1611
1596
* If this is the top-level index, the command is done overall;
1612
- * otherwise, increment progress to report one child index is done.
1597
+ * otherwise (when being called recursively), increment progress to
1598
+ * report that one child index is done. Except in the concurrent
1599
+ * (catalog-only) case, which is handled later.
1613
1600
*/
1614
1601
if (!OidIsValid (parentIndexId ))
1615
1602
pgstat_progress_end_command ();
1616
- else
1603
+ else if (! concurrent )
1617
1604
pgstat_progress_incr_param (PROGRESS_CREATEIDX_PARTITIONS_DONE , 1 );
1618
1605
1619
1606
return address ;
@@ -1624,6 +1611,141 @@ DefineIndex(Oid tableId,
1624
1611
SET_LOCKTAG_RELATION (heaplocktag , heaprelid .dbId , heaprelid .relId );
1625
1612
table_close (rel , NoLock );
1626
1613
1614
+ if (!partitioned )
1615
+ {
1616
+ /* CREATE INDEX CONCURRENTLY on a nonpartitioned table */
1617
+ DefineIndexConcurrentInternal (tableId , indexRelationId ,
1618
+ indexInfo , heaplocktag , heaprelid );
1619
+ pgstat_progress_end_command ();
1620
+ return address ;
1621
+ }
1622
+ else
1623
+ {
1624
+ /*
1625
+ * For CIC on a partitioned table, finish by building indexes on
1626
+ * partitions
1627
+ */
1628
+
1629
+ ListCell * lc ;
1630
+ List * childs ;
1631
+ List * tosetvalid = NIL ;
1632
+ MemoryContext cic_context ,
1633
+ old_context ;
1634
+
1635
+ /* Create special memory context for cross-transaction storage */
1636
+ cic_context = AllocSetContextCreate (PortalContext ,
1637
+ "Create index concurrently" ,
1638
+ ALLOCSET_DEFAULT_SIZES );
1639
+
1640
+ old_context = MemoryContextSwitchTo (cic_context );
1641
+ childs = find_all_inheritors (indexRelationId , ShareUpdateExclusiveLock , NULL );
1642
+ MemoryContextSwitchTo (old_context );
1643
+
1644
+ foreach (lc , childs )
1645
+ {
1646
+ Oid indrelid = lfirst_oid (lc );
1647
+ Oid tabrelid ;
1648
+ char relkind ;
1649
+
1650
+ /*
1651
+ * Partition could have been dropped, since we looked it up. In
1652
+ * this case consider it done and go to the next one.
1653
+ */
1654
+ tabrelid = IndexGetRelation (indrelid , true);
1655
+ if (!tabrelid )
1656
+ {
1657
+ pgstat_progress_incr_param (PROGRESS_CREATEIDX_PARTITIONS_DONE , 1 );
1658
+ continue ;
1659
+ }
1660
+ rel = try_table_open (tabrelid , ShareUpdateExclusiveLock );
1661
+ if (!rel )
1662
+ {
1663
+ pgstat_progress_incr_param (PROGRESS_CREATEIDX_PARTITIONS_DONE , 1 );
1664
+ continue ;
1665
+ }
1666
+
1667
+ /*
1668
+ * Pre-existing partitions which were ATTACHED were already
1669
+ * counted in the progress report.
1670
+ */
1671
+ if (get_index_isvalid (indrelid ))
1672
+ {
1673
+ table_close (rel , ShareUpdateExclusiveLock );
1674
+ continue ;
1675
+ }
1676
+
1677
+ /*
1678
+ * Partitioned indexes are counted in the progress report, but
1679
+ * don't need to be further processed.
1680
+ */
1681
+ relkind = get_rel_relkind (indrelid );
1682
+ if (!RELKIND_HAS_STORAGE (relkind ))
1683
+ {
1684
+ /* The toplevel index doesn't count towards "partitions done" */
1685
+ if (indrelid != indexRelationId )
1686
+ pgstat_progress_incr_param (PROGRESS_CREATEIDX_PARTITIONS_DONE , 1 );
1687
+
1688
+ /*
1689
+ * Build up a list of all the intermediate partitioned tables
1690
+ * which will later need to be set valid.
1691
+ */
1692
+ old_context = MemoryContextSwitchTo (cic_context );
1693
+ tosetvalid = lappend_oid (tosetvalid , indrelid );
1694
+ MemoryContextSwitchTo (old_context );
1695
+ table_close (rel , ShareUpdateExclusiveLock );
1696
+ continue ;
1697
+ }
1698
+
1699
+ heaprelid = rel -> rd_lockInfo .lockRelId ;
1700
+
1701
+ /*
1702
+ * Close the table but retain the lock, that should be extended to
1703
+ * session level in DefineIndexConcurrentInternal.
1704
+ */
1705
+ table_close (rel , NoLock );
1706
+ SET_LOCKTAG_RELATION (heaplocktag , heaprelid .dbId , heaprelid .relId );
1707
+
1708
+ /* Process each partition in a separate transaction */
1709
+ DefineIndexConcurrentInternal (tabrelid , indrelid , indexInfo ,
1710
+ heaplocktag , heaprelid );
1711
+
1712
+ PushActiveSnapshot (GetTransactionSnapshot ());
1713
+ pgstat_progress_incr_param (PROGRESS_CREATEIDX_PARTITIONS_DONE , 1 );
1714
+ }
1715
+
1716
+ /* Set as valid all partitioned indexes, including the parent */
1717
+ foreach (lc , tosetvalid )
1718
+ {
1719
+ Oid indrelid = lfirst_oid (lc );
1720
+ Relation indrel = try_index_open (indrelid , ShareUpdateExclusiveLock );
1721
+
1722
+ if (!indrel )
1723
+ continue ;
1724
+ index_set_state_flags (indrelid , INDEX_CREATE_SET_READY );
1725
+ CommandCounterIncrement ();
1726
+ index_set_state_flags (indrelid , INDEX_CREATE_SET_VALID );
1727
+ index_close (indrel , ShareUpdateExclusiveLock );
1728
+ }
1729
+
1730
+ MemoryContextDelete (cic_context );
1731
+ pgstat_progress_end_command ();
1732
+ PopActiveSnapshot ();
1733
+ return address ;
1734
+ }
1735
+ }
1736
+
1737
+
1738
+ static void
1739
+ DefineIndexConcurrentInternal (Oid tableId , Oid indexRelationId , IndexInfo * indexInfo ,
1740
+ LOCKTAG heaplocktag , LockRelId heaprelid )
1741
+ {
1742
+ TransactionId limitXmin ;
1743
+ Snapshot snapshot ;
1744
+
1745
+ /* Is index safe for others to ignore? See set_indexsafe_procflags() */
1746
+ bool safe_index = indexInfo -> ii_Expressions == NIL &&
1747
+ indexInfo -> ii_Predicate == NIL ;
1748
+
1627
1749
/*
1628
1750
* For a concurrent build, it's important to make the catalog entries
1629
1751
* visible to other transactions before we start to build the index. That
@@ -1827,10 +1949,6 @@ DefineIndex(Oid tableId,
1827
1949
* Last thing to do is release the session-level lock on the parent table.
1828
1950
*/
1829
1951
UnlockRelationIdForSession (& heaprelid , ShareUpdateExclusiveLock );
1830
-
1831
- pgstat_progress_end_command ();
1832
-
1833
- return address ;
1834
1952
}
1835
1953
1836
1954
0 commit comments