@@ -129,6 +129,10 @@ static void ResetReindexPending(void);
129129 *See whether an existing relation has a primary key.
130130 *
131131 * 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.
132136 */
133137static bool
134138relationHasPrimaryKey (Relation rel )
@@ -1247,8 +1251,9 @@ index_constraint_create(Relation heapRelation,
12471251 * Note: since this is a transactional update, it's unsafe against
12481252 * concurrent SnapshotNow scans of pg_index. When making an existing
12491253 * 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.
12521257 */
12531258if (update_pgindex && (mark_as_primary || deferrable ))
12541259{
@@ -1450,7 +1455,7 @@ BuildIndexInfo(Relation index)
14501455
14511456/* other info */
14521457ii -> ii_Unique = indexStruct -> indisunique ;
1453- ii -> ii_ReadyForInserts = indexStruct -> indisready ;
1458+ ii -> ii_ReadyForInserts = IndexIsReady ( indexStruct ) ;
14541459
14551460/* initialize index-build state to default */
14561461ii -> ii_Concurrent = false;
@@ -1789,8 +1794,20 @@ index_build(Relation heapRelation,
17891794 * index's usability horizon. Moreover, we *must not* try to change the
17901795 * index's pg_index entry while reindexing pg_index itself, and this
17911796 * 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 )
17941811{
17951812Oid indexId = RelationGetRelid (indexRelation );
17961813Relation pg_index ;
@@ -2753,6 +2770,65 @@ validate_index_heapscan(Relation heapRelation,
27532770}
27542771
27552772
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+
27562832/*
27572833 * IndexGetRelation: given an index's relation OID, get the OID of the
27582834 * relation it is an index on.Uses the system cache.
@@ -2782,12 +2858,9 @@ void
27822858reindex_index (Oid indexId ,bool skip_constraint_checks )
27832859{
27842860Relation iRel ,
2785- heapRelation ,
2786- pg_index ;
2861+ heapRelation ;
27872862Oid heapId ;
27882863IndexInfo * indexInfo ;
2789- HeapTuple indexTuple ;
2790- Form_pg_index indexForm ;
27912864volatile bool skipped_constraint = false;
27922865
27932866/*
@@ -2867,25 +2940,39 @@ reindex_index(Oid indexId, bool skip_constraint_checks)
28672940 *
28682941 * We can also reset indcheckxmin, because we have now done a
28692942 * 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.
28762963 *
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.
28862968 */
28872969if (!skipped_constraint )
28882970{
2971+ Relation pg_index ;
2972+ HeapTuple indexTuple ;
2973+ Form_pg_index indexForm ;
2974+ bool index_bad ;
2975+
28892976pg_index = heap_open (IndexRelationId ,RowExclusiveLock );
28902977
28912978indexTuple = SearchSysCacheCopy1 (INDEXRELID ,
@@ -2894,17 +2981,28 @@ reindex_index(Oid indexId, bool skip_constraint_checks)
28942981elog (ERROR ,"cache lookup failed for index %u" ,indexId );
28952982indexForm = (Form_pg_index )GETSTRUCT (indexTuple );
28962983
2897- if (!indexForm -> indisvalid || !indexForm -> indisready ||
2984+ index_bad = (!indexForm -> indisvalid ||
2985+ !indexForm -> indisready );
2986+ if (index_bad ||
28982987(indexForm -> indcheckxmin && !indexInfo -> ii_BrokenHotChain ))
28992988{
29002989if (!indexInfo -> ii_BrokenHotChain )
29012990indexForm -> indcheckxmin = false;
2902- else if (! indexForm -> indisvalid || ! indexForm -> indisready )
2991+ else if (index_bad )
29032992indexForm -> indcheckxmin = true;
29042993indexForm -> indisvalid = true;
29052994indexForm -> indisready = true;
29062995simple_heap_update (pg_index ,& indexTuple -> t_self ,indexTuple );
29072996CatalogUpdateIndexes (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 );
29083006}
29093007
29103008heap_close (pg_index ,RowExclusiveLock );