@@ -1282,14 +1282,20 @@ index_constraint_create(Relation heapRelation,
12821282 * else associated dependencies won't be cleaned up.
12831283 */
12841284void
1285- index_drop (Oid indexId )
1285+ index_drop (Oid indexId , bool concurrent )
12861286{
12871287 Oid heapId ;
12881288 Relation userHeapRelation ;
12891289 Relation userIndexRelation ;
12901290 Relation indexRelation ;
12911291 HeapTuple tuple ;
12921292 bool hasexprs ;
1293+ LockRelId heaprelid ,
1294+ indexrelid ;
1295+ LOCKTAG heaplocktag ,
1296+ indexlocktag ;
1297+ VirtualTransactionId * old_lockholders ;
1298+ Form_pg_index indexForm ;
12931299
12941300 /*
12951301 * To drop an index safely, we must grab exclusive lock on its parent
@@ -1302,16 +1308,128 @@ index_drop(Oid indexId)
13021308 * that will make them update their index lists.
13031309 */
13041310 heapId = IndexGetRelation (indexId , false);
1305- userHeapRelation = heap_open (heapId , AccessExclusiveLock );
1306-
1307- userIndexRelation = index_open (indexId , AccessExclusiveLock );
1311+ if (concurrent )
1312+ {
1313+ userHeapRelation = heap_open (heapId , ShareUpdateExclusiveLock );
1314+ userIndexRelation = index_open (indexId , ShareUpdateExclusiveLock );
1315+ }
1316+ else
1317+ {
1318+ userHeapRelation = heap_open (heapId , AccessExclusiveLock );
1319+ userIndexRelation = index_open (indexId , AccessExclusiveLock );
1320+ }
13081321
13091322 /*
1310- * There can no longer be anyone *else* touching the index, but we might
1311- * still have open queries using it in our own session.
1323+ * We might still have open queries using it in our own session.
13121324 */
13131325 CheckTableNotInUse (userIndexRelation , "DROP INDEX" );
13141326
1327+ /*
1328+ * Drop Index concurrently is similar in many ways to creating an
1329+ * index concurrently, so some actions are similar to DefineIndex()
1330+ */
1331+ if (concurrent )
1332+ {
1333+ /*
1334+ * Mark index invalid by updating its pg_index entry
1335+ *
1336+ * Don't Assert(indexForm->indisvalid) because we may be trying to
1337+ * clear up after an error when trying to create an index which left
1338+ * the index invalid
1339+ */
1340+ indexRelation = heap_open (IndexRelationId , RowExclusiveLock );
1341+
1342+ tuple = SearchSysCacheCopy1 (INDEXRELID ,
1343+ ObjectIdGetDatum (indexId ));
1344+ if (!HeapTupleIsValid (tuple ))
1345+ elog (ERROR , "cache lookup failed for index %u" , indexId );
1346+ indexForm = (Form_pg_index ) GETSTRUCT (tuple );
1347+
1348+ indexForm -> indisvalid = false; /* make unusable for queries */
1349+ indexForm -> indisready = false; /* make invisible to changes */
1350+
1351+ simple_heap_update (indexRelation , & tuple -> t_self , tuple );
1352+ CatalogUpdateIndexes (indexRelation , tuple );
1353+
1354+ heap_close (indexRelation , RowExclusiveLock );
1355+
1356+ /*
1357+ * Invalidate the relcache for the table, so that after this
1358+ * transaction we will refresh the index list. Forgetting just the
1359+ * index is not enough.
1360+ */
1361+ CacheInvalidateRelcache (userHeapRelation );
1362+
1363+ /* save lockrelid and locktag for below, then close but keep locks */
1364+ heaprelid = userHeapRelation -> rd_lockInfo .lockRelId ;
1365+ SET_LOCKTAG_RELATION (heaplocktag , heaprelid .dbId , heaprelid .relId );
1366+ heap_close (userHeapRelation , NoLock );
1367+
1368+ indexrelid = userIndexRelation -> rd_lockInfo .lockRelId ;
1369+ SET_LOCKTAG_RELATION (indexlocktag , indexrelid .dbId , indexrelid .relId );
1370+ index_close (userIndexRelation , NoLock );
1371+
1372+ /*
1373+ * For a concurrent drop, it's important to make the catalog entries
1374+ * visible to other transactions before we drop the index. The index
1375+ * will be marked not indisvalid, so that no one else tries to either
1376+ * insert into it or use it for queries.
1377+ *
1378+ * We must commit our current transaction so that the index update becomes
1379+ * visible; then start another. Note that all the data structures we just
1380+ * built are lost in the commit. The only data we keep past here are the
1381+ * relation IDs.
1382+ *
1383+ * Before committing, get a session-level lock on the table, to ensure
1384+ * that neither it nor the index can be dropped before we finish. This
1385+ * cannot block, even if someone else is waiting for access, because we
1386+ * already have the same lock within our transaction.
1387+ */
1388+ LockRelationIdForSession (& heaprelid , ShareUpdateExclusiveLock );
1389+ LockRelationIdForSession (& indexrelid , ShareUpdateExclusiveLock );
1390+
1391+ PopActiveSnapshot ();
1392+ CommitTransactionCommand ();
1393+ StartTransactionCommand ();
1394+
1395+ /*
1396+ * Now we must wait until no running transaction could have the table open
1397+ * with the old list of indexes. To do this, inquire which xacts
1398+ * currently would conflict with AccessExclusiveLock on the table -- ie,
1399+ * which ones have a lock of any kind on the table. Then wait for each of
1400+ * these xacts to commit or abort. Note we do not need to worry about
1401+ * xacts that open the table for writing after this point; they will see
1402+ * the index as invalid when they open the relation.
1403+ *
1404+ * Note: the reason we use actual lock acquisition here, rather than just
1405+ * checking the ProcArray and sleeping, is that deadlock is possible if
1406+ * one of the transactions in question is blocked trying to acquire an
1407+ * exclusive lock on our table. The lock code will detect deadlock and
1408+ * error out properly.
1409+ *
1410+ * Note: GetLockConflicts() never reports our own xid, hence we need not
1411+ * check for that. Also, prepared xacts are not reported, which is fine
1412+ * since they certainly aren't going to do anything more.
1413+ */
1414+ old_lockholders = GetLockConflicts (& heaplocktag , AccessExclusiveLock );
1415+
1416+ while (VirtualTransactionIdIsValid (* old_lockholders ))
1417+ {
1418+ VirtualXactLock (* old_lockholders , true);
1419+ old_lockholders ++ ;
1420+ }
1421+
1422+ /*
1423+ * Re-open relations to allow us to complete our actions.
1424+ *
1425+ * At this point, nothing should be accessing the index, but lets
1426+ * leave nothing to chance and grab AccessExclusiveLock on the index
1427+ * before the physical deletion.
1428+ */
1429+ userHeapRelation = heap_open (heapId , ShareUpdateExclusiveLock );
1430+ userIndexRelation = index_open (indexId , AccessExclusiveLock );
1431+ }
1432+
13151433 /*
13161434 * All predicate locks on the index are about to be made invalid. Promote
13171435 * them to relation locks on the heap.
@@ -1378,6 +1496,15 @@ index_drop(Oid indexId)
13781496 * Close owning rel, but keep lock
13791497 */
13801498 heap_close (userHeapRelation , NoLock );
1499+
1500+ /*
1501+ * Release the session locks before we go.
1502+ */
1503+ if (concurrent )
1504+ {
1505+ UnlockRelationIdForSession (& heaprelid , ShareUpdateExclusiveLock );
1506+ UnlockRelationIdForSession (& indexrelid , ShareUpdateExclusiveLock );
1507+ }
13811508}
13821509
13831510/* ----------------------------------------------------------------
0 commit comments