@@ -129,6 +129,10 @@ static void ResetReindexPending(void);
129
129
* See whether an existing relation has a primary key.
130
130
*
131
131
* Caller must have suitable lock on the relation.
132
+ *
133
+ * Note: we intentionally do not check IndexIsValid here; that's because this
134
+ * is used to enforce the rule that there can be only one indisprimary index,
135
+ * and we want that to be true even if said index is invalid.
132
136
*/
133
137
static bool
134
138
relationHasPrimaryKey (Relation rel )
@@ -1247,8 +1251,9 @@ index_constraint_create(Relation heapRelation,
1247
1251
* Note: since this is a transactional update, it's unsafe against
1248
1252
* concurrent SnapshotNow scans of pg_index. When making an existing
1249
1253
* index into a constraint, caller must have a table lock that prevents
1250
- * concurrent table updates, and there is a risk that concurrent readers
1251
- * of the table will miss seeing this index at all.
1254
+ * concurrent table updates; if it's less than a full exclusive lock,
1255
+ * there is a risk that concurrent readers of the table will miss seeing
1256
+ * this index at all.
1252
1257
*/
1253
1258
if (update_pgindex && (mark_as_primary || deferrable ))
1254
1259
{
@@ -1450,7 +1455,7 @@ BuildIndexInfo(Relation index)
1450
1455
1451
1456
/* other info */
1452
1457
ii -> ii_Unique = indexStruct -> indisunique ;
1453
- ii -> ii_ReadyForInserts = indexStruct -> indisready ;
1458
+ ii -> ii_ReadyForInserts = IndexIsReady ( indexStruct ) ;
1454
1459
1455
1460
/* initialize index-build state to default */
1456
1461
ii -> ii_Concurrent = false;
@@ -1789,8 +1794,20 @@ index_build(Relation heapRelation,
1789
1794
* index's usability horizon. Moreover, we *must not* try to change the
1790
1795
* index's pg_index entry while reindexing pg_index itself, and this
1791
1796
* optimization nicely prevents that.
1792
- */
1793
- if (indexInfo -> ii_BrokenHotChain && !isreindex )
1797
+ *
1798
+ * We also need not set indcheckxmin during a concurrent index build,
1799
+ * because we won't set indisvalid true until all transactions that care
1800
+ * about the broken HOT chains are gone.
1801
+ *
1802
+ * Therefore, this code path can only be taken during non-concurrent
1803
+ * CREATE INDEX. Thus the fact that heap_update will set the pg_index
1804
+ * tuple's xmin doesn't matter, because that tuple was created in the
1805
+ * current transaction anyway. That also means we don't need to worry
1806
+ * about any concurrent readers of the tuple; no other transaction can see
1807
+ * it yet.
1808
+ */
1809
+ if (indexInfo -> ii_BrokenHotChain && !isreindex &&
1810
+ !indexInfo -> ii_Concurrent )
1794
1811
{
1795
1812
Oid indexId = RelationGetRelid (indexRelation );
1796
1813
Relation pg_index ;
@@ -2753,6 +2770,65 @@ validate_index_heapscan(Relation heapRelation,
2753
2770
}
2754
2771
2755
2772
2773
+ /*
2774
+ * index_set_state_flags - adjust pg_index state flags
2775
+ *
2776
+ * This is used during CREATE INDEX CONCURRENTLY to adjust the pg_index
2777
+ * flags that denote the index's state. We must use an in-place update of
2778
+ * the pg_index tuple, because we do not have exclusive lock on the parent
2779
+ * table and so other sessions might concurrently be doing SnapshotNow scans
2780
+ * of pg_index to identify the table's indexes. A transactional update would
2781
+ * risk somebody not seeing the index at all. Because the update is not
2782
+ * transactional and will not roll back on error, this must only be used as
2783
+ * the last step in a transaction that has not made any transactional catalog
2784
+ * updates!
2785
+ *
2786
+ * Note that heap_inplace_update does send a cache inval message for the
2787
+ * tuple, so other sessions will hear about the update as soon as we commit.
2788
+ */
2789
+ void
2790
+ index_set_state_flags (Oid indexId , IndexStateFlagsAction action )
2791
+ {
2792
+ Relation pg_index ;
2793
+ HeapTuple indexTuple ;
2794
+ Form_pg_index indexForm ;
2795
+
2796
+ /* Assert that current xact hasn't done any transactional updates */
2797
+ Assert (GetTopTransactionIdIfAny () == InvalidTransactionId );
2798
+
2799
+ /* Open pg_index and fetch a writable copy of the index's tuple */
2800
+ pg_index = heap_open (IndexRelationId , RowExclusiveLock );
2801
+
2802
+ indexTuple = SearchSysCacheCopy1 (INDEXRELID ,
2803
+ ObjectIdGetDatum (indexId ));
2804
+ if (!HeapTupleIsValid (indexTuple ))
2805
+ elog (ERROR , "cache lookup failed for index %u" , indexId );
2806
+ indexForm = (Form_pg_index ) GETSTRUCT (indexTuple );
2807
+
2808
+ /* Perform the requested state change on the copy */
2809
+ switch (action )
2810
+ {
2811
+ case INDEX_CREATE_SET_READY :
2812
+ /* Set indisready during a CREATE INDEX CONCURRENTLY sequence */
2813
+ Assert (!indexForm -> indisready );
2814
+ Assert (!indexForm -> indisvalid );
2815
+ indexForm -> indisready = true;
2816
+ break ;
2817
+ case INDEX_CREATE_SET_VALID :
2818
+ /* Set indisvalid during a CREATE INDEX CONCURRENTLY sequence */
2819
+ Assert (indexForm -> indisready );
2820
+ Assert (!indexForm -> indisvalid );
2821
+ indexForm -> indisvalid = true;
2822
+ break ;
2823
+ }
2824
+
2825
+ /* ... and write it back in-place */
2826
+ heap_inplace_update (pg_index , indexTuple );
2827
+
2828
+ heap_close (pg_index , RowExclusiveLock );
2829
+ }
2830
+
2831
+
2756
2832
/*
2757
2833
* IndexGetRelation: given an index's relation OID, get the OID of the
2758
2834
* relation it is an index on. Uses the system cache.
@@ -2782,12 +2858,9 @@ void
2782
2858
reindex_index (Oid indexId , bool skip_constraint_checks )
2783
2859
{
2784
2860
Relation iRel ,
2785
- heapRelation ,
2786
- pg_index ;
2861
+ heapRelation ;
2787
2862
Oid heapId ;
2788
2863
IndexInfo * indexInfo ;
2789
- HeapTuple indexTuple ;
2790
- Form_pg_index indexForm ;
2791
2864
volatile bool skipped_constraint = false;
2792
2865
2793
2866
/*
@@ -2867,25 +2940,39 @@ reindex_index(Oid indexId, bool skip_constraint_checks)
2867
2940
*
2868
2941
* We can also reset indcheckxmin, because we have now done a
2869
2942
* non-concurrent index build, *except* in the case where index_build
2870
- * found some still-broken HOT chains. If it did, we normally leave
2871
- * indcheckxmin alone (note that index_build won't have changed it,
2872
- * because this is a reindex). But if the index was invalid or not ready
2873
- * and there were broken HOT chains, it seems best to force indcheckxmin
2874
- * true, because the normal argument that the HOT chains couldn't conflict
2875
- * with the index is suspect for an invalid index.
2943
+ * found some still-broken HOT chains. If it did, and we don't have to
2944
+ * change any of the other flags, we just leave indcheckxmin alone (note
2945
+ * that index_build won't have changed it, because this is a reindex).
2946
+ * This is okay and desirable because not updating the tuple leaves the
2947
+ * index's usability horizon (recorded as the tuple's xmin value) the same
2948
+ * as it was.
2949
+ *
2950
+ * But, if the index was invalid/not-ready and there were broken HOT
2951
+ * chains, we had better force indcheckxmin true, because the normal
2952
+ * argument that the HOT chains couldn't conflict with the index is
2953
+ * suspect for an invalid index. In this case advancing the usability
2954
+ * horizon is appropriate.
2955
+ *
2956
+ * Note that if we have to update the tuple, there is a risk of concurrent
2957
+ * transactions not seeing it during their SnapshotNow scans of pg_index.
2958
+ * While not especially desirable, this is safe because no such
2959
+ * transaction could be trying to update the table (since we have
2960
+ * ShareLock on it). The worst case is that someone might transiently
2961
+ * fail to use the index for a query --- but it was probably unusable
2962
+ * before anyway, if we are updating the tuple.
2876
2963
*
2877
- * Note that it is important to not update the pg_index entry if we don't
2878
- * have to, because updating it will move the index's usability horizon
2879
- * (recorded as the tuple's xmin value) if indcheckxmin is true. We don't
2880
- * really want REINDEX to move the usability horizon forward ever, but we
2881
- * have no choice if we are to fix indisvalid or indisready. Of course,
2882
- * clearing indcheckxmin eliminates the issue, so we're happy to do that
2883
- * if we can. Another reason for caution here is that while reindexing
2884
- * pg_index itself, we must not try to update it. We assume that
2885
- * pg_index's indexes will always have these flags in their clean state.
2964
+ * Another reason for avoiding unnecessary updates here is that while
2965
+ * reindexing pg_index itself, we must not try to update tuples in it.
2966
+ * pg_index's indexes should always have these flags in their clean state,
2967
+ * so that won't happen.
2886
2968
*/
2887
2969
if (!skipped_constraint )
2888
2970
{
2971
+ Relation pg_index ;
2972
+ HeapTuple indexTuple ;
2973
+ Form_pg_index indexForm ;
2974
+ bool index_bad ;
2975
+
2889
2976
pg_index = heap_open (IndexRelationId , RowExclusiveLock );
2890
2977
2891
2978
indexTuple = SearchSysCacheCopy1 (INDEXRELID ,
@@ -2894,17 +2981,28 @@ reindex_index(Oid indexId, bool skip_constraint_checks)
2894
2981
elog (ERROR , "cache lookup failed for index %u" , indexId );
2895
2982
indexForm = (Form_pg_index ) GETSTRUCT (indexTuple );
2896
2983
2897
- if (!indexForm -> indisvalid || !indexForm -> indisready ||
2984
+ index_bad = (!indexForm -> indisvalid ||
2985
+ !indexForm -> indisready );
2986
+ if (index_bad ||
2898
2987
(indexForm -> indcheckxmin && !indexInfo -> ii_BrokenHotChain ))
2899
2988
{
2900
2989
if (!indexInfo -> ii_BrokenHotChain )
2901
2990
indexForm -> indcheckxmin = false;
2902
- else if (! indexForm -> indisvalid || ! indexForm -> indisready )
2991
+ else if (index_bad )
2903
2992
indexForm -> indcheckxmin = true;
2904
2993
indexForm -> indisvalid = true;
2905
2994
indexForm -> indisready = true;
2906
2995
simple_heap_update (pg_index , & indexTuple -> t_self , indexTuple );
2907
2996
CatalogUpdateIndexes (pg_index , indexTuple );
2997
+
2998
+ /*
2999
+ * Invalidate the relcache for the table, so that after we commit
3000
+ * all sessions will refresh the table's index list. This ensures
3001
+ * that if anyone misses seeing the pg_index row during this
3002
+ * update, they'll refresh their list before attempting any update
3003
+ * on the table.
3004
+ */
3005
+ CacheInvalidateRelcache (heapRelation );
2908
3006
}
2909
3007
2910
3008
heap_close (pg_index , RowExclusiveLock );
0 commit comments