@@ -1316,6 +1316,10 @@ index_drop(Oid indexId, bool concurrent)
13161316 * table lock strong enough to prevent all queries on the table from
13171317 * proceeding until we commit and send out a shared-cache-inval notice
13181318 * that will make them update their index lists.
1319+ *
1320+ * In the concurrent case we make sure that nobody can be looking at the
1321+ * indexes by dropping the index in multiple steps, so we don't need a full
1322+ * AccessExclusiveLock yet.
13191323 */
13201324 heapId = IndexGetRelation (indexId , false);
13211325 if (concurrent )
@@ -1336,7 +1340,19 @@ index_drop(Oid indexId, bool concurrent)
13361340
13371341 /*
13381342 * Drop Index concurrently is similar in many ways to creating an index
1339- * concurrently, so some actions are similar to DefineIndex()
1343+ * concurrently, so some actions are similar to DefineIndex() just in the
1344+ * reverse order.
1345+ *
1346+ * First we unset indisvalid so queries starting afterwards don't use the
1347+ * index to answer queries anymore. We have to keep indisready = true
1348+ * so transactions that are still scanning the index can continue to
1349+ * see valid index contents. E.g. when they are using READ COMMITTED mode,
1350+ * and another transactions that started later commits makes changes and
1351+ * commits, they need to see those new tuples in the index.
1352+ *
1353+ * After all transactions that could possibly have used it for queries
1354+ * ended we can unset indisready and wait till nobody could be updating it
1355+ * anymore.
13401356 */
13411357 if (concurrent )
13421358 {
@@ -1355,21 +1371,21 @@ index_drop(Oid indexId, bool concurrent)
13551371 elog (ERROR , "cache lookup failed for index %u" , indexId );
13561372 indexForm = (Form_pg_index ) GETSTRUCT (tuple );
13571373
1358- indexForm -> indisvalid = false; /* make unusable for queries */
1359- indexForm -> indisready = false; /* make invisible to changes */
1374+ /*
1375+ * If indisready == true we leave it set so the index still gets
1376+ * maintained by pre-existing transactions. We only need to ensure
1377+ * that indisvalid is false.
1378+ */
1379+ if (indexForm -> indisvalid )
1380+ {
1381+ indexForm -> indisvalid = false; /* make unusable for new queries */
13601382
1361- simple_heap_update (indexRelation , & tuple -> t_self , tuple );
1362- CatalogUpdateIndexes (indexRelation , tuple );
1383+ simple_heap_update (indexRelation , & tuple -> t_self , tuple );
1384+ CatalogUpdateIndexes (indexRelation , tuple );
1385+ }
13631386
13641387 heap_close (indexRelation , RowExclusiveLock );
13651388
1366- /*
1367- * Invalidate the relcache for the table, so that after this
1368- * transaction we will refresh the index list. Forgetting just the
1369- * index is not enough.
1370- */
1371- CacheInvalidateRelcache (userHeapRelation );
1372-
13731389 /* save lockrelid and locktag for below, then close but keep locks */
13741390 heaprelid = userHeapRelation -> rd_lockInfo .lockRelId ;
13751391 SET_LOCKTAG_RELATION (heaplocktag , heaprelid .dbId , heaprelid .relId );
@@ -1381,8 +1397,8 @@ index_drop(Oid indexId, bool concurrent)
13811397 /*
13821398 * For a concurrent drop, it's important to make the catalog entries
13831399 * visible to other transactions before we drop the index. The index
1384- * will be marked not indisvalid, so that no one else tries to either
1385- * insert into it or use it for queries.
1400+ * will be marked not indisvalid, so that no one else tries to use it
1401+ * for queries.
13861402 *
13871403 * We must commit our current transaction so that the index update
13881404 * becomes visible; then start another. Note that all the data
@@ -1428,6 +1444,66 @@ index_drop(Oid indexId, bool concurrent)
14281444 old_lockholders ++ ;
14291445 }
14301446
1447+ /*
1448+ * Now we are sure that nobody uses the index for queries, they just
1449+ * might have it opened for updating it. So now we can unset
1450+ * indisready and wait till nobody could update the index anymore.
1451+ */
1452+ indexRelation = heap_open (IndexRelationId , RowExclusiveLock );
1453+
1454+ userHeapRelation = heap_open (heapId , ShareUpdateExclusiveLock );
1455+ userIndexRelation = index_open (indexId , ShareUpdateExclusiveLock );
1456+
1457+ tuple = SearchSysCacheCopy1 (INDEXRELID ,
1458+ ObjectIdGetDatum (indexId ));
1459+ if (!HeapTupleIsValid (tuple ))
1460+ elog (ERROR , "cache lookup failed for index %u" , indexId );
1461+ indexForm = (Form_pg_index ) GETSTRUCT (tuple );
1462+
1463+ Assert (indexForm -> indisvalid == false);
1464+ if (indexForm -> indisready )
1465+ {
1466+ indexForm -> indisready = false; /* don't update index anymore */
1467+
1468+ simple_heap_update (indexRelation , & tuple -> t_self , tuple );
1469+ CatalogUpdateIndexes (indexRelation , tuple );
1470+ }
1471+
1472+ heap_close (indexRelation , RowExclusiveLock );
1473+
1474+ /*
1475+ * Close the relations again, though still holding session lock.
1476+ */
1477+ heap_close (userHeapRelation , NoLock );
1478+ index_close (userIndexRelation , NoLock );
1479+
1480+ /*
1481+ * Invalidate the relcache for the table, so that after this
1482+ * transaction we will refresh the index list. Forgetting just the
1483+ * index is not enough.
1484+ */
1485+ CacheInvalidateRelcache (userHeapRelation );
1486+
1487+ /*
1488+ * Just as with indisvalid = false we need to make sure indisready
1489+ * is false is visible for everyone.
1490+ */
1491+ CommitTransactionCommand ();
1492+ StartTransactionCommand ();
1493+
1494+ /*
1495+ * Wait till everyone that saw indisready = true finished so we can
1496+ * finally really remove the index. The logic here is the same as
1497+ * above.
1498+ */
1499+ old_lockholders = GetLockConflicts (& heaplocktag , AccessExclusiveLock );
1500+
1501+ while (VirtualTransactionIdIsValid (* old_lockholders ))
1502+ {
1503+ VirtualXactLock (* old_lockholders , true);
1504+ old_lockholders ++ ;
1505+ }
1506+
14311507 /*
14321508 * Re-open relations to allow us to complete our actions.
14331509 *
0 commit comments