@@ -1126,7 +1126,7 @@ BuildIndexInfo(Relation index)
1126
1126
1127
1127
/* other info */
1128
1128
ii -> ii_Unique = indexStruct -> indisunique ;
1129
- ii -> ii_ReadyForInserts = indexStruct -> indisready ;
1129
+ ii -> ii_ReadyForInserts = IndexIsReady ( indexStruct ) ;
1130
1130
1131
1131
/* initialize index-build state to default */
1132
1132
ii -> ii_Concurrent = false;
@@ -1465,8 +1465,20 @@ index_build(Relation heapRelation,
1465
1465
* index's usability horizon. Moreover, we *must not* try to change
1466
1466
* the index's pg_index entry while reindexing pg_index itself, and this
1467
1467
* optimization nicely prevents that.
1468
- */
1469
- if (indexInfo -> ii_BrokenHotChain && !isreindex )
1468
+ *
1469
+ * We also need not set indcheckxmin during a concurrent index build,
1470
+ * because we won't set indisvalid true until all transactions that care
1471
+ * about the broken HOT chains are gone.
1472
+ *
1473
+ * Therefore, this code path can only be taken during non-concurrent
1474
+ * CREATE INDEX. Thus the fact that heap_update will set the pg_index
1475
+ * tuple's xmin doesn't matter, because that tuple was created in the
1476
+ * current transaction anyway. That also means we don't need to worry
1477
+ * about any concurrent readers of the tuple; no other transaction can see
1478
+ * it yet.
1479
+ */
1480
+ if (indexInfo -> ii_BrokenHotChain && !isreindex &&
1481
+ !indexInfo -> ii_Concurrent )
1470
1482
{
1471
1483
Oid indexId = RelationGetRelid (indexRelation );
1472
1484
Relation pg_index ;
@@ -2408,6 +2420,65 @@ validate_index_heapscan(Relation heapRelation,
2408
2420
}
2409
2421
2410
2422
2423
+ /*
2424
+ * index_set_state_flags - adjust pg_index state flags
2425
+ *
2426
+ * This is used during CREATE INDEX CONCURRENTLY to adjust the pg_index
2427
+ * flags that denote the index's state. We must use an in-place update of
2428
+ * the pg_index tuple, because we do not have exclusive lock on the parent
2429
+ * table and so other sessions might concurrently be doing SnapshotNow scans
2430
+ * of pg_index to identify the table's indexes. A transactional update would
2431
+ * risk somebody not seeing the index at all. Because the update is not
2432
+ * transactional and will not roll back on error, this must only be used as
2433
+ * the last step in a transaction that has not made any transactional catalog
2434
+ * updates!
2435
+ *
2436
+ * Note that heap_inplace_update does send a cache inval message for the
2437
+ * tuple, so other sessions will hear about the update as soon as we commit.
2438
+ */
2439
+ void
2440
+ index_set_state_flags (Oid indexId , IndexStateFlagsAction action )
2441
+ {
2442
+ Relation pg_index ;
2443
+ HeapTuple indexTuple ;
2444
+ Form_pg_index indexForm ;
2445
+
2446
+ /* Assert that current xact hasn't done any transactional updates */
2447
+ Assert (GetTopTransactionIdIfAny () == InvalidTransactionId );
2448
+
2449
+ /* Open pg_index and fetch a writable copy of the index's tuple */
2450
+ pg_index = heap_open (IndexRelationId , RowExclusiveLock );
2451
+
2452
+ indexTuple = SearchSysCacheCopy1 (INDEXRELID ,
2453
+ ObjectIdGetDatum (indexId ));
2454
+ if (!HeapTupleIsValid (indexTuple ))
2455
+ elog (ERROR , "cache lookup failed for index %u" , indexId );
2456
+ indexForm = (Form_pg_index ) GETSTRUCT (indexTuple );
2457
+
2458
+ /* Perform the requested state change on the copy */
2459
+ switch (action )
2460
+ {
2461
+ case INDEX_CREATE_SET_READY :
2462
+ /* Set indisready during a CREATE INDEX CONCURRENTLY sequence */
2463
+ Assert (!indexForm -> indisready );
2464
+ Assert (!indexForm -> indisvalid );
2465
+ indexForm -> indisready = true;
2466
+ break ;
2467
+ case INDEX_CREATE_SET_VALID :
2468
+ /* Set indisvalid during a CREATE INDEX CONCURRENTLY sequence */
2469
+ Assert (indexForm -> indisready );
2470
+ Assert (!indexForm -> indisvalid );
2471
+ indexForm -> indisvalid = true;
2472
+ break ;
2473
+ }
2474
+
2475
+ /* ... and write it back in-place */
2476
+ heap_inplace_update (pg_index , indexTuple );
2477
+
2478
+ heap_close (pg_index , RowExclusiveLock );
2479
+ }
2480
+
2481
+
2411
2482
/*
2412
2483
* IndexGetRelation: given an index's relation OID, get the OID of the
2413
2484
* relation it is an index on. Uses the system cache.
@@ -2437,12 +2508,9 @@ void
2437
2508
reindex_index (Oid indexId , bool skip_constraint_checks )
2438
2509
{
2439
2510
Relation iRel ,
2440
- heapRelation ,
2441
- pg_index ;
2511
+ heapRelation ;
2442
2512
Oid heapId ;
2443
2513
IndexInfo * indexInfo ;
2444
- HeapTuple indexTuple ;
2445
- Form_pg_index indexForm ;
2446
2514
volatile bool skipped_constraint = false;
2447
2515
2448
2516
/*
@@ -2516,25 +2584,39 @@ reindex_index(Oid indexId, bool skip_constraint_checks)
2516
2584
*
2517
2585
* We can also reset indcheckxmin, because we have now done a
2518
2586
* non-concurrent index build, *except* in the case where index_build
2519
- * found some still-broken HOT chains. If it did, we normally leave
2520
- * indcheckxmin alone (note that index_build won't have changed it,
2521
- * because this is a reindex). But if the index was invalid or not ready
2522
- * and there were broken HOT chains, it seems best to force indcheckxmin
2523
- * true, because the normal argument that the HOT chains couldn't conflict
2524
- * with the index is suspect for an invalid index .
2587
+ * found some still-broken HOT chains. If it did, and we don't have to
2588
+ * change any of the other flags, we just leave indcheckxmin alone (note
2589
+ * that index_build won't have changed it, because this is a reindex).
2590
+ * This is okay and desirable because not updating the tuple leaves the
2591
+ * index's usability horizon (recorded as the tuple's xmin value) the same
2592
+ * as it was .
2525
2593
*
2526
- * Note that it is important to not update the pg_index entry if we don't
2527
- * have to, because updating it will move the index's usability horizon
2528
- * (recorded as the tuple's xmin value) if indcheckxmin is true. We don't
2529
- * really want REINDEX to move the usability horizon forward ever, but we
2530
- * have no choice if we are to fix indisvalid or indisready. Of course,
2531
- * clearing indcheckxmin eliminates the issue, so we're happy to do that
2532
- * if we can. Another reason for caution here is that while reindexing
2533
- * pg_index itself, we must not try to update it. We assume that
2534
- * pg_index's indexes will always have these flags in their clean state.
2594
+ * But, if the index was invalid/not-ready and there were broken HOT
2595
+ * chains, we had better force indcheckxmin true, because the normal
2596
+ * argument that the HOT chains couldn't conflict with the index is
2597
+ * suspect for an invalid index. In this case advancing the usability
2598
+ * horizon is appropriate.
2599
+ *
2600
+ * Note that if we have to update the tuple, there is a risk of concurrent
2601
+ * transactions not seeing it during their SnapshotNow scans of pg_index.
2602
+ * While not especially desirable, this is safe because no such
2603
+ * transaction could be trying to update the table (since we have
2604
+ * ShareLock on it). The worst case is that someone might transiently
2605
+ * fail to use the index for a query --- but it was probably unusable
2606
+ * before anyway, if we are updating the tuple.
2607
+ *
2608
+ * Another reason for avoiding unnecessary updates here is that while
2609
+ * reindexing pg_index itself, we must not try to update tuples in it.
2610
+ * pg_index's indexes should always have these flags in their clean state,
2611
+ * so that won't happen.
2535
2612
*/
2536
2613
if (!skipped_constraint )
2537
2614
{
2615
+ Relation pg_index ;
2616
+ HeapTuple indexTuple ;
2617
+ Form_pg_index indexForm ;
2618
+ bool index_bad ;
2619
+
2538
2620
pg_index = heap_open (IndexRelationId , RowExclusiveLock );
2539
2621
2540
2622
indexTuple = SearchSysCacheCopy1 (INDEXRELID ,
@@ -2543,17 +2625,28 @@ reindex_index(Oid indexId, bool skip_constraint_checks)
2543
2625
elog (ERROR , "cache lookup failed for index %u" , indexId );
2544
2626
indexForm = (Form_pg_index ) GETSTRUCT (indexTuple );
2545
2627
2546
- if (!indexForm -> indisvalid || !indexForm -> indisready ||
2628
+ index_bad = (!indexForm -> indisvalid ||
2629
+ !indexForm -> indisready );
2630
+ if (index_bad ||
2547
2631
(indexForm -> indcheckxmin && !indexInfo -> ii_BrokenHotChain ))
2548
2632
{
2549
2633
if (!indexInfo -> ii_BrokenHotChain )
2550
2634
indexForm -> indcheckxmin = false;
2551
- else if (! indexForm -> indisvalid || ! indexForm -> indisready )
2635
+ else if (index_bad )
2552
2636
indexForm -> indcheckxmin = true;
2553
2637
indexForm -> indisvalid = true;
2554
2638
indexForm -> indisready = true;
2555
2639
simple_heap_update (pg_index , & indexTuple -> t_self , indexTuple );
2556
2640
CatalogUpdateIndexes (pg_index , indexTuple );
2641
+
2642
+ /*
2643
+ * Invalidate the relcache for the table, so that after we commit
2644
+ * all sessions will refresh the table's index list. This ensures
2645
+ * that if anyone misses seeing the pg_index row during this
2646
+ * update, they'll refresh their list before attempting any update
2647
+ * on the table.
2648
+ */
2649
+ CacheInvalidateRelcache (heapRelation );
2557
2650
}
2558
2651
2559
2652
heap_close (pg_index , RowExclusiveLock );
0 commit comments