@@ -89,10 +89,10 @@ static void CatCachePrintStats(int code, Datum arg);
8989static void CatCacheRemoveCTup (CatCache * cache ,CatCTup * ct );
9090static void CatCacheRemoveCList (CatCache * cache ,CatCList * cl );
9191static void CatalogCacheInitializeCache (CatCache * cache );
92- static CatCTup * CatalogCacheCreateEntry (CatCache * cache ,HeapTuple ntp ,
92+ static CatCTup * CatalogCacheCreateEntry (CatCache * cache ,
93+ HeapTuple ntp ,SysScanDesc scandesc ,
9394Datum * arguments ,
94- uint32 hashValue ,Index hashIndex ,
95- bool negative );
95+ uint32 hashValue ,Index hashIndex );
9696
9797static void CatCacheFreeKeys (TupleDesc tupdesc ,int nkeys ,int * attnos ,
9898Datum * keys );
@@ -1324,6 +1324,7 @@ SearchCatCacheMiss(CatCache *cache,
13241324SysScanDesc scandesc ;
13251325HeapTuple ntp ;
13261326CatCTup * ct ;
1327+ bool stale ;
13271328Datum arguments [CATCACHE_MAXKEYS ];
13281329
13291330/* Initialize local parameter array */
@@ -1332,16 +1333,6 @@ SearchCatCacheMiss(CatCache *cache,
13321333arguments [2 ]= v3 ;
13331334arguments [3 ]= v4 ;
13341335
1335- /*
1336- * Ok, need to make a lookup in the relation, copy the scankey and fill
1337- * out any per-call fields.
1338- */
1339- memcpy (cur_skey ,cache -> cc_skey ,sizeof (ScanKeyData )* nkeys );
1340- cur_skey [0 ].sk_argument = v1 ;
1341- cur_skey [1 ].sk_argument = v2 ;
1342- cur_skey [2 ].sk_argument = v3 ;
1343- cur_skey [3 ].sk_argument = v4 ;
1344-
13451336/*
13461337 * Tuple was not found in cache, so we have to try to retrieve it directly
13471338 * from the relation. If found, we will add it to the cache; if not
@@ -1356,9 +1347,28 @@ SearchCatCacheMiss(CatCache *cache,
13561347 * will eventually age out of the cache, so there's no functional problem.
13571348 * This case is rare enough that it's not worth expending extra cycles to
13581349 * detect.
1350+ *
1351+ * Another case, which we *must* handle, is that the tuple could become
1352+ * outdated during CatalogCacheCreateEntry's attempt to detoast it (since
1353+ * AcceptInvalidationMessages can run during TOAST table access). We do
1354+ * not want to return already-stale catcache entries, so we loop around
1355+ * and do the table scan again if that happens.
13591356 */
13601357relation = table_open (cache -> cc_reloid ,AccessShareLock );
13611358
1359+ do
1360+ {
1361+ /*
1362+ * Ok, need to make a lookup in the relation, copy the scankey and
1363+ * fill out any per-call fields. (We must re-do this when retrying,
1364+ * because systable_beginscan scribbles on the scankey.)
1365+ */
1366+ memcpy (cur_skey ,cache -> cc_skey ,sizeof (ScanKeyData )* nkeys );
1367+ cur_skey [0 ].sk_argument = v1 ;
1368+ cur_skey [1 ].sk_argument = v2 ;
1369+ cur_skey [2 ].sk_argument = v3 ;
1370+ cur_skey [3 ].sk_argument = v4 ;
1371+
13621372scandesc = systable_beginscan (relation ,
13631373cache -> cc_indexoid ,
13641374IndexScanOK (cache ,cur_skey ),
@@ -1367,12 +1377,18 @@ SearchCatCacheMiss(CatCache *cache,
13671377cur_skey );
13681378
13691379ct = NULL ;
1380+ stale = false;
13701381
13711382while (HeapTupleIsValid (ntp = systable_getnext (scandesc )))
13721383{
1373- ct = CatalogCacheCreateEntry (cache ,ntp ,arguments ,
1374- hashValue ,hashIndex ,
1375- false);
1384+ ct = CatalogCacheCreateEntry (cache ,ntp ,scandesc ,NULL ,
1385+ hashValue ,hashIndex );
1386+ /* upon failure, we must start the scan over */
1387+ if (ct == NULL )
1388+ {
1389+ stale = true;
1390+ break ;
1391+ }
13761392/* immediately set the refcount to 1 */
13771393ResourceOwnerEnlargeCatCacheRefs (CurrentResourceOwner );
13781394ct -> refcount ++ ;
@@ -1381,6 +1397,7 @@ SearchCatCacheMiss(CatCache *cache,
13811397}
13821398
13831399systable_endscan (scandesc );
1400+ }while (stale );
13841401
13851402table_close (relation ,AccessShareLock );
13861403
@@ -1399,9 +1416,11 @@ SearchCatCacheMiss(CatCache *cache,
13991416if (IsBootstrapProcessingMode ())
14001417return NULL ;
14011418
1402- ct = CatalogCacheCreateEntry (cache ,NULL ,arguments ,
1403- hashValue ,hashIndex ,
1404- true);
1419+ ct = CatalogCacheCreateEntry (cache ,NULL ,NULL ,arguments ,
1420+ hashValue ,hashIndex );
1421+
1422+ /* Creating a negative cache entry shouldn't fail */
1423+ Assert (ct != NULL );
14051424
14061425CACHE_elog (DEBUG2 ,"SearchCatCache(%s): Contains %d/%d tuples" ,
14071426cache -> cc_relname ,cache -> cc_ntup ,CacheHdr -> ch_ntup );
@@ -1608,7 +1627,8 @@ SearchCatCacheList(CatCache *cache,
16081627 * We have to bump the member refcounts temporarily to ensure they won't
16091628 * get dropped from the cache while loading other members. We use a PG_TRY
16101629 * block to ensure we can undo those refcounts if we get an error before
1611- * we finish constructing the CatCList.
1630+ * we finish constructing the CatCList. ctlist must be valid throughout
1631+ * the PG_TRY block.
16121632 */
16131633ResourceOwnerEnlargeCatCacheListRefs (CurrentResourceOwner );
16141634
@@ -1619,19 +1639,23 @@ SearchCatCacheList(CatCache *cache,
16191639ScanKeyData cur_skey [CATCACHE_MAXKEYS ];
16201640Relation relation ;
16211641SysScanDesc scandesc ;
1622-
1623- /*
1624- * Ok, need to make a lookup in the relation, copy the scankey and
1625- * fill out any per-call fields.
1626- */
1627- memcpy (cur_skey ,cache -> cc_skey ,sizeof (ScanKeyData )* cache -> cc_nkeys );
1628- cur_skey [0 ].sk_argument = v1 ;
1629- cur_skey [1 ].sk_argument = v2 ;
1630- cur_skey [2 ].sk_argument = v3 ;
1631- cur_skey [3 ].sk_argument = v4 ;
1642+ bool stale ;
16321643
16331644relation = table_open (cache -> cc_reloid ,AccessShareLock );
16341645
1646+ do
1647+ {
1648+ /*
1649+ * Ok, need to make a lookup in the relation, copy the scankey and
1650+ * fill out any per-call fields. (We must re-do this when
1651+ * retrying, because systable_beginscan scribbles on the scankey.)
1652+ */
1653+ memcpy (cur_skey ,cache -> cc_skey ,sizeof (ScanKeyData )* cache -> cc_nkeys );
1654+ cur_skey [0 ].sk_argument = v1 ;
1655+ cur_skey [1 ].sk_argument = v2 ;
1656+ cur_skey [2 ].sk_argument = v3 ;
1657+ cur_skey [3 ].sk_argument = v4 ;
1658+
16351659scandesc = systable_beginscan (relation ,
16361660cache -> cc_indexoid ,
16371661IndexScanOK (cache ,cur_skey ),
@@ -1642,6 +1666,8 @@ SearchCatCacheList(CatCache *cache,
16421666/* The list will be ordered iff we are doing an index scan */
16431667ordered = (scandesc -> irel != NULL );
16441668
1669+ stale = false;
1670+
16451671while (HeapTupleIsValid (ntp = systable_getnext (scandesc )))
16461672{
16471673uint32 hashValue ;
@@ -1684,9 +1710,32 @@ SearchCatCacheList(CatCache *cache,
16841710if (!found )
16851711{
16861712/* We didn't find a usable entry, so make a new one */
1687- ct = CatalogCacheCreateEntry (cache ,ntp ,arguments ,
1688- hashValue ,hashIndex ,
1689- false);
1713+ ct = CatalogCacheCreateEntry (cache ,ntp ,scandesc ,NULL ,
1714+ hashValue ,hashIndex );
1715+ /* upon failure, we must start the scan over */
1716+ if (ct == NULL )
1717+ {
1718+ /*
1719+ * Release refcounts on any items we already had. We dare
1720+ * not try to free them if they're now unreferenced, since
1721+ * an error while doing that would result in the PG_CATCH
1722+ * below doing extra refcount decrements. Besides, we'll
1723+ * likely re-adopt those items in the next iteration, so
1724+ * it's not worth complicating matters to try to get rid
1725+ * of them.
1726+ */
1727+ foreach (ctlist_item ,ctlist )
1728+ {
1729+ ct = (CatCTup * )lfirst (ctlist_item );
1730+ Assert (ct -> c_list == NULL );
1731+ Assert (ct -> refcount > 0 );
1732+ ct -> refcount -- ;
1733+ }
1734+ /* Reset ctlist in preparation for new try */
1735+ ctlist = NIL ;
1736+ stale = true;
1737+ break ;
1738+ }
16901739}
16911740
16921741/* Careful here: add entry to ctlist, then bump its refcount */
@@ -1696,6 +1745,7 @@ SearchCatCacheList(CatCache *cache,
16961745}
16971746
16981747systable_endscan (scandesc );
1748+ }while (stale );
16991749
17001750table_close (relation ,AccessShareLock );
17011751
@@ -1803,22 +1853,42 @@ ReleaseCatCacheList(CatCList *list)
18031853 * CatalogCacheCreateEntry
18041854 *Create a new CatCTup entry, copying the given HeapTuple and other
18051855 *supplied data into it. The new entry initially has refcount 0.
1856+ *
1857+ * To create a normal cache entry, ntp must be the HeapTuple just fetched
1858+ * from scandesc, and "arguments" is not used. To create a negative cache
1859+ * entry, pass NULL for ntp and scandesc; then "arguments" is the cache
1860+ * keys to use. In either case, hashValue/hashIndex are the hash values
1861+ * computed from the cache keys.
1862+ *
1863+ * Returns NULL if we attempt to detoast the tuple and observe that it
1864+ * became stale. (This cannot happen for a negative entry.) Caller must
1865+ * retry the tuple lookup in that case.
18061866 */
18071867static CatCTup *
1808- CatalogCacheCreateEntry (CatCache * cache ,HeapTuple ntp ,Datum * arguments ,
1809- uint32 hashValue , Index hashIndex ,
1810- bool negative )
1868+ CatalogCacheCreateEntry (CatCache * cache ,HeapTuple ntp ,SysScanDesc scandesc ,
1869+ Datum * arguments ,
1870+ uint32 hashValue , Index hashIndex )
18111871{
18121872CatCTup * ct ;
18131873HeapTuple dtp ;
18141874MemoryContext oldcxt ;
18151875
1816- /* negative entries have no tuple associated */
18171876if (ntp )
18181877{
18191878int i ;
18201879
1821- Assert (!negative );
1880+ /*
1881+ * The visibility recheck below essentially never fails during our
1882+ * regression tests, and there's no easy way to force it to fail for
1883+ * testing purposes. To ensure we have test coverage for the retry
1884+ * paths in our callers, make debug builds randomly fail about 0.1% of
1885+ * the times through this code path, even when there's no toasted
1886+ * fields.
1887+ */
1888+ #ifdef USE_ASSERT_CHECKING
1889+ if (random () <= (MAX_RANDOM_VALUE /1000 ))
1890+ return NULL ;
1891+ #endif
18221892
18231893/*
18241894 * If there are any out-of-line toasted fields in the tuple, expand
@@ -1828,7 +1898,20 @@ CatalogCacheCreateEntry(CatCache *cache, HeapTuple ntp, Datum *arguments,
18281898 * something using a slightly stale catcache entry.
18291899 */
18301900if (HeapTupleHasExternal (ntp ))
1901+ {
18311902dtp = toast_flatten_tuple (ntp ,cache -> cc_tupdesc );
1903+
1904+ /*
1905+ * The tuple could become stale while we are doing toast table
1906+ * access (since AcceptInvalidationMessages can run then), so we
1907+ * must recheck its visibility afterwards.
1908+ */
1909+ if (!systable_recheck_tuple (scandesc ,ntp ))
1910+ {
1911+ heap_freetuple (dtp );
1912+ return NULL ;
1913+ }
1914+ }
18321915else
18331916dtp = ntp ;
18341917
@@ -1867,7 +1950,7 @@ CatalogCacheCreateEntry(CatCache *cache, HeapTuple ntp, Datum *arguments,
18671950}
18681951else
18691952{
1870- Assert ( negative );
1953+ /* Set up keys for a negative cache entry */
18711954oldcxt = MemoryContextSwitchTo (CacheMemoryContext );
18721955ct = (CatCTup * )palloc (sizeof (CatCTup ));
18731956
@@ -1889,7 +1972,7 @@ CatalogCacheCreateEntry(CatCache *cache, HeapTuple ntp, Datum *arguments,
18891972ct -> c_list = NULL ;
18901973ct -> refcount = 0 ;/* for the moment */
18911974ct -> dead = false;
1892- ct -> negative = negative ;
1975+ ct -> negative = ( ntp == NULL ) ;
18931976ct -> hash_value = hashValue ;
18941977
18951978dlist_push_head (& cache -> cc_bucket [hashIndex ],& ct -> cache_elem );