I wrote: > The syscache use of GETSTRUCT still uses a simple cast of the tuple (for > pg_cast those calls live in parse_coerce.c, which is unchanged from master in > v3). Next step I think is to see about the syscache piece -- teaching a > syscache miss to deform the entire tuple into a struct and store it in the > syscache.
v4 is a hackish attempt at getting the syscache to deform the tuple and store the struct. Using only pg_cast again for ease, it seems to work for that. It's now about as far as I can get without thinking about byref types. 0001 just adds copies of some syscache / catcache functions for private use by pg_cast, as scaffolding. 0002 teaches the temporary CatalogCacheCreateEntry_STRUCT() to call heap_deform_tuple(). This then calls a for-now handwritten function (similar to the one generated in v3) which palloc's space for the struct and copies the fields over. Only this function knows the catalog struct type, so a future design could call the per-cache function via a pointer in the catcache control array. Since we already have the isnull/values array, it's also trivial to copy the datums for the cache keys. WIP: CatCTup->tuple is still declared a tuple, but the t_data contents are now bogus, so there is a temporary GETSTRUCT_NEW that knows it's looking directly at the struct. Getting to varlen attributes: For one, I think it was mentioned above that we will need a way to perform a deep copy of structs that contain pointers to varlen fields. I imagine keeping track of the attributes length will come up for some types and might be tricky. And before that, the catalog machinery will need some preprocessor tricks to declare pointers in the structs. -- John Naylor EDB: http://www.enterprisedb.com
From 20fba44412e8ef1bb4cd5b051b9d7e82618a6d93 Mon Sep 17 00:00:00 2001 From: John Naylor <john.nay...@postgresql.org> Date: Wed, 10 Aug 2022 17:19:24 +0700 Subject: [PATCH v4 2/2] Teach catcache to store structs for pg_cast --- src/backend/parser/parse_coerce.c | 6 +-- src/backend/utils/cache/catcache.c | 73 ++++++++++++++++++++---------- src/include/access/htup_details.h | 2 + 3 files changed, 55 insertions(+), 26 deletions(-) diff --git a/src/backend/parser/parse_coerce.c b/src/backend/parser/parse_coerce.c index 39b7e5707b..07a1b047e3 100644 --- a/src/backend/parser/parse_coerce.c +++ b/src/backend/parser/parse_coerce.c @@ -3056,7 +3056,7 @@ IsBinaryCoercible(Oid srctype, Oid targettype) ObjectIdGetDatum(targettype)); if (!HeapTupleIsValid(tuple)) return false; /* no cast */ - castForm = (Form_pg_cast) GETSTRUCT(tuple); + castForm = GETSTRUCT_NEW(pg_cast, tuple); result = (castForm->castmethod == COERCION_METHOD_BINARY && castForm->castcontext == COERCION_CODE_IMPLICIT); @@ -3120,7 +3120,7 @@ find_coercion_pathway(Oid targetTypeId, Oid sourceTypeId, if (HeapTupleIsValid(tuple)) { - Form_pg_cast castForm = (Form_pg_cast) GETSTRUCT(tuple); + Form_pg_cast castForm = GETSTRUCT_NEW(pg_cast, tuple); CoercionContext castcontext; /* convert char value for castcontext to CoercionContext enum */ @@ -3287,7 +3287,7 @@ find_typmod_coercion_function(Oid typeId, if (HeapTupleIsValid(tuple)) { - Form_pg_cast castForm = (Form_pg_cast) GETSTRUCT(tuple); + Form_pg_cast castForm = GETSTRUCT_NEW(pg_cast, tuple); *funcid = castForm->castfunc; ReleaseSysCache(tuple); diff --git a/src/backend/utils/cache/catcache.c b/src/backend/utils/cache/catcache.c index b1287bb6a0..8ddc109052 100644 --- a/src/backend/utils/cache/catcache.c +++ b/src/backend/utils/cache/catcache.c @@ -21,6 +21,7 @@ #include "access/table.h" #include "access/valid.h" #include "access/xact.h" +#include "catalog/pg_cast.h" // fixme #include "catalog/pg_collation.h" #include "catalog/pg_operator.h" #include "catalog/pg_type.h" @@ -2158,6 +2159,42 @@ CatalogCacheCreateEntry(CatCache *cache, HeapTuple ntp, Datum *arguments, return ct; } +// WIP: generated functions would look like this and be called through a pointer +// FIXME: ct->tuple is no longer a real tuple +// XXX: for now assume the caller has switched to the right memory context +static CatCTup * +CatCArrayGetStruct_pg_cast(HeapTuple pg_cast_tuple, CatCTup *ct, Datum *values, bool *isnull) +{ + Form_pg_cast pg_cast_struct; + + /* Allocate memory for CatCTup and the cached struct in one go */ + ct = (CatCTup *) palloc(sizeof(CatCTup) + + MAXIMUM_ALIGNOF + sizeof(FormData_pg_cast)); + + /* copy the identification info */ + // WIP: for caches we only need t_self, can we just have that as a + // separate field in CatCTup? + ct->tuple.t_len = pg_cast_tuple->t_len; + ct->tuple.t_self = pg_cast_tuple->t_self; + ct->tuple.t_tableOid = pg_cast_tuple->t_tableOid; + + // WIP: treat t_data as a pointer to the struct + ct->tuple.t_data = (HeapTupleHeader) + MAXALIGN(((char *) ct) + sizeof(CatCTup)); + pg_cast_struct = (Form_pg_cast) ct->tuple.t_data; + + /* copy tuple contents */ + // WIP: we can just assign because there are no varlen attributes + pg_cast_struct->oid = DatumGetObjectId(values[Anum_pg_cast_oid - 1]); + pg_cast_struct->castsource = DatumGetObjectId(values[Anum_pg_cast_castsource - 1]); + pg_cast_struct->casttarget = DatumGetObjectId(values[Anum_pg_cast_casttarget - 1]); + pg_cast_struct->castfunc = DatumGetObjectId(values[Anum_pg_cast_castfunc - 1]); + pg_cast_struct->castcontext = DatumGetChar(values[Anum_pg_cast_castcontext - 1]); + pg_cast_struct->castmethod = DatumGetChar(values[Anum_pg_cast_castmethod - 1]); + + return ct; +} + /* * CatalogCacheCreateEntry * Create a new CatCTup entry, copying the given HeapTuple and other @@ -2176,6 +2213,8 @@ CatalogCacheCreateEntry_STRUCT(CatCache *cache, HeapTuple ntp, Datum *arguments, if (ntp) { int i; + Datum *values; + bool *isnull; Assert(!negative); @@ -2191,37 +2230,25 @@ CatalogCacheCreateEntry_STRUCT(CatCache *cache, HeapTuple ntp, Datum *arguments, else dtp = ntp; - /* Allocate memory for CatCTup and the cached tuple in one go */ - oldcxt = MemoryContextSwitchTo(CacheMemoryContext); + /* deform the tuple */ + values = palloc(cache->cc_tupdesc->natts * sizeof(Datum)); + isnull = palloc(cache->cc_tupdesc->natts * sizeof(bool)); + heap_deform_tuple(dtp, cache->cc_tupdesc, values, isnull); - ct = (CatCTup *) palloc(sizeof(CatCTup) + - MAXIMUM_ALIGNOF + dtp->t_len); - ct->tuple.t_len = dtp->t_len; - ct->tuple.t_self = dtp->t_self; - ct->tuple.t_tableOid = dtp->t_tableOid; - ct->tuple.t_data = (HeapTupleHeader) - MAXALIGN(((char *) ct) + sizeof(CatCTup)); - /* copy tuple contents */ - memcpy((char *) ct->tuple.t_data, - (const char *) dtp->t_data, - dtp->t_len); + /* copy the tuple-as-struct into the cache */ + oldcxt = MemoryContextSwitchTo(CacheMemoryContext); + ct = CatCArrayGetStruct_pg_cast(dtp, ct, values, isnull); MemoryContextSwitchTo(oldcxt); if (dtp != ntp) heap_freetuple(dtp); - /* extract keys - they'll point into the tuple if not by-value */ + /* extract keys */ + // FIXME: this is broken for varlen attributes -- need a way to copy them for (i = 0; i < cache->cc_nkeys; i++) { - Datum atp; - bool isnull; - - atp = heap_getattr(&ct->tuple, - cache->cc_keyno[i], - cache->cc_tupdesc, - &isnull); - Assert(!isnull); - ct->keys[i] = atp; + Assert(!isnull[cache->cc_keyno[i]]); + ct->keys[i] = values[cache->cc_keyno[i]]; } } else diff --git a/src/include/access/htup_details.h b/src/include/access/htup_details.h index 51a60eda08..7928c2d133 100644 --- a/src/include/access/htup_details.h +++ b/src/include/access/htup_details.h @@ -652,6 +652,8 @@ struct MinimalTupleData * GETSTRUCT - given a HeapTuple pointer, return address of the user data */ #define GETSTRUCT(TUP) ((char *) ((TUP)->t_data) + (TUP)->t_data->t_hoff) +// FIXME +#define GETSTRUCT_NEW(CAT, TUP) ((Form_##CAT) ((char *) (TUP)->t_data)) /* * Accessor macros to be used with HeapTuple pointers. -- 2.36.1
From 06ca223b1d863ee2cdeafccf5b588a7af03078fe Mon Sep 17 00:00:00 2001 From: John Naylor <john.nay...@postgresql.org> Date: Wed, 10 Aug 2022 17:09:47 +0700 Subject: [PATCH v4 1/2] Make copies of syscache / catcache routines for pg_cast --- src/backend/parser/parse_coerce.c | 6 +- src/backend/utils/cache/catcache.c | 366 +++++++++++++++++++++++++++++ src/backend/utils/cache/syscache.c | 11 + src/include/utils/catcache.h | 2 + src/include/utils/syscache.h | 2 + 5 files changed, 384 insertions(+), 3 deletions(-) diff --git a/src/backend/parser/parse_coerce.c b/src/backend/parser/parse_coerce.c index c4e958e4aa..39b7e5707b 100644 --- a/src/backend/parser/parse_coerce.c +++ b/src/backend/parser/parse_coerce.c @@ -3051,7 +3051,7 @@ IsBinaryCoercible(Oid srctype, Oid targettype) return true; /* Else look in pg_cast */ - tuple = SearchSysCache2(CASTSOURCETARGET, + tuple = SearchSysCache2CASTSOURCETARGET( ObjectIdGetDatum(srctype), ObjectIdGetDatum(targettype)); if (!HeapTupleIsValid(tuple)) @@ -3114,7 +3114,7 @@ find_coercion_pathway(Oid targetTypeId, Oid sourceTypeId, return COERCION_PATH_RELABELTYPE; /* Look in pg_cast */ - tuple = SearchSysCache2(CASTSOURCETARGET, + tuple = SearchSysCache2CASTSOURCETARGET( ObjectIdGetDatum(sourceTypeId), ObjectIdGetDatum(targetTypeId)); @@ -3281,7 +3281,7 @@ find_typmod_coercion_function(Oid typeId, ReleaseSysCache(targetType); /* Look in pg_cast */ - tuple = SearchSysCache2(CASTSOURCETARGET, + tuple = SearchSysCache2CASTSOURCETARGET( ObjectIdGetDatum(typeId), ObjectIdGetDatum(typeId)); diff --git a/src/backend/utils/cache/catcache.c b/src/backend/utils/cache/catcache.c index 38e943fab2..b1287bb6a0 100644 --- a/src/backend/utils/cache/catcache.c +++ b/src/backend/utils/cache/catcache.c @@ -69,6 +69,11 @@ static inline HeapTuple SearchCatCacheInternal(CatCache *cache, Datum v1, Datum v2, Datum v3, Datum v4); +static inline HeapTuple SearchCatCacheInternal_STRUCT(CatCache *cache, + int nkeys, + Datum v1, Datum v2, + Datum v3, Datum v4); + static pg_noinline HeapTuple SearchCatCacheMiss(CatCache *cache, int nkeys, uint32 hashValue, @@ -76,6 +81,13 @@ static pg_noinline HeapTuple SearchCatCacheMiss(CatCache *cache, Datum v1, Datum v2, Datum v3, Datum v4); +static pg_noinline HeapTuple SearchCatCacheMiss_STRUCT(CatCache *cache, + int nkeys, + uint32 hashValue, + Index hashIndex, + Datum v1, Datum v2, + Datum v3, Datum v4); + static uint32 CatalogCacheComputeHashValue(CatCache *cache, int nkeys, Datum v1, Datum v2, Datum v3, Datum v4); static uint32 CatalogCacheComputeTupleHashValue(CatCache *cache, int nkeys, @@ -95,6 +107,11 @@ static CatCTup *CatalogCacheCreateEntry(CatCache *cache, HeapTuple ntp, uint32 hashValue, Index hashIndex, bool negative); +static CatCTup *CatalogCacheCreateEntry_STRUCT(CatCache *cache, HeapTuple ntp, + Datum *arguments, + uint32 hashValue, Index hashIndex, + bool negative); + static void CatCacheFreeKeys(TupleDesc tupdesc, int nkeys, int *attnos, Datum *keys); static void CatCacheCopyKeys(TupleDesc tupdesc, int nkeys, int *attnos, @@ -1172,6 +1189,13 @@ SearchCatCache2(CatCache *cache, return SearchCatCacheInternal(cache, 2, v1, v2, 0, 0); } +HeapTuple +SearchCatCache2_STRUCT(CatCache *cache, + Datum v1, Datum v2) +{ + return SearchCatCacheInternal_STRUCT(cache, 2, v1, v2, 0, 0); +} + HeapTuple SearchCatCache3(CatCache *cache, @@ -1296,6 +1320,114 @@ SearchCatCacheInternal(CatCache *cache, return SearchCatCacheMiss(cache, nkeys, hashValue, hashIndex, v1, v2, v3, v4); } +/* + * Work-horse for SearchCatCache/SearchCatCacheN. + */ +static inline HeapTuple +SearchCatCacheInternal_STRUCT(CatCache *cache, + int nkeys, + Datum v1, + Datum v2, + Datum v3, + Datum v4) +{ + Datum arguments[CATCACHE_MAXKEYS]; + uint32 hashValue; + Index hashIndex; + dlist_iter iter; + dlist_head *bucket; + CatCTup *ct; + + /* Make sure we're in an xact, even if this ends up being a cache hit */ + Assert(IsTransactionState()); + + Assert(cache->cc_nkeys == nkeys); + + /* + * one-time startup overhead for each cache + */ + if (unlikely(cache->cc_tupdesc == NULL)) + CatalogCacheInitializeCache(cache); + +#ifdef CATCACHE_STATS + cache->cc_searches++; +#endif + + /* Initialize local parameter array */ + arguments[0] = v1; + arguments[1] = v2; + arguments[2] = v3; + arguments[3] = v4; + + /* + * find the hash bucket in which to look for the tuple + */ + hashValue = CatalogCacheComputeHashValue(cache, nkeys, v1, v2, v3, v4); + hashIndex = HASH_INDEX(hashValue, cache->cc_nbuckets); + + /* + * scan the hash bucket until we find a match or exhaust our tuples + * + * Note: it's okay to use dlist_foreach here, even though we modify the + * dlist within the loop, because we don't continue the loop afterwards. + */ + bucket = &cache->cc_bucket[hashIndex]; + dlist_foreach(iter, bucket) + { + ct = dlist_container(CatCTup, cache_elem, iter.cur); + + if (ct->dead) + continue; /* ignore dead entries */ + + if (ct->hash_value != hashValue) + continue; /* quickly skip entry if wrong hash val */ + + if (!CatalogCacheCompareTuple(cache, nkeys, ct->keys, arguments)) + continue; + + /* + * We found a match in the cache. Move it to the front of the list + * for its hashbucket, in order to speed subsequent searches. (The + * most frequently accessed elements in any hashbucket will tend to be + * near the front of the hashbucket's list.) + */ + dlist_move_head(bucket, &ct->cache_elem); + + /* + * If it's a positive entry, bump its refcount and return it. If it's + * negative, we can report failure to the caller. + */ + if (!ct->negative) + { + ResourceOwnerEnlargeCatCacheRefs(CurrentResourceOwner); + ct->refcount++; + ResourceOwnerRememberCatCacheRef(CurrentResourceOwner, &ct->tuple); + + CACHE_elog(DEBUG2, "SearchCatCache(%s): found in bucket %d", + cache->cc_relname, hashIndex); + +#ifdef CATCACHE_STATS + cache->cc_hits++; +#endif + + return &ct->tuple; + } + else + { + CACHE_elog(DEBUG2, "SearchCatCache(%s): found neg entry in bucket %d", + cache->cc_relname, hashIndex); + +#ifdef CATCACHE_STATS + cache->cc_neg_hits++; +#endif + + return NULL; + } + } + + return SearchCatCacheMiss_STRUCT(cache, nkeys, hashValue, hashIndex, v1, v2, v3, v4); +} + /* * Search the actual catalogs, rather than the cache. * @@ -1422,6 +1554,132 @@ SearchCatCacheMiss(CatCache *cache, return &ct->tuple; } +/* + * Search the actual catalogs, rather than the cache. + * + * This is kept separate from SearchCatCacheInternal() to keep the fast-path + * as small as possible. To avoid that effort being undone by a helpful + * compiler, try to explicitly forbid inlining. + */ +static pg_noinline HeapTuple +SearchCatCacheMiss_STRUCT(CatCache *cache, + int nkeys, + uint32 hashValue, + Index hashIndex, + Datum v1, + Datum v2, + Datum v3, + Datum v4) +{ + ScanKeyData cur_skey[CATCACHE_MAXKEYS]; + Relation relation; + SysScanDesc scandesc; + HeapTuple ntp; + CatCTup *ct; + Datum arguments[CATCACHE_MAXKEYS]; + + /* Initialize local parameter array */ + arguments[0] = v1; + arguments[1] = v2; + arguments[2] = v3; + arguments[3] = v4; + + /* + * Ok, need to make a lookup in the relation, copy the scankey and fill + * out any per-call fields. + */ + memcpy(cur_skey, cache->cc_skey, sizeof(ScanKeyData) * nkeys); + cur_skey[0].sk_argument = v1; + cur_skey[1].sk_argument = v2; + cur_skey[2].sk_argument = v3; + cur_skey[3].sk_argument = v4; + + /* + * Tuple was not found in cache, so we have to try to retrieve it directly + * from the relation. If found, we will add it to the cache; if not + * found, we will add a negative cache entry instead. + * + * NOTE: it is possible for recursive cache lookups to occur while reading + * the relation --- for example, due to shared-cache-inval messages being + * processed during table_open(). This is OK. It's even possible for one + * of those lookups to find and enter the very same tuple we are trying to + * fetch here. If that happens, we will enter a second copy of the tuple + * into the cache. The first copy will never be referenced again, and + * will eventually age out of the cache, so there's no functional problem. + * This case is rare enough that it's not worth expending extra cycles to + * detect. + */ + relation = table_open(cache->cc_reloid, AccessShareLock); + + scandesc = systable_beginscan(relation, + cache->cc_indexoid, + IndexScanOK(cache, cur_skey), + NULL, + nkeys, + cur_skey); + + ct = NULL; + + while (HeapTupleIsValid(ntp = systable_getnext(scandesc))) + { + ct = CatalogCacheCreateEntry_STRUCT(cache, ntp, arguments, + hashValue, hashIndex, + false); + /* immediately set the refcount to 1 */ + ResourceOwnerEnlargeCatCacheRefs(CurrentResourceOwner); + ct->refcount++; + ResourceOwnerRememberCatCacheRef(CurrentResourceOwner, &ct->tuple); + break; /* assume only one match */ + } + + systable_endscan(scandesc); + + table_close(relation, AccessShareLock); + + /* + * If tuple was not found, we need to build a negative cache entry + * containing a fake tuple. The fake tuple has the correct key columns, + * but nulls everywhere else. + * + * In bootstrap mode, we don't build negative entries, because the cache + * invalidation mechanism isn't alive and can't clear them if the tuple + * gets created later. (Bootstrap doesn't do UPDATEs, so it doesn't need + * cache inval for that.) + */ + if (ct == NULL) + { + if (IsBootstrapProcessingMode()) + return NULL; + + ct = CatalogCacheCreateEntry_STRUCT(cache, NULL, arguments, + hashValue, hashIndex, + true); + + CACHE_elog(DEBUG2, "SearchCatCache(%s): Contains %d/%d tuples", + cache->cc_relname, cache->cc_ntup, CacheHdr->ch_ntup); + CACHE_elog(DEBUG2, "SearchCatCache(%s): put neg entry in bucket %d", + cache->cc_relname, hashIndex); + + /* + * We are not returning the negative entry to the caller, so leave its + * refcount zero. + */ + + return NULL; + } + + CACHE_elog(DEBUG2, "SearchCatCache(%s): Contains %d/%d tuples", + cache->cc_relname, cache->cc_ntup, CacheHdr->ch_ntup); + CACHE_elog(DEBUG2, "SearchCatCache(%s): put in bucket %d", + cache->cc_relname, hashIndex); + +#ifdef CATCACHE_STATS + cache->cc_newloads++; +#endif + + return &ct->tuple; +} + /* * ReleaseCatCache * @@ -1900,6 +2158,114 @@ CatalogCacheCreateEntry(CatCache *cache, HeapTuple ntp, Datum *arguments, return ct; } +/* + * CatalogCacheCreateEntry + * Create a new CatCTup entry, copying the given HeapTuple and other + * supplied data into it. The new entry initially has refcount 0. + */ +static CatCTup * +CatalogCacheCreateEntry_STRUCT(CatCache *cache, HeapTuple ntp, Datum *arguments, + uint32 hashValue, Index hashIndex, + bool negative) +{ + CatCTup *ct; + HeapTuple dtp; + MemoryContext oldcxt; + + /* negative entries have no tuple associated */ + if (ntp) + { + int i; + + Assert(!negative); + + /* + * If there are any out-of-line toasted fields in the tuple, expand + * them in-line. This saves cycles during later use of the catcache + * entry, and also protects us against the possibility of the toast + * tuples being freed before we attempt to fetch them, in case of + * something using a slightly stale catcache entry. + */ + if (HeapTupleHasExternal(ntp)) + dtp = toast_flatten_tuple(ntp, cache->cc_tupdesc); + else + dtp = ntp; + + /* Allocate memory for CatCTup and the cached tuple in one go */ + oldcxt = MemoryContextSwitchTo(CacheMemoryContext); + + ct = (CatCTup *) palloc(sizeof(CatCTup) + + MAXIMUM_ALIGNOF + dtp->t_len); + ct->tuple.t_len = dtp->t_len; + ct->tuple.t_self = dtp->t_self; + ct->tuple.t_tableOid = dtp->t_tableOid; + ct->tuple.t_data = (HeapTupleHeader) + MAXALIGN(((char *) ct) + sizeof(CatCTup)); + /* copy tuple contents */ + memcpy((char *) ct->tuple.t_data, + (const char *) dtp->t_data, + dtp->t_len); + MemoryContextSwitchTo(oldcxt); + + if (dtp != ntp) + heap_freetuple(dtp); + + /* extract keys - they'll point into the tuple if not by-value */ + for (i = 0; i < cache->cc_nkeys; i++) + { + Datum atp; + bool isnull; + + atp = heap_getattr(&ct->tuple, + cache->cc_keyno[i], + cache->cc_tupdesc, + &isnull); + Assert(!isnull); + ct->keys[i] = atp; + } + } + else + { + Assert(negative); + oldcxt = MemoryContextSwitchTo(CacheMemoryContext); + ct = (CatCTup *) palloc(sizeof(CatCTup)); + + /* + * Store keys - they'll point into separately allocated memory if not + * by-value. + */ + CatCacheCopyKeys(cache->cc_tupdesc, cache->cc_nkeys, cache->cc_keyno, + arguments, ct->keys); + MemoryContextSwitchTo(oldcxt); + } + + /* + * Finish initializing the CatCTup header, and add it to the cache's + * linked list and counts. + */ + ct->ct_magic = CT_MAGIC; + ct->my_cache = cache; + ct->c_list = NULL; + ct->refcount = 0; /* for the moment */ + ct->dead = false; + ct->negative = negative; + ct->hash_value = hashValue; + + dlist_push_head(&cache->cc_bucket[hashIndex], &ct->cache_elem); + + cache->cc_ntup++; + CacheHdr->ch_ntup++; + + /* + * If the hash table has become too full, enlarge the buckets array. Quite + * arbitrarily, we enlarge when fill factor > 2. + */ + if (cache->cc_ntup > cache->cc_nbuckets * 2) + RehashCatCache(cache); + + return ct; +} + /* * Helper routine that frees keys stored in the keys array. */ diff --git a/src/backend/utils/cache/syscache.c b/src/backend/utils/cache/syscache.c index 1912b12146..d333f6f3bd 100644 --- a/src/backend/utils/cache/syscache.c +++ b/src/backend/utils/cache/syscache.c @@ -1191,6 +1191,17 @@ SearchSysCache2(int cacheId, return SearchCatCache2(SysCache[cacheId], key1, key2); } +HeapTuple +SearchSysCache2CASTSOURCETARGET( + Datum key1, Datum key2) +{ + Assert(CASTSOURCETARGET >= 0 && CASTSOURCETARGET < SysCacheSize && + PointerIsValid(SysCache[CASTSOURCETARGET])); + Assert(SysCache[CASTSOURCETARGET]->cc_nkeys == 2); + + return SearchCatCache2_STRUCT(SysCache[CASTSOURCETARGET], key1, key2); +} + HeapTuple SearchSysCache3(int cacheId, Datum key1, Datum key2, Datum key3) diff --git a/src/include/utils/catcache.h b/src/include/utils/catcache.h index d81e6fabb7..ad866325d4 100644 --- a/src/include/utils/catcache.h +++ b/src/include/utils/catcache.h @@ -202,6 +202,8 @@ extern HeapTuple SearchCatCache1(CatCache *cache, Datum v1); extern HeapTuple SearchCatCache2(CatCache *cache, Datum v1, Datum v2); +extern HeapTuple SearchCatCache2_STRUCT(CatCache *cache, + Datum v1, Datum v2); extern HeapTuple SearchCatCache3(CatCache *cache, Datum v1, Datum v2, Datum v3); extern HeapTuple SearchCatCache4(CatCache *cache, diff --git a/src/include/utils/syscache.h b/src/include/utils/syscache.h index 4463ea66be..3196016a49 100644 --- a/src/include/utils/syscache.h +++ b/src/include/utils/syscache.h @@ -132,6 +132,8 @@ extern HeapTuple SearchSysCache1(int cacheId, Datum key1); extern HeapTuple SearchSysCache2(int cacheId, Datum key1, Datum key2); +extern HeapTuple SearchSysCache2CASTSOURCETARGET( + Datum key1, Datum key2); extern HeapTuple SearchSysCache3(int cacheId, Datum key1, Datum key2, Datum key3); extern HeapTuple SearchSysCache4(int cacheId, -- 2.36.1