@@ -150,6 +150,24 @@ boolcriticalSharedRelcachesBuilt = false;
150150 */
151151static long relcacheInvalsReceived = 0L ;
152152
153+ /*
154+ * in_progress_list is a stack of ongoing RelationBuildDesc() calls. CREATE
155+ * INDEX CONCURRENTLY makes catalog changes under ShareUpdateExclusiveLock.
156+ * It critically relies on each backend absorbing those changes no later than
157+ * next transaction start. Hence, RelationBuildDesc() loops until it finishes
158+ * without accepting a relevant invalidation. (Most invalidation consumers
159+ * don't do this.)
160+ */
161+ typedef struct inprogressent
162+ {
163+ Oid reloid ;/* OID of relation being built */
164+ bool invalidated ;/* whether an invalidation arrived for it */
165+ }InProgressEnt ;
166+
167+ static InProgressEnt * in_progress_list ;
168+ static int in_progress_list_len ;
169+ static int in_progress_list_maxlen ;
170+
153171/*
154172 * eoxact_list[] stores the OIDs of relations that (might) need AtEOXact
155173 * cleanup work. This list intentionally has limited size; if it overflows,
@@ -1043,6 +1061,7 @@ equalRSDesc(RowSecurityDesc *rsdesc1, RowSecurityDesc *rsdesc2)
10431061static Relation
10441062RelationBuildDesc (Oid targetRelId ,bool insertIt )
10451063{
1064+ int in_progress_offset ;
10461065Relation relation ;
10471066Oid relid ;
10481067HeapTuple pg_class_tuple ;
@@ -1070,6 +1089,21 @@ RelationBuildDesc(Oid targetRelId, bool insertIt)
10701089oldcxt = MemoryContextSwitchTo (tmpcxt );
10711090#endif
10721091
1092+ /* Register to catch invalidation messages */
1093+ if (in_progress_list_len >=in_progress_list_maxlen )
1094+ {
1095+ int allocsize ;
1096+
1097+ allocsize = in_progress_list_maxlen * 2 ;
1098+ in_progress_list = repalloc (in_progress_list ,
1099+ allocsize * sizeof (* in_progress_list ));
1100+ in_progress_list_maxlen = allocsize ;
1101+ }
1102+ in_progress_offset = in_progress_list_len ++ ;
1103+ in_progress_list [in_progress_offset ].reloid = targetRelId ;
1104+ retry :
1105+ in_progress_list [in_progress_offset ].invalidated = false;
1106+
10731107/*
10741108 * find the tuple in pg_class corresponding to the given relation id
10751109 */
@@ -1085,6 +1119,8 @@ RelationBuildDesc(Oid targetRelId, bool insertIt)
10851119MemoryContextSwitchTo (oldcxt );
10861120MemoryContextDelete (tmpcxt );
10871121#endif
1122+ Assert (in_progress_offset + 1 == in_progress_list_len );
1123+ in_progress_list_len -- ;
10881124return NULL ;
10891125}
10901126
@@ -1244,6 +1280,21 @@ RelationBuildDesc(Oid targetRelId, bool insertIt)
12441280 */
12451281heap_freetuple (pg_class_tuple );
12461282
1283+ /*
1284+ * If an invalidation arrived mid-build, start over. Between here and the
1285+ * end of this function, don't add code that does or reasonably could read
1286+ * system catalogs. That range must be free from invalidation processing
1287+ * for the !insertIt case. For the insertIt case, RelationCacheInsert()
1288+ * will enroll this relation in ordinary relcache invalidation processing,
1289+ */
1290+ if (in_progress_list [in_progress_offset ].invalidated )
1291+ {
1292+ RelationDestroyRelation (relation , false);
1293+ gotoretry ;
1294+ }
1295+ Assert (in_progress_offset + 1 == in_progress_list_len );
1296+ in_progress_list_len -- ;
1297+
12471298/*
12481299 * Insert newly created relation into relcache hash table, if requested.
12491300 *
@@ -2586,6 +2637,14 @@ RelationClearRelation(Relation relation, bool rebuild)
25862637
25872638/* Build temporary entry, but don't link it into hashtable */
25882639newrel = RelationBuildDesc (save_relid , false);
2640+
2641+ /*
2642+ * Between here and the end of the swap, don't add code that does or
2643+ * reasonably could read system catalogs. That range must be free
2644+ * from invalidation processing. See RelationBuildDesc() manipulation
2645+ * of in_progress_list.
2646+ */
2647+
25892648if (newrel == NULL )
25902649{
25912650/*
@@ -2816,6 +2875,14 @@ RelationCacheInvalidateEntry(Oid relationId)
28162875relcacheInvalsReceived ++ ;
28172876RelationFlushRelation (relation );
28182877}
2878+ else
2879+ {
2880+ int i ;
2881+
2882+ for (i = 0 ;i < in_progress_list_len ;i ++ )
2883+ if (in_progress_list [i ].reloid == relationId )
2884+ in_progress_list [i ].invalidated = true;
2885+ }
28192886}
28202887
28212888/*
@@ -2824,11 +2891,11 @@ RelationCacheInvalidateEntry(Oid relationId)
28242891 * and rebuild those with positive reference counts. Also reset the smgr
28252892 * relation cache and re-read relation mapping data.
28262893 *
2827- *This is currently used only to recover from SI message buffer overflow,
2828- * so we do not touch relations having new-in-transaction relfilenodes; they
2829- * cannot be targets of cross-backend SI updates (and our own updates now go
2830- *through a separate linked list that isn't limited by the SI message
2831- * buffer size).
2894+ *Apart from debug_discard_caches, this is currently used only to recover
2895+ *from SI message buffer overflow, so we do not touch relations having
2896+ *new-in-transaction relfilenodes; they cannot be targets of cross-backend
2897+ *SI updates (and our own updates now go through a separate linked list
2898+ *that isn't limited by the SI message buffer size).
28322899 *
28332900 * We do this in two phases: the first pass deletes deletable items, and
28342901 * the second one rebuilds the rebuildable items. This is essential for
@@ -2846,16 +2913,22 @@ RelationCacheInvalidateEntry(Oid relationId)
28462913 * second pass processes nailed-in-cache items before other nondeletable
28472914 * items. This should ensure that system catalogs are up to date before
28482915 * we attempt to use them to reload information about other open relations.
2916+ *
2917+ * After those two phases of work having immediate effects, we normally
2918+ * signal any RelationBuildDesc() on the stack to start over. However, we
2919+ * don't do this if called as part of debug_discard_caches. Otherwise,
2920+ * RelationBuildDesc() would become an infinite loop.
28492921 */
28502922void
2851- RelationCacheInvalidate (void )
2923+ RelationCacheInvalidate (bool debug_discard )
28522924{
28532925HASH_SEQ_STATUS status ;
28542926RelIdCacheEnt * idhentry ;
28552927Relation relation ;
28562928List * rebuildFirstList = NIL ;
28572929List * rebuildList = NIL ;
28582930ListCell * l ;
2931+ int i ;
28592932
28602933/*
28612934 * Reload relation mapping data before starting to reconstruct cache.
@@ -2942,6 +3015,11 @@ RelationCacheInvalidate(void)
29423015RelationClearRelation (relation , true);
29433016}
29443017list_free (rebuildList );
3018+
3019+ if (!debug_discard )
3020+ /* Any RelationBuildDesc() on the stack must start over. */
3021+ for (i = 0 ;i < in_progress_list_len ;i ++ )
3022+ in_progress_list [i ].invalidated = true;
29453023}
29463024
29473025/*
@@ -3092,6 +3170,13 @@ AtEOXact_RelationCache(bool isCommit)
30923170RelIdCacheEnt * idhentry ;
30933171int i ;
30943172
3173+ /*
3174+ * Forget in_progress_list. This is relevant when we're aborting due to
3175+ * an error during RelationBuildDesc().
3176+ */
3177+ Assert (in_progress_list_len == 0 || !isCommit );
3178+ in_progress_list_len = 0 ;
3179+
30953180/*
30963181 * Unless the eoxact_list[] overflowed, we only need to examine the rels
30973182 * listed in it. Otherwise fall back on a hash_seq_search scan.
@@ -3238,6 +3323,14 @@ AtEOSubXact_RelationCache(bool isCommit, SubTransactionId mySubid,
32383323RelIdCacheEnt * idhentry ;
32393324int i ;
32403325
3326+ /*
3327+ * Forget in_progress_list. This is relevant when we're aborting due to
3328+ * an error during RelationBuildDesc(). We don't commit subtransactions
3329+ * during RelationBuildDesc().
3330+ */
3331+ Assert (in_progress_list_len == 0 || !isCommit );
3332+ in_progress_list_len = 0 ;
3333+
32413334/*
32423335 * Unless the eoxact_list[] overflowed, we only need to examine the rels
32433336 * listed in it. Otherwise fall back on a hash_seq_search scan. Same
@@ -3786,6 +3879,7 @@ void
37863879RelationCacheInitialize (void )
37873880{
37883881HASHCTL ctl ;
3882+ int allocsize ;
37893883
37903884/*
37913885 * make sure cache memory context exists
@@ -3802,6 +3896,15 @@ RelationCacheInitialize(void)
38023896RelationIdCache = hash_create ("Relcache by OID" ,INITRELCACHESIZE ,
38033897& ctl ,HASH_ELEM |HASH_BLOBS );
38043898
3899+ /*
3900+ * reserve enough in_progress_list slots for many cases
3901+ */
3902+ allocsize = 4 ;
3903+ in_progress_list =
3904+ MemoryContextAlloc (CacheMemoryContext ,
3905+ allocsize * sizeof (* in_progress_list ));
3906+ in_progress_list_maxlen = allocsize ;
3907+
38053908/*
38063909 * relation mapper needs to be initialized too
38073910 */