agora inbox for [email protected]
help / color / mirror / Atom feed[PATCH v2 3/3] CatCache expiration feature.
92+ messages / 8 participants
[nested] [flat]
* [PATCH v1 3/3] CatCache expiration feature.
@ 2020-01-10 06:08 Kyotaro Horiguchi <[email protected]>
0 siblings, 0 replies; 92+ messages in thread
From: Kyotaro Horiguchi @ 2020-01-10 06:08 UTC (permalink / raw)
This adds the catcache expiration feature to the catcache mechanism.
Current catcache doesn't remove an entry and there's a case where many
hash entries occupy large amont of memory , being not accessed ever
after. This can be a quite serious issue on the cases of long-running
sessions. The expiration feature keeps process memory usage below
certain amount, in exchange of some extent of degradation if it is
turned on.
---
src/backend/utils/cache/catcache.c | 343 +++++++++++++++++++++++++++--
1 file changed, 326 insertions(+), 17 deletions(-)
diff --git a/src/backend/utils/cache/catcache.c b/src/backend/utils/cache/catcache.c
index a4e3676a89..29bc980d8e 100644
--- a/src/backend/utils/cache/catcache.c
+++ b/src/backend/utils/cache/catcache.c
@@ -72,10 +72,11 @@ static CatCacheHeader *CacheHdr = NULL;
/* Clock for the last accessed time of a catcache entry. */
TimestampTz catcacheclock = 0;
-static inline HeapTuple SearchCatCacheInternal(CatCache *cache,
- int nkeys,
- Datum v1, Datum v2,
- Datum v3, Datum v4);
+/* basic catcache search functions */
+static inline HeapTuple SearchCatCacheInternalb(CatCache *cache,
+ int nkeys,
+ Datum v1, Datum v2,
+ Datum v3, Datum v4);
static pg_noinline HeapTuple SearchCatCacheMiss(CatCache *cache,
int nkeys,
@@ -93,6 +94,23 @@ static HeapTuple SearchCatCache3b(CatCache *cache,
static HeapTuple SearchCatCache4b(CatCache *cache,
Datum v1, Datum v2, Datum v3, Datum v4);
+/* catcache search functions with expiration feature */
+static inline HeapTuple SearchCatCacheInternale(CatCache *cache,
+ int nkeys,
+ Datum v1, Datum v2,
+ Datum v3, Datum v4);
+
+static HeapTuple SearchCatCachee(CatCache *cache,
+ Datum v1, Datum v2, Datum v3, Datum v4);
+static HeapTuple SearchCatCache1e(CatCache *cache, Datum v1);
+static HeapTuple SearchCatCache2e(CatCache *cache, Datum v1, Datum v2);
+static HeapTuple SearchCatCache3e(CatCache *cache,
+ Datum v1, Datum v2, Datum v3);
+static HeapTuple SearchCatCache4e(CatCache *cache,
+ Datum v1, Datum v2, Datum v3, Datum v4);
+
+static bool CatCacheCleanupOldEntries(CatCache *cp);
+
static uint32 CatalogCacheComputeHashValue(CatCache *cache, int nkeys,
Datum v1, Datum v2, Datum v3, Datum v4);
static uint32 CatalogCacheComputeTupleHashValue(CatCache *cache, int nkeys,
@@ -125,13 +143,35 @@ static SearchCatCacheFuncsType catcache_base = {
SearchCatCache4b
};
+static SearchCatCacheFuncsType catcache_expire = {
+ SearchCatCachee,
+ SearchCatCache1e,
+ SearchCatCache2e,
+ SearchCatCache3e,
+ SearchCatCache4e
+};
+
SearchCatCacheFuncsType *SearchCatCacheFuncs = NULL;
+/* set catcache function set according to guc variables */
+static void
+set_catcache_functions(void)
+{
+ if (catalog_cache_prune_min_age < 0)
+ SearchCatCacheFuncs = &catcache_base;
+ else
+ SearchCatCacheFuncs = &catcache_expire;
+}
+
+
/* GUC assign function */
void
assign_catalog_cache_prune_min_age(int newval, void *extra)
{
catalog_cache_prune_min_age = newval;
+
+ /* choose corresponding function set */
+ set_catcache_functions();
}
/*
@@ -837,7 +877,7 @@ InitCatCache(int id,
slist_init(&CacheHdr->ch_caches);
CacheHdr->ch_ntup = 0;
- SearchCatCacheFuncs = &catcache_base;
+ set_catcache_functions();
#ifdef CATCACHE_STATS
/* set up to dump stats at backend exit */
@@ -900,6 +940,10 @@ RehashCatCache(CatCache *cp)
int newnbuckets;
int i;
+ /* try removing old entries before expanding hash */
+ if (CatCacheCleanupOldEntries(cp))
+ return;
+
elog(DEBUG1, "rehashing catalog cache id %d for %s; %d tups, %d buckets",
cp->id, cp->cc_relname, cp->cc_ntup, cp->cc_nbuckets);
@@ -1187,7 +1231,7 @@ SearchCatCacheb(CatCache *cache,
Datum v3,
Datum v4)
{
- return SearchCatCacheInternal(cache, cache->cc_nkeys, v1, v2, v3, v4);
+ return SearchCatCacheInternalb(cache, cache->cc_nkeys, v1, v2, v3, v4);
}
@@ -1201,7 +1245,7 @@ static HeapTuple
SearchCatCache1b(CatCache *cache,
Datum v1)
{
- return SearchCatCacheInternal(cache, 1, v1, 0, 0, 0);
+ return SearchCatCacheInternalb(cache, 1, v1, 0, 0, 0);
}
@@ -1209,7 +1253,7 @@ static HeapTuple
SearchCatCache2b(CatCache *cache,
Datum v1, Datum v2)
{
- return SearchCatCacheInternal(cache, 2, v1, v2, 0, 0);
+ return SearchCatCacheInternalb(cache, 2, v1, v2, 0, 0);
}
@@ -1217,7 +1261,7 @@ static HeapTuple
SearchCatCache3b(CatCache *cache,
Datum v1, Datum v2, Datum v3)
{
- return SearchCatCacheInternal(cache, 3, v1, v2, v3, 0);
+ return SearchCatCacheInternalb(cache, 3, v1, v2, v3, 0);
}
@@ -1225,19 +1269,19 @@ static HeapTuple
SearchCatCache4b(CatCache *cache,
Datum v1, Datum v2, Datum v3, Datum v4)
{
- return SearchCatCacheInternal(cache, 4, v1, v2, v3, v4);
+ return SearchCatCacheInternalb(cache, 4, v1, v2, v3, v4);
}
/*
- * Work-horse for SearchCatCache/SearchCatCacheN.
+ * Work-horse for SearchCatCacheb/SearchCatCacheNb.
*/
static inline HeapTuple
-SearchCatCacheInternal(CatCache *cache,
- int nkeys,
- Datum v1,
- Datum v2,
- Datum v3,
- Datum v4)
+SearchCatCacheInternalb(CatCache *cache,
+ int nkeys,
+ Datum v1,
+ Datum v2,
+ Datum v3,
+ Datum v4)
{
Datum arguments[CATCACHE_MAXKEYS];
uint32 hashValue;
@@ -1462,6 +1506,269 @@ SearchCatCacheMiss(CatCache *cache,
return &ct->tuple;
}
+/*
+ * SearchCatCache with entry pruning
+ *
+ * These functions works the same way with SearchCatCacheNb() functions except
+ * that less-used entries are removed following catalog_cache_prune_min_age
+ * setting.
+ */
+static HeapTuple
+SearchCatCachee(CatCache *cache,
+ Datum v1,
+ Datum v2,
+ Datum v3,
+ Datum v4)
+{
+ return SearchCatCacheInternale(cache, cache->cc_nkeys, v1, v2, v3, v4);
+}
+
+
+/*
+ * SearchCatCacheN() are SearchCatCache() versions for a specific number of
+ * arguments. The compiler can inline the body and unroll loops, making them a
+ * bit faster than SearchCatCache().
+ */
+
+static HeapTuple
+SearchCatCache1e(CatCache *cache,
+ Datum v1)
+{
+ return SearchCatCacheInternale(cache, 1, v1, 0, 0, 0);
+}
+
+
+static HeapTuple
+SearchCatCache2e(CatCache *cache,
+ Datum v1, Datum v2)
+{
+ return SearchCatCacheInternale(cache, 2, v1, v2, 0, 0);
+}
+
+
+static HeapTuple
+SearchCatCache3e(CatCache *cache,
+ Datum v1, Datum v2, Datum v3)
+{
+ return SearchCatCacheInternale(cache, 3, v1, v2, v3, 0);
+}
+
+
+static HeapTuple
+SearchCatCache4e(CatCache *cache,
+ Datum v1, Datum v2, Datum v3, Datum v4)
+{
+ return SearchCatCacheInternale(cache, 4, v1, v2, v3, v4);
+}
+
+/*
+ * Work-horse for SearchCatCachee/SearchCatCacheNe.
+ */
+static inline HeapTuple
+SearchCatCacheInternale(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);
+
+ /*
+ * Prolong life of this entry. Since we want run as less instructions
+ * as possible and want the branch be stable for performance reasons,
+ * we don't give a strict cap on the counter. All numbers above 1 will
+ * be regarded as 2 in CatCacheCleanupOldEntries().
+ */
+ ct->naccess++;
+ if (unlikely(ct->naccess == 0))
+ ct->naccess = 2;
+ ct->lastaccess = catcacheclock;
+
+ /*
+ * 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(cache, nkeys, hashValue, hashIndex, v1, v2, v3, v4);
+}
+
+/*
+ * CatCacheCleanupOldEntries - Remove infrequently-used entries
+ *
+ * Catcache entries happen to be left unused for a long time for several
+ * reasons. Remove such entries to prevent catcache from bloating. It is based
+ * on the similar algorithm with buffer eviction. Entries that are accessed
+ * several times in a certain period live longer than those that have had less
+ * access in the same duration.
+ */
+static bool
+CatCacheCleanupOldEntries(CatCache *cp)
+{
+ int nremoved = 0;
+ int i;
+ long oldest_ts = catcacheclock;
+ long age;
+ int us;
+
+ /* Return immediately if disabled */
+ if (catalog_cache_prune_min_age < 0)
+ return false;
+
+ /* Don't scan the hash when we know we don't have prunable entries */
+ TimestampDifference(cp->cc_oldest_ts, catcacheclock, &age, &us);
+ if (age < catalog_cache_prune_min_age)
+ return false;
+
+ /* Scan over the whole hash to find entries to remove */
+ for (i = 0 ; i < cp->cc_nbuckets ; i++)
+ {
+ dlist_mutable_iter iter;
+
+ dlist_foreach_modify(iter, &cp->cc_bucket[i])
+ {
+ CatCTup *ct = dlist_container(CatCTup, cache_elem, iter.cur);
+
+ /* Don't remove referenced entries */
+ if (ct->refcount == 0 &&
+ (ct->c_list == NULL || ct->c_list->refcount == 0))
+ {
+ /*
+ * Calculate the duration from the time from the last access
+ * to the "current" time. catcacheclock is updated
+ * per-statement basis and additionaly udpated periodically
+ * during a long running query.
+ */
+ TimestampDifference(ct->lastaccess, catcacheclock, &age, &us);
+
+ if (age > catalog_cache_prune_min_age)
+ {
+ /*
+ * Entries that are not accessed after the last pruning
+ * are removed in that seconds, and their lives are
+ * prolonged according to how many times they are accessed
+ * up to three times of the duration. We don't try shrink
+ * buckets since pruning effectively caps catcache
+ * expansion in the long term.
+ */
+ if (ct->naccess > 2)
+ ct->naccess = 1;
+ else if (ct->naccess > 0)
+ ct->naccess--;
+ else
+ {
+ CatCacheRemoveCTup(cp, ct);
+ nremoved++;
+
+ /* don't update oldest_ts by removed entry */
+ continue;
+ }
+ }
+ }
+
+ /* update oldest timestamp if the entry remains alive */
+ if (ct->lastaccess < oldest_ts)
+ oldest_ts = ct->lastaccess;
+ }
+ }
+
+ cp->cc_oldest_ts = oldest_ts;
+
+ if (nremoved > 0)
+ elog(DEBUG1, "pruning catalog cache id=%d for %s: removed %d / %d",
+ cp->id, cp->cc_relname, nremoved, cp->cc_ntup + nremoved);
+
+ return nremoved > 0;
+}
+
+
/*
* ReleaseCatCache
*
@@ -1925,6 +2232,8 @@ CatalogCacheCreateEntry(CatCache *cache, HeapTuple ntp, Datum *arguments,
ct->dead = false;
ct->negative = negative;
ct->hash_value = hashValue;
+ ct->naccess = 0;
+ ct->lastaccess = catcacheclock;
dlist_push_head(&cache->cc_bucket[hashIndex], &ct->cache_elem);
--
2.23.0
----Next_Part(Wed_Jan_22_14_38_19_2020_534)--
Content-Type: Text/Plain; charset=us-ascii
Content-Transfer-Encoding: 7bit
Content-Disposition: inline;
filename="catcache-benchmark-extension.patch.txt"
^ permalink raw reply [nested|flat] 92+ messages in thread
* [PATCH v2 3/3] CatCache expiration feature.
@ 2020-01-10 06:08 Kyotaro Horiguchi <[email protected]>
0 siblings, 0 replies; 92+ messages in thread
From: Kyotaro Horiguchi @ 2020-01-10 06:08 UTC (permalink / raw)
This adds the catcache expiration feature to the catcache mechanism.
Current catcache doesn't remove an entry and there's a case where many
hash entries occupy large amont of memory , being not accessed ever
after. This can be a quite serious issue on the cases of long-running
sessions. The expiration feature keeps process memory usage below
certain amount, in exchange of some extent of degradation if it is
turned on.
---
src/backend/utils/cache/catcache.c | 344 +++++++++++++++++++++++++++--
1 file changed, 327 insertions(+), 17 deletions(-)
diff --git a/src/backend/utils/cache/catcache.c b/src/backend/utils/cache/catcache.c
index e047389e16..be4c90af7d 100644
--- a/src/backend/utils/cache/catcache.c
+++ b/src/backend/utils/cache/catcache.c
@@ -38,6 +38,7 @@
#include "utils/rel.h"
#include "utils/resowner_private.h"
#include "utils/syscache.h"
+#include "utils/timestamp.h"
/* #define CACHEDEBUG */ /* turns DEBUG elogs on */
@@ -72,10 +73,11 @@ static CatCacheHeader *CacheHdr = NULL;
/* Clock for the last accessed time of a catcache entry. */
TimestampTz catcacheclock = 0;
-static inline HeapTuple SearchCatCacheInternal(CatCache *cache,
- int nkeys,
- Datum v1, Datum v2,
- Datum v3, Datum v4);
+/* basic catcache search functions */
+static inline HeapTuple SearchCatCacheInternalb(CatCache *cache,
+ int nkeys,
+ Datum v1, Datum v2,
+ Datum v3, Datum v4);
static pg_noinline HeapTuple SearchCatCacheMiss(CatCache *cache,
int nkeys,
@@ -93,6 +95,23 @@ static HeapTuple SearchCatCache3b(CatCache *cache,
static HeapTuple SearchCatCache4b(CatCache *cache,
Datum v1, Datum v2, Datum v3, Datum v4);
+/* catcache search functions with expiration feature */
+static inline HeapTuple SearchCatCacheInternale(CatCache *cache,
+ int nkeys,
+ Datum v1, Datum v2,
+ Datum v3, Datum v4);
+
+static HeapTuple SearchCatCachee(CatCache *cache,
+ Datum v1, Datum v2, Datum v3, Datum v4);
+static HeapTuple SearchCatCache1e(CatCache *cache, Datum v1);
+static HeapTuple SearchCatCache2e(CatCache *cache, Datum v1, Datum v2);
+static HeapTuple SearchCatCache3e(CatCache *cache,
+ Datum v1, Datum v2, Datum v3);
+static HeapTuple SearchCatCache4e(CatCache *cache,
+ Datum v1, Datum v2, Datum v3, Datum v4);
+
+static bool CatCacheCleanupOldEntries(CatCache *cp);
+
static uint32 CatalogCacheComputeHashValue(CatCache *cache, int nkeys,
Datum v1, Datum v2, Datum v3, Datum v4);
static uint32 CatalogCacheComputeTupleHashValue(CatCache *cache, int nkeys,
@@ -125,13 +144,35 @@ static SearchCatCacheFuncsType catcache_base = {
SearchCatCache4b
};
+static SearchCatCacheFuncsType catcache_expire = {
+ SearchCatCachee,
+ SearchCatCache1e,
+ SearchCatCache2e,
+ SearchCatCache3e,
+ SearchCatCache4e
+};
+
SearchCatCacheFuncsType *SearchCatCacheFuncs = NULL;
+/* set catcache function set according to guc variables */
+static void
+set_catcache_functions(void)
+{
+ if (catalog_cache_prune_min_age < 0)
+ SearchCatCacheFuncs = &catcache_base;
+ else
+ SearchCatCacheFuncs = &catcache_expire;
+}
+
+
/* GUC assign function */
void
assign_catalog_cache_prune_min_age(int newval, void *extra)
{
catalog_cache_prune_min_age = newval;
+
+ /* choose corresponding function set */
+ set_catcache_functions();
}
/*
@@ -837,7 +878,7 @@ InitCatCache(int id,
slist_init(&CacheHdr->ch_caches);
CacheHdr->ch_ntup = 0;
- SearchCatCacheFuncs = &catcache_base;
+ set_catcache_functions();
#ifdef CATCACHE_STATS
/* set up to dump stats at backend exit */
@@ -900,6 +941,10 @@ RehashCatCache(CatCache *cp)
int newnbuckets;
int i;
+ /* try removing old entries before expanding hash */
+ if (CatCacheCleanupOldEntries(cp))
+ return;
+
elog(DEBUG1, "rehashing catalog cache id %d for %s; %d tups, %d buckets",
cp->id, cp->cc_relname, cp->cc_ntup, cp->cc_nbuckets);
@@ -1187,7 +1232,7 @@ SearchCatCacheb(CatCache *cache,
Datum v3,
Datum v4)
{
- return SearchCatCacheInternal(cache, cache->cc_nkeys, v1, v2, v3, v4);
+ return SearchCatCacheInternalb(cache, cache->cc_nkeys, v1, v2, v3, v4);
}
@@ -1201,7 +1246,7 @@ static HeapTuple
SearchCatCache1b(CatCache *cache,
Datum v1)
{
- return SearchCatCacheInternal(cache, 1, v1, 0, 0, 0);
+ return SearchCatCacheInternalb(cache, 1, v1, 0, 0, 0);
}
@@ -1209,7 +1254,7 @@ static HeapTuple
SearchCatCache2b(CatCache *cache,
Datum v1, Datum v2)
{
- return SearchCatCacheInternal(cache, 2, v1, v2, 0, 0);
+ return SearchCatCacheInternalb(cache, 2, v1, v2, 0, 0);
}
@@ -1217,7 +1262,7 @@ static HeapTuple
SearchCatCache3b(CatCache *cache,
Datum v1, Datum v2, Datum v3)
{
- return SearchCatCacheInternal(cache, 3, v1, v2, v3, 0);
+ return SearchCatCacheInternalb(cache, 3, v1, v2, v3, 0);
}
@@ -1225,19 +1270,19 @@ static HeapTuple
SearchCatCache4b(CatCache *cache,
Datum v1, Datum v2, Datum v3, Datum v4)
{
- return SearchCatCacheInternal(cache, 4, v1, v2, v3, v4);
+ return SearchCatCacheInternalb(cache, 4, v1, v2, v3, v4);
}
/*
- * Work-horse for SearchCatCache/SearchCatCacheN.
+ * Work-horse for SearchCatCacheb/SearchCatCacheNb.
*/
static inline HeapTuple
-SearchCatCacheInternal(CatCache *cache,
- int nkeys,
- Datum v1,
- Datum v2,
- Datum v3,
- Datum v4)
+SearchCatCacheInternalb(CatCache *cache,
+ int nkeys,
+ Datum v1,
+ Datum v2,
+ Datum v3,
+ Datum v4)
{
Datum arguments[CATCACHE_MAXKEYS];
uint32 hashValue;
@@ -1462,6 +1507,269 @@ SearchCatCacheMiss(CatCache *cache,
return &ct->tuple;
}
+/*
+ * SearchCatCache with entry pruning
+ *
+ * These functions works the same way with SearchCatCacheNb() functions except
+ * that less-used entries are removed following catalog_cache_prune_min_age
+ * setting.
+ */
+static HeapTuple
+SearchCatCachee(CatCache *cache,
+ Datum v1,
+ Datum v2,
+ Datum v3,
+ Datum v4)
+{
+ return SearchCatCacheInternale(cache, cache->cc_nkeys, v1, v2, v3, v4);
+}
+
+
+/*
+ * SearchCatCacheN() are SearchCatCache() versions for a specific number of
+ * arguments. The compiler can inline the body and unroll loops, making them a
+ * bit faster than SearchCatCache().
+ */
+
+static HeapTuple
+SearchCatCache1e(CatCache *cache,
+ Datum v1)
+{
+ return SearchCatCacheInternale(cache, 1, v1, 0, 0, 0);
+}
+
+
+static HeapTuple
+SearchCatCache2e(CatCache *cache,
+ Datum v1, Datum v2)
+{
+ return SearchCatCacheInternale(cache, 2, v1, v2, 0, 0);
+}
+
+
+static HeapTuple
+SearchCatCache3e(CatCache *cache,
+ Datum v1, Datum v2, Datum v3)
+{
+ return SearchCatCacheInternale(cache, 3, v1, v2, v3, 0);
+}
+
+
+static HeapTuple
+SearchCatCache4e(CatCache *cache,
+ Datum v1, Datum v2, Datum v3, Datum v4)
+{
+ return SearchCatCacheInternale(cache, 4, v1, v2, v3, v4);
+}
+
+/*
+ * Work-horse for SearchCatCachee/SearchCatCacheNe.
+ */
+static inline HeapTuple
+SearchCatCacheInternale(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);
+
+ /*
+ * Prolong life of this entry. Since we want run as less instructions
+ * as possible and want the branch be stable for performance reasons,
+ * we don't give a strict cap on the counter. All numbers above 1 will
+ * be regarded as 2 in CatCacheCleanupOldEntries().
+ */
+ ct->naccess++;
+ if (unlikely(ct->naccess == 0))
+ ct->naccess = 2;
+ ct->lastaccess = catcacheclock;
+
+ /*
+ * 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(cache, nkeys, hashValue, hashIndex, v1, v2, v3, v4);
+}
+
+/*
+ * CatCacheCleanupOldEntries - Remove infrequently-used entries
+ *
+ * Catcache entries happen to be left unused for a long time for several
+ * reasons. Remove such entries to prevent catcache from bloating. It is based
+ * on the similar algorithm with buffer eviction. Entries that are accessed
+ * several times in a certain period live longer than those that have had less
+ * access in the same duration.
+ */
+static bool
+CatCacheCleanupOldEntries(CatCache *cp)
+{
+ int nremoved = 0;
+ int i;
+ long oldest_ts = catcacheclock;
+ long age;
+ int us;
+
+ /* Return immediately if disabled */
+ if (catalog_cache_prune_min_age < 0)
+ return false;
+
+ /* Don't scan the hash when we know we don't have prunable entries */
+ TimestampDifference(cp->cc_oldest_ts, catcacheclock, &age, &us);
+ if (age < catalog_cache_prune_min_age)
+ return false;
+
+ /* Scan over the whole hash to find entries to remove */
+ for (i = 0 ; i < cp->cc_nbuckets ; i++)
+ {
+ dlist_mutable_iter iter;
+
+ dlist_foreach_modify(iter, &cp->cc_bucket[i])
+ {
+ CatCTup *ct = dlist_container(CatCTup, cache_elem, iter.cur);
+
+ /* Don't remove referenced entries */
+ if (ct->refcount == 0 &&
+ (ct->c_list == NULL || ct->c_list->refcount == 0))
+ {
+ /*
+ * Calculate the duration from the time from the last access
+ * to the "current" time. catcacheclock is updated
+ * per-statement basis and additionaly udpated periodically
+ * during a long running query.
+ */
+ TimestampDifference(ct->lastaccess, catcacheclock, &age, &us);
+
+ if (age > catalog_cache_prune_min_age)
+ {
+ /*
+ * Entries that are not accessed after the last pruning
+ * are removed in that seconds, and their lives are
+ * prolonged according to how many times they are accessed
+ * up to three times of the duration. We don't try shrink
+ * buckets since pruning effectively caps catcache
+ * expansion in the long term.
+ */
+ if (ct->naccess > 2)
+ ct->naccess = 1;
+ else if (ct->naccess > 0)
+ ct->naccess--;
+ else
+ {
+ CatCacheRemoveCTup(cp, ct);
+ nremoved++;
+
+ /* don't update oldest_ts by removed entry */
+ continue;
+ }
+ }
+ }
+
+ /* update oldest timestamp if the entry remains alive */
+ if (ct->lastaccess < oldest_ts)
+ oldest_ts = ct->lastaccess;
+ }
+ }
+
+ cp->cc_oldest_ts = oldest_ts;
+
+ if (nremoved > 0)
+ elog(DEBUG1, "pruning catalog cache id=%d for %s: removed %d / %d",
+ cp->id, cp->cc_relname, nremoved, cp->cc_ntup + nremoved);
+
+ return nremoved > 0;
+}
+
+
/*
* ReleaseCatCache
*
@@ -1925,6 +2233,8 @@ CatalogCacheCreateEntry(CatCache *cache, HeapTuple ntp, Datum *arguments,
ct->dead = false;
ct->negative = negative;
ct->hash_value = hashValue;
+ ct->naccess = 0;
+ ct->lastaccess = catcacheclock;
dlist_push_head(&cache->cc_bucket[hashIndex], &ct->cache_elem);
--
2.18.4
----Next_Part(Thu_Oct__1_16_47_18_2020_205)----
^ permalink raw reply [nested|flat] 92+ messages in thread
* [PATCH 4/4] CatCache expiration feature.
@ 2020-01-10 06:08 Kyotaro Horiguchi <[email protected]>
0 siblings, 0 replies; 92+ messages in thread
From: Kyotaro Horiguchi @ 2020-01-10 06:08 UTC (permalink / raw)
This adds the catcache expiration feature to the catcache mechanism.
Current catcache doesn't remove a entry and there's a case where many
hash entries occupy large amont of memory , being not accessed ever
after. This is a quire serious issue on the cases of long-running
sessions. The expiration feature saves the case in exchange of some
extent of degradation if it is turned on.
---
src/backend/utils/cache/catcache.c | 343 +++++++++++++++++++++++++++--
1 file changed, 326 insertions(+), 17 deletions(-)
diff --git a/src/backend/utils/cache/catcache.c b/src/backend/utils/cache/catcache.c
index 74c893ba4e..35e1a07e57 100644
--- a/src/backend/utils/cache/catcache.c
+++ b/src/backend/utils/cache/catcache.c
@@ -72,10 +72,11 @@ static CatCacheHeader *CacheHdr = NULL;
/* Clock for the last accessed time of a catcache entry. */
TimestampTz catcacheclock = 0;
-static inline HeapTuple SearchCatCacheInternal(CatCache *cache,
- int nkeys,
- Datum v1, Datum v2,
- Datum v3, Datum v4);
+/* basic catcache search functions */
+static inline HeapTuple SearchCatCacheInternalb(CatCache *cache,
+ int nkeys,
+ Datum v1, Datum v2,
+ Datum v3, Datum v4);
static pg_noinline HeapTuple SearchCatCacheMiss(CatCache *cache,
int nkeys,
@@ -93,6 +94,23 @@ static HeapTuple SearchCatCache3b(CatCache *cache,
static HeapTuple SearchCatCache4b(CatCache *cache,
Datum v1, Datum v2, Datum v3, Datum v4);
+/* catcache search functions with expiration feature */
+static inline HeapTuple SearchCatCacheInternale(CatCache *cache,
+ int nkeys,
+ Datum v1, Datum v2,
+ Datum v3, Datum v4);
+
+static HeapTuple SearchCatCachee(CatCache *cache,
+ Datum v1, Datum v2, Datum v3, Datum v4);
+static HeapTuple SearchCatCache1e(CatCache *cache, Datum v1);
+static HeapTuple SearchCatCache2e(CatCache *cache, Datum v1, Datum v2);
+static HeapTuple SearchCatCache3e(CatCache *cache,
+ Datum v1, Datum v2, Datum v3);
+static HeapTuple SearchCatCache4e(CatCache *cache,
+ Datum v1, Datum v2, Datum v3, Datum v4);
+
+static bool CatCacheCleanupOldEntries(CatCache *cp);
+
static uint32 CatalogCacheComputeHashValue(CatCache *cache, int nkeys,
Datum v1, Datum v2, Datum v3, Datum v4);
static uint32 CatalogCacheComputeTupleHashValue(CatCache *cache, int nkeys,
@@ -125,13 +143,35 @@ static SearchCatCacheFuncsType catcache_base = {
SearchCatCache4b
};
+static SearchCatCacheFuncsType catcache_expire = {
+ SearchCatCachee,
+ SearchCatCache1e,
+ SearchCatCache2e,
+ SearchCatCache3e,
+ SearchCatCache4e
+};
+
SearchCatCacheFuncsType *SearchCatCacheFuncs = NULL;
+/* set catcache function set according to guc variables */
+static void
+set_catcache_functions(void)
+{
+ if (catalog_cache_prune_min_age < 0)
+ SearchCatCacheFuncs = &catcache_base;
+ else
+ SearchCatCacheFuncs = &catcache_expire;
+}
+
+
/* GUC assign function */
void
assign_catalog_cache_prune_min_age(int newval, void *extra)
{
catalog_cache_prune_min_age = newval;
+
+ /* choose corresponding function set */
+ set_catcache_functions();
}
/*
@@ -872,7 +912,7 @@ InitCatCache(int id,
slist_init(&CacheHdr->ch_caches);
CacheHdr->ch_ntup = 0;
- SearchCatCacheFuncs = &catcache_base;
+ set_catcache_functions();
#ifdef CATCACHE_STATS
/* set up to dump stats at backend exit */
@@ -938,6 +978,10 @@ RehashCatCache(CatCache *cp)
elog(DEBUG1, "rehashing catalog cache id %d for %s; %d tups, %d buckets",
cp->id, cp->cc_relname, cp->cc_ntup, cp->cc_nbuckets);
+ /* try removing old entries before expanding hash */
+ if (CatCacheCleanupOldEntries(cp))
+ return;
+
/* Allocate a new, larger, hash table. */
newnbuckets = cp->cc_nbuckets * 2;
newbucket = (dlist_head *) MemoryContextAllocZero(CacheMemoryContext, newnbuckets * sizeof(dlist_head));
@@ -1222,7 +1266,7 @@ SearchCatCacheb(CatCache *cache,
Datum v3,
Datum v4)
{
- return SearchCatCacheInternal(cache, cache->cc_nkeys, v1, v2, v3, v4);
+ return SearchCatCacheInternalb(cache, cache->cc_nkeys, v1, v2, v3, v4);
}
@@ -1236,7 +1280,7 @@ static HeapTuple
SearchCatCache1b(CatCache *cache,
Datum v1)
{
- return SearchCatCacheInternal(cache, 1, v1, 0, 0, 0);
+ return SearchCatCacheInternalb(cache, 1, v1, 0, 0, 0);
}
@@ -1244,7 +1288,7 @@ static HeapTuple
SearchCatCache2b(CatCache *cache,
Datum v1, Datum v2)
{
- return SearchCatCacheInternal(cache, 2, v1, v2, 0, 0);
+ return SearchCatCacheInternalb(cache, 2, v1, v2, 0, 0);
}
@@ -1252,7 +1296,7 @@ static HeapTuple
SearchCatCache3b(CatCache *cache,
Datum v1, Datum v2, Datum v3)
{
- return SearchCatCacheInternal(cache, 3, v1, v2, v3, 0);
+ return SearchCatCacheInternalb(cache, 3, v1, v2, v3, 0);
}
@@ -1260,19 +1304,19 @@ static HeapTuple
SearchCatCache4b(CatCache *cache,
Datum v1, Datum v2, Datum v3, Datum v4)
{
- return SearchCatCacheInternal(cache, 4, v1, v2, v3, v4);
+ return SearchCatCacheInternalb(cache, 4, v1, v2, v3, v4);
}
/*
- * Work-horse for SearchCatCache/SearchCatCacheN.
+ * Work-horse for SearchCatCacheb/SearchCatCacheNb.
*/
static inline HeapTuple
-SearchCatCacheInternal(CatCache *cache,
- int nkeys,
- Datum v1,
- Datum v2,
- Datum v3,
- Datum v4)
+SearchCatCacheInternalb(CatCache *cache,
+ int nkeys,
+ Datum v1,
+ Datum v2,
+ Datum v3,
+ Datum v4)
{
Datum arguments[CATCACHE_MAXKEYS];
uint32 hashValue;
@@ -1497,6 +1541,269 @@ SearchCatCacheMiss(CatCache *cache,
return &ct->tuple;
}
+/*
+ * SearchCatCache with entry pruning
+ *
+ * These functions works the same way with SearchCatCacheNb() functions except
+ * that less-used entries are removed following catalog_cache_prune_min_age
+ * setting.
+ */
+static HeapTuple
+SearchCatCachee(CatCache *cache,
+ Datum v1,
+ Datum v2,
+ Datum v3,
+ Datum v4)
+{
+ return SearchCatCacheInternale(cache, cache->cc_nkeys, v1, v2, v3, v4);
+}
+
+
+/*
+ * SearchCatCacheN() are SearchCatCache() versions for a specific number of
+ * arguments. The compiler can inline the body and unroll loops, making them a
+ * bit faster than SearchCatCache().
+ */
+
+static HeapTuple
+SearchCatCache1e(CatCache *cache,
+ Datum v1)
+{
+ return SearchCatCacheInternale(cache, 1, v1, 0, 0, 0);
+}
+
+
+static HeapTuple
+SearchCatCache2e(CatCache *cache,
+ Datum v1, Datum v2)
+{
+ return SearchCatCacheInternale(cache, 2, v1, v2, 0, 0);
+}
+
+
+static HeapTuple
+SearchCatCache3e(CatCache *cache,
+ Datum v1, Datum v2, Datum v3)
+{
+ return SearchCatCacheInternale(cache, 3, v1, v2, v3, 0);
+}
+
+
+static HeapTuple
+SearchCatCache4e(CatCache *cache,
+ Datum v1, Datum v2, Datum v3, Datum v4)
+{
+ return SearchCatCacheInternale(cache, 4, v1, v2, v3, v4);
+}
+
+/*
+ * Work-horse for SearchCatCachee/SearchCatCacheNe.
+ */
+static inline HeapTuple
+SearchCatCacheInternale(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);
+
+ /*
+ * Prolong life of this entry. Since we want run as less instructions
+ * as possible and want the branch be stable for performance reasons,
+ * we don't give a strict cap on the counter. All numbers above 1 will
+ * be regarded as 2 in CatCacheCleanupOldEntries().
+ */
+ ct->naccess++;
+ if (unlikely(ct->naccess == 0))
+ ct->naccess = 2;
+ ct->lastaccess = catcacheclock;
+
+ /*
+ * 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(cache, nkeys, hashValue, hashIndex, v1, v2, v3, v4);
+}
+
+/*
+ * CatCacheCleanupOldEntries - Remove infrequently-used entries
+ *
+ * Catcache entries happen to be left unused for a long time for several
+ * reasons. Remove such entries to prevent catcache from bloating. It is based
+ * on the similar algorithm with buffer eviction. Entries that are accessed
+ * several times in a certain period live longer than those that have had less
+ * access in the same duration.
+ */
+static bool
+CatCacheCleanupOldEntries(CatCache *cp)
+{
+ int nremoved = 0;
+ int i;
+ long oldest_ts = catcacheclock;
+ long age;
+ int us;
+
+ /* Return immediately if disabled */
+ if (catalog_cache_prune_min_age < 0)
+ return false;
+
+ /* Don't scan the hash when we know we don't have prunable entries */
+ TimestampDifference(cp->cc_oldest_ts, catcacheclock, &age, &us);
+ if (age < catalog_cache_prune_min_age)
+ return false;
+
+ /* Scan over the whole hash to find entries to remove */
+ for (i = 0 ; i < cp->cc_nbuckets ; i++)
+ {
+ dlist_mutable_iter iter;
+
+ dlist_foreach_modify(iter, &cp->cc_bucket[i])
+ {
+ CatCTup *ct = dlist_container(CatCTup, cache_elem, iter.cur);
+
+ /* Don't remove referenced entries */
+ if (ct->refcount == 0 &&
+ (ct->c_list == NULL || ct->c_list->refcount == 0))
+ {
+ /*
+ * Calculate the duration from the time from the last access
+ * to the "current" time. catcacheclock is updated
+ * per-statement basis and additionaly udpated periodically
+ * during a long running query.
+ */
+ TimestampDifference(ct->lastaccess, catcacheclock, &age, &us);
+
+ if (age > catalog_cache_prune_min_age)
+ {
+ /*
+ * Entries that are not accessed after the last pruning
+ * are removed in that seconds, and their lives are
+ * prolonged according to how many times they are accessed
+ * up to three times of the duration. We don't try shrink
+ * buckets since pruning effectively caps catcache
+ * expansion in the long term.
+ */
+ if (ct->naccess > 2)
+ ct->naccess = 1;
+ else if (ct->naccess > 0)
+ ct->naccess--;
+ else
+ {
+ CatCacheRemoveCTup(cp, ct);
+ nremoved++;
+
+ /* don't update oldest_ts by removed entry */
+ continue;
+ }
+ }
+ }
+
+ /* update oldest timestamp if the entry remains alive */
+ if (ct->lastaccess < oldest_ts)
+ oldest_ts = ct->lastaccess;
+ }
+ }
+
+ cp->cc_oldest_ts = oldest_ts;
+
+ if (nremoved > 0)
+ elog(DEBUG1, "pruning catalog cache id=%d for %s: removed %d / %d",
+ cp->id, cp->cc_relname, nremoved, cp->cc_ntup + nremoved);
+
+ return nremoved > 0;
+}
+
+
/*
* ReleaseCatCache
*
@@ -1960,6 +2267,8 @@ CatalogCacheCreateEntry(CatCache *cache, HeapTuple ntp, Datum *arguments,
ct->dead = false;
ct->negative = negative;
ct->hash_value = hashValue;
+ ct->naccess = 0;
+ ct->lastaccess = catcacheclock;
dlist_push_head(&cache->cc_bucket[hashIndex], &ct->cache_elem);
--
2.23.0
----Next_Part(Tue_Jan_14_12_49_32_2020_834)--
Content-Type: Text/Plain; charset=us-ascii
Content-Transfer-Encoding: 7bit
Content-Disposition: inline; filename="gen_tbl2.pl"
#! /usr/bin/perl
$collist = "";
foreach $i (0..1000) {
$collist .= sprintf(", c%05d int", $i);
}
$collist = substr($collist, 2);
printf "drop schema if exists test cascade;\n";
printf "create schema test;\n";
printf "create table test.p ($collist) partition by list (c00000);\n";
foreach $i (0..2999) {
printf "create table test.t%04d partition of test.p for values in (%d);\n", $i, $i;
}
----Next_Part(Tue_Jan_14_12_49_32_2020_834)--
Content-Type: Text/Plain; charset=us-ascii
Content-Transfer-Encoding: 7bit
Content-Disposition: inline; filename="run4.sh"
#!/bin/bash
LOOPS=20
ITERATION=10
BINROOT=/home/horiguti/bin
DATADIR=/home/horiguti/data/data_catexpe
PREC="numeric(10,2)"
/usr/bin/killall postgres
/usr/bin/sleep 3
run() {
local BINARY=$1
local PGCTL=$2/bin/pg_ctl
local PGSQL=$2/bin/postgres
local PSQL=$2/bin/psql
if [ "$3" != "" ]; then
local SETTING1="set catalog_cache_prune_min_age to \"$3\";"
local SETTING2="set catalog_cache_prune_min_age to \"$4\";"
local SETTING3="set catalog_cache_prune_min_age to \"$5\";"
fi
# ($PGSQL -D $DATADIR 2>&1 > /dev/null)&
($PGSQL -D $DATADIR 2>&1 > /dev/null | /usr/bin/sed -e 's/^/# /')&
/usr/bin/sleep 3
${PSQL} postgres <<EOF
create extension if not exists catcachebench;
select catcachebench(0);
$SETTING3
select * from generate_series(2, 2) test,
LATERAL
(select '${BINARY}' as version,
count(r)::text || '/${LOOPS}' as n,
min(r)::${PREC},
stddev(r)::${PREC}
from (select catcachebench(test) as r
from generate_series(1, ${LOOPS})) r) r
EOF
$PGCTL --pgdata=$DATADIR stop 2>&1 > /dev/null | /usr/bin/sed -e 's/^/# /'
# oreport > $BINARY_perf.txt
}
for i in $(seq 0 ${ITERATION}); do
run "master" $BINROOT/pgsql_master_o2 "" "" ""
run "base" $BINROOT/pgsql_catexp-base "" "" ""
run "ind" $BINROOT/pgsql_catexp-ind "" "" ""
run "expire-off" $BINROOT/pgsql_catexpe "-1" "-1" "-1"
run "expire-on" $BINROOT/pgsql_catexpe "300s" "1s" "0"
done
----Next_Part(Tue_Jan_14_12_49_32_2020_834)--
Content-Type: Text/Plain; charset=us-ascii
Content-Transfer-Encoding: 7bit
Content-Disposition: inline; filename="build2.sh"
#! /usr/bin/bash
BINROOT=/home/horiguti/bin/pgsql_
for i in master_o2 catexp-base catexp-ind catexpe; do rm -r /home/horiguti/bin/pgsql_$i/*; done
for i in master_o2 catexp-base catexp-ind catexpe; do ls -l /home/horiguti/bin/pgsql_$i/bin/postgres; done
function build () {
echo $1
make distclean
git checkout $2
git diff master..HEAD > diff_$1.txt
./configure --enable-debug --enable-tap-tests --enable-nls --with-openssl --with-libxml --with-llvm --prefix=${BINROOT}$1 LLVM_CONFIG="/usr/bin/llvm-config"
make -sj8 all
make install
cd contrib/catcachebench
make clean
make all
make install
cd ../..
}
build "master_o2" "795e92756cd1"
build "catexp-base" "b2ebc9b4f1c"
build "catexp-ind" "631a04026d"
build "catexpe" "025e5e8a98d"
for i in master_o2 catexp-base catexp-ind catexpe; do ls -l /home/horiguti/bin/pgsql_$i/bin/postgres; done
----Next_Part(Tue_Jan_14_12_49_32_2020_834)----
^ permalink raw reply [nested|flat] 92+ messages in thread
* [PATCH v3 3/3] CatCache expiration feature.
@ 2020-01-10 06:08 Kyotaro Horiguchi <[email protected]>
0 siblings, 0 replies; 92+ messages in thread
From: Kyotaro Horiguchi @ 2020-01-10 06:08 UTC (permalink / raw)
This adds the catcache expiration feature to the catcache mechanism.
Current catcache doesn't remove an entry and there's a case where many
hash entries occupy large amont of memory , being not accessed ever
after. This can be a quite serious issue on the cases of long-running
sessions. The expiration feature keeps process memory usage below
certain amount, in exchange of some extent of degradation if it is
turned on.
---
src/backend/utils/cache/catcache.c | 219 +++++++++++++++++++++++++++--
1 file changed, 211 insertions(+), 8 deletions(-)
diff --git a/src/backend/utils/cache/catcache.c b/src/backend/utils/cache/catcache.c
index 7ac59bcdd6..a07b434fb6 100644
--- a/src/backend/utils/cache/catcache.c
+++ b/src/backend/utils/cache/catcache.c
@@ -38,6 +38,7 @@
#include "utils/rel.h"
#include "utils/resowner_private.h"
#include "utils/syscache.h"
+#include "utils/timestamp.h"
/* #define CACHEDEBUG */ /* turns DEBUG elogs on */
@@ -72,7 +73,8 @@ static CatCacheHeader *CacheHdr = NULL;
/* Clock for the last accessed time of a catcache entry. */
TimestampTz catcacheclock = 0;
-static inline HeapTuple SearchCatCacheInternal(CatCache *cache,
+static inline HeapTuple SearchCatCacheInternal(bool do_expire,
+ CatCache *cache,
int nkeys,
Datum v1, Datum v2,
Datum v3, Datum v4);
@@ -93,6 +95,17 @@ static HeapTuple SearchCatCache3b(CatCache *cache,
static HeapTuple SearchCatCache4b(CatCache *cache,
Datum v1, Datum v2, Datum v3, Datum v4);
+static HeapTuple SearchCatCachee(CatCache *cache,
+ Datum v1, Datum v2, Datum v3, Datum v4);
+static HeapTuple SearchCatCache1e(CatCache *cache, Datum v1);
+static HeapTuple SearchCatCache2e(CatCache *cache, Datum v1, Datum v2);
+static HeapTuple SearchCatCache3e(CatCache *cache,
+ Datum v1, Datum v2, Datum v3);
+static HeapTuple SearchCatCache4e(CatCache *cache,
+ Datum v1, Datum v2, Datum v3, Datum v4);
+
+static bool CatCacheCleanupOldEntries(CatCache *cp);
+
static uint32 CatalogCacheComputeHashValue(CatCache *cache, int nkeys,
Datum v1, Datum v2, Datum v3, Datum v4);
static uint32 CatalogCacheComputeTupleHashValue(CatCache *cache, int nkeys,
@@ -125,13 +138,35 @@ static SearchCatCacheFuncsType catcache_base = {
SearchCatCache4b
};
+static SearchCatCacheFuncsType catcache_expire = {
+ SearchCatCachee,
+ SearchCatCache1e,
+ SearchCatCache2e,
+ SearchCatCache3e,
+ SearchCatCache4e
+};
+
SearchCatCacheFuncsType *SearchCatCacheFuncs = NULL;
+/* set catcache function set according to guc variables */
+static void
+set_catcache_functions(void)
+{
+ if (catalog_cache_prune_min_age < 0)
+ SearchCatCacheFuncs = &catcache_base;
+ else
+ SearchCatCacheFuncs = &catcache_expire;
+}
+
+
/* GUC assign function */
void
assign_catalog_cache_prune_min_age(int newval, void *extra)
{
catalog_cache_prune_min_age = newval;
+
+ /* choose corresponding function set */
+ set_catcache_functions();
}
/*
@@ -837,7 +872,7 @@ InitCatCache(int id,
slist_init(&CacheHdr->ch_caches);
CacheHdr->ch_ntup = 0;
- SearchCatCacheFuncs = &catcache_base;
+ set_catcache_functions();
#ifdef CATCACHE_STATS
/* set up to dump stats at backend exit */
@@ -900,6 +935,10 @@ RehashCatCache(CatCache *cp)
int newnbuckets;
int i;
+ /* try removing old entries before expanding hash */
+ if (CatCacheCleanupOldEntries(cp))
+ return;
+
elog(DEBUG1, "rehashing catalog cache id %d for %s; %d tups, %d buckets",
cp->id, cp->cc_relname, cp->cc_ntup, cp->cc_nbuckets);
@@ -1187,7 +1226,7 @@ SearchCatCacheb(CatCache *cache,
Datum v3,
Datum v4)
{
- return SearchCatCacheInternal(cache, cache->cc_nkeys, v1, v2, v3, v4);
+ return SearchCatCacheInternal(false, cache, cache->cc_nkeys, v1, v2, v3, v4);
}
@@ -1201,7 +1240,7 @@ static HeapTuple
SearchCatCache1b(CatCache *cache,
Datum v1)
{
- return SearchCatCacheInternal(cache, 1, v1, 0, 0, 0);
+ return SearchCatCacheInternal(false, cache, 1, v1, 0, 0, 0);
}
@@ -1209,7 +1248,7 @@ static HeapTuple
SearchCatCache2b(CatCache *cache,
Datum v1, Datum v2)
{
- return SearchCatCacheInternal(cache, 2, v1, v2, 0, 0);
+ return SearchCatCacheInternal(false, cache, 2, v1, v2, 0, 0);
}
@@ -1217,7 +1256,7 @@ static HeapTuple
SearchCatCache3b(CatCache *cache,
Datum v1, Datum v2, Datum v3)
{
- return SearchCatCacheInternal(cache, 3, v1, v2, v3, 0);
+ return SearchCatCacheInternal(false, cache, 3, v1, v2, v3, 0);
}
@@ -1225,14 +1264,18 @@ static HeapTuple
SearchCatCache4b(CatCache *cache,
Datum v1, Datum v2, Datum v3, Datum v4)
{
- return SearchCatCacheInternal(cache, 4, v1, v2, v3, v4);
+ return SearchCatCacheInternal(false, cache, 4, v1, v2, v3, v4);
}
/*
* Work-horse for SearchCatCache/SearchCatCacheN.
+ *
+ * This function expects smarter compiler reduces useless paths by calling with
+ * fixed parameters.
*/
static inline HeapTuple
-SearchCatCacheInternal(CatCache *cache,
+SearchCatCacheInternal(bool do_expire,
+ CatCache *cache,
int nkeys,
Datum v1,
Datum v2,
@@ -1301,6 +1344,20 @@ SearchCatCacheInternal(CatCache *cache,
*/
dlist_move_head(bucket, &ct->cache_elem);
+ /*
+ * Prolong life of this entry. Since we want run as less instructions
+ * as possible and want the branch be stable for performance reasons,
+ * we don't give a strict cap on the counter. All numbers above 1 will
+ * be regarded as 2 in CatCacheCleanupOldEntries().
+ */
+ if (do_expire)
+ {
+ ct->naccess++;
+ if (unlikely(ct->naccess == 0))
+ ct->naccess = 2;
+ ct->lastaccess = catcacheclock;
+ }
+
/*
* If it's a positive entry, bump its refcount and return it. If it's
* negative, we can report failure to the caller.
@@ -1462,6 +1519,150 @@ SearchCatCacheMiss(CatCache *cache,
return &ct->tuple;
}
+/*
+ * SearchCatCache with entry pruning
+ *
+ * These functions works the same way with SearchCatCacheNb() functions except
+ * that less-used entries are removed following catalog_cache_prune_min_age
+ * setting.
+ */
+static HeapTuple
+SearchCatCachee(CatCache *cache,
+ Datum v1,
+ Datum v2,
+ Datum v3,
+ Datum v4)
+{
+ return SearchCatCacheInternal(true, cache, cache->cc_nkeys, v1, v2, v3, v4);
+}
+
+
+/*
+ * SearchCatCacheN() are SearchCatCache() versions for a specific number of
+ * arguments. The compiler can inline the body and unroll loops, making them a
+ * bit faster than SearchCatCache().
+ */
+
+static HeapTuple
+SearchCatCache1e(CatCache *cache,
+ Datum v1)
+{
+ return SearchCatCacheInternal(true, cache, 1, v1, 0, 0, 0);
+}
+
+
+static HeapTuple
+SearchCatCache2e(CatCache *cache,
+ Datum v1, Datum v2)
+{
+ return SearchCatCacheInternal(true, cache, 2, v1, v2, 0, 0);
+}
+
+
+static HeapTuple
+SearchCatCache3e(CatCache *cache,
+ Datum v1, Datum v2, Datum v3)
+{
+ return SearchCatCacheInternal(true, cache, 3, v1, v2, v3, 0);
+}
+
+
+static HeapTuple
+SearchCatCache4e(CatCache *cache,
+ Datum v1, Datum v2, Datum v3, Datum v4)
+{
+ return SearchCatCacheInternal(true, cache, 4, v1, v2, v3, v4);
+}
+
+/*
+ * CatCacheCleanupOldEntries - Remove infrequently-used entries
+ *
+ * Catcache entries happen to be left unused for a long time for several
+ * reasons. Remove such entries to prevent catcache from bloating. It is based
+ * on the similar algorithm with buffer eviction. Entries that are accessed
+ * several times in a certain period live longer than those that have had less
+ * access in the same duration.
+ */
+static bool
+CatCacheCleanupOldEntries(CatCache *cp)
+{
+ int nremoved = 0;
+ int i;
+ long oldest_ts = catcacheclock;
+ long age;
+ int us;
+
+ /* Return immediately if disabled */
+ if (catalog_cache_prune_min_age < 0)
+ return false;
+
+ /* Don't scan the hash when we know we don't have prunable entries */
+ TimestampDifference(cp->cc_oldest_ts, catcacheclock, &age, &us);
+ if (age < catalog_cache_prune_min_age)
+ return false;
+
+ /* Scan over the whole hash to find entries to remove */
+ for (i = 0 ; i < cp->cc_nbuckets ; i++)
+ {
+ dlist_mutable_iter iter;
+
+ dlist_foreach_modify(iter, &cp->cc_bucket[i])
+ {
+ CatCTup *ct = dlist_container(CatCTup, cache_elem, iter.cur);
+
+ /* Don't remove referenced entries */
+ if (ct->refcount == 0 &&
+ (ct->c_list == NULL || ct->c_list->refcount == 0))
+ {
+ /*
+ * Calculate the duration from the time from the last access
+ * to the "current" time. catcacheclock is updated
+ * per-statement basis and additionaly udpated periodically
+ * during a long running query.
+ */
+ TimestampDifference(ct->lastaccess, catcacheclock, &age, &us);
+
+ if (age > catalog_cache_prune_min_age)
+ {
+ /*
+ * Entries that are not accessed after the last pruning
+ * are removed in that seconds, and their lives are
+ * prolonged according to how many times they are accessed
+ * up to three times of the duration. We don't try shrink
+ * buckets since pruning effectively caps catcache
+ * expansion in the long term.
+ */
+ if (ct->naccess > 2)
+ ct->naccess = 1;
+ else if (ct->naccess > 0)
+ ct->naccess--;
+ else
+ {
+ CatCacheRemoveCTup(cp, ct);
+ nremoved++;
+
+ /* don't update oldest_ts by removed entry */
+ continue;
+ }
+ }
+ }
+
+ /* update oldest timestamp if the entry remains alive */
+ if (ct->lastaccess < oldest_ts)
+ oldest_ts = ct->lastaccess;
+ }
+ }
+
+ cp->cc_oldest_ts = oldest_ts;
+
+ if (nremoved > 0)
+ elog(DEBUG1, "pruning catalog cache id=%d for %s: removed %d / %d",
+ cp->id, cp->cc_relname, nremoved, cp->cc_ntup + nremoved);
+
+ return nremoved > 0;
+}
+
+
/*
* ReleaseCatCache
*
@@ -1925,6 +2126,8 @@ CatalogCacheCreateEntry(CatCache *cache, HeapTuple ntp, Datum *arguments,
ct->dead = false;
ct->negative = negative;
ct->hash_value = hashValue;
+ ct->naccess = 0;
+ ct->lastaccess = catcacheclock;
dlist_push_head(&cache->cc_bucket[hashIndex], &ct->cache_elem);
--
2.18.4
----Next_Part(Thu_Nov__5_16_26_55_2020_457)----
^ permalink raw reply [nested|flat] 92+ messages in thread
* [PATCH v4] CatCache expiration feature
@ 2020-11-06 08:27 Kyotaro Horiguchi <[email protected]>
0 siblings, 0 replies; 92+ messages in thread
From: Kyotaro Horiguchi @ 2020-11-06 08:27 UTC (permalink / raw)
---
src/backend/access/transam/xact.c | 3 +
src/backend/utils/cache/catcache.c | 125 +++++++++++++++++++++++++++++
src/backend/utils/misc/guc.c | 12 +++
src/include/utils/catcache.h | 20 +++++
4 files changed, 160 insertions(+)
diff --git a/src/backend/access/transam/xact.c b/src/backend/access/transam/xact.c
index af6afcebb1..a246fcc4c0 100644
--- a/src/backend/access/transam/xact.c
+++ b/src/backend/access/transam/xact.c
@@ -1086,6 +1086,9 @@ static void
AtStart_Cache(void)
{
AcceptInvalidationMessages();
+
+ if (xactStartTimestamp != 0)
+ SetCatCacheClock(xactStartTimestamp);
}
/*
diff --git a/src/backend/utils/cache/catcache.c b/src/backend/utils/cache/catcache.c
index 3613ae5f44..f63224bfd5 100644
--- a/src/backend/utils/cache/catcache.c
+++ b/src/backend/utils/cache/catcache.c
@@ -38,6 +38,7 @@
#include "utils/rel.h"
#include "utils/resowner_private.h"
#include "utils/syscache.h"
+#include "utils/timestamp.h"
/* #define CACHEDEBUG */ /* turns DEBUG elogs on */
@@ -60,9 +61,18 @@
#define CACHE_elog(...)
#endif
+/*
+ * GUC variable to define the minimum age of entries that will be considered
+ * to be evicted in seconds. -1 to disable the feature.
+ */
+int catalog_cache_prune_min_age = -1;
+
/* Cache management header --- pointer is NULL until created */
static CatCacheHeader *CacheHdr = NULL;
+/* Clock for the last accessed time of a catcache entry. */
+TimestampTz catcacheclock = 0;
+
static inline HeapTuple SearchCatCacheInternal(CatCache *cache,
int nkeys,
Datum v1, Datum v2,
@@ -74,6 +84,7 @@ static pg_noinline HeapTuple SearchCatCacheMiss(CatCache *cache,
Index hashIndex,
Datum v1, Datum v2,
Datum v3, Datum v4);
+static bool CatCacheCleanupOldEntries(CatCache *cp);
static uint32 CatalogCacheComputeHashValue(CatCache *cache, int nkeys,
Datum v1, Datum v2, Datum v3, Datum v4);
@@ -99,6 +110,12 @@ static void CatCacheFreeKeys(TupleDesc tupdesc, int nkeys, int *attnos,
static void CatCacheCopyKeys(TupleDesc tupdesc, int nkeys, int *attnos,
Datum *srckeys, Datum *dstkeys);
+/* GUC assign function */
+void
+assign_catalog_cache_prune_min_age(int newval, void *extra)
+{
+ catalog_cache_prune_min_age = newval;
+}
/*
* internal support functions
@@ -863,6 +880,10 @@ RehashCatCache(CatCache *cp)
int newnbuckets;
int i;
+ /* try removing old entries before expanding hash */
+ if (CatCacheCleanupOldEntries(cp))
+ return;
+
elog(DEBUG1, "rehashing catalog cache id %d for %s; %d tups, %d buckets",
cp->id, cp->cc_relname, cp->cc_ntup, cp->cc_nbuckets);
@@ -1264,6 +1285,20 @@ SearchCatCacheInternal(CatCache *cache,
*/
dlist_move_head(bucket, &ct->cache_elem);
+ /*
+ * Prolong life of this entry. Since we want run as less instructions
+ * as possible and want the branch be stable for performance reasons,
+ * we don't give a strict cap on the counter. All numbers above 1 will
+ * be regarded as 2 in CatCacheCleanupOldEntries().
+ */
+ if (unlikely(catalog_cache_prune_min_age >= 0))
+ {
+ ct->naccess++;
+ if (unlikely(ct->naccess == 0))
+ ct->naccess = 2;
+ ct->lastaccess = catcacheclock;
+ }
+
/*
* If it's a positive entry, bump its refcount and return it. If it's
* negative, we can report failure to the caller.
@@ -1425,6 +1460,94 @@ SearchCatCacheMiss(CatCache *cache,
return &ct->tuple;
}
+/*
+ * CatCacheCleanupOldEntries - Remove infrequently-used entries
+ *
+ * Catcache entries happen to be left unused for a long time for several
+ * reasons. Remove such entries to prevent catcache from bloating. It is based
+ * on the similar algorithm with buffer eviction. Entries that are accessed
+ * several times in a certain period live longer than those that have had less
+ * access in the same duration.
+ */
+static bool
+CatCacheCleanupOldEntries(CatCache *cp)
+{
+ int nremoved = 0;
+ int i;
+ long oldest_ts = catcacheclock;
+ long age;
+ int us;
+
+ /* Return immediately if disabled */
+ if (likely(catalog_cache_prune_min_age < 0))
+ return false;
+
+ /* Don't scan the hash when we know we don't have prunable entries */
+ TimestampDifference(cp->cc_oldest_ts, catcacheclock, &age, &us);
+ if (age < catalog_cache_prune_min_age)
+ return false;
+
+ /* Scan over the whole hash to find entries to remove */
+ for (i = 0 ; i < cp->cc_nbuckets ; i++)
+ {
+ dlist_mutable_iter iter;
+
+ dlist_foreach_modify(iter, &cp->cc_bucket[i])
+ {
+ CatCTup *ct = dlist_container(CatCTup, cache_elem, iter.cur);
+
+ /* Don't remove referenced entries */
+ if (ct->refcount == 0 &&
+ (ct->c_list == NULL || ct->c_list->refcount == 0))
+ {
+ /*
+ * Calculate the duration from the time from the last access
+ * to the "current" time. catcacheclock is updated
+ * per-statement basis and additionaly udpated periodically
+ * during a long running query.
+ */
+ TimestampDifference(ct->lastaccess, catcacheclock, &age, &us);
+
+ if (age > catalog_cache_prune_min_age)
+ {
+ /*
+ * Entries that are not accessed after the last pruning
+ * are removed in that seconds, and their lives are
+ * prolonged according to how many times they are accessed
+ * up to three times of the duration. We don't try shrink
+ * buckets since pruning effectively caps catcache
+ * expansion in the long term.
+ */
+ if (ct->naccess > 2)
+ ct->naccess = 1;
+ else if (ct->naccess > 0)
+ ct->naccess--;
+ else
+ {
+ CatCacheRemoveCTup(cp, ct);
+ nremoved++;
+
+ /* don't update oldest_ts by removed entry */
+ continue;
+ }
+ }
+ }
+
+ /* update oldest timestamp if the entry remains alive */
+ if (ct->lastaccess < oldest_ts)
+ oldest_ts = ct->lastaccess;
+ }
+ }
+
+ cp->cc_oldest_ts = oldest_ts;
+
+ if (nremoved > 0)
+ elog(DEBUG1, "pruning catalog cache id=%d for %s: removed %d / %d",
+ cp->id, cp->cc_relname, nremoved, cp->cc_ntup + nremoved);
+
+ return nremoved > 0;
+}
+
/*
* ReleaseCatCache
*
@@ -1888,6 +2011,8 @@ CatalogCacheCreateEntry(CatCache *cache, HeapTuple ntp, Datum *arguments,
ct->dead = false;
ct->negative = negative;
ct->hash_value = hashValue;
+ ct->naccess = 0;
+ ct->lastaccess = catcacheclock;
dlist_push_head(&cache->cc_bucket[hashIndex], &ct->cache_elem);
diff --git a/src/backend/utils/misc/guc.c b/src/backend/utils/misc/guc.c
index a62d64eaa4..ca897cab2e 100644
--- a/src/backend/utils/misc/guc.c
+++ b/src/backend/utils/misc/guc.c
@@ -88,6 +88,7 @@
#include "utils/acl.h"
#include "utils/builtins.h"
#include "utils/bytea.h"
+#include "utils/catcache.h"
#include "utils/float.h"
#include "utils/guc_tables.h"
#include "utils/memutils.h"
@@ -3399,6 +3400,17 @@ static struct config_int ConfigureNamesInt[] =
check_huge_page_size, NULL, NULL
},
+ {
+ {"catalog_cache_prune_min_age", PGC_USERSET, RESOURCES_MEM,
+ gettext_noop("System catalog cache entries that are living unused more than this seconds are considered for removal."),
+ gettext_noop("The value of -1 turns off pruning."),
+ GUC_UNIT_S
+ },
+ &catalog_cache_prune_min_age,
+ -1, -1, INT_MAX,
+ NULL, assign_catalog_cache_prune_min_age, NULL
+ },
+
/* End-of-list marker */
{
{NULL, 0, 0, NULL, NULL}, NULL, 0, 0, 0, NULL, NULL, NULL
diff --git a/src/include/utils/catcache.h b/src/include/utils/catcache.h
index f4aa316604..a11736f767 100644
--- a/src/include/utils/catcache.h
+++ b/src/include/utils/catcache.h
@@ -22,6 +22,7 @@
#include "access/htup.h"
#include "access/skey.h"
+#include "datatype/timestamp.h"
#include "lib/ilist.h"
#include "utils/relcache.h"
@@ -61,6 +62,7 @@ typedef struct catcache
slist_node cc_next; /* list link */
ScanKeyData cc_skey[CATCACHE_MAXKEYS]; /* precomputed key info for heap
* scans */
+ TimestampTz cc_oldest_ts; /* timestamp of the oldest tuple in the hash */
/*
* Keep these at the end, so that compiling catcache.c with CATCACHE_STATS
@@ -119,6 +121,8 @@ typedef struct catctup
bool dead; /* dead but not yet removed? */
bool negative; /* negative cache entry? */
HeapTupleData tuple; /* tuple management header */
+ unsigned int naccess; /* # of access to this entry */
+ TimestampTz lastaccess; /* timestamp of the last usage */
/*
* The tuple may also be a member of at most one CatCList. (If a single
@@ -189,6 +193,22 @@ typedef struct catcacheheader
/* this extern duplicates utils/memutils.h... */
extern PGDLLIMPORT MemoryContext CacheMemoryContext;
+
+/* for guc.c, not PGDLLPMPORT'ed */
+extern int catalog_cache_prune_min_age;
+
+/* source clock for access timestamp of catcache entries */
+extern TimestampTz catcacheclock;
+
+/* SetCatCacheClock - set catcache timestamp source clodk */
+static inline void
+SetCatCacheClock(TimestampTz ts)
+{
+ catcacheclock = ts;
+}
+
+extern void assign_catalog_cache_prune_min_age(int newval, void *extra);
+
extern void CreateCacheMemoryContext(void);
extern CatCache *InitCatCache(int id, Oid reloid, Oid indexoid,
--
2.18.4
----Next_Part(Fri_Nov__6_17_29_58_2020_781)----
^ permalink raw reply [nested|flat] 92+ messages in thread
* [PATCH v5] CatCache expiration feature
@ 2020-11-06 08:27 Kyotaro Horiguchi <[email protected]>
0 siblings, 0 replies; 92+ messages in thread
From: Kyotaro Horiguchi @ 2020-11-06 08:27 UTC (permalink / raw)
---
src/backend/access/transam/xact.c | 3 +
src/backend/utils/cache/catcache.c | 118 +++++++++++++++++++++++++++++
src/backend/utils/misc/guc.c | 12 +++
src/include/utils/catcache.h | 20 +++++
4 files changed, 153 insertions(+)
diff --git a/src/backend/access/transam/xact.c b/src/backend/access/transam/xact.c
index af6afcebb1..a246fcc4c0 100644
--- a/src/backend/access/transam/xact.c
+++ b/src/backend/access/transam/xact.c
@@ -1086,6 +1086,9 @@ static void
AtStart_Cache(void)
{
AcceptInvalidationMessages();
+
+ if (xactStartTimestamp != 0)
+ SetCatCacheClock(xactStartTimestamp);
}
/*
diff --git a/src/backend/utils/cache/catcache.c b/src/backend/utils/cache/catcache.c
index 3613ae5f44..b457fed7ab 100644
--- a/src/backend/utils/cache/catcache.c
+++ b/src/backend/utils/cache/catcache.c
@@ -38,6 +38,7 @@
#include "utils/rel.h"
#include "utils/resowner_private.h"
#include "utils/syscache.h"
+#include "utils/timestamp.h"
/* #define CACHEDEBUG */ /* turns DEBUG elogs on */
@@ -60,9 +61,18 @@
#define CACHE_elog(...)
#endif
+/*
+ * GUC variable to define the minimum age of entries that will be considered
+ * to be evicted in seconds. -1 to disable the feature.
+ */
+int catalog_cache_prune_min_age = -1;
+
/* Cache management header --- pointer is NULL until created */
static CatCacheHeader *CacheHdr = NULL;
+/* Clock for the last accessed time of a catcache entry. */
+TimestampTz catcacheclock = 0;
+
static inline HeapTuple SearchCatCacheInternal(CatCache *cache,
int nkeys,
Datum v1, Datum v2,
@@ -74,6 +84,7 @@ static pg_noinline HeapTuple SearchCatCacheMiss(CatCache *cache,
Index hashIndex,
Datum v1, Datum v2,
Datum v3, Datum v4);
+static bool CatCacheCleanupOldEntries(CatCache *cp);
static uint32 CatalogCacheComputeHashValue(CatCache *cache, int nkeys,
Datum v1, Datum v2, Datum v3, Datum v4);
@@ -99,6 +110,12 @@ static void CatCacheFreeKeys(TupleDesc tupdesc, int nkeys, int *attnos,
static void CatCacheCopyKeys(TupleDesc tupdesc, int nkeys, int *attnos,
Datum *srckeys, Datum *dstkeys);
+/* GUC assign function */
+void
+assign_catalog_cache_prune_min_age(int newval, void *extra)
+{
+ catalog_cache_prune_min_age = newval;
+}
/*
* internal support functions
@@ -863,6 +880,10 @@ RehashCatCache(CatCache *cp)
int newnbuckets;
int i;
+ /* try removing old entries before expanding hash */
+ if (CatCacheCleanupOldEntries(cp))
+ return;
+
elog(DEBUG1, "rehashing catalog cache id %d for %s; %d tups, %d buckets",
cp->id, cp->cc_relname, cp->cc_ntup, cp->cc_nbuckets);
@@ -1264,6 +1285,16 @@ SearchCatCacheInternal(CatCache *cache,
*/
dlist_move_head(bucket, &ct->cache_elem);
+ /*
+ * Prolong life of this entry. Since we want run as less instructions
+ * as possible and want the branch be stable for performance reasons,
+ * we don't care of wrap-around and possible false-negative for old
+ * entries. The window is quite narrow and the counter doesn't gets so
+ * large while expiration is active.
+ */
+ ct->naccess++;
+ ct->lastaccess = catcacheclock;
+
/*
* If it's a positive entry, bump its refcount and return it. If it's
* negative, we can report failure to the caller.
@@ -1425,6 +1456,91 @@ SearchCatCacheMiss(CatCache *cache,
return &ct->tuple;
}
+/*
+ * CatCacheCleanupOldEntries - Remove infrequently-used entries
+ *
+ * Catcache entries happen to be left unused for a long time for several
+ * reasons. Remove such entries to prevent catcache from bloating. It is based
+ * on the similar algorithm with buffer eviction. Entries that are accessed
+ * several times in a certain period live longer than those that have had less
+ * access in the same duration.
+ */
+static bool
+CatCacheCleanupOldEntries(CatCache *cp)
+{
+ int nremoved = 0;
+ int i;
+ long oldest_ts = catcacheclock;
+ long age;
+ int us;
+
+ /* Return immediately if disabled */
+ if (likely(catalog_cache_prune_min_age < 0))
+ return false;
+
+ /* Don't scan the hash when we know we don't have prunable entries */
+ TimestampDifference(cp->cc_oldest_ts, catcacheclock, &age, &us);
+ if (age < catalog_cache_prune_min_age)
+ return false;
+
+ /* Scan over the whole hash to find entries to remove */
+ for (i = 0 ; i < cp->cc_nbuckets ; i++)
+ {
+ dlist_mutable_iter iter;
+
+ dlist_foreach_modify(iter, &cp->cc_bucket[i])
+ {
+ CatCTup *ct = dlist_container(CatCTup, cache_elem, iter.cur);
+
+ /* Don't remove referenced entries */
+ if (ct->refcount == 0 &&
+ (ct->c_list == NULL || ct->c_list->refcount == 0))
+ {
+ /*
+ * Calculate the duration from the time from the last access
+ * to the "current" time. catcacheclock is updated
+ * per-statement basis and additionaly udpated periodically
+ * during a long running query.
+ */
+ TimestampDifference(ct->lastaccess, catcacheclock, &age, &us);
+
+ if (age > catalog_cache_prune_min_age)
+ {
+ /*
+ * Entries that are not accessed after the last pruning
+ * are removed in that seconds, and their lives are
+ * prolonged according to how many times they are accessed
+ * up to three times of the duration. We don't try shrink
+ * buckets since pruning effectively caps catcache
+ * expansion in the long term.
+ */
+ ct->naccess = Min(2, ct->naccess);
+ if (--ct->naccess == 0)
+ {
+ CatCacheRemoveCTup(cp, ct);
+ nremoved++;
+
+ /* don't update oldest_ts by removed entry */
+ continue;
+ }
+ }
+ }
+
+ /* update oldest timestamp if the entry remains alive */
+ if (ct->lastaccess < oldest_ts)
+ oldest_ts = ct->lastaccess;
+ }
+ }
+
+ cp->cc_oldest_ts = oldest_ts;
+
+ if (nremoved > 0)
+ elog(DEBUG1, "pruning catalog cache id=%d for %s: removed %d / %d",
+ cp->id, cp->cc_relname, nremoved, cp->cc_ntup + nremoved);
+
+ return nremoved > 0;
+}
+
/*
* ReleaseCatCache
*
@@ -1888,6 +2004,8 @@ CatalogCacheCreateEntry(CatCache *cache, HeapTuple ntp, Datum *arguments,
ct->dead = false;
ct->negative = negative;
ct->hash_value = hashValue;
+ ct->naccess = 1;
+ ct->lastaccess = catcacheclock;
dlist_push_head(&cache->cc_bucket[hashIndex], &ct->cache_elem);
diff --git a/src/backend/utils/misc/guc.c b/src/backend/utils/misc/guc.c
index bb34630e8e..95213853aa 100644
--- a/src/backend/utils/misc/guc.c
+++ b/src/backend/utils/misc/guc.c
@@ -88,6 +88,7 @@
#include "utils/acl.h"
#include "utils/builtins.h"
#include "utils/bytea.h"
+#include "utils/catcache.h"
#include "utils/float.h"
#include "utils/guc_tables.h"
#include "utils/memutils.h"
@@ -3399,6 +3400,17 @@ static struct config_int ConfigureNamesInt[] =
check_huge_page_size, NULL, NULL
},
+ {
+ {"catalog_cache_prune_min_age", PGC_USERSET, RESOURCES_MEM,
+ gettext_noop("System catalog cache entries that are living unused more than this seconds are considered for removal."),
+ gettext_noop("The value of -1 turns off pruning."),
+ GUC_UNIT_S
+ },
+ &catalog_cache_prune_min_age,
+ -1, -1, INT_MAX,
+ NULL, assign_catalog_cache_prune_min_age, NULL
+ },
+
/* End-of-list marker */
{
{NULL, 0, 0, NULL, NULL}, NULL, 0, 0, 0, NULL, NULL, NULL
diff --git a/src/include/utils/catcache.h b/src/include/utils/catcache.h
index f4aa316604..a11736f767 100644
--- a/src/include/utils/catcache.h
+++ b/src/include/utils/catcache.h
@@ -22,6 +22,7 @@
#include "access/htup.h"
#include "access/skey.h"
+#include "datatype/timestamp.h"
#include "lib/ilist.h"
#include "utils/relcache.h"
@@ -61,6 +62,7 @@ typedef struct catcache
slist_node cc_next; /* list link */
ScanKeyData cc_skey[CATCACHE_MAXKEYS]; /* precomputed key info for heap
* scans */
+ TimestampTz cc_oldest_ts; /* timestamp of the oldest tuple in the hash */
/*
* Keep these at the end, so that compiling catcache.c with CATCACHE_STATS
@@ -119,6 +121,8 @@ typedef struct catctup
bool dead; /* dead but not yet removed? */
bool negative; /* negative cache entry? */
HeapTupleData tuple; /* tuple management header */
+ unsigned int naccess; /* # of access to this entry */
+ TimestampTz lastaccess; /* timestamp of the last usage */
/*
* The tuple may also be a member of at most one CatCList. (If a single
@@ -189,6 +193,22 @@ typedef struct catcacheheader
/* this extern duplicates utils/memutils.h... */
extern PGDLLIMPORT MemoryContext CacheMemoryContext;
+
+/* for guc.c, not PGDLLPMPORT'ed */
+extern int catalog_cache_prune_min_age;
+
+/* source clock for access timestamp of catcache entries */
+extern TimestampTz catcacheclock;
+
+/* SetCatCacheClock - set catcache timestamp source clodk */
+static inline void
+SetCatCacheClock(TimestampTz ts)
+{
+ catcacheclock = ts;
+}
+
+extern void assign_catalog_cache_prune_min_age(int newval, void *extra);
+
extern void CreateCacheMemoryContext(void);
extern CatCache *InitCatCache(int id, Oid reloid, Oid indexoid,
--
2.18.4
----Next_Part(Mon_Nov__9_18_34_47_2020_166)----
^ permalink raw reply [nested|flat] 92+ messages in thread
* [PATCH v7 1/3] CatCache expiration feature
@ 2020-11-18 07:54 Kyotaro Horiguchi <[email protected]>
0 siblings, 0 replies; 92+ messages in thread
From: Kyotaro Horiguchi @ 2020-11-18 07:54 UTC (permalink / raw)
---
src/backend/access/transam/xact.c | 3 ++
src/backend/utils/cache/catcache.c | 87 +++++++++++++++++++++++++++++-
src/backend/utils/misc/guc.c | 12 +++++
src/include/utils/catcache.h | 19 +++++++
4 files changed, 120 insertions(+), 1 deletion(-)
diff --git a/src/backend/access/transam/xact.c b/src/backend/access/transam/xact.c
index a2068e3fd4..86888d2409 100644
--- a/src/backend/access/transam/xact.c
+++ b/src/backend/access/transam/xact.c
@@ -1086,6 +1086,9 @@ static void
AtStart_Cache(void)
{
AcceptInvalidationMessages();
+
+ if (xactStartTimestamp != 0)
+ SetCatCacheClock(xactStartTimestamp);
}
/*
diff --git a/src/backend/utils/cache/catcache.c b/src/backend/utils/cache/catcache.c
index fa2b49c676..644d92dd9a 100644
--- a/src/backend/utils/cache/catcache.c
+++ b/src/backend/utils/cache/catcache.c
@@ -38,6 +38,7 @@
#include "utils/rel.h"
#include "utils/resowner_private.h"
#include "utils/syscache.h"
+#include "utils/timestamp.h"
/* #define CACHEDEBUG */ /* turns DEBUG elogs on */
@@ -60,9 +61,19 @@
#define CACHE_elog(...)
#endif
+/*
+ * GUC variable to define the minimum age of entries that will be considered
+ * to be evicted in seconds. -1 to disable the feature.
+ */
+int catalog_cache_prune_min_age = -1;
+uint64 prune_min_age_us;
+
/* Cache management header --- pointer is NULL until created */
static CatCacheHeader *CacheHdr = NULL;
+/* Clock for the last accessed time of a catcache entry. */
+uint64 catcacheclock = 0;
+
static inline HeapTuple SearchCatCacheInternal(CatCache *cache,
int nkeys,
Datum v1, Datum v2,
@@ -74,6 +85,7 @@ static pg_noinline HeapTuple SearchCatCacheMiss(CatCache *cache,
Index hashIndex,
Datum v1, Datum v2,
Datum v3, Datum v4);
+static bool CatCacheCleanupOldEntries(CatCache *cp);
static uint32 CatalogCacheComputeHashValue(CatCache *cache, int nkeys,
Datum v1, Datum v2, Datum v3, Datum v4);
@@ -99,6 +111,15 @@ static void CatCacheFreeKeys(TupleDesc tupdesc, int nkeys, int *attnos,
static void CatCacheCopyKeys(TupleDesc tupdesc, int nkeys, int *attnos,
Datum *srckeys, Datum *dstkeys);
+/* GUC assign function */
+void
+assign_catalog_cache_prune_min_age(int newval, void *extra)
+{
+ if (newval < 0)
+ prune_min_age_us = UINT64_MAX;
+ else
+ prune_min_age_us = ((uint64) newval) * USECS_PER_SEC;
+}
/*
* internal support functions
@@ -1264,6 +1285,9 @@ SearchCatCacheInternal(CatCache *cache,
*/
dlist_move_head(bucket, &ct->cache_elem);
+ /* Record the last access timestamp */
+ ct->lastaccess = catcacheclock;
+
/*
* If it's a positive entry, bump its refcount and return it. If it's
* negative, we can report failure to the caller.
@@ -1425,6 +1449,61 @@ SearchCatCacheMiss(CatCache *cache,
return &ct->tuple;
}
+/*
+ * CatCacheCleanupOldEntries - Remove infrequently-used entries
+ *
+ * Catcache entries happen to be left unused for a long time for several
+ * reasons. Remove such entries to prevent catcache from bloating. It is based
+ * on the similar algorithm with buffer eviction. Entries that are accessed
+ * several times in a certain period live longer than those that have had less
+ * access in the same duration.
+ */
+static bool
+CatCacheCleanupOldEntries(CatCache *cp)
+{
+ int nremoved = 0;
+ int i;
+ long oldest_ts = catcacheclock;
+ uint64 prune_threshold = catcacheclock - prune_min_age_us;
+
+ /* Scan over the whole hash to find entries to remove */
+ for (i = 0 ; i < cp->cc_nbuckets ; i++)
+ {
+ dlist_mutable_iter iter;
+
+ dlist_foreach_modify(iter, &cp->cc_bucket[i])
+ {
+ CatCTup *ct = dlist_container(CatCTup, cache_elem, iter.cur);
+
+ /* Don't remove referenced entries */
+ if (ct->refcount == 0 &&
+ (ct->c_list == NULL || ct->c_list->refcount == 0))
+ {
+ if (ct->lastaccess < prune_threshold)
+ {
+ CatCacheRemoveCTup(cp, ct);
+ nremoved++;
+
+ /* don't let the removed entry update oldest_ts */
+ continue;
+ }
+ }
+
+ /* update the oldest timestamp if the entry remains alive */
+ if (ct->lastaccess < oldest_ts)
+ oldest_ts = ct->lastaccess;
+ }
+ }
+
+ cp->cc_oldest_ts = oldest_ts;
+
+ if (nremoved > 0)
+ elog(DEBUG1, "pruning catalog cache id=%d for %s: removed %d / %d",
+ cp->id, cp->cc_relname, nremoved, cp->cc_ntup + nremoved);
+
+ return nremoved > 0;
+}
+
/*
* ReleaseCatCache
*
@@ -1888,6 +1967,7 @@ CatalogCacheCreateEntry(CatCache *cache, HeapTuple ntp, Datum *arguments,
ct->dead = false;
ct->negative = negative;
ct->hash_value = hashValue;
+ ct->lastaccess = catcacheclock;
dlist_push_head(&cache->cc_bucket[hashIndex], &ct->cache_elem);
@@ -1899,7 +1979,12 @@ CatalogCacheCreateEntry(CatCache *cache, HeapTuple ntp, Datum *arguments,
* arbitrarily, we enlarge when fill factor > 2.
*/
if (cache->cc_ntup > cache->cc_nbuckets * 2)
- RehashCatCache(cache);
+ {
+ /* try removing old entries before expanding hash */
+ if (catcacheclock - cache->cc_oldest_ts < prune_min_age_us ||
+ !CatCacheCleanupOldEntries(cache))
+ RehashCatCache(cache);
+ }
return ct;
}
diff --git a/src/backend/utils/misc/guc.c b/src/backend/utils/misc/guc.c
index 17579eeaca..255e9fa73d 100644
--- a/src/backend/utils/misc/guc.c
+++ b/src/backend/utils/misc/guc.c
@@ -88,6 +88,7 @@
#include "utils/acl.h"
#include "utils/builtins.h"
#include "utils/bytea.h"
+#include "utils/catcache.h"
#include "utils/float.h"
#include "utils/guc_tables.h"
#include "utils/memutils.h"
@@ -3445,6 +3446,17 @@ static struct config_int ConfigureNamesInt[] =
NULL, NULL, NULL
},
+ {
+ {"catalog_cache_prune_min_age", PGC_USERSET, RESOURCES_MEM,
+ gettext_noop("System catalog cache entries that are living unused more than this seconds are considered for removal."),
+ gettext_noop("The value of -1 turns off pruning."),
+ GUC_UNIT_S
+ },
+ &catalog_cache_prune_min_age,
+ -1, -1, INT_MAX,
+ NULL, assign_catalog_cache_prune_min_age, NULL
+ },
+
/* End-of-list marker */
{
{NULL, 0, 0, NULL, NULL}, NULL, 0, 0, 0, NULL, NULL, NULL
diff --git a/src/include/utils/catcache.h b/src/include/utils/catcache.h
index ddc2762eb3..291e857e38 100644
--- a/src/include/utils/catcache.h
+++ b/src/include/utils/catcache.h
@@ -22,6 +22,7 @@
#include "access/htup.h"
#include "access/skey.h"
+#include "datatype/timestamp.h"
#include "lib/ilist.h"
#include "utils/relcache.h"
@@ -61,6 +62,7 @@ typedef struct catcache
slist_node cc_next; /* list link */
ScanKeyData cc_skey[CATCACHE_MAXKEYS]; /* precomputed key info for heap
* scans */
+ uint64 cc_oldest_ts; /* timestamp (us) of the oldest tuple */
/*
* Keep these at the end, so that compiling catcache.c with CATCACHE_STATS
@@ -119,6 +121,7 @@ typedef struct catctup
bool dead; /* dead but not yet removed? */
bool negative; /* negative cache entry? */
HeapTupleData tuple; /* tuple management header */
+ uint64 lastaccess; /* timestamp in us of the last usage */
/*
* The tuple may also be a member of at most one CatCList. (If a single
@@ -189,6 +192,22 @@ typedef struct catcacheheader
/* this extern duplicates utils/memutils.h... */
extern PGDLLIMPORT MemoryContext CacheMemoryContext;
+
+/* for guc.c, not PGDLLPMPORT'ed */
+extern int catalog_cache_prune_min_age;
+
+/* source clock for access timestamp of catcache entries */
+extern uint64 catcacheclock;
+
+/* SetCatCacheClock - set catcache timestamp source clock */
+static inline void
+SetCatCacheClock(TimestampTz ts)
+{
+ catcacheclock = (uint64) ts;
+}
+
+extern void assign_catalog_cache_prune_min_age(int newval, void *extra);
+
extern void CreateCacheMemoryContext(void);
extern CatCache *InitCatCache(int id, Oid reloid, Oid indexoid,
--
2.27.0
----Next_Part(Wed_Jan_27_10_13_08_2021_482)--
Content-Type: Text/X-Patch; charset=us-ascii
Content-Transfer-Encoding: 7bit
Content-Disposition: inline;
filename="v7-0002-Remove-dead-flag-from-catcache-tuple.patch"
^ permalink raw reply [nested|flat] 92+ messages in thread
* [PATCH v6 1/3] CatCache expiration feature
@ 2020-11-18 07:54 Kyotaro Horiguchi <[email protected]>
0 siblings, 0 replies; 92+ messages in thread
From: Kyotaro Horiguchi @ 2020-11-18 07:54 UTC (permalink / raw)
---
src/backend/access/transam/xact.c | 3 ++
src/backend/utils/cache/catcache.c | 87 +++++++++++++++++++++++++++++-
src/backend/utils/misc/guc.c | 12 +++++
src/include/utils/catcache.h | 19 +++++++
4 files changed, 120 insertions(+), 1 deletion(-)
diff --git a/src/backend/access/transam/xact.c b/src/backend/access/transam/xact.c
index a2068e3fd4..86888d2409 100644
--- a/src/backend/access/transam/xact.c
+++ b/src/backend/access/transam/xact.c
@@ -1086,6 +1086,9 @@ static void
AtStart_Cache(void)
{
AcceptInvalidationMessages();
+
+ if (xactStartTimestamp != 0)
+ SetCatCacheClock(xactStartTimestamp);
}
/*
diff --git a/src/backend/utils/cache/catcache.c b/src/backend/utils/cache/catcache.c
index fa2b49c676..644d92dd9a 100644
--- a/src/backend/utils/cache/catcache.c
+++ b/src/backend/utils/cache/catcache.c
@@ -38,6 +38,7 @@
#include "utils/rel.h"
#include "utils/resowner_private.h"
#include "utils/syscache.h"
+#include "utils/timestamp.h"
/* #define CACHEDEBUG */ /* turns DEBUG elogs on */
@@ -60,9 +61,19 @@
#define CACHE_elog(...)
#endif
+/*
+ * GUC variable to define the minimum age of entries that will be considered
+ * to be evicted in seconds. -1 to disable the feature.
+ */
+int catalog_cache_prune_min_age = -1;
+uint64 prune_min_age_us;
+
/* Cache management header --- pointer is NULL until created */
static CatCacheHeader *CacheHdr = NULL;
+/* Clock for the last accessed time of a catcache entry. */
+uint64 catcacheclock = 0;
+
static inline HeapTuple SearchCatCacheInternal(CatCache *cache,
int nkeys,
Datum v1, Datum v2,
@@ -74,6 +85,7 @@ static pg_noinline HeapTuple SearchCatCacheMiss(CatCache *cache,
Index hashIndex,
Datum v1, Datum v2,
Datum v3, Datum v4);
+static bool CatCacheCleanupOldEntries(CatCache *cp);
static uint32 CatalogCacheComputeHashValue(CatCache *cache, int nkeys,
Datum v1, Datum v2, Datum v3, Datum v4);
@@ -99,6 +111,15 @@ static void CatCacheFreeKeys(TupleDesc tupdesc, int nkeys, int *attnos,
static void CatCacheCopyKeys(TupleDesc tupdesc, int nkeys, int *attnos,
Datum *srckeys, Datum *dstkeys);
+/* GUC assign function */
+void
+assign_catalog_cache_prune_min_age(int newval, void *extra)
+{
+ if (newval < 0)
+ prune_min_age_us = UINT64_MAX;
+ else
+ prune_min_age_us = ((uint64) newval) * USECS_PER_SEC;
+}
/*
* internal support functions
@@ -1264,6 +1285,9 @@ SearchCatCacheInternal(CatCache *cache,
*/
dlist_move_head(bucket, &ct->cache_elem);
+ /* Record the last access timestamp */
+ ct->lastaccess = catcacheclock;
+
/*
* If it's a positive entry, bump its refcount and return it. If it's
* negative, we can report failure to the caller.
@@ -1425,6 +1449,61 @@ SearchCatCacheMiss(CatCache *cache,
return &ct->tuple;
}
+/*
+ * CatCacheCleanupOldEntries - Remove infrequently-used entries
+ *
+ * Catcache entries happen to be left unused for a long time for several
+ * reasons. Remove such entries to prevent catcache from bloating. It is based
+ * on the similar algorithm with buffer eviction. Entries that are accessed
+ * several times in a certain period live longer than those that have had less
+ * access in the same duration.
+ */
+static bool
+CatCacheCleanupOldEntries(CatCache *cp)
+{
+ int nremoved = 0;
+ int i;
+ long oldest_ts = catcacheclock;
+ uint64 prune_threshold = catcacheclock - prune_min_age_us;
+
+ /* Scan over the whole hash to find entries to remove */
+ for (i = 0 ; i < cp->cc_nbuckets ; i++)
+ {
+ dlist_mutable_iter iter;
+
+ dlist_foreach_modify(iter, &cp->cc_bucket[i])
+ {
+ CatCTup *ct = dlist_container(CatCTup, cache_elem, iter.cur);
+
+ /* Don't remove referenced entries */
+ if (ct->refcount == 0 &&
+ (ct->c_list == NULL || ct->c_list->refcount == 0))
+ {
+ if (ct->lastaccess < prune_threshold)
+ {
+ CatCacheRemoveCTup(cp, ct);
+ nremoved++;
+
+ /* don't let the removed entry update oldest_ts */
+ continue;
+ }
+ }
+
+ /* update the oldest timestamp if the entry remains alive */
+ if (ct->lastaccess < oldest_ts)
+ oldest_ts = ct->lastaccess;
+ }
+ }
+
+ cp->cc_oldest_ts = oldest_ts;
+
+ if (nremoved > 0)
+ elog(DEBUG1, "pruning catalog cache id=%d for %s: removed %d / %d",
+ cp->id, cp->cc_relname, nremoved, cp->cc_ntup + nremoved);
+
+ return nremoved > 0;
+}
+
/*
* ReleaseCatCache
*
@@ -1888,6 +1967,7 @@ CatalogCacheCreateEntry(CatCache *cache, HeapTuple ntp, Datum *arguments,
ct->dead = false;
ct->negative = negative;
ct->hash_value = hashValue;
+ ct->lastaccess = catcacheclock;
dlist_push_head(&cache->cc_bucket[hashIndex], &ct->cache_elem);
@@ -1899,7 +1979,12 @@ CatalogCacheCreateEntry(CatCache *cache, HeapTuple ntp, Datum *arguments,
* arbitrarily, we enlarge when fill factor > 2.
*/
if (cache->cc_ntup > cache->cc_nbuckets * 2)
- RehashCatCache(cache);
+ {
+ /* try removing old entries before expanding hash */
+ if (catcacheclock - cache->cc_oldest_ts < prune_min_age_us ||
+ !CatCacheCleanupOldEntries(cache))
+ RehashCatCache(cache);
+ }
return ct;
}
diff --git a/src/backend/utils/misc/guc.c b/src/backend/utils/misc/guc.c
index 17579eeaca..255e9fa73d 100644
--- a/src/backend/utils/misc/guc.c
+++ b/src/backend/utils/misc/guc.c
@@ -88,6 +88,7 @@
#include "utils/acl.h"
#include "utils/builtins.h"
#include "utils/bytea.h"
+#include "utils/catcache.h"
#include "utils/float.h"
#include "utils/guc_tables.h"
#include "utils/memutils.h"
@@ -3445,6 +3446,17 @@ static struct config_int ConfigureNamesInt[] =
NULL, NULL, NULL
},
+ {
+ {"catalog_cache_prune_min_age", PGC_USERSET, RESOURCES_MEM,
+ gettext_noop("System catalog cache entries that are living unused more than this seconds are considered for removal."),
+ gettext_noop("The value of -1 turns off pruning."),
+ GUC_UNIT_S
+ },
+ &catalog_cache_prune_min_age,
+ -1, -1, INT_MAX,
+ NULL, assign_catalog_cache_prune_min_age, NULL
+ },
+
/* End-of-list marker */
{
{NULL, 0, 0, NULL, NULL}, NULL, 0, 0, 0, NULL, NULL, NULL
diff --git a/src/include/utils/catcache.h b/src/include/utils/catcache.h
index ddc2762eb3..291e857e38 100644
--- a/src/include/utils/catcache.h
+++ b/src/include/utils/catcache.h
@@ -22,6 +22,7 @@
#include "access/htup.h"
#include "access/skey.h"
+#include "datatype/timestamp.h"
#include "lib/ilist.h"
#include "utils/relcache.h"
@@ -61,6 +62,7 @@ typedef struct catcache
slist_node cc_next; /* list link */
ScanKeyData cc_skey[CATCACHE_MAXKEYS]; /* precomputed key info for heap
* scans */
+ uint64 cc_oldest_ts; /* timestamp (us) of the oldest tuple */
/*
* Keep these at the end, so that compiling catcache.c with CATCACHE_STATS
@@ -119,6 +121,7 @@ typedef struct catctup
bool dead; /* dead but not yet removed? */
bool negative; /* negative cache entry? */
HeapTupleData tuple; /* tuple management header */
+ uint64 lastaccess; /* timestamp in us of the last usage */
/*
* The tuple may also be a member of at most one CatCList. (If a single
@@ -189,6 +192,22 @@ typedef struct catcacheheader
/* this extern duplicates utils/memutils.h... */
extern PGDLLIMPORT MemoryContext CacheMemoryContext;
+
+/* for guc.c, not PGDLLPMPORT'ed */
+extern int catalog_cache_prune_min_age;
+
+/* source clock for access timestamp of catcache entries */
+extern uint64 catcacheclock;
+
+/* SetCatCacheClock - set catcache timestamp source clock */
+static inline void
+SetCatCacheClock(TimestampTz ts)
+{
+ catcacheclock = (uint64) ts;
+}
+
+extern void assign_catalog_cache_prune_min_age(int newval, void *extra);
+
extern void CreateCacheMemoryContext(void);
extern CatCache *InitCatCache(int id, Oid reloid, Oid indexoid,
--
2.27.0
----Next_Part(Thu_Jan_14_17_32_27_2021_995)--
Content-Type: Text/X-Patch; charset=us-ascii
Content-Transfer-Encoding: 7bit
Content-Disposition: inline;
filename="v6-0002-Remove-dead-flag-from-catcache-tuple.patch"
^ permalink raw reply [nested|flat] 92+ messages in thread
* [PATCH v7 1/3] CatCache expiration feature
@ 2020-11-18 07:54 Kyotaro Horiguchi <[email protected]>
0 siblings, 0 replies; 92+ messages in thread
From: Kyotaro Horiguchi @ 2020-11-18 07:54 UTC (permalink / raw)
---
src/backend/access/transam/xact.c | 3 ++
src/backend/utils/cache/catcache.c | 87 +++++++++++++++++++++++++++++-
src/backend/utils/misc/guc.c | 12 +++++
src/include/utils/catcache.h | 19 +++++++
4 files changed, 120 insertions(+), 1 deletion(-)
diff --git a/src/backend/access/transam/xact.c b/src/backend/access/transam/xact.c
index a2068e3fd4..86888d2409 100644
--- a/src/backend/access/transam/xact.c
+++ b/src/backend/access/transam/xact.c
@@ -1086,6 +1086,9 @@ static void
AtStart_Cache(void)
{
AcceptInvalidationMessages();
+
+ if (xactStartTimestamp != 0)
+ SetCatCacheClock(xactStartTimestamp);
}
/*
diff --git a/src/backend/utils/cache/catcache.c b/src/backend/utils/cache/catcache.c
index fa2b49c676..644d92dd9a 100644
--- a/src/backend/utils/cache/catcache.c
+++ b/src/backend/utils/cache/catcache.c
@@ -38,6 +38,7 @@
#include "utils/rel.h"
#include "utils/resowner_private.h"
#include "utils/syscache.h"
+#include "utils/timestamp.h"
/* #define CACHEDEBUG */ /* turns DEBUG elogs on */
@@ -60,9 +61,19 @@
#define CACHE_elog(...)
#endif
+/*
+ * GUC variable to define the minimum age of entries that will be considered
+ * to be evicted in seconds. -1 to disable the feature.
+ */
+int catalog_cache_prune_min_age = -1;
+uint64 prune_min_age_us;
+
/* Cache management header --- pointer is NULL until created */
static CatCacheHeader *CacheHdr = NULL;
+/* Clock for the last accessed time of a catcache entry. */
+uint64 catcacheclock = 0;
+
static inline HeapTuple SearchCatCacheInternal(CatCache *cache,
int nkeys,
Datum v1, Datum v2,
@@ -74,6 +85,7 @@ static pg_noinline HeapTuple SearchCatCacheMiss(CatCache *cache,
Index hashIndex,
Datum v1, Datum v2,
Datum v3, Datum v4);
+static bool CatCacheCleanupOldEntries(CatCache *cp);
static uint32 CatalogCacheComputeHashValue(CatCache *cache, int nkeys,
Datum v1, Datum v2, Datum v3, Datum v4);
@@ -99,6 +111,15 @@ static void CatCacheFreeKeys(TupleDesc tupdesc, int nkeys, int *attnos,
static void CatCacheCopyKeys(TupleDesc tupdesc, int nkeys, int *attnos,
Datum *srckeys, Datum *dstkeys);
+/* GUC assign function */
+void
+assign_catalog_cache_prune_min_age(int newval, void *extra)
+{
+ if (newval < 0)
+ prune_min_age_us = UINT64_MAX;
+ else
+ prune_min_age_us = ((uint64) newval) * USECS_PER_SEC;
+}
/*
* internal support functions
@@ -1264,6 +1285,9 @@ SearchCatCacheInternal(CatCache *cache,
*/
dlist_move_head(bucket, &ct->cache_elem);
+ /* Record the last access timestamp */
+ ct->lastaccess = catcacheclock;
+
/*
* If it's a positive entry, bump its refcount and return it. If it's
* negative, we can report failure to the caller.
@@ -1425,6 +1449,61 @@ SearchCatCacheMiss(CatCache *cache,
return &ct->tuple;
}
+/*
+ * CatCacheCleanupOldEntries - Remove infrequently-used entries
+ *
+ * Catcache entries happen to be left unused for a long time for several
+ * reasons. Remove such entries to prevent catcache from bloating. It is based
+ * on the similar algorithm with buffer eviction. Entries that are accessed
+ * several times in a certain period live longer than those that have had less
+ * access in the same duration.
+ */
+static bool
+CatCacheCleanupOldEntries(CatCache *cp)
+{
+ int nremoved = 0;
+ int i;
+ long oldest_ts = catcacheclock;
+ uint64 prune_threshold = catcacheclock - prune_min_age_us;
+
+ /* Scan over the whole hash to find entries to remove */
+ for (i = 0 ; i < cp->cc_nbuckets ; i++)
+ {
+ dlist_mutable_iter iter;
+
+ dlist_foreach_modify(iter, &cp->cc_bucket[i])
+ {
+ CatCTup *ct = dlist_container(CatCTup, cache_elem, iter.cur);
+
+ /* Don't remove referenced entries */
+ if (ct->refcount == 0 &&
+ (ct->c_list == NULL || ct->c_list->refcount == 0))
+ {
+ if (ct->lastaccess < prune_threshold)
+ {
+ CatCacheRemoveCTup(cp, ct);
+ nremoved++;
+
+ /* don't let the removed entry update oldest_ts */
+ continue;
+ }
+ }
+
+ /* update the oldest timestamp if the entry remains alive */
+ if (ct->lastaccess < oldest_ts)
+ oldest_ts = ct->lastaccess;
+ }
+ }
+
+ cp->cc_oldest_ts = oldest_ts;
+
+ if (nremoved > 0)
+ elog(DEBUG1, "pruning catalog cache id=%d for %s: removed %d / %d",
+ cp->id, cp->cc_relname, nremoved, cp->cc_ntup + nremoved);
+
+ return nremoved > 0;
+}
+
/*
* ReleaseCatCache
*
@@ -1888,6 +1967,7 @@ CatalogCacheCreateEntry(CatCache *cache, HeapTuple ntp, Datum *arguments,
ct->dead = false;
ct->negative = negative;
ct->hash_value = hashValue;
+ ct->lastaccess = catcacheclock;
dlist_push_head(&cache->cc_bucket[hashIndex], &ct->cache_elem);
@@ -1899,7 +1979,12 @@ CatalogCacheCreateEntry(CatCache *cache, HeapTuple ntp, Datum *arguments,
* arbitrarily, we enlarge when fill factor > 2.
*/
if (cache->cc_ntup > cache->cc_nbuckets * 2)
- RehashCatCache(cache);
+ {
+ /* try removing old entries before expanding hash */
+ if (catcacheclock - cache->cc_oldest_ts < prune_min_age_us ||
+ !CatCacheCleanupOldEntries(cache))
+ RehashCatCache(cache);
+ }
return ct;
}
diff --git a/src/backend/utils/misc/guc.c b/src/backend/utils/misc/guc.c
index 17579eeaca..255e9fa73d 100644
--- a/src/backend/utils/misc/guc.c
+++ b/src/backend/utils/misc/guc.c
@@ -88,6 +88,7 @@
#include "utils/acl.h"
#include "utils/builtins.h"
#include "utils/bytea.h"
+#include "utils/catcache.h"
#include "utils/float.h"
#include "utils/guc_tables.h"
#include "utils/memutils.h"
@@ -3445,6 +3446,17 @@ static struct config_int ConfigureNamesInt[] =
NULL, NULL, NULL
},
+ {
+ {"catalog_cache_prune_min_age", PGC_USERSET, RESOURCES_MEM,
+ gettext_noop("System catalog cache entries that are living unused more than this seconds are considered for removal."),
+ gettext_noop("The value of -1 turns off pruning."),
+ GUC_UNIT_S
+ },
+ &catalog_cache_prune_min_age,
+ -1, -1, INT_MAX,
+ NULL, assign_catalog_cache_prune_min_age, NULL
+ },
+
/* End-of-list marker */
{
{NULL, 0, 0, NULL, NULL}, NULL, 0, 0, 0, NULL, NULL, NULL
diff --git a/src/include/utils/catcache.h b/src/include/utils/catcache.h
index ddc2762eb3..291e857e38 100644
--- a/src/include/utils/catcache.h
+++ b/src/include/utils/catcache.h
@@ -22,6 +22,7 @@
#include "access/htup.h"
#include "access/skey.h"
+#include "datatype/timestamp.h"
#include "lib/ilist.h"
#include "utils/relcache.h"
@@ -61,6 +62,7 @@ typedef struct catcache
slist_node cc_next; /* list link */
ScanKeyData cc_skey[CATCACHE_MAXKEYS]; /* precomputed key info for heap
* scans */
+ uint64 cc_oldest_ts; /* timestamp (us) of the oldest tuple */
/*
* Keep these at the end, so that compiling catcache.c with CATCACHE_STATS
@@ -119,6 +121,7 @@ typedef struct catctup
bool dead; /* dead but not yet removed? */
bool negative; /* negative cache entry? */
HeapTupleData tuple; /* tuple management header */
+ uint64 lastaccess; /* timestamp in us of the last usage */
/*
* The tuple may also be a member of at most one CatCList. (If a single
@@ -189,6 +192,22 @@ typedef struct catcacheheader
/* this extern duplicates utils/memutils.h... */
extern PGDLLIMPORT MemoryContext CacheMemoryContext;
+
+/* for guc.c, not PGDLLPMPORT'ed */
+extern int catalog_cache_prune_min_age;
+
+/* source clock for access timestamp of catcache entries */
+extern uint64 catcacheclock;
+
+/* SetCatCacheClock - set catcache timestamp source clock */
+static inline void
+SetCatCacheClock(TimestampTz ts)
+{
+ catcacheclock = (uint64) ts;
+}
+
+extern void assign_catalog_cache_prune_min_age(int newval, void *extra);
+
extern void CreateCacheMemoryContext(void);
extern CatCache *InitCatCache(int id, Oid reloid, Oid indexoid,
--
2.27.0
----Next_Part(Wed_Jan_27_10_13_08_2021_482)--
Content-Type: Text/X-Patch; charset=us-ascii
Content-Transfer-Encoding: 7bit
Content-Disposition: inline;
filename="v7-0002-Remove-dead-flag-from-catcache-tuple.patch"
^ permalink raw reply [nested|flat] 92+ messages in thread
* [PATCH v6 1/3] CatCache expiration feature
@ 2020-11-18 07:54 Kyotaro Horiguchi <[email protected]>
0 siblings, 0 replies; 92+ messages in thread
From: Kyotaro Horiguchi @ 2020-11-18 07:54 UTC (permalink / raw)
---
src/backend/access/transam/xact.c | 3 ++
src/backend/utils/cache/catcache.c | 87 +++++++++++++++++++++++++++++-
src/backend/utils/misc/guc.c | 12 +++++
src/include/utils/catcache.h | 19 +++++++
4 files changed, 120 insertions(+), 1 deletion(-)
diff --git a/src/backend/access/transam/xact.c b/src/backend/access/transam/xact.c
index a2068e3fd4..86888d2409 100644
--- a/src/backend/access/transam/xact.c
+++ b/src/backend/access/transam/xact.c
@@ -1086,6 +1086,9 @@ static void
AtStart_Cache(void)
{
AcceptInvalidationMessages();
+
+ if (xactStartTimestamp != 0)
+ SetCatCacheClock(xactStartTimestamp);
}
/*
diff --git a/src/backend/utils/cache/catcache.c b/src/backend/utils/cache/catcache.c
index fa2b49c676..644d92dd9a 100644
--- a/src/backend/utils/cache/catcache.c
+++ b/src/backend/utils/cache/catcache.c
@@ -38,6 +38,7 @@
#include "utils/rel.h"
#include "utils/resowner_private.h"
#include "utils/syscache.h"
+#include "utils/timestamp.h"
/* #define CACHEDEBUG */ /* turns DEBUG elogs on */
@@ -60,9 +61,19 @@
#define CACHE_elog(...)
#endif
+/*
+ * GUC variable to define the minimum age of entries that will be considered
+ * to be evicted in seconds. -1 to disable the feature.
+ */
+int catalog_cache_prune_min_age = -1;
+uint64 prune_min_age_us;
+
/* Cache management header --- pointer is NULL until created */
static CatCacheHeader *CacheHdr = NULL;
+/* Clock for the last accessed time of a catcache entry. */
+uint64 catcacheclock = 0;
+
static inline HeapTuple SearchCatCacheInternal(CatCache *cache,
int nkeys,
Datum v1, Datum v2,
@@ -74,6 +85,7 @@ static pg_noinline HeapTuple SearchCatCacheMiss(CatCache *cache,
Index hashIndex,
Datum v1, Datum v2,
Datum v3, Datum v4);
+static bool CatCacheCleanupOldEntries(CatCache *cp);
static uint32 CatalogCacheComputeHashValue(CatCache *cache, int nkeys,
Datum v1, Datum v2, Datum v3, Datum v4);
@@ -99,6 +111,15 @@ static void CatCacheFreeKeys(TupleDesc tupdesc, int nkeys, int *attnos,
static void CatCacheCopyKeys(TupleDesc tupdesc, int nkeys, int *attnos,
Datum *srckeys, Datum *dstkeys);
+/* GUC assign function */
+void
+assign_catalog_cache_prune_min_age(int newval, void *extra)
+{
+ if (newval < 0)
+ prune_min_age_us = UINT64_MAX;
+ else
+ prune_min_age_us = ((uint64) newval) * USECS_PER_SEC;
+}
/*
* internal support functions
@@ -1264,6 +1285,9 @@ SearchCatCacheInternal(CatCache *cache,
*/
dlist_move_head(bucket, &ct->cache_elem);
+ /* Record the last access timestamp */
+ ct->lastaccess = catcacheclock;
+
/*
* If it's a positive entry, bump its refcount and return it. If it's
* negative, we can report failure to the caller.
@@ -1425,6 +1449,61 @@ SearchCatCacheMiss(CatCache *cache,
return &ct->tuple;
}
+/*
+ * CatCacheCleanupOldEntries - Remove infrequently-used entries
+ *
+ * Catcache entries happen to be left unused for a long time for several
+ * reasons. Remove such entries to prevent catcache from bloating. It is based
+ * on the similar algorithm with buffer eviction. Entries that are accessed
+ * several times in a certain period live longer than those that have had less
+ * access in the same duration.
+ */
+static bool
+CatCacheCleanupOldEntries(CatCache *cp)
+{
+ int nremoved = 0;
+ int i;
+ long oldest_ts = catcacheclock;
+ uint64 prune_threshold = catcacheclock - prune_min_age_us;
+
+ /* Scan over the whole hash to find entries to remove */
+ for (i = 0 ; i < cp->cc_nbuckets ; i++)
+ {
+ dlist_mutable_iter iter;
+
+ dlist_foreach_modify(iter, &cp->cc_bucket[i])
+ {
+ CatCTup *ct = dlist_container(CatCTup, cache_elem, iter.cur);
+
+ /* Don't remove referenced entries */
+ if (ct->refcount == 0 &&
+ (ct->c_list == NULL || ct->c_list->refcount == 0))
+ {
+ if (ct->lastaccess < prune_threshold)
+ {
+ CatCacheRemoveCTup(cp, ct);
+ nremoved++;
+
+ /* don't let the removed entry update oldest_ts */
+ continue;
+ }
+ }
+
+ /* update the oldest timestamp if the entry remains alive */
+ if (ct->lastaccess < oldest_ts)
+ oldest_ts = ct->lastaccess;
+ }
+ }
+
+ cp->cc_oldest_ts = oldest_ts;
+
+ if (nremoved > 0)
+ elog(DEBUG1, "pruning catalog cache id=%d for %s: removed %d / %d",
+ cp->id, cp->cc_relname, nremoved, cp->cc_ntup + nremoved);
+
+ return nremoved > 0;
+}
+
/*
* ReleaseCatCache
*
@@ -1888,6 +1967,7 @@ CatalogCacheCreateEntry(CatCache *cache, HeapTuple ntp, Datum *arguments,
ct->dead = false;
ct->negative = negative;
ct->hash_value = hashValue;
+ ct->lastaccess = catcacheclock;
dlist_push_head(&cache->cc_bucket[hashIndex], &ct->cache_elem);
@@ -1899,7 +1979,12 @@ CatalogCacheCreateEntry(CatCache *cache, HeapTuple ntp, Datum *arguments,
* arbitrarily, we enlarge when fill factor > 2.
*/
if (cache->cc_ntup > cache->cc_nbuckets * 2)
- RehashCatCache(cache);
+ {
+ /* try removing old entries before expanding hash */
+ if (catcacheclock - cache->cc_oldest_ts < prune_min_age_us ||
+ !CatCacheCleanupOldEntries(cache))
+ RehashCatCache(cache);
+ }
return ct;
}
diff --git a/src/backend/utils/misc/guc.c b/src/backend/utils/misc/guc.c
index 17579eeaca..255e9fa73d 100644
--- a/src/backend/utils/misc/guc.c
+++ b/src/backend/utils/misc/guc.c
@@ -88,6 +88,7 @@
#include "utils/acl.h"
#include "utils/builtins.h"
#include "utils/bytea.h"
+#include "utils/catcache.h"
#include "utils/float.h"
#include "utils/guc_tables.h"
#include "utils/memutils.h"
@@ -3445,6 +3446,17 @@ static struct config_int ConfigureNamesInt[] =
NULL, NULL, NULL
},
+ {
+ {"catalog_cache_prune_min_age", PGC_USERSET, RESOURCES_MEM,
+ gettext_noop("System catalog cache entries that are living unused more than this seconds are considered for removal."),
+ gettext_noop("The value of -1 turns off pruning."),
+ GUC_UNIT_S
+ },
+ &catalog_cache_prune_min_age,
+ -1, -1, INT_MAX,
+ NULL, assign_catalog_cache_prune_min_age, NULL
+ },
+
/* End-of-list marker */
{
{NULL, 0, 0, NULL, NULL}, NULL, 0, 0, 0, NULL, NULL, NULL
diff --git a/src/include/utils/catcache.h b/src/include/utils/catcache.h
index ddc2762eb3..291e857e38 100644
--- a/src/include/utils/catcache.h
+++ b/src/include/utils/catcache.h
@@ -22,6 +22,7 @@
#include "access/htup.h"
#include "access/skey.h"
+#include "datatype/timestamp.h"
#include "lib/ilist.h"
#include "utils/relcache.h"
@@ -61,6 +62,7 @@ typedef struct catcache
slist_node cc_next; /* list link */
ScanKeyData cc_skey[CATCACHE_MAXKEYS]; /* precomputed key info for heap
* scans */
+ uint64 cc_oldest_ts; /* timestamp (us) of the oldest tuple */
/*
* Keep these at the end, so that compiling catcache.c with CATCACHE_STATS
@@ -119,6 +121,7 @@ typedef struct catctup
bool dead; /* dead but not yet removed? */
bool negative; /* negative cache entry? */
HeapTupleData tuple; /* tuple management header */
+ uint64 lastaccess; /* timestamp in us of the last usage */
/*
* The tuple may also be a member of at most one CatCList. (If a single
@@ -189,6 +192,22 @@ typedef struct catcacheheader
/* this extern duplicates utils/memutils.h... */
extern PGDLLIMPORT MemoryContext CacheMemoryContext;
+
+/* for guc.c, not PGDLLPMPORT'ed */
+extern int catalog_cache_prune_min_age;
+
+/* source clock for access timestamp of catcache entries */
+extern uint64 catcacheclock;
+
+/* SetCatCacheClock - set catcache timestamp source clock */
+static inline void
+SetCatCacheClock(TimestampTz ts)
+{
+ catcacheclock = (uint64) ts;
+}
+
+extern void assign_catalog_cache_prune_min_age(int newval, void *extra);
+
extern void CreateCacheMemoryContext(void);
extern CatCache *InitCatCache(int id, Oid reloid, Oid indexoid,
--
2.27.0
----Next_Part(Thu_Jan_14_17_32_27_2021_995)--
Content-Type: Text/X-Patch; charset=us-ascii
Content-Transfer-Encoding: 7bit
Content-Disposition: inline;
filename="v6-0002-Remove-dead-flag-from-catcache-tuple.patch"
^ permalink raw reply [nested|flat] 92+ messages in thread
* [PATCH v7 1/3] CatCache expiration feature
@ 2020-11-18 07:54 Kyotaro Horiguchi <[email protected]>
0 siblings, 0 replies; 92+ messages in thread
From: Kyotaro Horiguchi @ 2020-11-18 07:54 UTC (permalink / raw)
---
src/backend/access/transam/xact.c | 3 ++
src/backend/utils/cache/catcache.c | 87 +++++++++++++++++++++++++++++-
src/backend/utils/misc/guc.c | 12 +++++
src/include/utils/catcache.h | 19 +++++++
4 files changed, 120 insertions(+), 1 deletion(-)
diff --git a/src/backend/access/transam/xact.c b/src/backend/access/transam/xact.c
index a2068e3fd4..86888d2409 100644
--- a/src/backend/access/transam/xact.c
+++ b/src/backend/access/transam/xact.c
@@ -1086,6 +1086,9 @@ static void
AtStart_Cache(void)
{
AcceptInvalidationMessages();
+
+ if (xactStartTimestamp != 0)
+ SetCatCacheClock(xactStartTimestamp);
}
/*
diff --git a/src/backend/utils/cache/catcache.c b/src/backend/utils/cache/catcache.c
index fa2b49c676..644d92dd9a 100644
--- a/src/backend/utils/cache/catcache.c
+++ b/src/backend/utils/cache/catcache.c
@@ -38,6 +38,7 @@
#include "utils/rel.h"
#include "utils/resowner_private.h"
#include "utils/syscache.h"
+#include "utils/timestamp.h"
/* #define CACHEDEBUG */ /* turns DEBUG elogs on */
@@ -60,9 +61,19 @@
#define CACHE_elog(...)
#endif
+/*
+ * GUC variable to define the minimum age of entries that will be considered
+ * to be evicted in seconds. -1 to disable the feature.
+ */
+int catalog_cache_prune_min_age = -1;
+uint64 prune_min_age_us;
+
/* Cache management header --- pointer is NULL until created */
static CatCacheHeader *CacheHdr = NULL;
+/* Clock for the last accessed time of a catcache entry. */
+uint64 catcacheclock = 0;
+
static inline HeapTuple SearchCatCacheInternal(CatCache *cache,
int nkeys,
Datum v1, Datum v2,
@@ -74,6 +85,7 @@ static pg_noinline HeapTuple SearchCatCacheMiss(CatCache *cache,
Index hashIndex,
Datum v1, Datum v2,
Datum v3, Datum v4);
+static bool CatCacheCleanupOldEntries(CatCache *cp);
static uint32 CatalogCacheComputeHashValue(CatCache *cache, int nkeys,
Datum v1, Datum v2, Datum v3, Datum v4);
@@ -99,6 +111,15 @@ static void CatCacheFreeKeys(TupleDesc tupdesc, int nkeys, int *attnos,
static void CatCacheCopyKeys(TupleDesc tupdesc, int nkeys, int *attnos,
Datum *srckeys, Datum *dstkeys);
+/* GUC assign function */
+void
+assign_catalog_cache_prune_min_age(int newval, void *extra)
+{
+ if (newval < 0)
+ prune_min_age_us = UINT64_MAX;
+ else
+ prune_min_age_us = ((uint64) newval) * USECS_PER_SEC;
+}
/*
* internal support functions
@@ -1264,6 +1285,9 @@ SearchCatCacheInternal(CatCache *cache,
*/
dlist_move_head(bucket, &ct->cache_elem);
+ /* Record the last access timestamp */
+ ct->lastaccess = catcacheclock;
+
/*
* If it's a positive entry, bump its refcount and return it. If it's
* negative, we can report failure to the caller.
@@ -1425,6 +1449,61 @@ SearchCatCacheMiss(CatCache *cache,
return &ct->tuple;
}
+/*
+ * CatCacheCleanupOldEntries - Remove infrequently-used entries
+ *
+ * Catcache entries happen to be left unused for a long time for several
+ * reasons. Remove such entries to prevent catcache from bloating. It is based
+ * on the similar algorithm with buffer eviction. Entries that are accessed
+ * several times in a certain period live longer than those that have had less
+ * access in the same duration.
+ */
+static bool
+CatCacheCleanupOldEntries(CatCache *cp)
+{
+ int nremoved = 0;
+ int i;
+ long oldest_ts = catcacheclock;
+ uint64 prune_threshold = catcacheclock - prune_min_age_us;
+
+ /* Scan over the whole hash to find entries to remove */
+ for (i = 0 ; i < cp->cc_nbuckets ; i++)
+ {
+ dlist_mutable_iter iter;
+
+ dlist_foreach_modify(iter, &cp->cc_bucket[i])
+ {
+ CatCTup *ct = dlist_container(CatCTup, cache_elem, iter.cur);
+
+ /* Don't remove referenced entries */
+ if (ct->refcount == 0 &&
+ (ct->c_list == NULL || ct->c_list->refcount == 0))
+ {
+ if (ct->lastaccess < prune_threshold)
+ {
+ CatCacheRemoveCTup(cp, ct);
+ nremoved++;
+
+ /* don't let the removed entry update oldest_ts */
+ continue;
+ }
+ }
+
+ /* update the oldest timestamp if the entry remains alive */
+ if (ct->lastaccess < oldest_ts)
+ oldest_ts = ct->lastaccess;
+ }
+ }
+
+ cp->cc_oldest_ts = oldest_ts;
+
+ if (nremoved > 0)
+ elog(DEBUG1, "pruning catalog cache id=%d for %s: removed %d / %d",
+ cp->id, cp->cc_relname, nremoved, cp->cc_ntup + nremoved);
+
+ return nremoved > 0;
+}
+
/*
* ReleaseCatCache
*
@@ -1888,6 +1967,7 @@ CatalogCacheCreateEntry(CatCache *cache, HeapTuple ntp, Datum *arguments,
ct->dead = false;
ct->negative = negative;
ct->hash_value = hashValue;
+ ct->lastaccess = catcacheclock;
dlist_push_head(&cache->cc_bucket[hashIndex], &ct->cache_elem);
@@ -1899,7 +1979,12 @@ CatalogCacheCreateEntry(CatCache *cache, HeapTuple ntp, Datum *arguments,
* arbitrarily, we enlarge when fill factor > 2.
*/
if (cache->cc_ntup > cache->cc_nbuckets * 2)
- RehashCatCache(cache);
+ {
+ /* try removing old entries before expanding hash */
+ if (catcacheclock - cache->cc_oldest_ts < prune_min_age_us ||
+ !CatCacheCleanupOldEntries(cache))
+ RehashCatCache(cache);
+ }
return ct;
}
diff --git a/src/backend/utils/misc/guc.c b/src/backend/utils/misc/guc.c
index 17579eeaca..255e9fa73d 100644
--- a/src/backend/utils/misc/guc.c
+++ b/src/backend/utils/misc/guc.c
@@ -88,6 +88,7 @@
#include "utils/acl.h"
#include "utils/builtins.h"
#include "utils/bytea.h"
+#include "utils/catcache.h"
#include "utils/float.h"
#include "utils/guc_tables.h"
#include "utils/memutils.h"
@@ -3445,6 +3446,17 @@ static struct config_int ConfigureNamesInt[] =
NULL, NULL, NULL
},
+ {
+ {"catalog_cache_prune_min_age", PGC_USERSET, RESOURCES_MEM,
+ gettext_noop("System catalog cache entries that are living unused more than this seconds are considered for removal."),
+ gettext_noop("The value of -1 turns off pruning."),
+ GUC_UNIT_S
+ },
+ &catalog_cache_prune_min_age,
+ -1, -1, INT_MAX,
+ NULL, assign_catalog_cache_prune_min_age, NULL
+ },
+
/* End-of-list marker */
{
{NULL, 0, 0, NULL, NULL}, NULL, 0, 0, 0, NULL, NULL, NULL
diff --git a/src/include/utils/catcache.h b/src/include/utils/catcache.h
index ddc2762eb3..291e857e38 100644
--- a/src/include/utils/catcache.h
+++ b/src/include/utils/catcache.h
@@ -22,6 +22,7 @@
#include "access/htup.h"
#include "access/skey.h"
+#include "datatype/timestamp.h"
#include "lib/ilist.h"
#include "utils/relcache.h"
@@ -61,6 +62,7 @@ typedef struct catcache
slist_node cc_next; /* list link */
ScanKeyData cc_skey[CATCACHE_MAXKEYS]; /* precomputed key info for heap
* scans */
+ uint64 cc_oldest_ts; /* timestamp (us) of the oldest tuple */
/*
* Keep these at the end, so that compiling catcache.c with CATCACHE_STATS
@@ -119,6 +121,7 @@ typedef struct catctup
bool dead; /* dead but not yet removed? */
bool negative; /* negative cache entry? */
HeapTupleData tuple; /* tuple management header */
+ uint64 lastaccess; /* timestamp in us of the last usage */
/*
* The tuple may also be a member of at most one CatCList. (If a single
@@ -189,6 +192,22 @@ typedef struct catcacheheader
/* this extern duplicates utils/memutils.h... */
extern PGDLLIMPORT MemoryContext CacheMemoryContext;
+
+/* for guc.c, not PGDLLPMPORT'ed */
+extern int catalog_cache_prune_min_age;
+
+/* source clock for access timestamp of catcache entries */
+extern uint64 catcacheclock;
+
+/* SetCatCacheClock - set catcache timestamp source clock */
+static inline void
+SetCatCacheClock(TimestampTz ts)
+{
+ catcacheclock = (uint64) ts;
+}
+
+extern void assign_catalog_cache_prune_min_age(int newval, void *extra);
+
extern void CreateCacheMemoryContext(void);
extern CatCache *InitCatCache(int id, Oid reloid, Oid indexoid,
--
2.27.0
----Next_Part(Wed_Jan_27_10_13_08_2021_482)--
Content-Type: Text/X-Patch; charset=us-ascii
Content-Transfer-Encoding: 7bit
Content-Disposition: inline;
filename="v7-0002-Remove-dead-flag-from-catcache-tuple.patch"
^ permalink raw reply [nested|flat] 92+ messages in thread
* [PATCH v7 1/3] CatCache expiration feature
@ 2020-11-18 07:54 Kyotaro Horiguchi <[email protected]>
0 siblings, 0 replies; 92+ messages in thread
From: Kyotaro Horiguchi @ 2020-11-18 07:54 UTC (permalink / raw)
---
src/backend/access/transam/xact.c | 3 ++
src/backend/utils/cache/catcache.c | 87 +++++++++++++++++++++++++++++-
src/backend/utils/misc/guc.c | 12 +++++
src/include/utils/catcache.h | 19 +++++++
4 files changed, 120 insertions(+), 1 deletion(-)
diff --git a/src/backend/access/transam/xact.c b/src/backend/access/transam/xact.c
index a2068e3fd4..86888d2409 100644
--- a/src/backend/access/transam/xact.c
+++ b/src/backend/access/transam/xact.c
@@ -1086,6 +1086,9 @@ static void
AtStart_Cache(void)
{
AcceptInvalidationMessages();
+
+ if (xactStartTimestamp != 0)
+ SetCatCacheClock(xactStartTimestamp);
}
/*
diff --git a/src/backend/utils/cache/catcache.c b/src/backend/utils/cache/catcache.c
index fa2b49c676..644d92dd9a 100644
--- a/src/backend/utils/cache/catcache.c
+++ b/src/backend/utils/cache/catcache.c
@@ -38,6 +38,7 @@
#include "utils/rel.h"
#include "utils/resowner_private.h"
#include "utils/syscache.h"
+#include "utils/timestamp.h"
/* #define CACHEDEBUG */ /* turns DEBUG elogs on */
@@ -60,9 +61,19 @@
#define CACHE_elog(...)
#endif
+/*
+ * GUC variable to define the minimum age of entries that will be considered
+ * to be evicted in seconds. -1 to disable the feature.
+ */
+int catalog_cache_prune_min_age = -1;
+uint64 prune_min_age_us;
+
/* Cache management header --- pointer is NULL until created */
static CatCacheHeader *CacheHdr = NULL;
+/* Clock for the last accessed time of a catcache entry. */
+uint64 catcacheclock = 0;
+
static inline HeapTuple SearchCatCacheInternal(CatCache *cache,
int nkeys,
Datum v1, Datum v2,
@@ -74,6 +85,7 @@ static pg_noinline HeapTuple SearchCatCacheMiss(CatCache *cache,
Index hashIndex,
Datum v1, Datum v2,
Datum v3, Datum v4);
+static bool CatCacheCleanupOldEntries(CatCache *cp);
static uint32 CatalogCacheComputeHashValue(CatCache *cache, int nkeys,
Datum v1, Datum v2, Datum v3, Datum v4);
@@ -99,6 +111,15 @@ static void CatCacheFreeKeys(TupleDesc tupdesc, int nkeys, int *attnos,
static void CatCacheCopyKeys(TupleDesc tupdesc, int nkeys, int *attnos,
Datum *srckeys, Datum *dstkeys);
+/* GUC assign function */
+void
+assign_catalog_cache_prune_min_age(int newval, void *extra)
+{
+ if (newval < 0)
+ prune_min_age_us = UINT64_MAX;
+ else
+ prune_min_age_us = ((uint64) newval) * USECS_PER_SEC;
+}
/*
* internal support functions
@@ -1264,6 +1285,9 @@ SearchCatCacheInternal(CatCache *cache,
*/
dlist_move_head(bucket, &ct->cache_elem);
+ /* Record the last access timestamp */
+ ct->lastaccess = catcacheclock;
+
/*
* If it's a positive entry, bump its refcount and return it. If it's
* negative, we can report failure to the caller.
@@ -1425,6 +1449,61 @@ SearchCatCacheMiss(CatCache *cache,
return &ct->tuple;
}
+/*
+ * CatCacheCleanupOldEntries - Remove infrequently-used entries
+ *
+ * Catcache entries happen to be left unused for a long time for several
+ * reasons. Remove such entries to prevent catcache from bloating. It is based
+ * on the similar algorithm with buffer eviction. Entries that are accessed
+ * several times in a certain period live longer than those that have had less
+ * access in the same duration.
+ */
+static bool
+CatCacheCleanupOldEntries(CatCache *cp)
+{
+ int nremoved = 0;
+ int i;
+ long oldest_ts = catcacheclock;
+ uint64 prune_threshold = catcacheclock - prune_min_age_us;
+
+ /* Scan over the whole hash to find entries to remove */
+ for (i = 0 ; i < cp->cc_nbuckets ; i++)
+ {
+ dlist_mutable_iter iter;
+
+ dlist_foreach_modify(iter, &cp->cc_bucket[i])
+ {
+ CatCTup *ct = dlist_container(CatCTup, cache_elem, iter.cur);
+
+ /* Don't remove referenced entries */
+ if (ct->refcount == 0 &&
+ (ct->c_list == NULL || ct->c_list->refcount == 0))
+ {
+ if (ct->lastaccess < prune_threshold)
+ {
+ CatCacheRemoveCTup(cp, ct);
+ nremoved++;
+
+ /* don't let the removed entry update oldest_ts */
+ continue;
+ }
+ }
+
+ /* update the oldest timestamp if the entry remains alive */
+ if (ct->lastaccess < oldest_ts)
+ oldest_ts = ct->lastaccess;
+ }
+ }
+
+ cp->cc_oldest_ts = oldest_ts;
+
+ if (nremoved > 0)
+ elog(DEBUG1, "pruning catalog cache id=%d for %s: removed %d / %d",
+ cp->id, cp->cc_relname, nremoved, cp->cc_ntup + nremoved);
+
+ return nremoved > 0;
+}
+
/*
* ReleaseCatCache
*
@@ -1888,6 +1967,7 @@ CatalogCacheCreateEntry(CatCache *cache, HeapTuple ntp, Datum *arguments,
ct->dead = false;
ct->negative = negative;
ct->hash_value = hashValue;
+ ct->lastaccess = catcacheclock;
dlist_push_head(&cache->cc_bucket[hashIndex], &ct->cache_elem);
@@ -1899,7 +1979,12 @@ CatalogCacheCreateEntry(CatCache *cache, HeapTuple ntp, Datum *arguments,
* arbitrarily, we enlarge when fill factor > 2.
*/
if (cache->cc_ntup > cache->cc_nbuckets * 2)
- RehashCatCache(cache);
+ {
+ /* try removing old entries before expanding hash */
+ if (catcacheclock - cache->cc_oldest_ts < prune_min_age_us ||
+ !CatCacheCleanupOldEntries(cache))
+ RehashCatCache(cache);
+ }
return ct;
}
diff --git a/src/backend/utils/misc/guc.c b/src/backend/utils/misc/guc.c
index 17579eeaca..255e9fa73d 100644
--- a/src/backend/utils/misc/guc.c
+++ b/src/backend/utils/misc/guc.c
@@ -88,6 +88,7 @@
#include "utils/acl.h"
#include "utils/builtins.h"
#include "utils/bytea.h"
+#include "utils/catcache.h"
#include "utils/float.h"
#include "utils/guc_tables.h"
#include "utils/memutils.h"
@@ -3445,6 +3446,17 @@ static struct config_int ConfigureNamesInt[] =
NULL, NULL, NULL
},
+ {
+ {"catalog_cache_prune_min_age", PGC_USERSET, RESOURCES_MEM,
+ gettext_noop("System catalog cache entries that are living unused more than this seconds are considered for removal."),
+ gettext_noop("The value of -1 turns off pruning."),
+ GUC_UNIT_S
+ },
+ &catalog_cache_prune_min_age,
+ -1, -1, INT_MAX,
+ NULL, assign_catalog_cache_prune_min_age, NULL
+ },
+
/* End-of-list marker */
{
{NULL, 0, 0, NULL, NULL}, NULL, 0, 0, 0, NULL, NULL, NULL
diff --git a/src/include/utils/catcache.h b/src/include/utils/catcache.h
index ddc2762eb3..291e857e38 100644
--- a/src/include/utils/catcache.h
+++ b/src/include/utils/catcache.h
@@ -22,6 +22,7 @@
#include "access/htup.h"
#include "access/skey.h"
+#include "datatype/timestamp.h"
#include "lib/ilist.h"
#include "utils/relcache.h"
@@ -61,6 +62,7 @@ typedef struct catcache
slist_node cc_next; /* list link */
ScanKeyData cc_skey[CATCACHE_MAXKEYS]; /* precomputed key info for heap
* scans */
+ uint64 cc_oldest_ts; /* timestamp (us) of the oldest tuple */
/*
* Keep these at the end, so that compiling catcache.c with CATCACHE_STATS
@@ -119,6 +121,7 @@ typedef struct catctup
bool dead; /* dead but not yet removed? */
bool negative; /* negative cache entry? */
HeapTupleData tuple; /* tuple management header */
+ uint64 lastaccess; /* timestamp in us of the last usage */
/*
* The tuple may also be a member of at most one CatCList. (If a single
@@ -189,6 +192,22 @@ typedef struct catcacheheader
/* this extern duplicates utils/memutils.h... */
extern PGDLLIMPORT MemoryContext CacheMemoryContext;
+
+/* for guc.c, not PGDLLPMPORT'ed */
+extern int catalog_cache_prune_min_age;
+
+/* source clock for access timestamp of catcache entries */
+extern uint64 catcacheclock;
+
+/* SetCatCacheClock - set catcache timestamp source clock */
+static inline void
+SetCatCacheClock(TimestampTz ts)
+{
+ catcacheclock = (uint64) ts;
+}
+
+extern void assign_catalog_cache_prune_min_age(int newval, void *extra);
+
extern void CreateCacheMemoryContext(void);
extern CatCache *InitCatCache(int id, Oid reloid, Oid indexoid,
--
2.27.0
----Next_Part(Wed_Jan_27_10_13_08_2021_482)--
Content-Type: Text/X-Patch; charset=us-ascii
Content-Transfer-Encoding: 7bit
Content-Disposition: inline;
filename="v7-0002-Remove-dead-flag-from-catcache-tuple.patch"
^ permalink raw reply [nested|flat] 92+ messages in thread
* [PATCH v6 1/3] CatCache expiration feature
@ 2020-11-18 07:54 Kyotaro Horiguchi <[email protected]>
0 siblings, 0 replies; 92+ messages in thread
From: Kyotaro Horiguchi @ 2020-11-18 07:54 UTC (permalink / raw)
---
src/backend/access/transam/xact.c | 3 ++
src/backend/utils/cache/catcache.c | 87 +++++++++++++++++++++++++++++-
src/backend/utils/misc/guc.c | 12 +++++
src/include/utils/catcache.h | 19 +++++++
4 files changed, 120 insertions(+), 1 deletion(-)
diff --git a/src/backend/access/transam/xact.c b/src/backend/access/transam/xact.c
index a2068e3fd4..86888d2409 100644
--- a/src/backend/access/transam/xact.c
+++ b/src/backend/access/transam/xact.c
@@ -1086,6 +1086,9 @@ static void
AtStart_Cache(void)
{
AcceptInvalidationMessages();
+
+ if (xactStartTimestamp != 0)
+ SetCatCacheClock(xactStartTimestamp);
}
/*
diff --git a/src/backend/utils/cache/catcache.c b/src/backend/utils/cache/catcache.c
index fa2b49c676..644d92dd9a 100644
--- a/src/backend/utils/cache/catcache.c
+++ b/src/backend/utils/cache/catcache.c
@@ -38,6 +38,7 @@
#include "utils/rel.h"
#include "utils/resowner_private.h"
#include "utils/syscache.h"
+#include "utils/timestamp.h"
/* #define CACHEDEBUG */ /* turns DEBUG elogs on */
@@ -60,9 +61,19 @@
#define CACHE_elog(...)
#endif
+/*
+ * GUC variable to define the minimum age of entries that will be considered
+ * to be evicted in seconds. -1 to disable the feature.
+ */
+int catalog_cache_prune_min_age = -1;
+uint64 prune_min_age_us;
+
/* Cache management header --- pointer is NULL until created */
static CatCacheHeader *CacheHdr = NULL;
+/* Clock for the last accessed time of a catcache entry. */
+uint64 catcacheclock = 0;
+
static inline HeapTuple SearchCatCacheInternal(CatCache *cache,
int nkeys,
Datum v1, Datum v2,
@@ -74,6 +85,7 @@ static pg_noinline HeapTuple SearchCatCacheMiss(CatCache *cache,
Index hashIndex,
Datum v1, Datum v2,
Datum v3, Datum v4);
+static bool CatCacheCleanupOldEntries(CatCache *cp);
static uint32 CatalogCacheComputeHashValue(CatCache *cache, int nkeys,
Datum v1, Datum v2, Datum v3, Datum v4);
@@ -99,6 +111,15 @@ static void CatCacheFreeKeys(TupleDesc tupdesc, int nkeys, int *attnos,
static void CatCacheCopyKeys(TupleDesc tupdesc, int nkeys, int *attnos,
Datum *srckeys, Datum *dstkeys);
+/* GUC assign function */
+void
+assign_catalog_cache_prune_min_age(int newval, void *extra)
+{
+ if (newval < 0)
+ prune_min_age_us = UINT64_MAX;
+ else
+ prune_min_age_us = ((uint64) newval) * USECS_PER_SEC;
+}
/*
* internal support functions
@@ -1264,6 +1285,9 @@ SearchCatCacheInternal(CatCache *cache,
*/
dlist_move_head(bucket, &ct->cache_elem);
+ /* Record the last access timestamp */
+ ct->lastaccess = catcacheclock;
+
/*
* If it's a positive entry, bump its refcount and return it. If it's
* negative, we can report failure to the caller.
@@ -1425,6 +1449,61 @@ SearchCatCacheMiss(CatCache *cache,
return &ct->tuple;
}
+/*
+ * CatCacheCleanupOldEntries - Remove infrequently-used entries
+ *
+ * Catcache entries happen to be left unused for a long time for several
+ * reasons. Remove such entries to prevent catcache from bloating. It is based
+ * on the similar algorithm with buffer eviction. Entries that are accessed
+ * several times in a certain period live longer than those that have had less
+ * access in the same duration.
+ */
+static bool
+CatCacheCleanupOldEntries(CatCache *cp)
+{
+ int nremoved = 0;
+ int i;
+ long oldest_ts = catcacheclock;
+ uint64 prune_threshold = catcacheclock - prune_min_age_us;
+
+ /* Scan over the whole hash to find entries to remove */
+ for (i = 0 ; i < cp->cc_nbuckets ; i++)
+ {
+ dlist_mutable_iter iter;
+
+ dlist_foreach_modify(iter, &cp->cc_bucket[i])
+ {
+ CatCTup *ct = dlist_container(CatCTup, cache_elem, iter.cur);
+
+ /* Don't remove referenced entries */
+ if (ct->refcount == 0 &&
+ (ct->c_list == NULL || ct->c_list->refcount == 0))
+ {
+ if (ct->lastaccess < prune_threshold)
+ {
+ CatCacheRemoveCTup(cp, ct);
+ nremoved++;
+
+ /* don't let the removed entry update oldest_ts */
+ continue;
+ }
+ }
+
+ /* update the oldest timestamp if the entry remains alive */
+ if (ct->lastaccess < oldest_ts)
+ oldest_ts = ct->lastaccess;
+ }
+ }
+
+ cp->cc_oldest_ts = oldest_ts;
+
+ if (nremoved > 0)
+ elog(DEBUG1, "pruning catalog cache id=%d for %s: removed %d / %d",
+ cp->id, cp->cc_relname, nremoved, cp->cc_ntup + nremoved);
+
+ return nremoved > 0;
+}
+
/*
* ReleaseCatCache
*
@@ -1888,6 +1967,7 @@ CatalogCacheCreateEntry(CatCache *cache, HeapTuple ntp, Datum *arguments,
ct->dead = false;
ct->negative = negative;
ct->hash_value = hashValue;
+ ct->lastaccess = catcacheclock;
dlist_push_head(&cache->cc_bucket[hashIndex], &ct->cache_elem);
@@ -1899,7 +1979,12 @@ CatalogCacheCreateEntry(CatCache *cache, HeapTuple ntp, Datum *arguments,
* arbitrarily, we enlarge when fill factor > 2.
*/
if (cache->cc_ntup > cache->cc_nbuckets * 2)
- RehashCatCache(cache);
+ {
+ /* try removing old entries before expanding hash */
+ if (catcacheclock - cache->cc_oldest_ts < prune_min_age_us ||
+ !CatCacheCleanupOldEntries(cache))
+ RehashCatCache(cache);
+ }
return ct;
}
diff --git a/src/backend/utils/misc/guc.c b/src/backend/utils/misc/guc.c
index 17579eeaca..255e9fa73d 100644
--- a/src/backend/utils/misc/guc.c
+++ b/src/backend/utils/misc/guc.c
@@ -88,6 +88,7 @@
#include "utils/acl.h"
#include "utils/builtins.h"
#include "utils/bytea.h"
+#include "utils/catcache.h"
#include "utils/float.h"
#include "utils/guc_tables.h"
#include "utils/memutils.h"
@@ -3445,6 +3446,17 @@ static struct config_int ConfigureNamesInt[] =
NULL, NULL, NULL
},
+ {
+ {"catalog_cache_prune_min_age", PGC_USERSET, RESOURCES_MEM,
+ gettext_noop("System catalog cache entries that are living unused more than this seconds are considered for removal."),
+ gettext_noop("The value of -1 turns off pruning."),
+ GUC_UNIT_S
+ },
+ &catalog_cache_prune_min_age,
+ -1, -1, INT_MAX,
+ NULL, assign_catalog_cache_prune_min_age, NULL
+ },
+
/* End-of-list marker */
{
{NULL, 0, 0, NULL, NULL}, NULL, 0, 0, 0, NULL, NULL, NULL
diff --git a/src/include/utils/catcache.h b/src/include/utils/catcache.h
index ddc2762eb3..291e857e38 100644
--- a/src/include/utils/catcache.h
+++ b/src/include/utils/catcache.h
@@ -22,6 +22,7 @@
#include "access/htup.h"
#include "access/skey.h"
+#include "datatype/timestamp.h"
#include "lib/ilist.h"
#include "utils/relcache.h"
@@ -61,6 +62,7 @@ typedef struct catcache
slist_node cc_next; /* list link */
ScanKeyData cc_skey[CATCACHE_MAXKEYS]; /* precomputed key info for heap
* scans */
+ uint64 cc_oldest_ts; /* timestamp (us) of the oldest tuple */
/*
* Keep these at the end, so that compiling catcache.c with CATCACHE_STATS
@@ -119,6 +121,7 @@ typedef struct catctup
bool dead; /* dead but not yet removed? */
bool negative; /* negative cache entry? */
HeapTupleData tuple; /* tuple management header */
+ uint64 lastaccess; /* timestamp in us of the last usage */
/*
* The tuple may also be a member of at most one CatCList. (If a single
@@ -189,6 +192,22 @@ typedef struct catcacheheader
/* this extern duplicates utils/memutils.h... */
extern PGDLLIMPORT MemoryContext CacheMemoryContext;
+
+/* for guc.c, not PGDLLPMPORT'ed */
+extern int catalog_cache_prune_min_age;
+
+/* source clock for access timestamp of catcache entries */
+extern uint64 catcacheclock;
+
+/* SetCatCacheClock - set catcache timestamp source clock */
+static inline void
+SetCatCacheClock(TimestampTz ts)
+{
+ catcacheclock = (uint64) ts;
+}
+
+extern void assign_catalog_cache_prune_min_age(int newval, void *extra);
+
extern void CreateCacheMemoryContext(void);
extern CatCache *InitCatCache(int id, Oid reloid, Oid indexoid,
--
2.27.0
----Next_Part(Thu_Jan_14_17_32_27_2021_995)--
Content-Type: Text/X-Patch; charset=us-ascii
Content-Transfer-Encoding: 7bit
Content-Disposition: inline;
filename="v6-0002-Remove-dead-flag-from-catcache-tuple.patch"
^ permalink raw reply [nested|flat] 92+ messages in thread
* [PATCH v6 1/3] CatCache expiration feature
@ 2020-11-18 07:54 Kyotaro Horiguchi <[email protected]>
0 siblings, 0 replies; 92+ messages in thread
From: Kyotaro Horiguchi @ 2020-11-18 07:54 UTC (permalink / raw)
---
src/backend/access/transam/xact.c | 3 ++
src/backend/utils/cache/catcache.c | 87 +++++++++++++++++++++++++++++-
src/backend/utils/misc/guc.c | 12 +++++
src/include/utils/catcache.h | 19 +++++++
4 files changed, 120 insertions(+), 1 deletion(-)
diff --git a/src/backend/access/transam/xact.c b/src/backend/access/transam/xact.c
index a2068e3fd4..86888d2409 100644
--- a/src/backend/access/transam/xact.c
+++ b/src/backend/access/transam/xact.c
@@ -1086,6 +1086,9 @@ static void
AtStart_Cache(void)
{
AcceptInvalidationMessages();
+
+ if (xactStartTimestamp != 0)
+ SetCatCacheClock(xactStartTimestamp);
}
/*
diff --git a/src/backend/utils/cache/catcache.c b/src/backend/utils/cache/catcache.c
index fa2b49c676..644d92dd9a 100644
--- a/src/backend/utils/cache/catcache.c
+++ b/src/backend/utils/cache/catcache.c
@@ -38,6 +38,7 @@
#include "utils/rel.h"
#include "utils/resowner_private.h"
#include "utils/syscache.h"
+#include "utils/timestamp.h"
/* #define CACHEDEBUG */ /* turns DEBUG elogs on */
@@ -60,9 +61,19 @@
#define CACHE_elog(...)
#endif
+/*
+ * GUC variable to define the minimum age of entries that will be considered
+ * to be evicted in seconds. -1 to disable the feature.
+ */
+int catalog_cache_prune_min_age = -1;
+uint64 prune_min_age_us;
+
/* Cache management header --- pointer is NULL until created */
static CatCacheHeader *CacheHdr = NULL;
+/* Clock for the last accessed time of a catcache entry. */
+uint64 catcacheclock = 0;
+
static inline HeapTuple SearchCatCacheInternal(CatCache *cache,
int nkeys,
Datum v1, Datum v2,
@@ -74,6 +85,7 @@ static pg_noinline HeapTuple SearchCatCacheMiss(CatCache *cache,
Index hashIndex,
Datum v1, Datum v2,
Datum v3, Datum v4);
+static bool CatCacheCleanupOldEntries(CatCache *cp);
static uint32 CatalogCacheComputeHashValue(CatCache *cache, int nkeys,
Datum v1, Datum v2, Datum v3, Datum v4);
@@ -99,6 +111,15 @@ static void CatCacheFreeKeys(TupleDesc tupdesc, int nkeys, int *attnos,
static void CatCacheCopyKeys(TupleDesc tupdesc, int nkeys, int *attnos,
Datum *srckeys, Datum *dstkeys);
+/* GUC assign function */
+void
+assign_catalog_cache_prune_min_age(int newval, void *extra)
+{
+ if (newval < 0)
+ prune_min_age_us = UINT64_MAX;
+ else
+ prune_min_age_us = ((uint64) newval) * USECS_PER_SEC;
+}
/*
* internal support functions
@@ -1264,6 +1285,9 @@ SearchCatCacheInternal(CatCache *cache,
*/
dlist_move_head(bucket, &ct->cache_elem);
+ /* Record the last access timestamp */
+ ct->lastaccess = catcacheclock;
+
/*
* If it's a positive entry, bump its refcount and return it. If it's
* negative, we can report failure to the caller.
@@ -1425,6 +1449,61 @@ SearchCatCacheMiss(CatCache *cache,
return &ct->tuple;
}
+/*
+ * CatCacheCleanupOldEntries - Remove infrequently-used entries
+ *
+ * Catcache entries happen to be left unused for a long time for several
+ * reasons. Remove such entries to prevent catcache from bloating. It is based
+ * on the similar algorithm with buffer eviction. Entries that are accessed
+ * several times in a certain period live longer than those that have had less
+ * access in the same duration.
+ */
+static bool
+CatCacheCleanupOldEntries(CatCache *cp)
+{
+ int nremoved = 0;
+ int i;
+ long oldest_ts = catcacheclock;
+ uint64 prune_threshold = catcacheclock - prune_min_age_us;
+
+ /* Scan over the whole hash to find entries to remove */
+ for (i = 0 ; i < cp->cc_nbuckets ; i++)
+ {
+ dlist_mutable_iter iter;
+
+ dlist_foreach_modify(iter, &cp->cc_bucket[i])
+ {
+ CatCTup *ct = dlist_container(CatCTup, cache_elem, iter.cur);
+
+ /* Don't remove referenced entries */
+ if (ct->refcount == 0 &&
+ (ct->c_list == NULL || ct->c_list->refcount == 0))
+ {
+ if (ct->lastaccess < prune_threshold)
+ {
+ CatCacheRemoveCTup(cp, ct);
+ nremoved++;
+
+ /* don't let the removed entry update oldest_ts */
+ continue;
+ }
+ }
+
+ /* update the oldest timestamp if the entry remains alive */
+ if (ct->lastaccess < oldest_ts)
+ oldest_ts = ct->lastaccess;
+ }
+ }
+
+ cp->cc_oldest_ts = oldest_ts;
+
+ if (nremoved > 0)
+ elog(DEBUG1, "pruning catalog cache id=%d for %s: removed %d / %d",
+ cp->id, cp->cc_relname, nremoved, cp->cc_ntup + nremoved);
+
+ return nremoved > 0;
+}
+
/*
* ReleaseCatCache
*
@@ -1888,6 +1967,7 @@ CatalogCacheCreateEntry(CatCache *cache, HeapTuple ntp, Datum *arguments,
ct->dead = false;
ct->negative = negative;
ct->hash_value = hashValue;
+ ct->lastaccess = catcacheclock;
dlist_push_head(&cache->cc_bucket[hashIndex], &ct->cache_elem);
@@ -1899,7 +1979,12 @@ CatalogCacheCreateEntry(CatCache *cache, HeapTuple ntp, Datum *arguments,
* arbitrarily, we enlarge when fill factor > 2.
*/
if (cache->cc_ntup > cache->cc_nbuckets * 2)
- RehashCatCache(cache);
+ {
+ /* try removing old entries before expanding hash */
+ if (catcacheclock - cache->cc_oldest_ts < prune_min_age_us ||
+ !CatCacheCleanupOldEntries(cache))
+ RehashCatCache(cache);
+ }
return ct;
}
diff --git a/src/backend/utils/misc/guc.c b/src/backend/utils/misc/guc.c
index 17579eeaca..255e9fa73d 100644
--- a/src/backend/utils/misc/guc.c
+++ b/src/backend/utils/misc/guc.c
@@ -88,6 +88,7 @@
#include "utils/acl.h"
#include "utils/builtins.h"
#include "utils/bytea.h"
+#include "utils/catcache.h"
#include "utils/float.h"
#include "utils/guc_tables.h"
#include "utils/memutils.h"
@@ -3445,6 +3446,17 @@ static struct config_int ConfigureNamesInt[] =
NULL, NULL, NULL
},
+ {
+ {"catalog_cache_prune_min_age", PGC_USERSET, RESOURCES_MEM,
+ gettext_noop("System catalog cache entries that are living unused more than this seconds are considered for removal."),
+ gettext_noop("The value of -1 turns off pruning."),
+ GUC_UNIT_S
+ },
+ &catalog_cache_prune_min_age,
+ -1, -1, INT_MAX,
+ NULL, assign_catalog_cache_prune_min_age, NULL
+ },
+
/* End-of-list marker */
{
{NULL, 0, 0, NULL, NULL}, NULL, 0, 0, 0, NULL, NULL, NULL
diff --git a/src/include/utils/catcache.h b/src/include/utils/catcache.h
index ddc2762eb3..291e857e38 100644
--- a/src/include/utils/catcache.h
+++ b/src/include/utils/catcache.h
@@ -22,6 +22,7 @@
#include "access/htup.h"
#include "access/skey.h"
+#include "datatype/timestamp.h"
#include "lib/ilist.h"
#include "utils/relcache.h"
@@ -61,6 +62,7 @@ typedef struct catcache
slist_node cc_next; /* list link */
ScanKeyData cc_skey[CATCACHE_MAXKEYS]; /* precomputed key info for heap
* scans */
+ uint64 cc_oldest_ts; /* timestamp (us) of the oldest tuple */
/*
* Keep these at the end, so that compiling catcache.c with CATCACHE_STATS
@@ -119,6 +121,7 @@ typedef struct catctup
bool dead; /* dead but not yet removed? */
bool negative; /* negative cache entry? */
HeapTupleData tuple; /* tuple management header */
+ uint64 lastaccess; /* timestamp in us of the last usage */
/*
* The tuple may also be a member of at most one CatCList. (If a single
@@ -189,6 +192,22 @@ typedef struct catcacheheader
/* this extern duplicates utils/memutils.h... */
extern PGDLLIMPORT MemoryContext CacheMemoryContext;
+
+/* for guc.c, not PGDLLPMPORT'ed */
+extern int catalog_cache_prune_min_age;
+
+/* source clock for access timestamp of catcache entries */
+extern uint64 catcacheclock;
+
+/* SetCatCacheClock - set catcache timestamp source clock */
+static inline void
+SetCatCacheClock(TimestampTz ts)
+{
+ catcacheclock = (uint64) ts;
+}
+
+extern void assign_catalog_cache_prune_min_age(int newval, void *extra);
+
extern void CreateCacheMemoryContext(void);
extern CatCache *InitCatCache(int id, Oid reloid, Oid indexoid,
--
2.27.0
----Next_Part(Thu_Jan_14_17_32_27_2021_995)--
Content-Type: Text/X-Patch; charset=us-ascii
Content-Transfer-Encoding: 7bit
Content-Disposition: inline;
filename="v6-0002-Remove-dead-flag-from-catcache-tuple.patch"
^ permalink raw reply [nested|flat] 92+ messages in thread
* [PATCH v7 1/3] CatCache expiration feature
@ 2020-11-18 07:54 Kyotaro Horiguchi <[email protected]>
0 siblings, 0 replies; 92+ messages in thread
From: Kyotaro Horiguchi @ 2020-11-18 07:54 UTC (permalink / raw)
---
src/backend/access/transam/xact.c | 3 ++
src/backend/utils/cache/catcache.c | 87 +++++++++++++++++++++++++++++-
src/backend/utils/misc/guc.c | 12 +++++
src/include/utils/catcache.h | 19 +++++++
4 files changed, 120 insertions(+), 1 deletion(-)
diff --git a/src/backend/access/transam/xact.c b/src/backend/access/transam/xact.c
index a2068e3fd4..86888d2409 100644
--- a/src/backend/access/transam/xact.c
+++ b/src/backend/access/transam/xact.c
@@ -1086,6 +1086,9 @@ static void
AtStart_Cache(void)
{
AcceptInvalidationMessages();
+
+ if (xactStartTimestamp != 0)
+ SetCatCacheClock(xactStartTimestamp);
}
/*
diff --git a/src/backend/utils/cache/catcache.c b/src/backend/utils/cache/catcache.c
index fa2b49c676..644d92dd9a 100644
--- a/src/backend/utils/cache/catcache.c
+++ b/src/backend/utils/cache/catcache.c
@@ -38,6 +38,7 @@
#include "utils/rel.h"
#include "utils/resowner_private.h"
#include "utils/syscache.h"
+#include "utils/timestamp.h"
/* #define CACHEDEBUG */ /* turns DEBUG elogs on */
@@ -60,9 +61,19 @@
#define CACHE_elog(...)
#endif
+/*
+ * GUC variable to define the minimum age of entries that will be considered
+ * to be evicted in seconds. -1 to disable the feature.
+ */
+int catalog_cache_prune_min_age = -1;
+uint64 prune_min_age_us;
+
/* Cache management header --- pointer is NULL until created */
static CatCacheHeader *CacheHdr = NULL;
+/* Clock for the last accessed time of a catcache entry. */
+uint64 catcacheclock = 0;
+
static inline HeapTuple SearchCatCacheInternal(CatCache *cache,
int nkeys,
Datum v1, Datum v2,
@@ -74,6 +85,7 @@ static pg_noinline HeapTuple SearchCatCacheMiss(CatCache *cache,
Index hashIndex,
Datum v1, Datum v2,
Datum v3, Datum v4);
+static bool CatCacheCleanupOldEntries(CatCache *cp);
static uint32 CatalogCacheComputeHashValue(CatCache *cache, int nkeys,
Datum v1, Datum v2, Datum v3, Datum v4);
@@ -99,6 +111,15 @@ static void CatCacheFreeKeys(TupleDesc tupdesc, int nkeys, int *attnos,
static void CatCacheCopyKeys(TupleDesc tupdesc, int nkeys, int *attnos,
Datum *srckeys, Datum *dstkeys);
+/* GUC assign function */
+void
+assign_catalog_cache_prune_min_age(int newval, void *extra)
+{
+ if (newval < 0)
+ prune_min_age_us = UINT64_MAX;
+ else
+ prune_min_age_us = ((uint64) newval) * USECS_PER_SEC;
+}
/*
* internal support functions
@@ -1264,6 +1285,9 @@ SearchCatCacheInternal(CatCache *cache,
*/
dlist_move_head(bucket, &ct->cache_elem);
+ /* Record the last access timestamp */
+ ct->lastaccess = catcacheclock;
+
/*
* If it's a positive entry, bump its refcount and return it. If it's
* negative, we can report failure to the caller.
@@ -1425,6 +1449,61 @@ SearchCatCacheMiss(CatCache *cache,
return &ct->tuple;
}
+/*
+ * CatCacheCleanupOldEntries - Remove infrequently-used entries
+ *
+ * Catcache entries happen to be left unused for a long time for several
+ * reasons. Remove such entries to prevent catcache from bloating. It is based
+ * on the similar algorithm with buffer eviction. Entries that are accessed
+ * several times in a certain period live longer than those that have had less
+ * access in the same duration.
+ */
+static bool
+CatCacheCleanupOldEntries(CatCache *cp)
+{
+ int nremoved = 0;
+ int i;
+ long oldest_ts = catcacheclock;
+ uint64 prune_threshold = catcacheclock - prune_min_age_us;
+
+ /* Scan over the whole hash to find entries to remove */
+ for (i = 0 ; i < cp->cc_nbuckets ; i++)
+ {
+ dlist_mutable_iter iter;
+
+ dlist_foreach_modify(iter, &cp->cc_bucket[i])
+ {
+ CatCTup *ct = dlist_container(CatCTup, cache_elem, iter.cur);
+
+ /* Don't remove referenced entries */
+ if (ct->refcount == 0 &&
+ (ct->c_list == NULL || ct->c_list->refcount == 0))
+ {
+ if (ct->lastaccess < prune_threshold)
+ {
+ CatCacheRemoveCTup(cp, ct);
+ nremoved++;
+
+ /* don't let the removed entry update oldest_ts */
+ continue;
+ }
+ }
+
+ /* update the oldest timestamp if the entry remains alive */
+ if (ct->lastaccess < oldest_ts)
+ oldest_ts = ct->lastaccess;
+ }
+ }
+
+ cp->cc_oldest_ts = oldest_ts;
+
+ if (nremoved > 0)
+ elog(DEBUG1, "pruning catalog cache id=%d for %s: removed %d / %d",
+ cp->id, cp->cc_relname, nremoved, cp->cc_ntup + nremoved);
+
+ return nremoved > 0;
+}
+
/*
* ReleaseCatCache
*
@@ -1888,6 +1967,7 @@ CatalogCacheCreateEntry(CatCache *cache, HeapTuple ntp, Datum *arguments,
ct->dead = false;
ct->negative = negative;
ct->hash_value = hashValue;
+ ct->lastaccess = catcacheclock;
dlist_push_head(&cache->cc_bucket[hashIndex], &ct->cache_elem);
@@ -1899,7 +1979,12 @@ CatalogCacheCreateEntry(CatCache *cache, HeapTuple ntp, Datum *arguments,
* arbitrarily, we enlarge when fill factor > 2.
*/
if (cache->cc_ntup > cache->cc_nbuckets * 2)
- RehashCatCache(cache);
+ {
+ /* try removing old entries before expanding hash */
+ if (catcacheclock - cache->cc_oldest_ts < prune_min_age_us ||
+ !CatCacheCleanupOldEntries(cache))
+ RehashCatCache(cache);
+ }
return ct;
}
diff --git a/src/backend/utils/misc/guc.c b/src/backend/utils/misc/guc.c
index 17579eeaca..255e9fa73d 100644
--- a/src/backend/utils/misc/guc.c
+++ b/src/backend/utils/misc/guc.c
@@ -88,6 +88,7 @@
#include "utils/acl.h"
#include "utils/builtins.h"
#include "utils/bytea.h"
+#include "utils/catcache.h"
#include "utils/float.h"
#include "utils/guc_tables.h"
#include "utils/memutils.h"
@@ -3445,6 +3446,17 @@ static struct config_int ConfigureNamesInt[] =
NULL, NULL, NULL
},
+ {
+ {"catalog_cache_prune_min_age", PGC_USERSET, RESOURCES_MEM,
+ gettext_noop("System catalog cache entries that are living unused more than this seconds are considered for removal."),
+ gettext_noop("The value of -1 turns off pruning."),
+ GUC_UNIT_S
+ },
+ &catalog_cache_prune_min_age,
+ -1, -1, INT_MAX,
+ NULL, assign_catalog_cache_prune_min_age, NULL
+ },
+
/* End-of-list marker */
{
{NULL, 0, 0, NULL, NULL}, NULL, 0, 0, 0, NULL, NULL, NULL
diff --git a/src/include/utils/catcache.h b/src/include/utils/catcache.h
index ddc2762eb3..291e857e38 100644
--- a/src/include/utils/catcache.h
+++ b/src/include/utils/catcache.h
@@ -22,6 +22,7 @@
#include "access/htup.h"
#include "access/skey.h"
+#include "datatype/timestamp.h"
#include "lib/ilist.h"
#include "utils/relcache.h"
@@ -61,6 +62,7 @@ typedef struct catcache
slist_node cc_next; /* list link */
ScanKeyData cc_skey[CATCACHE_MAXKEYS]; /* precomputed key info for heap
* scans */
+ uint64 cc_oldest_ts; /* timestamp (us) of the oldest tuple */
/*
* Keep these at the end, so that compiling catcache.c with CATCACHE_STATS
@@ -119,6 +121,7 @@ typedef struct catctup
bool dead; /* dead but not yet removed? */
bool negative; /* negative cache entry? */
HeapTupleData tuple; /* tuple management header */
+ uint64 lastaccess; /* timestamp in us of the last usage */
/*
* The tuple may also be a member of at most one CatCList. (If a single
@@ -189,6 +192,22 @@ typedef struct catcacheheader
/* this extern duplicates utils/memutils.h... */
extern PGDLLIMPORT MemoryContext CacheMemoryContext;
+
+/* for guc.c, not PGDLLPMPORT'ed */
+extern int catalog_cache_prune_min_age;
+
+/* source clock for access timestamp of catcache entries */
+extern uint64 catcacheclock;
+
+/* SetCatCacheClock - set catcache timestamp source clock */
+static inline void
+SetCatCacheClock(TimestampTz ts)
+{
+ catcacheclock = (uint64) ts;
+}
+
+extern void assign_catalog_cache_prune_min_age(int newval, void *extra);
+
extern void CreateCacheMemoryContext(void);
extern CatCache *InitCatCache(int id, Oid reloid, Oid indexoid,
--
2.27.0
----Next_Part(Wed_Jan_27_10_13_08_2021_482)--
Content-Type: Text/X-Patch; charset=us-ascii
Content-Transfer-Encoding: 7bit
Content-Disposition: inline;
filename="v7-0002-Remove-dead-flag-from-catcache-tuple.patch"
^ permalink raw reply [nested|flat] 92+ messages in thread
* [PATCH v6 1/3] CatCache expiration feature
@ 2020-11-18 07:54 Kyotaro Horiguchi <[email protected]>
0 siblings, 0 replies; 92+ messages in thread
From: Kyotaro Horiguchi @ 2020-11-18 07:54 UTC (permalink / raw)
---
src/backend/access/transam/xact.c | 3 ++
src/backend/utils/cache/catcache.c | 87 +++++++++++++++++++++++++++++-
src/backend/utils/misc/guc.c | 12 +++++
src/include/utils/catcache.h | 19 +++++++
4 files changed, 120 insertions(+), 1 deletion(-)
diff --git a/src/backend/access/transam/xact.c b/src/backend/access/transam/xact.c
index a2068e3fd4..86888d2409 100644
--- a/src/backend/access/transam/xact.c
+++ b/src/backend/access/transam/xact.c
@@ -1086,6 +1086,9 @@ static void
AtStart_Cache(void)
{
AcceptInvalidationMessages();
+
+ if (xactStartTimestamp != 0)
+ SetCatCacheClock(xactStartTimestamp);
}
/*
diff --git a/src/backend/utils/cache/catcache.c b/src/backend/utils/cache/catcache.c
index fa2b49c676..644d92dd9a 100644
--- a/src/backend/utils/cache/catcache.c
+++ b/src/backend/utils/cache/catcache.c
@@ -38,6 +38,7 @@
#include "utils/rel.h"
#include "utils/resowner_private.h"
#include "utils/syscache.h"
+#include "utils/timestamp.h"
/* #define CACHEDEBUG */ /* turns DEBUG elogs on */
@@ -60,9 +61,19 @@
#define CACHE_elog(...)
#endif
+/*
+ * GUC variable to define the minimum age of entries that will be considered
+ * to be evicted in seconds. -1 to disable the feature.
+ */
+int catalog_cache_prune_min_age = -1;
+uint64 prune_min_age_us;
+
/* Cache management header --- pointer is NULL until created */
static CatCacheHeader *CacheHdr = NULL;
+/* Clock for the last accessed time of a catcache entry. */
+uint64 catcacheclock = 0;
+
static inline HeapTuple SearchCatCacheInternal(CatCache *cache,
int nkeys,
Datum v1, Datum v2,
@@ -74,6 +85,7 @@ static pg_noinline HeapTuple SearchCatCacheMiss(CatCache *cache,
Index hashIndex,
Datum v1, Datum v2,
Datum v3, Datum v4);
+static bool CatCacheCleanupOldEntries(CatCache *cp);
static uint32 CatalogCacheComputeHashValue(CatCache *cache, int nkeys,
Datum v1, Datum v2, Datum v3, Datum v4);
@@ -99,6 +111,15 @@ static void CatCacheFreeKeys(TupleDesc tupdesc, int nkeys, int *attnos,
static void CatCacheCopyKeys(TupleDesc tupdesc, int nkeys, int *attnos,
Datum *srckeys, Datum *dstkeys);
+/* GUC assign function */
+void
+assign_catalog_cache_prune_min_age(int newval, void *extra)
+{
+ if (newval < 0)
+ prune_min_age_us = UINT64_MAX;
+ else
+ prune_min_age_us = ((uint64) newval) * USECS_PER_SEC;
+}
/*
* internal support functions
@@ -1264,6 +1285,9 @@ SearchCatCacheInternal(CatCache *cache,
*/
dlist_move_head(bucket, &ct->cache_elem);
+ /* Record the last access timestamp */
+ ct->lastaccess = catcacheclock;
+
/*
* If it's a positive entry, bump its refcount and return it. If it's
* negative, we can report failure to the caller.
@@ -1425,6 +1449,61 @@ SearchCatCacheMiss(CatCache *cache,
return &ct->tuple;
}
+/*
+ * CatCacheCleanupOldEntries - Remove infrequently-used entries
+ *
+ * Catcache entries happen to be left unused for a long time for several
+ * reasons. Remove such entries to prevent catcache from bloating. It is based
+ * on the similar algorithm with buffer eviction. Entries that are accessed
+ * several times in a certain period live longer than those that have had less
+ * access in the same duration.
+ */
+static bool
+CatCacheCleanupOldEntries(CatCache *cp)
+{
+ int nremoved = 0;
+ int i;
+ long oldest_ts = catcacheclock;
+ uint64 prune_threshold = catcacheclock - prune_min_age_us;
+
+ /* Scan over the whole hash to find entries to remove */
+ for (i = 0 ; i < cp->cc_nbuckets ; i++)
+ {
+ dlist_mutable_iter iter;
+
+ dlist_foreach_modify(iter, &cp->cc_bucket[i])
+ {
+ CatCTup *ct = dlist_container(CatCTup, cache_elem, iter.cur);
+
+ /* Don't remove referenced entries */
+ if (ct->refcount == 0 &&
+ (ct->c_list == NULL || ct->c_list->refcount == 0))
+ {
+ if (ct->lastaccess < prune_threshold)
+ {
+ CatCacheRemoveCTup(cp, ct);
+ nremoved++;
+
+ /* don't let the removed entry update oldest_ts */
+ continue;
+ }
+ }
+
+ /* update the oldest timestamp if the entry remains alive */
+ if (ct->lastaccess < oldest_ts)
+ oldest_ts = ct->lastaccess;
+ }
+ }
+
+ cp->cc_oldest_ts = oldest_ts;
+
+ if (nremoved > 0)
+ elog(DEBUG1, "pruning catalog cache id=%d for %s: removed %d / %d",
+ cp->id, cp->cc_relname, nremoved, cp->cc_ntup + nremoved);
+
+ return nremoved > 0;
+}
+
/*
* ReleaseCatCache
*
@@ -1888,6 +1967,7 @@ CatalogCacheCreateEntry(CatCache *cache, HeapTuple ntp, Datum *arguments,
ct->dead = false;
ct->negative = negative;
ct->hash_value = hashValue;
+ ct->lastaccess = catcacheclock;
dlist_push_head(&cache->cc_bucket[hashIndex], &ct->cache_elem);
@@ -1899,7 +1979,12 @@ CatalogCacheCreateEntry(CatCache *cache, HeapTuple ntp, Datum *arguments,
* arbitrarily, we enlarge when fill factor > 2.
*/
if (cache->cc_ntup > cache->cc_nbuckets * 2)
- RehashCatCache(cache);
+ {
+ /* try removing old entries before expanding hash */
+ if (catcacheclock - cache->cc_oldest_ts < prune_min_age_us ||
+ !CatCacheCleanupOldEntries(cache))
+ RehashCatCache(cache);
+ }
return ct;
}
diff --git a/src/backend/utils/misc/guc.c b/src/backend/utils/misc/guc.c
index 17579eeaca..255e9fa73d 100644
--- a/src/backend/utils/misc/guc.c
+++ b/src/backend/utils/misc/guc.c
@@ -88,6 +88,7 @@
#include "utils/acl.h"
#include "utils/builtins.h"
#include "utils/bytea.h"
+#include "utils/catcache.h"
#include "utils/float.h"
#include "utils/guc_tables.h"
#include "utils/memutils.h"
@@ -3445,6 +3446,17 @@ static struct config_int ConfigureNamesInt[] =
NULL, NULL, NULL
},
+ {
+ {"catalog_cache_prune_min_age", PGC_USERSET, RESOURCES_MEM,
+ gettext_noop("System catalog cache entries that are living unused more than this seconds are considered for removal."),
+ gettext_noop("The value of -1 turns off pruning."),
+ GUC_UNIT_S
+ },
+ &catalog_cache_prune_min_age,
+ -1, -1, INT_MAX,
+ NULL, assign_catalog_cache_prune_min_age, NULL
+ },
+
/* End-of-list marker */
{
{NULL, 0, 0, NULL, NULL}, NULL, 0, 0, 0, NULL, NULL, NULL
diff --git a/src/include/utils/catcache.h b/src/include/utils/catcache.h
index ddc2762eb3..291e857e38 100644
--- a/src/include/utils/catcache.h
+++ b/src/include/utils/catcache.h
@@ -22,6 +22,7 @@
#include "access/htup.h"
#include "access/skey.h"
+#include "datatype/timestamp.h"
#include "lib/ilist.h"
#include "utils/relcache.h"
@@ -61,6 +62,7 @@ typedef struct catcache
slist_node cc_next; /* list link */
ScanKeyData cc_skey[CATCACHE_MAXKEYS]; /* precomputed key info for heap
* scans */
+ uint64 cc_oldest_ts; /* timestamp (us) of the oldest tuple */
/*
* Keep these at the end, so that compiling catcache.c with CATCACHE_STATS
@@ -119,6 +121,7 @@ typedef struct catctup
bool dead; /* dead but not yet removed? */
bool negative; /* negative cache entry? */
HeapTupleData tuple; /* tuple management header */
+ uint64 lastaccess; /* timestamp in us of the last usage */
/*
* The tuple may also be a member of at most one CatCList. (If a single
@@ -189,6 +192,22 @@ typedef struct catcacheheader
/* this extern duplicates utils/memutils.h... */
extern PGDLLIMPORT MemoryContext CacheMemoryContext;
+
+/* for guc.c, not PGDLLPMPORT'ed */
+extern int catalog_cache_prune_min_age;
+
+/* source clock for access timestamp of catcache entries */
+extern uint64 catcacheclock;
+
+/* SetCatCacheClock - set catcache timestamp source clock */
+static inline void
+SetCatCacheClock(TimestampTz ts)
+{
+ catcacheclock = (uint64) ts;
+}
+
+extern void assign_catalog_cache_prune_min_age(int newval, void *extra);
+
extern void CreateCacheMemoryContext(void);
extern CatCache *InitCatCache(int id, Oid reloid, Oid indexoid,
--
2.27.0
----Next_Part(Thu_Jan_14_17_32_27_2021_995)--
Content-Type: Text/X-Patch; charset=us-ascii
Content-Transfer-Encoding: 7bit
Content-Disposition: inline;
filename="v6-0002-Remove-dead-flag-from-catcache-tuple.patch"
^ permalink raw reply [nested|flat] 92+ messages in thread
* [PATCH v7 1/3] CatCache expiration feature
@ 2020-11-18 07:54 Kyotaro Horiguchi <[email protected]>
0 siblings, 0 replies; 92+ messages in thread
From: Kyotaro Horiguchi @ 2020-11-18 07:54 UTC (permalink / raw)
---
src/backend/access/transam/xact.c | 3 ++
src/backend/utils/cache/catcache.c | 87 +++++++++++++++++++++++++++++-
src/backend/utils/misc/guc.c | 12 +++++
src/include/utils/catcache.h | 19 +++++++
4 files changed, 120 insertions(+), 1 deletion(-)
diff --git a/src/backend/access/transam/xact.c b/src/backend/access/transam/xact.c
index a2068e3fd4..86888d2409 100644
--- a/src/backend/access/transam/xact.c
+++ b/src/backend/access/transam/xact.c
@@ -1086,6 +1086,9 @@ static void
AtStart_Cache(void)
{
AcceptInvalidationMessages();
+
+ if (xactStartTimestamp != 0)
+ SetCatCacheClock(xactStartTimestamp);
}
/*
diff --git a/src/backend/utils/cache/catcache.c b/src/backend/utils/cache/catcache.c
index fa2b49c676..644d92dd9a 100644
--- a/src/backend/utils/cache/catcache.c
+++ b/src/backend/utils/cache/catcache.c
@@ -38,6 +38,7 @@
#include "utils/rel.h"
#include "utils/resowner_private.h"
#include "utils/syscache.h"
+#include "utils/timestamp.h"
/* #define CACHEDEBUG */ /* turns DEBUG elogs on */
@@ -60,9 +61,19 @@
#define CACHE_elog(...)
#endif
+/*
+ * GUC variable to define the minimum age of entries that will be considered
+ * to be evicted in seconds. -1 to disable the feature.
+ */
+int catalog_cache_prune_min_age = -1;
+uint64 prune_min_age_us;
+
/* Cache management header --- pointer is NULL until created */
static CatCacheHeader *CacheHdr = NULL;
+/* Clock for the last accessed time of a catcache entry. */
+uint64 catcacheclock = 0;
+
static inline HeapTuple SearchCatCacheInternal(CatCache *cache,
int nkeys,
Datum v1, Datum v2,
@@ -74,6 +85,7 @@ static pg_noinline HeapTuple SearchCatCacheMiss(CatCache *cache,
Index hashIndex,
Datum v1, Datum v2,
Datum v3, Datum v4);
+static bool CatCacheCleanupOldEntries(CatCache *cp);
static uint32 CatalogCacheComputeHashValue(CatCache *cache, int nkeys,
Datum v1, Datum v2, Datum v3, Datum v4);
@@ -99,6 +111,15 @@ static void CatCacheFreeKeys(TupleDesc tupdesc, int nkeys, int *attnos,
static void CatCacheCopyKeys(TupleDesc tupdesc, int nkeys, int *attnos,
Datum *srckeys, Datum *dstkeys);
+/* GUC assign function */
+void
+assign_catalog_cache_prune_min_age(int newval, void *extra)
+{
+ if (newval < 0)
+ prune_min_age_us = UINT64_MAX;
+ else
+ prune_min_age_us = ((uint64) newval) * USECS_PER_SEC;
+}
/*
* internal support functions
@@ -1264,6 +1285,9 @@ SearchCatCacheInternal(CatCache *cache,
*/
dlist_move_head(bucket, &ct->cache_elem);
+ /* Record the last access timestamp */
+ ct->lastaccess = catcacheclock;
+
/*
* If it's a positive entry, bump its refcount and return it. If it's
* negative, we can report failure to the caller.
@@ -1425,6 +1449,61 @@ SearchCatCacheMiss(CatCache *cache,
return &ct->tuple;
}
+/*
+ * CatCacheCleanupOldEntries - Remove infrequently-used entries
+ *
+ * Catcache entries happen to be left unused for a long time for several
+ * reasons. Remove such entries to prevent catcache from bloating. It is based
+ * on the similar algorithm with buffer eviction. Entries that are accessed
+ * several times in a certain period live longer than those that have had less
+ * access in the same duration.
+ */
+static bool
+CatCacheCleanupOldEntries(CatCache *cp)
+{
+ int nremoved = 0;
+ int i;
+ long oldest_ts = catcacheclock;
+ uint64 prune_threshold = catcacheclock - prune_min_age_us;
+
+ /* Scan over the whole hash to find entries to remove */
+ for (i = 0 ; i < cp->cc_nbuckets ; i++)
+ {
+ dlist_mutable_iter iter;
+
+ dlist_foreach_modify(iter, &cp->cc_bucket[i])
+ {
+ CatCTup *ct = dlist_container(CatCTup, cache_elem, iter.cur);
+
+ /* Don't remove referenced entries */
+ if (ct->refcount == 0 &&
+ (ct->c_list == NULL || ct->c_list->refcount == 0))
+ {
+ if (ct->lastaccess < prune_threshold)
+ {
+ CatCacheRemoveCTup(cp, ct);
+ nremoved++;
+
+ /* don't let the removed entry update oldest_ts */
+ continue;
+ }
+ }
+
+ /* update the oldest timestamp if the entry remains alive */
+ if (ct->lastaccess < oldest_ts)
+ oldest_ts = ct->lastaccess;
+ }
+ }
+
+ cp->cc_oldest_ts = oldest_ts;
+
+ if (nremoved > 0)
+ elog(DEBUG1, "pruning catalog cache id=%d for %s: removed %d / %d",
+ cp->id, cp->cc_relname, nremoved, cp->cc_ntup + nremoved);
+
+ return nremoved > 0;
+}
+
/*
* ReleaseCatCache
*
@@ -1888,6 +1967,7 @@ CatalogCacheCreateEntry(CatCache *cache, HeapTuple ntp, Datum *arguments,
ct->dead = false;
ct->negative = negative;
ct->hash_value = hashValue;
+ ct->lastaccess = catcacheclock;
dlist_push_head(&cache->cc_bucket[hashIndex], &ct->cache_elem);
@@ -1899,7 +1979,12 @@ CatalogCacheCreateEntry(CatCache *cache, HeapTuple ntp, Datum *arguments,
* arbitrarily, we enlarge when fill factor > 2.
*/
if (cache->cc_ntup > cache->cc_nbuckets * 2)
- RehashCatCache(cache);
+ {
+ /* try removing old entries before expanding hash */
+ if (catcacheclock - cache->cc_oldest_ts < prune_min_age_us ||
+ !CatCacheCleanupOldEntries(cache))
+ RehashCatCache(cache);
+ }
return ct;
}
diff --git a/src/backend/utils/misc/guc.c b/src/backend/utils/misc/guc.c
index 17579eeaca..255e9fa73d 100644
--- a/src/backend/utils/misc/guc.c
+++ b/src/backend/utils/misc/guc.c
@@ -88,6 +88,7 @@
#include "utils/acl.h"
#include "utils/builtins.h"
#include "utils/bytea.h"
+#include "utils/catcache.h"
#include "utils/float.h"
#include "utils/guc_tables.h"
#include "utils/memutils.h"
@@ -3445,6 +3446,17 @@ static struct config_int ConfigureNamesInt[] =
NULL, NULL, NULL
},
+ {
+ {"catalog_cache_prune_min_age", PGC_USERSET, RESOURCES_MEM,
+ gettext_noop("System catalog cache entries that are living unused more than this seconds are considered for removal."),
+ gettext_noop("The value of -1 turns off pruning."),
+ GUC_UNIT_S
+ },
+ &catalog_cache_prune_min_age,
+ -1, -1, INT_MAX,
+ NULL, assign_catalog_cache_prune_min_age, NULL
+ },
+
/* End-of-list marker */
{
{NULL, 0, 0, NULL, NULL}, NULL, 0, 0, 0, NULL, NULL, NULL
diff --git a/src/include/utils/catcache.h b/src/include/utils/catcache.h
index ddc2762eb3..291e857e38 100644
--- a/src/include/utils/catcache.h
+++ b/src/include/utils/catcache.h
@@ -22,6 +22,7 @@
#include "access/htup.h"
#include "access/skey.h"
+#include "datatype/timestamp.h"
#include "lib/ilist.h"
#include "utils/relcache.h"
@@ -61,6 +62,7 @@ typedef struct catcache
slist_node cc_next; /* list link */
ScanKeyData cc_skey[CATCACHE_MAXKEYS]; /* precomputed key info for heap
* scans */
+ uint64 cc_oldest_ts; /* timestamp (us) of the oldest tuple */
/*
* Keep these at the end, so that compiling catcache.c with CATCACHE_STATS
@@ -119,6 +121,7 @@ typedef struct catctup
bool dead; /* dead but not yet removed? */
bool negative; /* negative cache entry? */
HeapTupleData tuple; /* tuple management header */
+ uint64 lastaccess; /* timestamp in us of the last usage */
/*
* The tuple may also be a member of at most one CatCList. (If a single
@@ -189,6 +192,22 @@ typedef struct catcacheheader
/* this extern duplicates utils/memutils.h... */
extern PGDLLIMPORT MemoryContext CacheMemoryContext;
+
+/* for guc.c, not PGDLLPMPORT'ed */
+extern int catalog_cache_prune_min_age;
+
+/* source clock for access timestamp of catcache entries */
+extern uint64 catcacheclock;
+
+/* SetCatCacheClock - set catcache timestamp source clock */
+static inline void
+SetCatCacheClock(TimestampTz ts)
+{
+ catcacheclock = (uint64) ts;
+}
+
+extern void assign_catalog_cache_prune_min_age(int newval, void *extra);
+
extern void CreateCacheMemoryContext(void);
extern CatCache *InitCatCache(int id, Oid reloid, Oid indexoid,
--
2.27.0
----Next_Part(Wed_Jan_27_10_13_08_2021_482)--
Content-Type: Text/X-Patch; charset=us-ascii
Content-Transfer-Encoding: 7bit
Content-Disposition: inline;
filename="v7-0002-Remove-dead-flag-from-catcache-tuple.patch"
^ permalink raw reply [nested|flat] 92+ messages in thread
* [PATCH v7 1/3] CatCache expiration feature
@ 2020-11-18 07:54 Kyotaro Horiguchi <[email protected]>
0 siblings, 0 replies; 92+ messages in thread
From: Kyotaro Horiguchi @ 2020-11-18 07:54 UTC (permalink / raw)
---
src/backend/access/transam/xact.c | 3 ++
src/backend/utils/cache/catcache.c | 87 +++++++++++++++++++++++++++++-
src/backend/utils/misc/guc.c | 12 +++++
src/include/utils/catcache.h | 19 +++++++
4 files changed, 120 insertions(+), 1 deletion(-)
diff --git a/src/backend/access/transam/xact.c b/src/backend/access/transam/xact.c
index a2068e3fd4..86888d2409 100644
--- a/src/backend/access/transam/xact.c
+++ b/src/backend/access/transam/xact.c
@@ -1086,6 +1086,9 @@ static void
AtStart_Cache(void)
{
AcceptInvalidationMessages();
+
+ if (xactStartTimestamp != 0)
+ SetCatCacheClock(xactStartTimestamp);
}
/*
diff --git a/src/backend/utils/cache/catcache.c b/src/backend/utils/cache/catcache.c
index fa2b49c676..644d92dd9a 100644
--- a/src/backend/utils/cache/catcache.c
+++ b/src/backend/utils/cache/catcache.c
@@ -38,6 +38,7 @@
#include "utils/rel.h"
#include "utils/resowner_private.h"
#include "utils/syscache.h"
+#include "utils/timestamp.h"
/* #define CACHEDEBUG */ /* turns DEBUG elogs on */
@@ -60,9 +61,19 @@
#define CACHE_elog(...)
#endif
+/*
+ * GUC variable to define the minimum age of entries that will be considered
+ * to be evicted in seconds. -1 to disable the feature.
+ */
+int catalog_cache_prune_min_age = -1;
+uint64 prune_min_age_us;
+
/* Cache management header --- pointer is NULL until created */
static CatCacheHeader *CacheHdr = NULL;
+/* Clock for the last accessed time of a catcache entry. */
+uint64 catcacheclock = 0;
+
static inline HeapTuple SearchCatCacheInternal(CatCache *cache,
int nkeys,
Datum v1, Datum v2,
@@ -74,6 +85,7 @@ static pg_noinline HeapTuple SearchCatCacheMiss(CatCache *cache,
Index hashIndex,
Datum v1, Datum v2,
Datum v3, Datum v4);
+static bool CatCacheCleanupOldEntries(CatCache *cp);
static uint32 CatalogCacheComputeHashValue(CatCache *cache, int nkeys,
Datum v1, Datum v2, Datum v3, Datum v4);
@@ -99,6 +111,15 @@ static void CatCacheFreeKeys(TupleDesc tupdesc, int nkeys, int *attnos,
static void CatCacheCopyKeys(TupleDesc tupdesc, int nkeys, int *attnos,
Datum *srckeys, Datum *dstkeys);
+/* GUC assign function */
+void
+assign_catalog_cache_prune_min_age(int newval, void *extra)
+{
+ if (newval < 0)
+ prune_min_age_us = UINT64_MAX;
+ else
+ prune_min_age_us = ((uint64) newval) * USECS_PER_SEC;
+}
/*
* internal support functions
@@ -1264,6 +1285,9 @@ SearchCatCacheInternal(CatCache *cache,
*/
dlist_move_head(bucket, &ct->cache_elem);
+ /* Record the last access timestamp */
+ ct->lastaccess = catcacheclock;
+
/*
* If it's a positive entry, bump its refcount and return it. If it's
* negative, we can report failure to the caller.
@@ -1425,6 +1449,61 @@ SearchCatCacheMiss(CatCache *cache,
return &ct->tuple;
}
+/*
+ * CatCacheCleanupOldEntries - Remove infrequently-used entries
+ *
+ * Catcache entries happen to be left unused for a long time for several
+ * reasons. Remove such entries to prevent catcache from bloating. It is based
+ * on the similar algorithm with buffer eviction. Entries that are accessed
+ * several times in a certain period live longer than those that have had less
+ * access in the same duration.
+ */
+static bool
+CatCacheCleanupOldEntries(CatCache *cp)
+{
+ int nremoved = 0;
+ int i;
+ long oldest_ts = catcacheclock;
+ uint64 prune_threshold = catcacheclock - prune_min_age_us;
+
+ /* Scan over the whole hash to find entries to remove */
+ for (i = 0 ; i < cp->cc_nbuckets ; i++)
+ {
+ dlist_mutable_iter iter;
+
+ dlist_foreach_modify(iter, &cp->cc_bucket[i])
+ {
+ CatCTup *ct = dlist_container(CatCTup, cache_elem, iter.cur);
+
+ /* Don't remove referenced entries */
+ if (ct->refcount == 0 &&
+ (ct->c_list == NULL || ct->c_list->refcount == 0))
+ {
+ if (ct->lastaccess < prune_threshold)
+ {
+ CatCacheRemoveCTup(cp, ct);
+ nremoved++;
+
+ /* don't let the removed entry update oldest_ts */
+ continue;
+ }
+ }
+
+ /* update the oldest timestamp if the entry remains alive */
+ if (ct->lastaccess < oldest_ts)
+ oldest_ts = ct->lastaccess;
+ }
+ }
+
+ cp->cc_oldest_ts = oldest_ts;
+
+ if (nremoved > 0)
+ elog(DEBUG1, "pruning catalog cache id=%d for %s: removed %d / %d",
+ cp->id, cp->cc_relname, nremoved, cp->cc_ntup + nremoved);
+
+ return nremoved > 0;
+}
+
/*
* ReleaseCatCache
*
@@ -1888,6 +1967,7 @@ CatalogCacheCreateEntry(CatCache *cache, HeapTuple ntp, Datum *arguments,
ct->dead = false;
ct->negative = negative;
ct->hash_value = hashValue;
+ ct->lastaccess = catcacheclock;
dlist_push_head(&cache->cc_bucket[hashIndex], &ct->cache_elem);
@@ -1899,7 +1979,12 @@ CatalogCacheCreateEntry(CatCache *cache, HeapTuple ntp, Datum *arguments,
* arbitrarily, we enlarge when fill factor > 2.
*/
if (cache->cc_ntup > cache->cc_nbuckets * 2)
- RehashCatCache(cache);
+ {
+ /* try removing old entries before expanding hash */
+ if (catcacheclock - cache->cc_oldest_ts < prune_min_age_us ||
+ !CatCacheCleanupOldEntries(cache))
+ RehashCatCache(cache);
+ }
return ct;
}
diff --git a/src/backend/utils/misc/guc.c b/src/backend/utils/misc/guc.c
index 17579eeaca..255e9fa73d 100644
--- a/src/backend/utils/misc/guc.c
+++ b/src/backend/utils/misc/guc.c
@@ -88,6 +88,7 @@
#include "utils/acl.h"
#include "utils/builtins.h"
#include "utils/bytea.h"
+#include "utils/catcache.h"
#include "utils/float.h"
#include "utils/guc_tables.h"
#include "utils/memutils.h"
@@ -3445,6 +3446,17 @@ static struct config_int ConfigureNamesInt[] =
NULL, NULL, NULL
},
+ {
+ {"catalog_cache_prune_min_age", PGC_USERSET, RESOURCES_MEM,
+ gettext_noop("System catalog cache entries that are living unused more than this seconds are considered for removal."),
+ gettext_noop("The value of -1 turns off pruning."),
+ GUC_UNIT_S
+ },
+ &catalog_cache_prune_min_age,
+ -1, -1, INT_MAX,
+ NULL, assign_catalog_cache_prune_min_age, NULL
+ },
+
/* End-of-list marker */
{
{NULL, 0, 0, NULL, NULL}, NULL, 0, 0, 0, NULL, NULL, NULL
diff --git a/src/include/utils/catcache.h b/src/include/utils/catcache.h
index ddc2762eb3..291e857e38 100644
--- a/src/include/utils/catcache.h
+++ b/src/include/utils/catcache.h
@@ -22,6 +22,7 @@
#include "access/htup.h"
#include "access/skey.h"
+#include "datatype/timestamp.h"
#include "lib/ilist.h"
#include "utils/relcache.h"
@@ -61,6 +62,7 @@ typedef struct catcache
slist_node cc_next; /* list link */
ScanKeyData cc_skey[CATCACHE_MAXKEYS]; /* precomputed key info for heap
* scans */
+ uint64 cc_oldest_ts; /* timestamp (us) of the oldest tuple */
/*
* Keep these at the end, so that compiling catcache.c with CATCACHE_STATS
@@ -119,6 +121,7 @@ typedef struct catctup
bool dead; /* dead but not yet removed? */
bool negative; /* negative cache entry? */
HeapTupleData tuple; /* tuple management header */
+ uint64 lastaccess; /* timestamp in us of the last usage */
/*
* The tuple may also be a member of at most one CatCList. (If a single
@@ -189,6 +192,22 @@ typedef struct catcacheheader
/* this extern duplicates utils/memutils.h... */
extern PGDLLIMPORT MemoryContext CacheMemoryContext;
+
+/* for guc.c, not PGDLLPMPORT'ed */
+extern int catalog_cache_prune_min_age;
+
+/* source clock for access timestamp of catcache entries */
+extern uint64 catcacheclock;
+
+/* SetCatCacheClock - set catcache timestamp source clock */
+static inline void
+SetCatCacheClock(TimestampTz ts)
+{
+ catcacheclock = (uint64) ts;
+}
+
+extern void assign_catalog_cache_prune_min_age(int newval, void *extra);
+
extern void CreateCacheMemoryContext(void);
extern CatCache *InitCatCache(int id, Oid reloid, Oid indexoid,
--
2.27.0
----Next_Part(Wed_Jan_27_10_13_08_2021_482)--
Content-Type: Text/X-Patch; charset=us-ascii
Content-Transfer-Encoding: 7bit
Content-Disposition: inline;
filename="v7-0002-Remove-dead-flag-from-catcache-tuple.patch"
^ permalink raw reply [nested|flat] 92+ messages in thread
* [PATCH v6 1/3] CatCache expiration feature
@ 2020-11-18 07:54 Kyotaro Horiguchi <[email protected]>
0 siblings, 0 replies; 92+ messages in thread
From: Kyotaro Horiguchi @ 2020-11-18 07:54 UTC (permalink / raw)
---
src/backend/access/transam/xact.c | 3 ++
src/backend/utils/cache/catcache.c | 87 +++++++++++++++++++++++++++++-
src/backend/utils/misc/guc.c | 12 +++++
src/include/utils/catcache.h | 19 +++++++
4 files changed, 120 insertions(+), 1 deletion(-)
diff --git a/src/backend/access/transam/xact.c b/src/backend/access/transam/xact.c
index a2068e3fd4..86888d2409 100644
--- a/src/backend/access/transam/xact.c
+++ b/src/backend/access/transam/xact.c
@@ -1086,6 +1086,9 @@ static void
AtStart_Cache(void)
{
AcceptInvalidationMessages();
+
+ if (xactStartTimestamp != 0)
+ SetCatCacheClock(xactStartTimestamp);
}
/*
diff --git a/src/backend/utils/cache/catcache.c b/src/backend/utils/cache/catcache.c
index fa2b49c676..644d92dd9a 100644
--- a/src/backend/utils/cache/catcache.c
+++ b/src/backend/utils/cache/catcache.c
@@ -38,6 +38,7 @@
#include "utils/rel.h"
#include "utils/resowner_private.h"
#include "utils/syscache.h"
+#include "utils/timestamp.h"
/* #define CACHEDEBUG */ /* turns DEBUG elogs on */
@@ -60,9 +61,19 @@
#define CACHE_elog(...)
#endif
+/*
+ * GUC variable to define the minimum age of entries that will be considered
+ * to be evicted in seconds. -1 to disable the feature.
+ */
+int catalog_cache_prune_min_age = -1;
+uint64 prune_min_age_us;
+
/* Cache management header --- pointer is NULL until created */
static CatCacheHeader *CacheHdr = NULL;
+/* Clock for the last accessed time of a catcache entry. */
+uint64 catcacheclock = 0;
+
static inline HeapTuple SearchCatCacheInternal(CatCache *cache,
int nkeys,
Datum v1, Datum v2,
@@ -74,6 +85,7 @@ static pg_noinline HeapTuple SearchCatCacheMiss(CatCache *cache,
Index hashIndex,
Datum v1, Datum v2,
Datum v3, Datum v4);
+static bool CatCacheCleanupOldEntries(CatCache *cp);
static uint32 CatalogCacheComputeHashValue(CatCache *cache, int nkeys,
Datum v1, Datum v2, Datum v3, Datum v4);
@@ -99,6 +111,15 @@ static void CatCacheFreeKeys(TupleDesc tupdesc, int nkeys, int *attnos,
static void CatCacheCopyKeys(TupleDesc tupdesc, int nkeys, int *attnos,
Datum *srckeys, Datum *dstkeys);
+/* GUC assign function */
+void
+assign_catalog_cache_prune_min_age(int newval, void *extra)
+{
+ if (newval < 0)
+ prune_min_age_us = UINT64_MAX;
+ else
+ prune_min_age_us = ((uint64) newval) * USECS_PER_SEC;
+}
/*
* internal support functions
@@ -1264,6 +1285,9 @@ SearchCatCacheInternal(CatCache *cache,
*/
dlist_move_head(bucket, &ct->cache_elem);
+ /* Record the last access timestamp */
+ ct->lastaccess = catcacheclock;
+
/*
* If it's a positive entry, bump its refcount and return it. If it's
* negative, we can report failure to the caller.
@@ -1425,6 +1449,61 @@ SearchCatCacheMiss(CatCache *cache,
return &ct->tuple;
}
+/*
+ * CatCacheCleanupOldEntries - Remove infrequently-used entries
+ *
+ * Catcache entries happen to be left unused for a long time for several
+ * reasons. Remove such entries to prevent catcache from bloating. It is based
+ * on the similar algorithm with buffer eviction. Entries that are accessed
+ * several times in a certain period live longer than those that have had less
+ * access in the same duration.
+ */
+static bool
+CatCacheCleanupOldEntries(CatCache *cp)
+{
+ int nremoved = 0;
+ int i;
+ long oldest_ts = catcacheclock;
+ uint64 prune_threshold = catcacheclock - prune_min_age_us;
+
+ /* Scan over the whole hash to find entries to remove */
+ for (i = 0 ; i < cp->cc_nbuckets ; i++)
+ {
+ dlist_mutable_iter iter;
+
+ dlist_foreach_modify(iter, &cp->cc_bucket[i])
+ {
+ CatCTup *ct = dlist_container(CatCTup, cache_elem, iter.cur);
+
+ /* Don't remove referenced entries */
+ if (ct->refcount == 0 &&
+ (ct->c_list == NULL || ct->c_list->refcount == 0))
+ {
+ if (ct->lastaccess < prune_threshold)
+ {
+ CatCacheRemoveCTup(cp, ct);
+ nremoved++;
+
+ /* don't let the removed entry update oldest_ts */
+ continue;
+ }
+ }
+
+ /* update the oldest timestamp if the entry remains alive */
+ if (ct->lastaccess < oldest_ts)
+ oldest_ts = ct->lastaccess;
+ }
+ }
+
+ cp->cc_oldest_ts = oldest_ts;
+
+ if (nremoved > 0)
+ elog(DEBUG1, "pruning catalog cache id=%d for %s: removed %d / %d",
+ cp->id, cp->cc_relname, nremoved, cp->cc_ntup + nremoved);
+
+ return nremoved > 0;
+}
+
/*
* ReleaseCatCache
*
@@ -1888,6 +1967,7 @@ CatalogCacheCreateEntry(CatCache *cache, HeapTuple ntp, Datum *arguments,
ct->dead = false;
ct->negative = negative;
ct->hash_value = hashValue;
+ ct->lastaccess = catcacheclock;
dlist_push_head(&cache->cc_bucket[hashIndex], &ct->cache_elem);
@@ -1899,7 +1979,12 @@ CatalogCacheCreateEntry(CatCache *cache, HeapTuple ntp, Datum *arguments,
* arbitrarily, we enlarge when fill factor > 2.
*/
if (cache->cc_ntup > cache->cc_nbuckets * 2)
- RehashCatCache(cache);
+ {
+ /* try removing old entries before expanding hash */
+ if (catcacheclock - cache->cc_oldest_ts < prune_min_age_us ||
+ !CatCacheCleanupOldEntries(cache))
+ RehashCatCache(cache);
+ }
return ct;
}
diff --git a/src/backend/utils/misc/guc.c b/src/backend/utils/misc/guc.c
index 17579eeaca..255e9fa73d 100644
--- a/src/backend/utils/misc/guc.c
+++ b/src/backend/utils/misc/guc.c
@@ -88,6 +88,7 @@
#include "utils/acl.h"
#include "utils/builtins.h"
#include "utils/bytea.h"
+#include "utils/catcache.h"
#include "utils/float.h"
#include "utils/guc_tables.h"
#include "utils/memutils.h"
@@ -3445,6 +3446,17 @@ static struct config_int ConfigureNamesInt[] =
NULL, NULL, NULL
},
+ {
+ {"catalog_cache_prune_min_age", PGC_USERSET, RESOURCES_MEM,
+ gettext_noop("System catalog cache entries that are living unused more than this seconds are considered for removal."),
+ gettext_noop("The value of -1 turns off pruning."),
+ GUC_UNIT_S
+ },
+ &catalog_cache_prune_min_age,
+ -1, -1, INT_MAX,
+ NULL, assign_catalog_cache_prune_min_age, NULL
+ },
+
/* End-of-list marker */
{
{NULL, 0, 0, NULL, NULL}, NULL, 0, 0, 0, NULL, NULL, NULL
diff --git a/src/include/utils/catcache.h b/src/include/utils/catcache.h
index ddc2762eb3..291e857e38 100644
--- a/src/include/utils/catcache.h
+++ b/src/include/utils/catcache.h
@@ -22,6 +22,7 @@
#include "access/htup.h"
#include "access/skey.h"
+#include "datatype/timestamp.h"
#include "lib/ilist.h"
#include "utils/relcache.h"
@@ -61,6 +62,7 @@ typedef struct catcache
slist_node cc_next; /* list link */
ScanKeyData cc_skey[CATCACHE_MAXKEYS]; /* precomputed key info for heap
* scans */
+ uint64 cc_oldest_ts; /* timestamp (us) of the oldest tuple */
/*
* Keep these at the end, so that compiling catcache.c with CATCACHE_STATS
@@ -119,6 +121,7 @@ typedef struct catctup
bool dead; /* dead but not yet removed? */
bool negative; /* negative cache entry? */
HeapTupleData tuple; /* tuple management header */
+ uint64 lastaccess; /* timestamp in us of the last usage */
/*
* The tuple may also be a member of at most one CatCList. (If a single
@@ -189,6 +192,22 @@ typedef struct catcacheheader
/* this extern duplicates utils/memutils.h... */
extern PGDLLIMPORT MemoryContext CacheMemoryContext;
+
+/* for guc.c, not PGDLLPMPORT'ed */
+extern int catalog_cache_prune_min_age;
+
+/* source clock for access timestamp of catcache entries */
+extern uint64 catcacheclock;
+
+/* SetCatCacheClock - set catcache timestamp source clock */
+static inline void
+SetCatCacheClock(TimestampTz ts)
+{
+ catcacheclock = (uint64) ts;
+}
+
+extern void assign_catalog_cache_prune_min_age(int newval, void *extra);
+
extern void CreateCacheMemoryContext(void);
extern CatCache *InitCatCache(int id, Oid reloid, Oid indexoid,
--
2.27.0
----Next_Part(Thu_Jan_14_17_32_27_2021_995)--
Content-Type: Text/X-Patch; charset=us-ascii
Content-Transfer-Encoding: 7bit
Content-Disposition: inline;
filename="v6-0002-Remove-dead-flag-from-catcache-tuple.patch"
^ permalink raw reply [nested|flat] 92+ messages in thread
* [PATCH v6 1/3] CatCache expiration feature
@ 2020-11-18 07:54 Kyotaro Horiguchi <[email protected]>
0 siblings, 0 replies; 92+ messages in thread
From: Kyotaro Horiguchi @ 2020-11-18 07:54 UTC (permalink / raw)
---
src/backend/access/transam/xact.c | 3 ++
src/backend/utils/cache/catcache.c | 87 +++++++++++++++++++++++++++++-
src/backend/utils/misc/guc.c | 12 +++++
src/include/utils/catcache.h | 19 +++++++
4 files changed, 120 insertions(+), 1 deletion(-)
diff --git a/src/backend/access/transam/xact.c b/src/backend/access/transam/xact.c
index a2068e3fd4..86888d2409 100644
--- a/src/backend/access/transam/xact.c
+++ b/src/backend/access/transam/xact.c
@@ -1086,6 +1086,9 @@ static void
AtStart_Cache(void)
{
AcceptInvalidationMessages();
+
+ if (xactStartTimestamp != 0)
+ SetCatCacheClock(xactStartTimestamp);
}
/*
diff --git a/src/backend/utils/cache/catcache.c b/src/backend/utils/cache/catcache.c
index fa2b49c676..644d92dd9a 100644
--- a/src/backend/utils/cache/catcache.c
+++ b/src/backend/utils/cache/catcache.c
@@ -38,6 +38,7 @@
#include "utils/rel.h"
#include "utils/resowner_private.h"
#include "utils/syscache.h"
+#include "utils/timestamp.h"
/* #define CACHEDEBUG */ /* turns DEBUG elogs on */
@@ -60,9 +61,19 @@
#define CACHE_elog(...)
#endif
+/*
+ * GUC variable to define the minimum age of entries that will be considered
+ * to be evicted in seconds. -1 to disable the feature.
+ */
+int catalog_cache_prune_min_age = -1;
+uint64 prune_min_age_us;
+
/* Cache management header --- pointer is NULL until created */
static CatCacheHeader *CacheHdr = NULL;
+/* Clock for the last accessed time of a catcache entry. */
+uint64 catcacheclock = 0;
+
static inline HeapTuple SearchCatCacheInternal(CatCache *cache,
int nkeys,
Datum v1, Datum v2,
@@ -74,6 +85,7 @@ static pg_noinline HeapTuple SearchCatCacheMiss(CatCache *cache,
Index hashIndex,
Datum v1, Datum v2,
Datum v3, Datum v4);
+static bool CatCacheCleanupOldEntries(CatCache *cp);
static uint32 CatalogCacheComputeHashValue(CatCache *cache, int nkeys,
Datum v1, Datum v2, Datum v3, Datum v4);
@@ -99,6 +111,15 @@ static void CatCacheFreeKeys(TupleDesc tupdesc, int nkeys, int *attnos,
static void CatCacheCopyKeys(TupleDesc tupdesc, int nkeys, int *attnos,
Datum *srckeys, Datum *dstkeys);
+/* GUC assign function */
+void
+assign_catalog_cache_prune_min_age(int newval, void *extra)
+{
+ if (newval < 0)
+ prune_min_age_us = UINT64_MAX;
+ else
+ prune_min_age_us = ((uint64) newval) * USECS_PER_SEC;
+}
/*
* internal support functions
@@ -1264,6 +1285,9 @@ SearchCatCacheInternal(CatCache *cache,
*/
dlist_move_head(bucket, &ct->cache_elem);
+ /* Record the last access timestamp */
+ ct->lastaccess = catcacheclock;
+
/*
* If it's a positive entry, bump its refcount and return it. If it's
* negative, we can report failure to the caller.
@@ -1425,6 +1449,61 @@ SearchCatCacheMiss(CatCache *cache,
return &ct->tuple;
}
+/*
+ * CatCacheCleanupOldEntries - Remove infrequently-used entries
+ *
+ * Catcache entries happen to be left unused for a long time for several
+ * reasons. Remove such entries to prevent catcache from bloating. It is based
+ * on the similar algorithm with buffer eviction. Entries that are accessed
+ * several times in a certain period live longer than those that have had less
+ * access in the same duration.
+ */
+static bool
+CatCacheCleanupOldEntries(CatCache *cp)
+{
+ int nremoved = 0;
+ int i;
+ long oldest_ts = catcacheclock;
+ uint64 prune_threshold = catcacheclock - prune_min_age_us;
+
+ /* Scan over the whole hash to find entries to remove */
+ for (i = 0 ; i < cp->cc_nbuckets ; i++)
+ {
+ dlist_mutable_iter iter;
+
+ dlist_foreach_modify(iter, &cp->cc_bucket[i])
+ {
+ CatCTup *ct = dlist_container(CatCTup, cache_elem, iter.cur);
+
+ /* Don't remove referenced entries */
+ if (ct->refcount == 0 &&
+ (ct->c_list == NULL || ct->c_list->refcount == 0))
+ {
+ if (ct->lastaccess < prune_threshold)
+ {
+ CatCacheRemoveCTup(cp, ct);
+ nremoved++;
+
+ /* don't let the removed entry update oldest_ts */
+ continue;
+ }
+ }
+
+ /* update the oldest timestamp if the entry remains alive */
+ if (ct->lastaccess < oldest_ts)
+ oldest_ts = ct->lastaccess;
+ }
+ }
+
+ cp->cc_oldest_ts = oldest_ts;
+
+ if (nremoved > 0)
+ elog(DEBUG1, "pruning catalog cache id=%d for %s: removed %d / %d",
+ cp->id, cp->cc_relname, nremoved, cp->cc_ntup + nremoved);
+
+ return nremoved > 0;
+}
+
/*
* ReleaseCatCache
*
@@ -1888,6 +1967,7 @@ CatalogCacheCreateEntry(CatCache *cache, HeapTuple ntp, Datum *arguments,
ct->dead = false;
ct->negative = negative;
ct->hash_value = hashValue;
+ ct->lastaccess = catcacheclock;
dlist_push_head(&cache->cc_bucket[hashIndex], &ct->cache_elem);
@@ -1899,7 +1979,12 @@ CatalogCacheCreateEntry(CatCache *cache, HeapTuple ntp, Datum *arguments,
* arbitrarily, we enlarge when fill factor > 2.
*/
if (cache->cc_ntup > cache->cc_nbuckets * 2)
- RehashCatCache(cache);
+ {
+ /* try removing old entries before expanding hash */
+ if (catcacheclock - cache->cc_oldest_ts < prune_min_age_us ||
+ !CatCacheCleanupOldEntries(cache))
+ RehashCatCache(cache);
+ }
return ct;
}
diff --git a/src/backend/utils/misc/guc.c b/src/backend/utils/misc/guc.c
index 17579eeaca..255e9fa73d 100644
--- a/src/backend/utils/misc/guc.c
+++ b/src/backend/utils/misc/guc.c
@@ -88,6 +88,7 @@
#include "utils/acl.h"
#include "utils/builtins.h"
#include "utils/bytea.h"
+#include "utils/catcache.h"
#include "utils/float.h"
#include "utils/guc_tables.h"
#include "utils/memutils.h"
@@ -3445,6 +3446,17 @@ static struct config_int ConfigureNamesInt[] =
NULL, NULL, NULL
},
+ {
+ {"catalog_cache_prune_min_age", PGC_USERSET, RESOURCES_MEM,
+ gettext_noop("System catalog cache entries that are living unused more than this seconds are considered for removal."),
+ gettext_noop("The value of -1 turns off pruning."),
+ GUC_UNIT_S
+ },
+ &catalog_cache_prune_min_age,
+ -1, -1, INT_MAX,
+ NULL, assign_catalog_cache_prune_min_age, NULL
+ },
+
/* End-of-list marker */
{
{NULL, 0, 0, NULL, NULL}, NULL, 0, 0, 0, NULL, NULL, NULL
diff --git a/src/include/utils/catcache.h b/src/include/utils/catcache.h
index ddc2762eb3..291e857e38 100644
--- a/src/include/utils/catcache.h
+++ b/src/include/utils/catcache.h
@@ -22,6 +22,7 @@
#include "access/htup.h"
#include "access/skey.h"
+#include "datatype/timestamp.h"
#include "lib/ilist.h"
#include "utils/relcache.h"
@@ -61,6 +62,7 @@ typedef struct catcache
slist_node cc_next; /* list link */
ScanKeyData cc_skey[CATCACHE_MAXKEYS]; /* precomputed key info for heap
* scans */
+ uint64 cc_oldest_ts; /* timestamp (us) of the oldest tuple */
/*
* Keep these at the end, so that compiling catcache.c with CATCACHE_STATS
@@ -119,6 +121,7 @@ typedef struct catctup
bool dead; /* dead but not yet removed? */
bool negative; /* negative cache entry? */
HeapTupleData tuple; /* tuple management header */
+ uint64 lastaccess; /* timestamp in us of the last usage */
/*
* The tuple may also be a member of at most one CatCList. (If a single
@@ -189,6 +192,22 @@ typedef struct catcacheheader
/* this extern duplicates utils/memutils.h... */
extern PGDLLIMPORT MemoryContext CacheMemoryContext;
+
+/* for guc.c, not PGDLLPMPORT'ed */
+extern int catalog_cache_prune_min_age;
+
+/* source clock for access timestamp of catcache entries */
+extern uint64 catcacheclock;
+
+/* SetCatCacheClock - set catcache timestamp source clock */
+static inline void
+SetCatCacheClock(TimestampTz ts)
+{
+ catcacheclock = (uint64) ts;
+}
+
+extern void assign_catalog_cache_prune_min_age(int newval, void *extra);
+
extern void CreateCacheMemoryContext(void);
extern CatCache *InitCatCache(int id, Oid reloid, Oid indexoid,
--
2.27.0
----Next_Part(Thu_Jan_14_17_32_27_2021_995)--
Content-Type: Text/X-Patch; charset=us-ascii
Content-Transfer-Encoding: 7bit
Content-Disposition: inline;
filename="v6-0002-Remove-dead-flag-from-catcache-tuple.patch"
^ permalink raw reply [nested|flat] 92+ messages in thread
* [PATCH v7 1/3] CatCache expiration feature
@ 2020-11-18 07:54 Kyotaro Horiguchi <[email protected]>
0 siblings, 0 replies; 92+ messages in thread
From: Kyotaro Horiguchi @ 2020-11-18 07:54 UTC (permalink / raw)
---
src/backend/access/transam/xact.c | 3 ++
src/backend/utils/cache/catcache.c | 87 +++++++++++++++++++++++++++++-
src/backend/utils/misc/guc.c | 12 +++++
src/include/utils/catcache.h | 19 +++++++
4 files changed, 120 insertions(+), 1 deletion(-)
diff --git a/src/backend/access/transam/xact.c b/src/backend/access/transam/xact.c
index a2068e3fd4..86888d2409 100644
--- a/src/backend/access/transam/xact.c
+++ b/src/backend/access/transam/xact.c
@@ -1086,6 +1086,9 @@ static void
AtStart_Cache(void)
{
AcceptInvalidationMessages();
+
+ if (xactStartTimestamp != 0)
+ SetCatCacheClock(xactStartTimestamp);
}
/*
diff --git a/src/backend/utils/cache/catcache.c b/src/backend/utils/cache/catcache.c
index fa2b49c676..644d92dd9a 100644
--- a/src/backend/utils/cache/catcache.c
+++ b/src/backend/utils/cache/catcache.c
@@ -38,6 +38,7 @@
#include "utils/rel.h"
#include "utils/resowner_private.h"
#include "utils/syscache.h"
+#include "utils/timestamp.h"
/* #define CACHEDEBUG */ /* turns DEBUG elogs on */
@@ -60,9 +61,19 @@
#define CACHE_elog(...)
#endif
+/*
+ * GUC variable to define the minimum age of entries that will be considered
+ * to be evicted in seconds. -1 to disable the feature.
+ */
+int catalog_cache_prune_min_age = -1;
+uint64 prune_min_age_us;
+
/* Cache management header --- pointer is NULL until created */
static CatCacheHeader *CacheHdr = NULL;
+/* Clock for the last accessed time of a catcache entry. */
+uint64 catcacheclock = 0;
+
static inline HeapTuple SearchCatCacheInternal(CatCache *cache,
int nkeys,
Datum v1, Datum v2,
@@ -74,6 +85,7 @@ static pg_noinline HeapTuple SearchCatCacheMiss(CatCache *cache,
Index hashIndex,
Datum v1, Datum v2,
Datum v3, Datum v4);
+static bool CatCacheCleanupOldEntries(CatCache *cp);
static uint32 CatalogCacheComputeHashValue(CatCache *cache, int nkeys,
Datum v1, Datum v2, Datum v3, Datum v4);
@@ -99,6 +111,15 @@ static void CatCacheFreeKeys(TupleDesc tupdesc, int nkeys, int *attnos,
static void CatCacheCopyKeys(TupleDesc tupdesc, int nkeys, int *attnos,
Datum *srckeys, Datum *dstkeys);
+/* GUC assign function */
+void
+assign_catalog_cache_prune_min_age(int newval, void *extra)
+{
+ if (newval < 0)
+ prune_min_age_us = UINT64_MAX;
+ else
+ prune_min_age_us = ((uint64) newval) * USECS_PER_SEC;
+}
/*
* internal support functions
@@ -1264,6 +1285,9 @@ SearchCatCacheInternal(CatCache *cache,
*/
dlist_move_head(bucket, &ct->cache_elem);
+ /* Record the last access timestamp */
+ ct->lastaccess = catcacheclock;
+
/*
* If it's a positive entry, bump its refcount and return it. If it's
* negative, we can report failure to the caller.
@@ -1425,6 +1449,61 @@ SearchCatCacheMiss(CatCache *cache,
return &ct->tuple;
}
+/*
+ * CatCacheCleanupOldEntries - Remove infrequently-used entries
+ *
+ * Catcache entries happen to be left unused for a long time for several
+ * reasons. Remove such entries to prevent catcache from bloating. It is based
+ * on the similar algorithm with buffer eviction. Entries that are accessed
+ * several times in a certain period live longer than those that have had less
+ * access in the same duration.
+ */
+static bool
+CatCacheCleanupOldEntries(CatCache *cp)
+{
+ int nremoved = 0;
+ int i;
+ long oldest_ts = catcacheclock;
+ uint64 prune_threshold = catcacheclock - prune_min_age_us;
+
+ /* Scan over the whole hash to find entries to remove */
+ for (i = 0 ; i < cp->cc_nbuckets ; i++)
+ {
+ dlist_mutable_iter iter;
+
+ dlist_foreach_modify(iter, &cp->cc_bucket[i])
+ {
+ CatCTup *ct = dlist_container(CatCTup, cache_elem, iter.cur);
+
+ /* Don't remove referenced entries */
+ if (ct->refcount == 0 &&
+ (ct->c_list == NULL || ct->c_list->refcount == 0))
+ {
+ if (ct->lastaccess < prune_threshold)
+ {
+ CatCacheRemoveCTup(cp, ct);
+ nremoved++;
+
+ /* don't let the removed entry update oldest_ts */
+ continue;
+ }
+ }
+
+ /* update the oldest timestamp if the entry remains alive */
+ if (ct->lastaccess < oldest_ts)
+ oldest_ts = ct->lastaccess;
+ }
+ }
+
+ cp->cc_oldest_ts = oldest_ts;
+
+ if (nremoved > 0)
+ elog(DEBUG1, "pruning catalog cache id=%d for %s: removed %d / %d",
+ cp->id, cp->cc_relname, nremoved, cp->cc_ntup + nremoved);
+
+ return nremoved > 0;
+}
+
/*
* ReleaseCatCache
*
@@ -1888,6 +1967,7 @@ CatalogCacheCreateEntry(CatCache *cache, HeapTuple ntp, Datum *arguments,
ct->dead = false;
ct->negative = negative;
ct->hash_value = hashValue;
+ ct->lastaccess = catcacheclock;
dlist_push_head(&cache->cc_bucket[hashIndex], &ct->cache_elem);
@@ -1899,7 +1979,12 @@ CatalogCacheCreateEntry(CatCache *cache, HeapTuple ntp, Datum *arguments,
* arbitrarily, we enlarge when fill factor > 2.
*/
if (cache->cc_ntup > cache->cc_nbuckets * 2)
- RehashCatCache(cache);
+ {
+ /* try removing old entries before expanding hash */
+ if (catcacheclock - cache->cc_oldest_ts < prune_min_age_us ||
+ !CatCacheCleanupOldEntries(cache))
+ RehashCatCache(cache);
+ }
return ct;
}
diff --git a/src/backend/utils/misc/guc.c b/src/backend/utils/misc/guc.c
index 17579eeaca..255e9fa73d 100644
--- a/src/backend/utils/misc/guc.c
+++ b/src/backend/utils/misc/guc.c
@@ -88,6 +88,7 @@
#include "utils/acl.h"
#include "utils/builtins.h"
#include "utils/bytea.h"
+#include "utils/catcache.h"
#include "utils/float.h"
#include "utils/guc_tables.h"
#include "utils/memutils.h"
@@ -3445,6 +3446,17 @@ static struct config_int ConfigureNamesInt[] =
NULL, NULL, NULL
},
+ {
+ {"catalog_cache_prune_min_age", PGC_USERSET, RESOURCES_MEM,
+ gettext_noop("System catalog cache entries that are living unused more than this seconds are considered for removal."),
+ gettext_noop("The value of -1 turns off pruning."),
+ GUC_UNIT_S
+ },
+ &catalog_cache_prune_min_age,
+ -1, -1, INT_MAX,
+ NULL, assign_catalog_cache_prune_min_age, NULL
+ },
+
/* End-of-list marker */
{
{NULL, 0, 0, NULL, NULL}, NULL, 0, 0, 0, NULL, NULL, NULL
diff --git a/src/include/utils/catcache.h b/src/include/utils/catcache.h
index ddc2762eb3..291e857e38 100644
--- a/src/include/utils/catcache.h
+++ b/src/include/utils/catcache.h
@@ -22,6 +22,7 @@
#include "access/htup.h"
#include "access/skey.h"
+#include "datatype/timestamp.h"
#include "lib/ilist.h"
#include "utils/relcache.h"
@@ -61,6 +62,7 @@ typedef struct catcache
slist_node cc_next; /* list link */
ScanKeyData cc_skey[CATCACHE_MAXKEYS]; /* precomputed key info for heap
* scans */
+ uint64 cc_oldest_ts; /* timestamp (us) of the oldest tuple */
/*
* Keep these at the end, so that compiling catcache.c with CATCACHE_STATS
@@ -119,6 +121,7 @@ typedef struct catctup
bool dead; /* dead but not yet removed? */
bool negative; /* negative cache entry? */
HeapTupleData tuple; /* tuple management header */
+ uint64 lastaccess; /* timestamp in us of the last usage */
/*
* The tuple may also be a member of at most one CatCList. (If a single
@@ -189,6 +192,22 @@ typedef struct catcacheheader
/* this extern duplicates utils/memutils.h... */
extern PGDLLIMPORT MemoryContext CacheMemoryContext;
+
+/* for guc.c, not PGDLLPMPORT'ed */
+extern int catalog_cache_prune_min_age;
+
+/* source clock for access timestamp of catcache entries */
+extern uint64 catcacheclock;
+
+/* SetCatCacheClock - set catcache timestamp source clock */
+static inline void
+SetCatCacheClock(TimestampTz ts)
+{
+ catcacheclock = (uint64) ts;
+}
+
+extern void assign_catalog_cache_prune_min_age(int newval, void *extra);
+
extern void CreateCacheMemoryContext(void);
extern CatCache *InitCatCache(int id, Oid reloid, Oid indexoid,
--
2.27.0
----Next_Part(Wed_Jan_27_10_13_08_2021_482)--
Content-Type: Text/X-Patch; charset=us-ascii
Content-Transfer-Encoding: 7bit
Content-Disposition: inline;
filename="v7-0002-Remove-dead-flag-from-catcache-tuple.patch"
^ permalink raw reply [nested|flat] 92+ messages in thread
* [PATCH v7 1/3] CatCache expiration feature
@ 2020-11-18 07:54 Kyotaro Horiguchi <[email protected]>
0 siblings, 0 replies; 92+ messages in thread
From: Kyotaro Horiguchi @ 2020-11-18 07:54 UTC (permalink / raw)
---
src/backend/access/transam/xact.c | 3 ++
src/backend/utils/cache/catcache.c | 87 +++++++++++++++++++++++++++++-
src/backend/utils/misc/guc.c | 12 +++++
src/include/utils/catcache.h | 19 +++++++
4 files changed, 120 insertions(+), 1 deletion(-)
diff --git a/src/backend/access/transam/xact.c b/src/backend/access/transam/xact.c
index a2068e3fd4..86888d2409 100644
--- a/src/backend/access/transam/xact.c
+++ b/src/backend/access/transam/xact.c
@@ -1086,6 +1086,9 @@ static void
AtStart_Cache(void)
{
AcceptInvalidationMessages();
+
+ if (xactStartTimestamp != 0)
+ SetCatCacheClock(xactStartTimestamp);
}
/*
diff --git a/src/backend/utils/cache/catcache.c b/src/backend/utils/cache/catcache.c
index fa2b49c676..644d92dd9a 100644
--- a/src/backend/utils/cache/catcache.c
+++ b/src/backend/utils/cache/catcache.c
@@ -38,6 +38,7 @@
#include "utils/rel.h"
#include "utils/resowner_private.h"
#include "utils/syscache.h"
+#include "utils/timestamp.h"
/* #define CACHEDEBUG */ /* turns DEBUG elogs on */
@@ -60,9 +61,19 @@
#define CACHE_elog(...)
#endif
+/*
+ * GUC variable to define the minimum age of entries that will be considered
+ * to be evicted in seconds. -1 to disable the feature.
+ */
+int catalog_cache_prune_min_age = -1;
+uint64 prune_min_age_us;
+
/* Cache management header --- pointer is NULL until created */
static CatCacheHeader *CacheHdr = NULL;
+/* Clock for the last accessed time of a catcache entry. */
+uint64 catcacheclock = 0;
+
static inline HeapTuple SearchCatCacheInternal(CatCache *cache,
int nkeys,
Datum v1, Datum v2,
@@ -74,6 +85,7 @@ static pg_noinline HeapTuple SearchCatCacheMiss(CatCache *cache,
Index hashIndex,
Datum v1, Datum v2,
Datum v3, Datum v4);
+static bool CatCacheCleanupOldEntries(CatCache *cp);
static uint32 CatalogCacheComputeHashValue(CatCache *cache, int nkeys,
Datum v1, Datum v2, Datum v3, Datum v4);
@@ -99,6 +111,15 @@ static void CatCacheFreeKeys(TupleDesc tupdesc, int nkeys, int *attnos,
static void CatCacheCopyKeys(TupleDesc tupdesc, int nkeys, int *attnos,
Datum *srckeys, Datum *dstkeys);
+/* GUC assign function */
+void
+assign_catalog_cache_prune_min_age(int newval, void *extra)
+{
+ if (newval < 0)
+ prune_min_age_us = UINT64_MAX;
+ else
+ prune_min_age_us = ((uint64) newval) * USECS_PER_SEC;
+}
/*
* internal support functions
@@ -1264,6 +1285,9 @@ SearchCatCacheInternal(CatCache *cache,
*/
dlist_move_head(bucket, &ct->cache_elem);
+ /* Record the last access timestamp */
+ ct->lastaccess = catcacheclock;
+
/*
* If it's a positive entry, bump its refcount and return it. If it's
* negative, we can report failure to the caller.
@@ -1425,6 +1449,61 @@ SearchCatCacheMiss(CatCache *cache,
return &ct->tuple;
}
+/*
+ * CatCacheCleanupOldEntries - Remove infrequently-used entries
+ *
+ * Catcache entries happen to be left unused for a long time for several
+ * reasons. Remove such entries to prevent catcache from bloating. It is based
+ * on the similar algorithm with buffer eviction. Entries that are accessed
+ * several times in a certain period live longer than those that have had less
+ * access in the same duration.
+ */
+static bool
+CatCacheCleanupOldEntries(CatCache *cp)
+{
+ int nremoved = 0;
+ int i;
+ long oldest_ts = catcacheclock;
+ uint64 prune_threshold = catcacheclock - prune_min_age_us;
+
+ /* Scan over the whole hash to find entries to remove */
+ for (i = 0 ; i < cp->cc_nbuckets ; i++)
+ {
+ dlist_mutable_iter iter;
+
+ dlist_foreach_modify(iter, &cp->cc_bucket[i])
+ {
+ CatCTup *ct = dlist_container(CatCTup, cache_elem, iter.cur);
+
+ /* Don't remove referenced entries */
+ if (ct->refcount == 0 &&
+ (ct->c_list == NULL || ct->c_list->refcount == 0))
+ {
+ if (ct->lastaccess < prune_threshold)
+ {
+ CatCacheRemoveCTup(cp, ct);
+ nremoved++;
+
+ /* don't let the removed entry update oldest_ts */
+ continue;
+ }
+ }
+
+ /* update the oldest timestamp if the entry remains alive */
+ if (ct->lastaccess < oldest_ts)
+ oldest_ts = ct->lastaccess;
+ }
+ }
+
+ cp->cc_oldest_ts = oldest_ts;
+
+ if (nremoved > 0)
+ elog(DEBUG1, "pruning catalog cache id=%d for %s: removed %d / %d",
+ cp->id, cp->cc_relname, nremoved, cp->cc_ntup + nremoved);
+
+ return nremoved > 0;
+}
+
/*
* ReleaseCatCache
*
@@ -1888,6 +1967,7 @@ CatalogCacheCreateEntry(CatCache *cache, HeapTuple ntp, Datum *arguments,
ct->dead = false;
ct->negative = negative;
ct->hash_value = hashValue;
+ ct->lastaccess = catcacheclock;
dlist_push_head(&cache->cc_bucket[hashIndex], &ct->cache_elem);
@@ -1899,7 +1979,12 @@ CatalogCacheCreateEntry(CatCache *cache, HeapTuple ntp, Datum *arguments,
* arbitrarily, we enlarge when fill factor > 2.
*/
if (cache->cc_ntup > cache->cc_nbuckets * 2)
- RehashCatCache(cache);
+ {
+ /* try removing old entries before expanding hash */
+ if (catcacheclock - cache->cc_oldest_ts < prune_min_age_us ||
+ !CatCacheCleanupOldEntries(cache))
+ RehashCatCache(cache);
+ }
return ct;
}
diff --git a/src/backend/utils/misc/guc.c b/src/backend/utils/misc/guc.c
index 17579eeaca..255e9fa73d 100644
--- a/src/backend/utils/misc/guc.c
+++ b/src/backend/utils/misc/guc.c
@@ -88,6 +88,7 @@
#include "utils/acl.h"
#include "utils/builtins.h"
#include "utils/bytea.h"
+#include "utils/catcache.h"
#include "utils/float.h"
#include "utils/guc_tables.h"
#include "utils/memutils.h"
@@ -3445,6 +3446,17 @@ static struct config_int ConfigureNamesInt[] =
NULL, NULL, NULL
},
+ {
+ {"catalog_cache_prune_min_age", PGC_USERSET, RESOURCES_MEM,
+ gettext_noop("System catalog cache entries that are living unused more than this seconds are considered for removal."),
+ gettext_noop("The value of -1 turns off pruning."),
+ GUC_UNIT_S
+ },
+ &catalog_cache_prune_min_age,
+ -1, -1, INT_MAX,
+ NULL, assign_catalog_cache_prune_min_age, NULL
+ },
+
/* End-of-list marker */
{
{NULL, 0, 0, NULL, NULL}, NULL, 0, 0, 0, NULL, NULL, NULL
diff --git a/src/include/utils/catcache.h b/src/include/utils/catcache.h
index ddc2762eb3..291e857e38 100644
--- a/src/include/utils/catcache.h
+++ b/src/include/utils/catcache.h
@@ -22,6 +22,7 @@
#include "access/htup.h"
#include "access/skey.h"
+#include "datatype/timestamp.h"
#include "lib/ilist.h"
#include "utils/relcache.h"
@@ -61,6 +62,7 @@ typedef struct catcache
slist_node cc_next; /* list link */
ScanKeyData cc_skey[CATCACHE_MAXKEYS]; /* precomputed key info for heap
* scans */
+ uint64 cc_oldest_ts; /* timestamp (us) of the oldest tuple */
/*
* Keep these at the end, so that compiling catcache.c with CATCACHE_STATS
@@ -119,6 +121,7 @@ typedef struct catctup
bool dead; /* dead but not yet removed? */
bool negative; /* negative cache entry? */
HeapTupleData tuple; /* tuple management header */
+ uint64 lastaccess; /* timestamp in us of the last usage */
/*
* The tuple may also be a member of at most one CatCList. (If a single
@@ -189,6 +192,22 @@ typedef struct catcacheheader
/* this extern duplicates utils/memutils.h... */
extern PGDLLIMPORT MemoryContext CacheMemoryContext;
+
+/* for guc.c, not PGDLLPMPORT'ed */
+extern int catalog_cache_prune_min_age;
+
+/* source clock for access timestamp of catcache entries */
+extern uint64 catcacheclock;
+
+/* SetCatCacheClock - set catcache timestamp source clock */
+static inline void
+SetCatCacheClock(TimestampTz ts)
+{
+ catcacheclock = (uint64) ts;
+}
+
+extern void assign_catalog_cache_prune_min_age(int newval, void *extra);
+
extern void CreateCacheMemoryContext(void);
extern CatCache *InitCatCache(int id, Oid reloid, Oid indexoid,
--
2.27.0
----Next_Part(Wed_Jan_27_10_13_08_2021_482)--
Content-Type: Text/X-Patch; charset=us-ascii
Content-Transfer-Encoding: 7bit
Content-Disposition: inline;
filename="v7-0002-Remove-dead-flag-from-catcache-tuple.patch"
^ permalink raw reply [nested|flat] 92+ messages in thread
* [PATCH v6 1/3] CatCache expiration feature
@ 2020-11-18 07:54 Kyotaro Horiguchi <[email protected]>
0 siblings, 0 replies; 92+ messages in thread
From: Kyotaro Horiguchi @ 2020-11-18 07:54 UTC (permalink / raw)
---
src/backend/access/transam/xact.c | 3 ++
src/backend/utils/cache/catcache.c | 87 +++++++++++++++++++++++++++++-
src/backend/utils/misc/guc.c | 12 +++++
src/include/utils/catcache.h | 19 +++++++
4 files changed, 120 insertions(+), 1 deletion(-)
diff --git a/src/backend/access/transam/xact.c b/src/backend/access/transam/xact.c
index a2068e3fd4..86888d2409 100644
--- a/src/backend/access/transam/xact.c
+++ b/src/backend/access/transam/xact.c
@@ -1086,6 +1086,9 @@ static void
AtStart_Cache(void)
{
AcceptInvalidationMessages();
+
+ if (xactStartTimestamp != 0)
+ SetCatCacheClock(xactStartTimestamp);
}
/*
diff --git a/src/backend/utils/cache/catcache.c b/src/backend/utils/cache/catcache.c
index fa2b49c676..644d92dd9a 100644
--- a/src/backend/utils/cache/catcache.c
+++ b/src/backend/utils/cache/catcache.c
@@ -38,6 +38,7 @@
#include "utils/rel.h"
#include "utils/resowner_private.h"
#include "utils/syscache.h"
+#include "utils/timestamp.h"
/* #define CACHEDEBUG */ /* turns DEBUG elogs on */
@@ -60,9 +61,19 @@
#define CACHE_elog(...)
#endif
+/*
+ * GUC variable to define the minimum age of entries that will be considered
+ * to be evicted in seconds. -1 to disable the feature.
+ */
+int catalog_cache_prune_min_age = -1;
+uint64 prune_min_age_us;
+
/* Cache management header --- pointer is NULL until created */
static CatCacheHeader *CacheHdr = NULL;
+/* Clock for the last accessed time of a catcache entry. */
+uint64 catcacheclock = 0;
+
static inline HeapTuple SearchCatCacheInternal(CatCache *cache,
int nkeys,
Datum v1, Datum v2,
@@ -74,6 +85,7 @@ static pg_noinline HeapTuple SearchCatCacheMiss(CatCache *cache,
Index hashIndex,
Datum v1, Datum v2,
Datum v3, Datum v4);
+static bool CatCacheCleanupOldEntries(CatCache *cp);
static uint32 CatalogCacheComputeHashValue(CatCache *cache, int nkeys,
Datum v1, Datum v2, Datum v3, Datum v4);
@@ -99,6 +111,15 @@ static void CatCacheFreeKeys(TupleDesc tupdesc, int nkeys, int *attnos,
static void CatCacheCopyKeys(TupleDesc tupdesc, int nkeys, int *attnos,
Datum *srckeys, Datum *dstkeys);
+/* GUC assign function */
+void
+assign_catalog_cache_prune_min_age(int newval, void *extra)
+{
+ if (newval < 0)
+ prune_min_age_us = UINT64_MAX;
+ else
+ prune_min_age_us = ((uint64) newval) * USECS_PER_SEC;
+}
/*
* internal support functions
@@ -1264,6 +1285,9 @@ SearchCatCacheInternal(CatCache *cache,
*/
dlist_move_head(bucket, &ct->cache_elem);
+ /* Record the last access timestamp */
+ ct->lastaccess = catcacheclock;
+
/*
* If it's a positive entry, bump its refcount and return it. If it's
* negative, we can report failure to the caller.
@@ -1425,6 +1449,61 @@ SearchCatCacheMiss(CatCache *cache,
return &ct->tuple;
}
+/*
+ * CatCacheCleanupOldEntries - Remove infrequently-used entries
+ *
+ * Catcache entries happen to be left unused for a long time for several
+ * reasons. Remove such entries to prevent catcache from bloating. It is based
+ * on the similar algorithm with buffer eviction. Entries that are accessed
+ * several times in a certain period live longer than those that have had less
+ * access in the same duration.
+ */
+static bool
+CatCacheCleanupOldEntries(CatCache *cp)
+{
+ int nremoved = 0;
+ int i;
+ long oldest_ts = catcacheclock;
+ uint64 prune_threshold = catcacheclock - prune_min_age_us;
+
+ /* Scan over the whole hash to find entries to remove */
+ for (i = 0 ; i < cp->cc_nbuckets ; i++)
+ {
+ dlist_mutable_iter iter;
+
+ dlist_foreach_modify(iter, &cp->cc_bucket[i])
+ {
+ CatCTup *ct = dlist_container(CatCTup, cache_elem, iter.cur);
+
+ /* Don't remove referenced entries */
+ if (ct->refcount == 0 &&
+ (ct->c_list == NULL || ct->c_list->refcount == 0))
+ {
+ if (ct->lastaccess < prune_threshold)
+ {
+ CatCacheRemoveCTup(cp, ct);
+ nremoved++;
+
+ /* don't let the removed entry update oldest_ts */
+ continue;
+ }
+ }
+
+ /* update the oldest timestamp if the entry remains alive */
+ if (ct->lastaccess < oldest_ts)
+ oldest_ts = ct->lastaccess;
+ }
+ }
+
+ cp->cc_oldest_ts = oldest_ts;
+
+ if (nremoved > 0)
+ elog(DEBUG1, "pruning catalog cache id=%d for %s: removed %d / %d",
+ cp->id, cp->cc_relname, nremoved, cp->cc_ntup + nremoved);
+
+ return nremoved > 0;
+}
+
/*
* ReleaseCatCache
*
@@ -1888,6 +1967,7 @@ CatalogCacheCreateEntry(CatCache *cache, HeapTuple ntp, Datum *arguments,
ct->dead = false;
ct->negative = negative;
ct->hash_value = hashValue;
+ ct->lastaccess = catcacheclock;
dlist_push_head(&cache->cc_bucket[hashIndex], &ct->cache_elem);
@@ -1899,7 +1979,12 @@ CatalogCacheCreateEntry(CatCache *cache, HeapTuple ntp, Datum *arguments,
* arbitrarily, we enlarge when fill factor > 2.
*/
if (cache->cc_ntup > cache->cc_nbuckets * 2)
- RehashCatCache(cache);
+ {
+ /* try removing old entries before expanding hash */
+ if (catcacheclock - cache->cc_oldest_ts < prune_min_age_us ||
+ !CatCacheCleanupOldEntries(cache))
+ RehashCatCache(cache);
+ }
return ct;
}
diff --git a/src/backend/utils/misc/guc.c b/src/backend/utils/misc/guc.c
index 17579eeaca..255e9fa73d 100644
--- a/src/backend/utils/misc/guc.c
+++ b/src/backend/utils/misc/guc.c
@@ -88,6 +88,7 @@
#include "utils/acl.h"
#include "utils/builtins.h"
#include "utils/bytea.h"
+#include "utils/catcache.h"
#include "utils/float.h"
#include "utils/guc_tables.h"
#include "utils/memutils.h"
@@ -3445,6 +3446,17 @@ static struct config_int ConfigureNamesInt[] =
NULL, NULL, NULL
},
+ {
+ {"catalog_cache_prune_min_age", PGC_USERSET, RESOURCES_MEM,
+ gettext_noop("System catalog cache entries that are living unused more than this seconds are considered for removal."),
+ gettext_noop("The value of -1 turns off pruning."),
+ GUC_UNIT_S
+ },
+ &catalog_cache_prune_min_age,
+ -1, -1, INT_MAX,
+ NULL, assign_catalog_cache_prune_min_age, NULL
+ },
+
/* End-of-list marker */
{
{NULL, 0, 0, NULL, NULL}, NULL, 0, 0, 0, NULL, NULL, NULL
diff --git a/src/include/utils/catcache.h b/src/include/utils/catcache.h
index ddc2762eb3..291e857e38 100644
--- a/src/include/utils/catcache.h
+++ b/src/include/utils/catcache.h
@@ -22,6 +22,7 @@
#include "access/htup.h"
#include "access/skey.h"
+#include "datatype/timestamp.h"
#include "lib/ilist.h"
#include "utils/relcache.h"
@@ -61,6 +62,7 @@ typedef struct catcache
slist_node cc_next; /* list link */
ScanKeyData cc_skey[CATCACHE_MAXKEYS]; /* precomputed key info for heap
* scans */
+ uint64 cc_oldest_ts; /* timestamp (us) of the oldest tuple */
/*
* Keep these at the end, so that compiling catcache.c with CATCACHE_STATS
@@ -119,6 +121,7 @@ typedef struct catctup
bool dead; /* dead but not yet removed? */
bool negative; /* negative cache entry? */
HeapTupleData tuple; /* tuple management header */
+ uint64 lastaccess; /* timestamp in us of the last usage */
/*
* The tuple may also be a member of at most one CatCList. (If a single
@@ -189,6 +192,22 @@ typedef struct catcacheheader
/* this extern duplicates utils/memutils.h... */
extern PGDLLIMPORT MemoryContext CacheMemoryContext;
+
+/* for guc.c, not PGDLLPMPORT'ed */
+extern int catalog_cache_prune_min_age;
+
+/* source clock for access timestamp of catcache entries */
+extern uint64 catcacheclock;
+
+/* SetCatCacheClock - set catcache timestamp source clock */
+static inline void
+SetCatCacheClock(TimestampTz ts)
+{
+ catcacheclock = (uint64) ts;
+}
+
+extern void assign_catalog_cache_prune_min_age(int newval, void *extra);
+
extern void CreateCacheMemoryContext(void);
extern CatCache *InitCatCache(int id, Oid reloid, Oid indexoid,
--
2.27.0
----Next_Part(Thu_Jan_14_17_32_27_2021_995)--
Content-Type: Text/X-Patch; charset=us-ascii
Content-Transfer-Encoding: 7bit
Content-Disposition: inline;
filename="v6-0002-Remove-dead-flag-from-catcache-tuple.patch"
^ permalink raw reply [nested|flat] 92+ messages in thread
* [PATCH v7 1/3] CatCache expiration feature
@ 2020-11-18 07:54 Kyotaro Horiguchi <[email protected]>
0 siblings, 0 replies; 92+ messages in thread
From: Kyotaro Horiguchi @ 2020-11-18 07:54 UTC (permalink / raw)
---
src/backend/access/transam/xact.c | 3 ++
src/backend/utils/cache/catcache.c | 87 +++++++++++++++++++++++++++++-
src/backend/utils/misc/guc.c | 12 +++++
src/include/utils/catcache.h | 19 +++++++
4 files changed, 120 insertions(+), 1 deletion(-)
diff --git a/src/backend/access/transam/xact.c b/src/backend/access/transam/xact.c
index a2068e3fd4..86888d2409 100644
--- a/src/backend/access/transam/xact.c
+++ b/src/backend/access/transam/xact.c
@@ -1086,6 +1086,9 @@ static void
AtStart_Cache(void)
{
AcceptInvalidationMessages();
+
+ if (xactStartTimestamp != 0)
+ SetCatCacheClock(xactStartTimestamp);
}
/*
diff --git a/src/backend/utils/cache/catcache.c b/src/backend/utils/cache/catcache.c
index fa2b49c676..644d92dd9a 100644
--- a/src/backend/utils/cache/catcache.c
+++ b/src/backend/utils/cache/catcache.c
@@ -38,6 +38,7 @@
#include "utils/rel.h"
#include "utils/resowner_private.h"
#include "utils/syscache.h"
+#include "utils/timestamp.h"
/* #define CACHEDEBUG */ /* turns DEBUG elogs on */
@@ -60,9 +61,19 @@
#define CACHE_elog(...)
#endif
+/*
+ * GUC variable to define the minimum age of entries that will be considered
+ * to be evicted in seconds. -1 to disable the feature.
+ */
+int catalog_cache_prune_min_age = -1;
+uint64 prune_min_age_us;
+
/* Cache management header --- pointer is NULL until created */
static CatCacheHeader *CacheHdr = NULL;
+/* Clock for the last accessed time of a catcache entry. */
+uint64 catcacheclock = 0;
+
static inline HeapTuple SearchCatCacheInternal(CatCache *cache,
int nkeys,
Datum v1, Datum v2,
@@ -74,6 +85,7 @@ static pg_noinline HeapTuple SearchCatCacheMiss(CatCache *cache,
Index hashIndex,
Datum v1, Datum v2,
Datum v3, Datum v4);
+static bool CatCacheCleanupOldEntries(CatCache *cp);
static uint32 CatalogCacheComputeHashValue(CatCache *cache, int nkeys,
Datum v1, Datum v2, Datum v3, Datum v4);
@@ -99,6 +111,15 @@ static void CatCacheFreeKeys(TupleDesc tupdesc, int nkeys, int *attnos,
static void CatCacheCopyKeys(TupleDesc tupdesc, int nkeys, int *attnos,
Datum *srckeys, Datum *dstkeys);
+/* GUC assign function */
+void
+assign_catalog_cache_prune_min_age(int newval, void *extra)
+{
+ if (newval < 0)
+ prune_min_age_us = UINT64_MAX;
+ else
+ prune_min_age_us = ((uint64) newval) * USECS_PER_SEC;
+}
/*
* internal support functions
@@ -1264,6 +1285,9 @@ SearchCatCacheInternal(CatCache *cache,
*/
dlist_move_head(bucket, &ct->cache_elem);
+ /* Record the last access timestamp */
+ ct->lastaccess = catcacheclock;
+
/*
* If it's a positive entry, bump its refcount and return it. If it's
* negative, we can report failure to the caller.
@@ -1425,6 +1449,61 @@ SearchCatCacheMiss(CatCache *cache,
return &ct->tuple;
}
+/*
+ * CatCacheCleanupOldEntries - Remove infrequently-used entries
+ *
+ * Catcache entries happen to be left unused for a long time for several
+ * reasons. Remove such entries to prevent catcache from bloating. It is based
+ * on the similar algorithm with buffer eviction. Entries that are accessed
+ * several times in a certain period live longer than those that have had less
+ * access in the same duration.
+ */
+static bool
+CatCacheCleanupOldEntries(CatCache *cp)
+{
+ int nremoved = 0;
+ int i;
+ long oldest_ts = catcacheclock;
+ uint64 prune_threshold = catcacheclock - prune_min_age_us;
+
+ /* Scan over the whole hash to find entries to remove */
+ for (i = 0 ; i < cp->cc_nbuckets ; i++)
+ {
+ dlist_mutable_iter iter;
+
+ dlist_foreach_modify(iter, &cp->cc_bucket[i])
+ {
+ CatCTup *ct = dlist_container(CatCTup, cache_elem, iter.cur);
+
+ /* Don't remove referenced entries */
+ if (ct->refcount == 0 &&
+ (ct->c_list == NULL || ct->c_list->refcount == 0))
+ {
+ if (ct->lastaccess < prune_threshold)
+ {
+ CatCacheRemoveCTup(cp, ct);
+ nremoved++;
+
+ /* don't let the removed entry update oldest_ts */
+ continue;
+ }
+ }
+
+ /* update the oldest timestamp if the entry remains alive */
+ if (ct->lastaccess < oldest_ts)
+ oldest_ts = ct->lastaccess;
+ }
+ }
+
+ cp->cc_oldest_ts = oldest_ts;
+
+ if (nremoved > 0)
+ elog(DEBUG1, "pruning catalog cache id=%d for %s: removed %d / %d",
+ cp->id, cp->cc_relname, nremoved, cp->cc_ntup + nremoved);
+
+ return nremoved > 0;
+}
+
/*
* ReleaseCatCache
*
@@ -1888,6 +1967,7 @@ CatalogCacheCreateEntry(CatCache *cache, HeapTuple ntp, Datum *arguments,
ct->dead = false;
ct->negative = negative;
ct->hash_value = hashValue;
+ ct->lastaccess = catcacheclock;
dlist_push_head(&cache->cc_bucket[hashIndex], &ct->cache_elem);
@@ -1899,7 +1979,12 @@ CatalogCacheCreateEntry(CatCache *cache, HeapTuple ntp, Datum *arguments,
* arbitrarily, we enlarge when fill factor > 2.
*/
if (cache->cc_ntup > cache->cc_nbuckets * 2)
- RehashCatCache(cache);
+ {
+ /* try removing old entries before expanding hash */
+ if (catcacheclock - cache->cc_oldest_ts < prune_min_age_us ||
+ !CatCacheCleanupOldEntries(cache))
+ RehashCatCache(cache);
+ }
return ct;
}
diff --git a/src/backend/utils/misc/guc.c b/src/backend/utils/misc/guc.c
index 17579eeaca..255e9fa73d 100644
--- a/src/backend/utils/misc/guc.c
+++ b/src/backend/utils/misc/guc.c
@@ -88,6 +88,7 @@
#include "utils/acl.h"
#include "utils/builtins.h"
#include "utils/bytea.h"
+#include "utils/catcache.h"
#include "utils/float.h"
#include "utils/guc_tables.h"
#include "utils/memutils.h"
@@ -3445,6 +3446,17 @@ static struct config_int ConfigureNamesInt[] =
NULL, NULL, NULL
},
+ {
+ {"catalog_cache_prune_min_age", PGC_USERSET, RESOURCES_MEM,
+ gettext_noop("System catalog cache entries that are living unused more than this seconds are considered for removal."),
+ gettext_noop("The value of -1 turns off pruning."),
+ GUC_UNIT_S
+ },
+ &catalog_cache_prune_min_age,
+ -1, -1, INT_MAX,
+ NULL, assign_catalog_cache_prune_min_age, NULL
+ },
+
/* End-of-list marker */
{
{NULL, 0, 0, NULL, NULL}, NULL, 0, 0, 0, NULL, NULL, NULL
diff --git a/src/include/utils/catcache.h b/src/include/utils/catcache.h
index ddc2762eb3..291e857e38 100644
--- a/src/include/utils/catcache.h
+++ b/src/include/utils/catcache.h
@@ -22,6 +22,7 @@
#include "access/htup.h"
#include "access/skey.h"
+#include "datatype/timestamp.h"
#include "lib/ilist.h"
#include "utils/relcache.h"
@@ -61,6 +62,7 @@ typedef struct catcache
slist_node cc_next; /* list link */
ScanKeyData cc_skey[CATCACHE_MAXKEYS]; /* precomputed key info for heap
* scans */
+ uint64 cc_oldest_ts; /* timestamp (us) of the oldest tuple */
/*
* Keep these at the end, so that compiling catcache.c with CATCACHE_STATS
@@ -119,6 +121,7 @@ typedef struct catctup
bool dead; /* dead but not yet removed? */
bool negative; /* negative cache entry? */
HeapTupleData tuple; /* tuple management header */
+ uint64 lastaccess; /* timestamp in us of the last usage */
/*
* The tuple may also be a member of at most one CatCList. (If a single
@@ -189,6 +192,22 @@ typedef struct catcacheheader
/* this extern duplicates utils/memutils.h... */
extern PGDLLIMPORT MemoryContext CacheMemoryContext;
+
+/* for guc.c, not PGDLLPMPORT'ed */
+extern int catalog_cache_prune_min_age;
+
+/* source clock for access timestamp of catcache entries */
+extern uint64 catcacheclock;
+
+/* SetCatCacheClock - set catcache timestamp source clock */
+static inline void
+SetCatCacheClock(TimestampTz ts)
+{
+ catcacheclock = (uint64) ts;
+}
+
+extern void assign_catalog_cache_prune_min_age(int newval, void *extra);
+
extern void CreateCacheMemoryContext(void);
extern CatCache *InitCatCache(int id, Oid reloid, Oid indexoid,
--
2.27.0
----Next_Part(Wed_Jan_27_10_13_08_2021_482)--
Content-Type: Text/X-Patch; charset=us-ascii
Content-Transfer-Encoding: 7bit
Content-Disposition: inline;
filename="v7-0002-Remove-dead-flag-from-catcache-tuple.patch"
^ permalink raw reply [nested|flat] 92+ messages in thread
* [PATCH v6 1/3] CatCache expiration feature
@ 2020-11-18 07:54 Kyotaro Horiguchi <[email protected]>
0 siblings, 0 replies; 92+ messages in thread
From: Kyotaro Horiguchi @ 2020-11-18 07:54 UTC (permalink / raw)
---
src/backend/access/transam/xact.c | 3 ++
src/backend/utils/cache/catcache.c | 87 +++++++++++++++++++++++++++++-
src/backend/utils/misc/guc.c | 12 +++++
src/include/utils/catcache.h | 19 +++++++
4 files changed, 120 insertions(+), 1 deletion(-)
diff --git a/src/backend/access/transam/xact.c b/src/backend/access/transam/xact.c
index a2068e3fd4..86888d2409 100644
--- a/src/backend/access/transam/xact.c
+++ b/src/backend/access/transam/xact.c
@@ -1086,6 +1086,9 @@ static void
AtStart_Cache(void)
{
AcceptInvalidationMessages();
+
+ if (xactStartTimestamp != 0)
+ SetCatCacheClock(xactStartTimestamp);
}
/*
diff --git a/src/backend/utils/cache/catcache.c b/src/backend/utils/cache/catcache.c
index fa2b49c676..644d92dd9a 100644
--- a/src/backend/utils/cache/catcache.c
+++ b/src/backend/utils/cache/catcache.c
@@ -38,6 +38,7 @@
#include "utils/rel.h"
#include "utils/resowner_private.h"
#include "utils/syscache.h"
+#include "utils/timestamp.h"
/* #define CACHEDEBUG */ /* turns DEBUG elogs on */
@@ -60,9 +61,19 @@
#define CACHE_elog(...)
#endif
+/*
+ * GUC variable to define the minimum age of entries that will be considered
+ * to be evicted in seconds. -1 to disable the feature.
+ */
+int catalog_cache_prune_min_age = -1;
+uint64 prune_min_age_us;
+
/* Cache management header --- pointer is NULL until created */
static CatCacheHeader *CacheHdr = NULL;
+/* Clock for the last accessed time of a catcache entry. */
+uint64 catcacheclock = 0;
+
static inline HeapTuple SearchCatCacheInternal(CatCache *cache,
int nkeys,
Datum v1, Datum v2,
@@ -74,6 +85,7 @@ static pg_noinline HeapTuple SearchCatCacheMiss(CatCache *cache,
Index hashIndex,
Datum v1, Datum v2,
Datum v3, Datum v4);
+static bool CatCacheCleanupOldEntries(CatCache *cp);
static uint32 CatalogCacheComputeHashValue(CatCache *cache, int nkeys,
Datum v1, Datum v2, Datum v3, Datum v4);
@@ -99,6 +111,15 @@ static void CatCacheFreeKeys(TupleDesc tupdesc, int nkeys, int *attnos,
static void CatCacheCopyKeys(TupleDesc tupdesc, int nkeys, int *attnos,
Datum *srckeys, Datum *dstkeys);
+/* GUC assign function */
+void
+assign_catalog_cache_prune_min_age(int newval, void *extra)
+{
+ if (newval < 0)
+ prune_min_age_us = UINT64_MAX;
+ else
+ prune_min_age_us = ((uint64) newval) * USECS_PER_SEC;
+}
/*
* internal support functions
@@ -1264,6 +1285,9 @@ SearchCatCacheInternal(CatCache *cache,
*/
dlist_move_head(bucket, &ct->cache_elem);
+ /* Record the last access timestamp */
+ ct->lastaccess = catcacheclock;
+
/*
* If it's a positive entry, bump its refcount and return it. If it's
* negative, we can report failure to the caller.
@@ -1425,6 +1449,61 @@ SearchCatCacheMiss(CatCache *cache,
return &ct->tuple;
}
+/*
+ * CatCacheCleanupOldEntries - Remove infrequently-used entries
+ *
+ * Catcache entries happen to be left unused for a long time for several
+ * reasons. Remove such entries to prevent catcache from bloating. It is based
+ * on the similar algorithm with buffer eviction. Entries that are accessed
+ * several times in a certain period live longer than those that have had less
+ * access in the same duration.
+ */
+static bool
+CatCacheCleanupOldEntries(CatCache *cp)
+{
+ int nremoved = 0;
+ int i;
+ long oldest_ts = catcacheclock;
+ uint64 prune_threshold = catcacheclock - prune_min_age_us;
+
+ /* Scan over the whole hash to find entries to remove */
+ for (i = 0 ; i < cp->cc_nbuckets ; i++)
+ {
+ dlist_mutable_iter iter;
+
+ dlist_foreach_modify(iter, &cp->cc_bucket[i])
+ {
+ CatCTup *ct = dlist_container(CatCTup, cache_elem, iter.cur);
+
+ /* Don't remove referenced entries */
+ if (ct->refcount == 0 &&
+ (ct->c_list == NULL || ct->c_list->refcount == 0))
+ {
+ if (ct->lastaccess < prune_threshold)
+ {
+ CatCacheRemoveCTup(cp, ct);
+ nremoved++;
+
+ /* don't let the removed entry update oldest_ts */
+ continue;
+ }
+ }
+
+ /* update the oldest timestamp if the entry remains alive */
+ if (ct->lastaccess < oldest_ts)
+ oldest_ts = ct->lastaccess;
+ }
+ }
+
+ cp->cc_oldest_ts = oldest_ts;
+
+ if (nremoved > 0)
+ elog(DEBUG1, "pruning catalog cache id=%d for %s: removed %d / %d",
+ cp->id, cp->cc_relname, nremoved, cp->cc_ntup + nremoved);
+
+ return nremoved > 0;
+}
+
/*
* ReleaseCatCache
*
@@ -1888,6 +1967,7 @@ CatalogCacheCreateEntry(CatCache *cache, HeapTuple ntp, Datum *arguments,
ct->dead = false;
ct->negative = negative;
ct->hash_value = hashValue;
+ ct->lastaccess = catcacheclock;
dlist_push_head(&cache->cc_bucket[hashIndex], &ct->cache_elem);
@@ -1899,7 +1979,12 @@ CatalogCacheCreateEntry(CatCache *cache, HeapTuple ntp, Datum *arguments,
* arbitrarily, we enlarge when fill factor > 2.
*/
if (cache->cc_ntup > cache->cc_nbuckets * 2)
- RehashCatCache(cache);
+ {
+ /* try removing old entries before expanding hash */
+ if (catcacheclock - cache->cc_oldest_ts < prune_min_age_us ||
+ !CatCacheCleanupOldEntries(cache))
+ RehashCatCache(cache);
+ }
return ct;
}
diff --git a/src/backend/utils/misc/guc.c b/src/backend/utils/misc/guc.c
index 17579eeaca..255e9fa73d 100644
--- a/src/backend/utils/misc/guc.c
+++ b/src/backend/utils/misc/guc.c
@@ -88,6 +88,7 @@
#include "utils/acl.h"
#include "utils/builtins.h"
#include "utils/bytea.h"
+#include "utils/catcache.h"
#include "utils/float.h"
#include "utils/guc_tables.h"
#include "utils/memutils.h"
@@ -3445,6 +3446,17 @@ static struct config_int ConfigureNamesInt[] =
NULL, NULL, NULL
},
+ {
+ {"catalog_cache_prune_min_age", PGC_USERSET, RESOURCES_MEM,
+ gettext_noop("System catalog cache entries that are living unused more than this seconds are considered for removal."),
+ gettext_noop("The value of -1 turns off pruning."),
+ GUC_UNIT_S
+ },
+ &catalog_cache_prune_min_age,
+ -1, -1, INT_MAX,
+ NULL, assign_catalog_cache_prune_min_age, NULL
+ },
+
/* End-of-list marker */
{
{NULL, 0, 0, NULL, NULL}, NULL, 0, 0, 0, NULL, NULL, NULL
diff --git a/src/include/utils/catcache.h b/src/include/utils/catcache.h
index ddc2762eb3..291e857e38 100644
--- a/src/include/utils/catcache.h
+++ b/src/include/utils/catcache.h
@@ -22,6 +22,7 @@
#include "access/htup.h"
#include "access/skey.h"
+#include "datatype/timestamp.h"
#include "lib/ilist.h"
#include "utils/relcache.h"
@@ -61,6 +62,7 @@ typedef struct catcache
slist_node cc_next; /* list link */
ScanKeyData cc_skey[CATCACHE_MAXKEYS]; /* precomputed key info for heap
* scans */
+ uint64 cc_oldest_ts; /* timestamp (us) of the oldest tuple */
/*
* Keep these at the end, so that compiling catcache.c with CATCACHE_STATS
@@ -119,6 +121,7 @@ typedef struct catctup
bool dead; /* dead but not yet removed? */
bool negative; /* negative cache entry? */
HeapTupleData tuple; /* tuple management header */
+ uint64 lastaccess; /* timestamp in us of the last usage */
/*
* The tuple may also be a member of at most one CatCList. (If a single
@@ -189,6 +192,22 @@ typedef struct catcacheheader
/* this extern duplicates utils/memutils.h... */
extern PGDLLIMPORT MemoryContext CacheMemoryContext;
+
+/* for guc.c, not PGDLLPMPORT'ed */
+extern int catalog_cache_prune_min_age;
+
+/* source clock for access timestamp of catcache entries */
+extern uint64 catcacheclock;
+
+/* SetCatCacheClock - set catcache timestamp source clock */
+static inline void
+SetCatCacheClock(TimestampTz ts)
+{
+ catcacheclock = (uint64) ts;
+}
+
+extern void assign_catalog_cache_prune_min_age(int newval, void *extra);
+
extern void CreateCacheMemoryContext(void);
extern CatCache *InitCatCache(int id, Oid reloid, Oid indexoid,
--
2.27.0
----Next_Part(Thu_Jan_14_17_32_27_2021_995)--
Content-Type: Text/X-Patch; charset=us-ascii
Content-Transfer-Encoding: 7bit
Content-Disposition: inline;
filename="v6-0002-Remove-dead-flag-from-catcache-tuple.patch"
^ permalink raw reply [nested|flat] 92+ messages in thread
* [PATCH v7 1/3] CatCache expiration feature
@ 2020-11-18 07:54 Kyotaro Horiguchi <[email protected]>
0 siblings, 0 replies; 92+ messages in thread
From: Kyotaro Horiguchi @ 2020-11-18 07:54 UTC (permalink / raw)
---
src/backend/access/transam/xact.c | 3 ++
src/backend/utils/cache/catcache.c | 87 +++++++++++++++++++++++++++++-
src/backend/utils/misc/guc.c | 12 +++++
src/include/utils/catcache.h | 19 +++++++
4 files changed, 120 insertions(+), 1 deletion(-)
diff --git a/src/backend/access/transam/xact.c b/src/backend/access/transam/xact.c
index a2068e3fd4..86888d2409 100644
--- a/src/backend/access/transam/xact.c
+++ b/src/backend/access/transam/xact.c
@@ -1086,6 +1086,9 @@ static void
AtStart_Cache(void)
{
AcceptInvalidationMessages();
+
+ if (xactStartTimestamp != 0)
+ SetCatCacheClock(xactStartTimestamp);
}
/*
diff --git a/src/backend/utils/cache/catcache.c b/src/backend/utils/cache/catcache.c
index fa2b49c676..644d92dd9a 100644
--- a/src/backend/utils/cache/catcache.c
+++ b/src/backend/utils/cache/catcache.c
@@ -38,6 +38,7 @@
#include "utils/rel.h"
#include "utils/resowner_private.h"
#include "utils/syscache.h"
+#include "utils/timestamp.h"
/* #define CACHEDEBUG */ /* turns DEBUG elogs on */
@@ -60,9 +61,19 @@
#define CACHE_elog(...)
#endif
+/*
+ * GUC variable to define the minimum age of entries that will be considered
+ * to be evicted in seconds. -1 to disable the feature.
+ */
+int catalog_cache_prune_min_age = -1;
+uint64 prune_min_age_us;
+
/* Cache management header --- pointer is NULL until created */
static CatCacheHeader *CacheHdr = NULL;
+/* Clock for the last accessed time of a catcache entry. */
+uint64 catcacheclock = 0;
+
static inline HeapTuple SearchCatCacheInternal(CatCache *cache,
int nkeys,
Datum v1, Datum v2,
@@ -74,6 +85,7 @@ static pg_noinline HeapTuple SearchCatCacheMiss(CatCache *cache,
Index hashIndex,
Datum v1, Datum v2,
Datum v3, Datum v4);
+static bool CatCacheCleanupOldEntries(CatCache *cp);
static uint32 CatalogCacheComputeHashValue(CatCache *cache, int nkeys,
Datum v1, Datum v2, Datum v3, Datum v4);
@@ -99,6 +111,15 @@ static void CatCacheFreeKeys(TupleDesc tupdesc, int nkeys, int *attnos,
static void CatCacheCopyKeys(TupleDesc tupdesc, int nkeys, int *attnos,
Datum *srckeys, Datum *dstkeys);
+/* GUC assign function */
+void
+assign_catalog_cache_prune_min_age(int newval, void *extra)
+{
+ if (newval < 0)
+ prune_min_age_us = UINT64_MAX;
+ else
+ prune_min_age_us = ((uint64) newval) * USECS_PER_SEC;
+}
/*
* internal support functions
@@ -1264,6 +1285,9 @@ SearchCatCacheInternal(CatCache *cache,
*/
dlist_move_head(bucket, &ct->cache_elem);
+ /* Record the last access timestamp */
+ ct->lastaccess = catcacheclock;
+
/*
* If it's a positive entry, bump its refcount and return it. If it's
* negative, we can report failure to the caller.
@@ -1425,6 +1449,61 @@ SearchCatCacheMiss(CatCache *cache,
return &ct->tuple;
}
+/*
+ * CatCacheCleanupOldEntries - Remove infrequently-used entries
+ *
+ * Catcache entries happen to be left unused for a long time for several
+ * reasons. Remove such entries to prevent catcache from bloating. It is based
+ * on the similar algorithm with buffer eviction. Entries that are accessed
+ * several times in a certain period live longer than those that have had less
+ * access in the same duration.
+ */
+static bool
+CatCacheCleanupOldEntries(CatCache *cp)
+{
+ int nremoved = 0;
+ int i;
+ long oldest_ts = catcacheclock;
+ uint64 prune_threshold = catcacheclock - prune_min_age_us;
+
+ /* Scan over the whole hash to find entries to remove */
+ for (i = 0 ; i < cp->cc_nbuckets ; i++)
+ {
+ dlist_mutable_iter iter;
+
+ dlist_foreach_modify(iter, &cp->cc_bucket[i])
+ {
+ CatCTup *ct = dlist_container(CatCTup, cache_elem, iter.cur);
+
+ /* Don't remove referenced entries */
+ if (ct->refcount == 0 &&
+ (ct->c_list == NULL || ct->c_list->refcount == 0))
+ {
+ if (ct->lastaccess < prune_threshold)
+ {
+ CatCacheRemoveCTup(cp, ct);
+ nremoved++;
+
+ /* don't let the removed entry update oldest_ts */
+ continue;
+ }
+ }
+
+ /* update the oldest timestamp if the entry remains alive */
+ if (ct->lastaccess < oldest_ts)
+ oldest_ts = ct->lastaccess;
+ }
+ }
+
+ cp->cc_oldest_ts = oldest_ts;
+
+ if (nremoved > 0)
+ elog(DEBUG1, "pruning catalog cache id=%d for %s: removed %d / %d",
+ cp->id, cp->cc_relname, nremoved, cp->cc_ntup + nremoved);
+
+ return nremoved > 0;
+}
+
/*
* ReleaseCatCache
*
@@ -1888,6 +1967,7 @@ CatalogCacheCreateEntry(CatCache *cache, HeapTuple ntp, Datum *arguments,
ct->dead = false;
ct->negative = negative;
ct->hash_value = hashValue;
+ ct->lastaccess = catcacheclock;
dlist_push_head(&cache->cc_bucket[hashIndex], &ct->cache_elem);
@@ -1899,7 +1979,12 @@ CatalogCacheCreateEntry(CatCache *cache, HeapTuple ntp, Datum *arguments,
* arbitrarily, we enlarge when fill factor > 2.
*/
if (cache->cc_ntup > cache->cc_nbuckets * 2)
- RehashCatCache(cache);
+ {
+ /* try removing old entries before expanding hash */
+ if (catcacheclock - cache->cc_oldest_ts < prune_min_age_us ||
+ !CatCacheCleanupOldEntries(cache))
+ RehashCatCache(cache);
+ }
return ct;
}
diff --git a/src/backend/utils/misc/guc.c b/src/backend/utils/misc/guc.c
index 17579eeaca..255e9fa73d 100644
--- a/src/backend/utils/misc/guc.c
+++ b/src/backend/utils/misc/guc.c
@@ -88,6 +88,7 @@
#include "utils/acl.h"
#include "utils/builtins.h"
#include "utils/bytea.h"
+#include "utils/catcache.h"
#include "utils/float.h"
#include "utils/guc_tables.h"
#include "utils/memutils.h"
@@ -3445,6 +3446,17 @@ static struct config_int ConfigureNamesInt[] =
NULL, NULL, NULL
},
+ {
+ {"catalog_cache_prune_min_age", PGC_USERSET, RESOURCES_MEM,
+ gettext_noop("System catalog cache entries that are living unused more than this seconds are considered for removal."),
+ gettext_noop("The value of -1 turns off pruning."),
+ GUC_UNIT_S
+ },
+ &catalog_cache_prune_min_age,
+ -1, -1, INT_MAX,
+ NULL, assign_catalog_cache_prune_min_age, NULL
+ },
+
/* End-of-list marker */
{
{NULL, 0, 0, NULL, NULL}, NULL, 0, 0, 0, NULL, NULL, NULL
diff --git a/src/include/utils/catcache.h b/src/include/utils/catcache.h
index ddc2762eb3..291e857e38 100644
--- a/src/include/utils/catcache.h
+++ b/src/include/utils/catcache.h
@@ -22,6 +22,7 @@
#include "access/htup.h"
#include "access/skey.h"
+#include "datatype/timestamp.h"
#include "lib/ilist.h"
#include "utils/relcache.h"
@@ -61,6 +62,7 @@ typedef struct catcache
slist_node cc_next; /* list link */
ScanKeyData cc_skey[CATCACHE_MAXKEYS]; /* precomputed key info for heap
* scans */
+ uint64 cc_oldest_ts; /* timestamp (us) of the oldest tuple */
/*
* Keep these at the end, so that compiling catcache.c with CATCACHE_STATS
@@ -119,6 +121,7 @@ typedef struct catctup
bool dead; /* dead but not yet removed? */
bool negative; /* negative cache entry? */
HeapTupleData tuple; /* tuple management header */
+ uint64 lastaccess; /* timestamp in us of the last usage */
/*
* The tuple may also be a member of at most one CatCList. (If a single
@@ -189,6 +192,22 @@ typedef struct catcacheheader
/* this extern duplicates utils/memutils.h... */
extern PGDLLIMPORT MemoryContext CacheMemoryContext;
+
+/* for guc.c, not PGDLLPMPORT'ed */
+extern int catalog_cache_prune_min_age;
+
+/* source clock for access timestamp of catcache entries */
+extern uint64 catcacheclock;
+
+/* SetCatCacheClock - set catcache timestamp source clock */
+static inline void
+SetCatCacheClock(TimestampTz ts)
+{
+ catcacheclock = (uint64) ts;
+}
+
+extern void assign_catalog_cache_prune_min_age(int newval, void *extra);
+
extern void CreateCacheMemoryContext(void);
extern CatCache *InitCatCache(int id, Oid reloid, Oid indexoid,
--
2.27.0
----Next_Part(Wed_Jan_27_10_13_08_2021_482)--
Content-Type: Text/X-Patch; charset=us-ascii
Content-Transfer-Encoding: 7bit
Content-Disposition: inline;
filename="v7-0002-Remove-dead-flag-from-catcache-tuple.patch"
^ permalink raw reply [nested|flat] 92+ messages in thread
* [PATCH v5 1/3] CatCache expiration feature
@ 2020-11-18 07:54 Kyotaro Horiguchi <[email protected]>
0 siblings, 0 replies; 92+ messages in thread
From: Kyotaro Horiguchi @ 2020-11-18 07:54 UTC (permalink / raw)
---
src/backend/access/transam/xact.c | 3 ++
src/backend/utils/cache/catcache.c | 87 +++++++++++++++++++++++++++++-
src/backend/utils/misc/guc.c | 12 +++++
src/include/utils/catcache.h | 19 +++++++
4 files changed, 120 insertions(+), 1 deletion(-)
diff --git a/src/backend/access/transam/xact.c b/src/backend/access/transam/xact.c
index 03c553e7ea..4a2a90ce0c 100644
--- a/src/backend/access/transam/xact.c
+++ b/src/backend/access/transam/xact.c
@@ -1086,6 +1086,9 @@ static void
AtStart_Cache(void)
{
AcceptInvalidationMessages();
+
+ if (xactStartTimestamp != 0)
+ SetCatCacheClock(xactStartTimestamp);
}
/*
diff --git a/src/backend/utils/cache/catcache.c b/src/backend/utils/cache/catcache.c
index 3613ae5f44..1ebcc7dcd3 100644
--- a/src/backend/utils/cache/catcache.c
+++ b/src/backend/utils/cache/catcache.c
@@ -38,6 +38,7 @@
#include "utils/rel.h"
#include "utils/resowner_private.h"
#include "utils/syscache.h"
+#include "utils/timestamp.h"
/* #define CACHEDEBUG */ /* turns DEBUG elogs on */
@@ -60,9 +61,19 @@
#define CACHE_elog(...)
#endif
+/*
+ * GUC variable to define the minimum age of entries that will be considered
+ * to be evicted in seconds. -1 to disable the feature.
+ */
+int catalog_cache_prune_min_age = -1;
+uint64 prune_min_age_us;
+
/* Cache management header --- pointer is NULL until created */
static CatCacheHeader *CacheHdr = NULL;
+/* Clock for the last accessed time of a catcache entry. */
+uint64 catcacheclock = 0;
+
static inline HeapTuple SearchCatCacheInternal(CatCache *cache,
int nkeys,
Datum v1, Datum v2,
@@ -74,6 +85,7 @@ static pg_noinline HeapTuple SearchCatCacheMiss(CatCache *cache,
Index hashIndex,
Datum v1, Datum v2,
Datum v3, Datum v4);
+static bool CatCacheCleanupOldEntries(CatCache *cp);
static uint32 CatalogCacheComputeHashValue(CatCache *cache, int nkeys,
Datum v1, Datum v2, Datum v3, Datum v4);
@@ -99,6 +111,15 @@ static void CatCacheFreeKeys(TupleDesc tupdesc, int nkeys, int *attnos,
static void CatCacheCopyKeys(TupleDesc tupdesc, int nkeys, int *attnos,
Datum *srckeys, Datum *dstkeys);
+/* GUC assign function */
+void
+assign_catalog_cache_prune_min_age(int newval, void *extra)
+{
+ if (newval < 0)
+ prune_min_age_us = UINT64_MAX;
+ else
+ prune_min_age_us = ((uint64) newval) * USECS_PER_SEC;
+}
/*
* internal support functions
@@ -1264,6 +1285,9 @@ SearchCatCacheInternal(CatCache *cache,
*/
dlist_move_head(bucket, &ct->cache_elem);
+ /* Record the last access timestamp */
+ ct->lastaccess = catcacheclock;
+
/*
* If it's a positive entry, bump its refcount and return it. If it's
* negative, we can report failure to the caller.
@@ -1425,6 +1449,61 @@ SearchCatCacheMiss(CatCache *cache,
return &ct->tuple;
}
+/*
+ * CatCacheCleanupOldEntries - Remove infrequently-used entries
+ *
+ * Catcache entries happen to be left unused for a long time for several
+ * reasons. Remove such entries to prevent catcache from bloating. It is based
+ * on the similar algorithm with buffer eviction. Entries that are accessed
+ * several times in a certain period live longer than those that have had less
+ * access in the same duration.
+ */
+static bool
+CatCacheCleanupOldEntries(CatCache *cp)
+{
+ int nremoved = 0;
+ int i;
+ long oldest_ts = catcacheclock;
+ uint64 prune_threshold = catcacheclock - prune_min_age_us;
+
+ /* Scan over the whole hash to find entries to remove */
+ for (i = 0 ; i < cp->cc_nbuckets ; i++)
+ {
+ dlist_mutable_iter iter;
+
+ dlist_foreach_modify(iter, &cp->cc_bucket[i])
+ {
+ CatCTup *ct = dlist_container(CatCTup, cache_elem, iter.cur);
+
+ /* Don't remove referenced entries */
+ if (ct->refcount == 0 &&
+ (ct->c_list == NULL || ct->c_list->refcount == 0))
+ {
+ if (ct->lastaccess < prune_threshold)
+ {
+ CatCacheRemoveCTup(cp, ct);
+ nremoved++;
+
+ /* don't let the removed entry update oldest_ts */
+ continue;
+ }
+ }
+
+ /* update the oldest timestamp if the entry remains alive */
+ if (ct->lastaccess < oldest_ts)
+ oldest_ts = ct->lastaccess;
+ }
+ }
+
+ cp->cc_oldest_ts = oldest_ts;
+
+ if (nremoved > 0)
+ elog(DEBUG1, "pruning catalog cache id=%d for %s: removed %d / %d",
+ cp->id, cp->cc_relname, nremoved, cp->cc_ntup + nremoved);
+
+ return nremoved > 0;
+}
+
/*
* ReleaseCatCache
*
@@ -1888,6 +1967,7 @@ CatalogCacheCreateEntry(CatCache *cache, HeapTuple ntp, Datum *arguments,
ct->dead = false;
ct->negative = negative;
ct->hash_value = hashValue;
+ ct->lastaccess = catcacheclock;
dlist_push_head(&cache->cc_bucket[hashIndex], &ct->cache_elem);
@@ -1899,7 +1979,12 @@ CatalogCacheCreateEntry(CatCache *cache, HeapTuple ntp, Datum *arguments,
* arbitrarily, we enlarge when fill factor > 2.
*/
if (cache->cc_ntup > cache->cc_nbuckets * 2)
- RehashCatCache(cache);
+ {
+ /* try removing old entries before expanding hash */
+ if (catcacheclock - cache->cc_oldest_ts < prune_min_age_us ||
+ !CatCacheCleanupOldEntries(cache))
+ RehashCatCache(cache);
+ }
return ct;
}
diff --git a/src/backend/utils/misc/guc.c b/src/backend/utils/misc/guc.c
index bb34630e8e..95213853aa 100644
--- a/src/backend/utils/misc/guc.c
+++ b/src/backend/utils/misc/guc.c
@@ -88,6 +88,7 @@
#include "utils/acl.h"
#include "utils/builtins.h"
#include "utils/bytea.h"
+#include "utils/catcache.h"
#include "utils/float.h"
#include "utils/guc_tables.h"
#include "utils/memutils.h"
@@ -3399,6 +3400,17 @@ static struct config_int ConfigureNamesInt[] =
check_huge_page_size, NULL, NULL
},
+ {
+ {"catalog_cache_prune_min_age", PGC_USERSET, RESOURCES_MEM,
+ gettext_noop("System catalog cache entries that are living unused more than this seconds are considered for removal."),
+ gettext_noop("The value of -1 turns off pruning."),
+ GUC_UNIT_S
+ },
+ &catalog_cache_prune_min_age,
+ -1, -1, INT_MAX,
+ NULL, assign_catalog_cache_prune_min_age, NULL
+ },
+
/* End-of-list marker */
{
{NULL, 0, 0, NULL, NULL}, NULL, 0, 0, 0, NULL, NULL, NULL
diff --git a/src/include/utils/catcache.h b/src/include/utils/catcache.h
index f4aa316604..81587c3fe6 100644
--- a/src/include/utils/catcache.h
+++ b/src/include/utils/catcache.h
@@ -22,6 +22,7 @@
#include "access/htup.h"
#include "access/skey.h"
+#include "datatype/timestamp.h"
#include "lib/ilist.h"
#include "utils/relcache.h"
@@ -61,6 +62,7 @@ typedef struct catcache
slist_node cc_next; /* list link */
ScanKeyData cc_skey[CATCACHE_MAXKEYS]; /* precomputed key info for heap
* scans */
+ uint64 cc_oldest_ts; /* timestamp (us) of the oldest tuple */
/*
* Keep these at the end, so that compiling catcache.c with CATCACHE_STATS
@@ -119,6 +121,7 @@ typedef struct catctup
bool dead; /* dead but not yet removed? */
bool negative; /* negative cache entry? */
HeapTupleData tuple; /* tuple management header */
+ uint64 lastaccess; /* timestamp in us of the last usage */
/*
* The tuple may also be a member of at most one CatCList. (If a single
@@ -189,6 +192,22 @@ typedef struct catcacheheader
/* this extern duplicates utils/memutils.h... */
extern PGDLLIMPORT MemoryContext CacheMemoryContext;
+
+/* for guc.c, not PGDLLPMPORT'ed */
+extern int catalog_cache_prune_min_age;
+
+/* source clock for access timestamp of catcache entries */
+extern uint64 catcacheclock;
+
+/* SetCatCacheClock - set catcache timestamp source clock */
+static inline void
+SetCatCacheClock(TimestampTz ts)
+{
+ catcacheclock = (uint64) ts;
+}
+
+extern void assign_catalog_cache_prune_min_age(int newval, void *extra);
+
extern void CreateCacheMemoryContext(void);
extern CatCache *InitCatCache(int id, Oid reloid, Oid indexoid,
--
2.18.4
----Next_Part(Thu_Nov_19_14_25_36_2020_063)--
Content-Type: Text/X-Patch; charset=us-ascii
Content-Transfer-Encoding: 7bit
Content-Disposition: inline;
filename="v5-0002-Remove-dead-flag-from-catcache-tuple.patch"
^ permalink raw reply [nested|flat] 92+ messages in thread
* [PATCH v7 1/3] CatCache expiration feature
@ 2020-11-18 07:54 Kyotaro Horiguchi <[email protected]>
0 siblings, 0 replies; 92+ messages in thread
From: Kyotaro Horiguchi @ 2020-11-18 07:54 UTC (permalink / raw)
---
src/backend/access/transam/xact.c | 3 ++
src/backend/utils/cache/catcache.c | 87 +++++++++++++++++++++++++++++-
src/backend/utils/misc/guc.c | 12 +++++
src/include/utils/catcache.h | 19 +++++++
4 files changed, 120 insertions(+), 1 deletion(-)
diff --git a/src/backend/access/transam/xact.c b/src/backend/access/transam/xact.c
index a2068e3fd4..86888d2409 100644
--- a/src/backend/access/transam/xact.c
+++ b/src/backend/access/transam/xact.c
@@ -1086,6 +1086,9 @@ static void
AtStart_Cache(void)
{
AcceptInvalidationMessages();
+
+ if (xactStartTimestamp != 0)
+ SetCatCacheClock(xactStartTimestamp);
}
/*
diff --git a/src/backend/utils/cache/catcache.c b/src/backend/utils/cache/catcache.c
index fa2b49c676..644d92dd9a 100644
--- a/src/backend/utils/cache/catcache.c
+++ b/src/backend/utils/cache/catcache.c
@@ -38,6 +38,7 @@
#include "utils/rel.h"
#include "utils/resowner_private.h"
#include "utils/syscache.h"
+#include "utils/timestamp.h"
/* #define CACHEDEBUG */ /* turns DEBUG elogs on */
@@ -60,9 +61,19 @@
#define CACHE_elog(...)
#endif
+/*
+ * GUC variable to define the minimum age of entries that will be considered
+ * to be evicted in seconds. -1 to disable the feature.
+ */
+int catalog_cache_prune_min_age = -1;
+uint64 prune_min_age_us;
+
/* Cache management header --- pointer is NULL until created */
static CatCacheHeader *CacheHdr = NULL;
+/* Clock for the last accessed time of a catcache entry. */
+uint64 catcacheclock = 0;
+
static inline HeapTuple SearchCatCacheInternal(CatCache *cache,
int nkeys,
Datum v1, Datum v2,
@@ -74,6 +85,7 @@ static pg_noinline HeapTuple SearchCatCacheMiss(CatCache *cache,
Index hashIndex,
Datum v1, Datum v2,
Datum v3, Datum v4);
+static bool CatCacheCleanupOldEntries(CatCache *cp);
static uint32 CatalogCacheComputeHashValue(CatCache *cache, int nkeys,
Datum v1, Datum v2, Datum v3, Datum v4);
@@ -99,6 +111,15 @@ static void CatCacheFreeKeys(TupleDesc tupdesc, int nkeys, int *attnos,
static void CatCacheCopyKeys(TupleDesc tupdesc, int nkeys, int *attnos,
Datum *srckeys, Datum *dstkeys);
+/* GUC assign function */
+void
+assign_catalog_cache_prune_min_age(int newval, void *extra)
+{
+ if (newval < 0)
+ prune_min_age_us = UINT64_MAX;
+ else
+ prune_min_age_us = ((uint64) newval) * USECS_PER_SEC;
+}
/*
* internal support functions
@@ -1264,6 +1285,9 @@ SearchCatCacheInternal(CatCache *cache,
*/
dlist_move_head(bucket, &ct->cache_elem);
+ /* Record the last access timestamp */
+ ct->lastaccess = catcacheclock;
+
/*
* If it's a positive entry, bump its refcount and return it. If it's
* negative, we can report failure to the caller.
@@ -1425,6 +1449,61 @@ SearchCatCacheMiss(CatCache *cache,
return &ct->tuple;
}
+/*
+ * CatCacheCleanupOldEntries - Remove infrequently-used entries
+ *
+ * Catcache entries happen to be left unused for a long time for several
+ * reasons. Remove such entries to prevent catcache from bloating. It is based
+ * on the similar algorithm with buffer eviction. Entries that are accessed
+ * several times in a certain period live longer than those that have had less
+ * access in the same duration.
+ */
+static bool
+CatCacheCleanupOldEntries(CatCache *cp)
+{
+ int nremoved = 0;
+ int i;
+ long oldest_ts = catcacheclock;
+ uint64 prune_threshold = catcacheclock - prune_min_age_us;
+
+ /* Scan over the whole hash to find entries to remove */
+ for (i = 0 ; i < cp->cc_nbuckets ; i++)
+ {
+ dlist_mutable_iter iter;
+
+ dlist_foreach_modify(iter, &cp->cc_bucket[i])
+ {
+ CatCTup *ct = dlist_container(CatCTup, cache_elem, iter.cur);
+
+ /* Don't remove referenced entries */
+ if (ct->refcount == 0 &&
+ (ct->c_list == NULL || ct->c_list->refcount == 0))
+ {
+ if (ct->lastaccess < prune_threshold)
+ {
+ CatCacheRemoveCTup(cp, ct);
+ nremoved++;
+
+ /* don't let the removed entry update oldest_ts */
+ continue;
+ }
+ }
+
+ /* update the oldest timestamp if the entry remains alive */
+ if (ct->lastaccess < oldest_ts)
+ oldest_ts = ct->lastaccess;
+ }
+ }
+
+ cp->cc_oldest_ts = oldest_ts;
+
+ if (nremoved > 0)
+ elog(DEBUG1, "pruning catalog cache id=%d for %s: removed %d / %d",
+ cp->id, cp->cc_relname, nremoved, cp->cc_ntup + nremoved);
+
+ return nremoved > 0;
+}
+
/*
* ReleaseCatCache
*
@@ -1888,6 +1967,7 @@ CatalogCacheCreateEntry(CatCache *cache, HeapTuple ntp, Datum *arguments,
ct->dead = false;
ct->negative = negative;
ct->hash_value = hashValue;
+ ct->lastaccess = catcacheclock;
dlist_push_head(&cache->cc_bucket[hashIndex], &ct->cache_elem);
@@ -1899,7 +1979,12 @@ CatalogCacheCreateEntry(CatCache *cache, HeapTuple ntp, Datum *arguments,
* arbitrarily, we enlarge when fill factor > 2.
*/
if (cache->cc_ntup > cache->cc_nbuckets * 2)
- RehashCatCache(cache);
+ {
+ /* try removing old entries before expanding hash */
+ if (catcacheclock - cache->cc_oldest_ts < prune_min_age_us ||
+ !CatCacheCleanupOldEntries(cache))
+ RehashCatCache(cache);
+ }
return ct;
}
diff --git a/src/backend/utils/misc/guc.c b/src/backend/utils/misc/guc.c
index 17579eeaca..255e9fa73d 100644
--- a/src/backend/utils/misc/guc.c
+++ b/src/backend/utils/misc/guc.c
@@ -88,6 +88,7 @@
#include "utils/acl.h"
#include "utils/builtins.h"
#include "utils/bytea.h"
+#include "utils/catcache.h"
#include "utils/float.h"
#include "utils/guc_tables.h"
#include "utils/memutils.h"
@@ -3445,6 +3446,17 @@ static struct config_int ConfigureNamesInt[] =
NULL, NULL, NULL
},
+ {
+ {"catalog_cache_prune_min_age", PGC_USERSET, RESOURCES_MEM,
+ gettext_noop("System catalog cache entries that are living unused more than this seconds are considered for removal."),
+ gettext_noop("The value of -1 turns off pruning."),
+ GUC_UNIT_S
+ },
+ &catalog_cache_prune_min_age,
+ -1, -1, INT_MAX,
+ NULL, assign_catalog_cache_prune_min_age, NULL
+ },
+
/* End-of-list marker */
{
{NULL, 0, 0, NULL, NULL}, NULL, 0, 0, 0, NULL, NULL, NULL
diff --git a/src/include/utils/catcache.h b/src/include/utils/catcache.h
index ddc2762eb3..291e857e38 100644
--- a/src/include/utils/catcache.h
+++ b/src/include/utils/catcache.h
@@ -22,6 +22,7 @@
#include "access/htup.h"
#include "access/skey.h"
+#include "datatype/timestamp.h"
#include "lib/ilist.h"
#include "utils/relcache.h"
@@ -61,6 +62,7 @@ typedef struct catcache
slist_node cc_next; /* list link */
ScanKeyData cc_skey[CATCACHE_MAXKEYS]; /* precomputed key info for heap
* scans */
+ uint64 cc_oldest_ts; /* timestamp (us) of the oldest tuple */
/*
* Keep these at the end, so that compiling catcache.c with CATCACHE_STATS
@@ -119,6 +121,7 @@ typedef struct catctup
bool dead; /* dead but not yet removed? */
bool negative; /* negative cache entry? */
HeapTupleData tuple; /* tuple management header */
+ uint64 lastaccess; /* timestamp in us of the last usage */
/*
* The tuple may also be a member of at most one CatCList. (If a single
@@ -189,6 +192,22 @@ typedef struct catcacheheader
/* this extern duplicates utils/memutils.h... */
extern PGDLLIMPORT MemoryContext CacheMemoryContext;
+
+/* for guc.c, not PGDLLPMPORT'ed */
+extern int catalog_cache_prune_min_age;
+
+/* source clock for access timestamp of catcache entries */
+extern uint64 catcacheclock;
+
+/* SetCatCacheClock - set catcache timestamp source clock */
+static inline void
+SetCatCacheClock(TimestampTz ts)
+{
+ catcacheclock = (uint64) ts;
+}
+
+extern void assign_catalog_cache_prune_min_age(int newval, void *extra);
+
extern void CreateCacheMemoryContext(void);
extern CatCache *InitCatCache(int id, Oid reloid, Oid indexoid,
--
2.27.0
----Next_Part(Wed_Jan_27_10_13_08_2021_482)--
Content-Type: Text/X-Patch; charset=us-ascii
Content-Transfer-Encoding: 7bit
Content-Disposition: inline;
filename="v7-0002-Remove-dead-flag-from-catcache-tuple.patch"
^ permalink raw reply [nested|flat] 92+ messages in thread
* [PATCH v6 1/3] CatCache expiration feature
@ 2020-11-18 07:54 Kyotaro Horiguchi <[email protected]>
0 siblings, 0 replies; 92+ messages in thread
From: Kyotaro Horiguchi @ 2020-11-18 07:54 UTC (permalink / raw)
---
src/backend/access/transam/xact.c | 3 ++
src/backend/utils/cache/catcache.c | 87 +++++++++++++++++++++++++++++-
src/backend/utils/misc/guc.c | 12 +++++
src/include/utils/catcache.h | 19 +++++++
4 files changed, 120 insertions(+), 1 deletion(-)
diff --git a/src/backend/access/transam/xact.c b/src/backend/access/transam/xact.c
index a2068e3fd4..86888d2409 100644
--- a/src/backend/access/transam/xact.c
+++ b/src/backend/access/transam/xact.c
@@ -1086,6 +1086,9 @@ static void
AtStart_Cache(void)
{
AcceptInvalidationMessages();
+
+ if (xactStartTimestamp != 0)
+ SetCatCacheClock(xactStartTimestamp);
}
/*
diff --git a/src/backend/utils/cache/catcache.c b/src/backend/utils/cache/catcache.c
index fa2b49c676..644d92dd9a 100644
--- a/src/backend/utils/cache/catcache.c
+++ b/src/backend/utils/cache/catcache.c
@@ -38,6 +38,7 @@
#include "utils/rel.h"
#include "utils/resowner_private.h"
#include "utils/syscache.h"
+#include "utils/timestamp.h"
/* #define CACHEDEBUG */ /* turns DEBUG elogs on */
@@ -60,9 +61,19 @@
#define CACHE_elog(...)
#endif
+/*
+ * GUC variable to define the minimum age of entries that will be considered
+ * to be evicted in seconds. -1 to disable the feature.
+ */
+int catalog_cache_prune_min_age = -1;
+uint64 prune_min_age_us;
+
/* Cache management header --- pointer is NULL until created */
static CatCacheHeader *CacheHdr = NULL;
+/* Clock for the last accessed time of a catcache entry. */
+uint64 catcacheclock = 0;
+
static inline HeapTuple SearchCatCacheInternal(CatCache *cache,
int nkeys,
Datum v1, Datum v2,
@@ -74,6 +85,7 @@ static pg_noinline HeapTuple SearchCatCacheMiss(CatCache *cache,
Index hashIndex,
Datum v1, Datum v2,
Datum v3, Datum v4);
+static bool CatCacheCleanupOldEntries(CatCache *cp);
static uint32 CatalogCacheComputeHashValue(CatCache *cache, int nkeys,
Datum v1, Datum v2, Datum v3, Datum v4);
@@ -99,6 +111,15 @@ static void CatCacheFreeKeys(TupleDesc tupdesc, int nkeys, int *attnos,
static void CatCacheCopyKeys(TupleDesc tupdesc, int nkeys, int *attnos,
Datum *srckeys, Datum *dstkeys);
+/* GUC assign function */
+void
+assign_catalog_cache_prune_min_age(int newval, void *extra)
+{
+ if (newval < 0)
+ prune_min_age_us = UINT64_MAX;
+ else
+ prune_min_age_us = ((uint64) newval) * USECS_PER_SEC;
+}
/*
* internal support functions
@@ -1264,6 +1285,9 @@ SearchCatCacheInternal(CatCache *cache,
*/
dlist_move_head(bucket, &ct->cache_elem);
+ /* Record the last access timestamp */
+ ct->lastaccess = catcacheclock;
+
/*
* If it's a positive entry, bump its refcount and return it. If it's
* negative, we can report failure to the caller.
@@ -1425,6 +1449,61 @@ SearchCatCacheMiss(CatCache *cache,
return &ct->tuple;
}
+/*
+ * CatCacheCleanupOldEntries - Remove infrequently-used entries
+ *
+ * Catcache entries happen to be left unused for a long time for several
+ * reasons. Remove such entries to prevent catcache from bloating. It is based
+ * on the similar algorithm with buffer eviction. Entries that are accessed
+ * several times in a certain period live longer than those that have had less
+ * access in the same duration.
+ */
+static bool
+CatCacheCleanupOldEntries(CatCache *cp)
+{
+ int nremoved = 0;
+ int i;
+ long oldest_ts = catcacheclock;
+ uint64 prune_threshold = catcacheclock - prune_min_age_us;
+
+ /* Scan over the whole hash to find entries to remove */
+ for (i = 0 ; i < cp->cc_nbuckets ; i++)
+ {
+ dlist_mutable_iter iter;
+
+ dlist_foreach_modify(iter, &cp->cc_bucket[i])
+ {
+ CatCTup *ct = dlist_container(CatCTup, cache_elem, iter.cur);
+
+ /* Don't remove referenced entries */
+ if (ct->refcount == 0 &&
+ (ct->c_list == NULL || ct->c_list->refcount == 0))
+ {
+ if (ct->lastaccess < prune_threshold)
+ {
+ CatCacheRemoveCTup(cp, ct);
+ nremoved++;
+
+ /* don't let the removed entry update oldest_ts */
+ continue;
+ }
+ }
+
+ /* update the oldest timestamp if the entry remains alive */
+ if (ct->lastaccess < oldest_ts)
+ oldest_ts = ct->lastaccess;
+ }
+ }
+
+ cp->cc_oldest_ts = oldest_ts;
+
+ if (nremoved > 0)
+ elog(DEBUG1, "pruning catalog cache id=%d for %s: removed %d / %d",
+ cp->id, cp->cc_relname, nremoved, cp->cc_ntup + nremoved);
+
+ return nremoved > 0;
+}
+
/*
* ReleaseCatCache
*
@@ -1888,6 +1967,7 @@ CatalogCacheCreateEntry(CatCache *cache, HeapTuple ntp, Datum *arguments,
ct->dead = false;
ct->negative = negative;
ct->hash_value = hashValue;
+ ct->lastaccess = catcacheclock;
dlist_push_head(&cache->cc_bucket[hashIndex], &ct->cache_elem);
@@ -1899,7 +1979,12 @@ CatalogCacheCreateEntry(CatCache *cache, HeapTuple ntp, Datum *arguments,
* arbitrarily, we enlarge when fill factor > 2.
*/
if (cache->cc_ntup > cache->cc_nbuckets * 2)
- RehashCatCache(cache);
+ {
+ /* try removing old entries before expanding hash */
+ if (catcacheclock - cache->cc_oldest_ts < prune_min_age_us ||
+ !CatCacheCleanupOldEntries(cache))
+ RehashCatCache(cache);
+ }
return ct;
}
diff --git a/src/backend/utils/misc/guc.c b/src/backend/utils/misc/guc.c
index 17579eeaca..255e9fa73d 100644
--- a/src/backend/utils/misc/guc.c
+++ b/src/backend/utils/misc/guc.c
@@ -88,6 +88,7 @@
#include "utils/acl.h"
#include "utils/builtins.h"
#include "utils/bytea.h"
+#include "utils/catcache.h"
#include "utils/float.h"
#include "utils/guc_tables.h"
#include "utils/memutils.h"
@@ -3445,6 +3446,17 @@ static struct config_int ConfigureNamesInt[] =
NULL, NULL, NULL
},
+ {
+ {"catalog_cache_prune_min_age", PGC_USERSET, RESOURCES_MEM,
+ gettext_noop("System catalog cache entries that are living unused more than this seconds are considered for removal."),
+ gettext_noop("The value of -1 turns off pruning."),
+ GUC_UNIT_S
+ },
+ &catalog_cache_prune_min_age,
+ -1, -1, INT_MAX,
+ NULL, assign_catalog_cache_prune_min_age, NULL
+ },
+
/* End-of-list marker */
{
{NULL, 0, 0, NULL, NULL}, NULL, 0, 0, 0, NULL, NULL, NULL
diff --git a/src/include/utils/catcache.h b/src/include/utils/catcache.h
index ddc2762eb3..291e857e38 100644
--- a/src/include/utils/catcache.h
+++ b/src/include/utils/catcache.h
@@ -22,6 +22,7 @@
#include "access/htup.h"
#include "access/skey.h"
+#include "datatype/timestamp.h"
#include "lib/ilist.h"
#include "utils/relcache.h"
@@ -61,6 +62,7 @@ typedef struct catcache
slist_node cc_next; /* list link */
ScanKeyData cc_skey[CATCACHE_MAXKEYS]; /* precomputed key info for heap
* scans */
+ uint64 cc_oldest_ts; /* timestamp (us) of the oldest tuple */
/*
* Keep these at the end, so that compiling catcache.c with CATCACHE_STATS
@@ -119,6 +121,7 @@ typedef struct catctup
bool dead; /* dead but not yet removed? */
bool negative; /* negative cache entry? */
HeapTupleData tuple; /* tuple management header */
+ uint64 lastaccess; /* timestamp in us of the last usage */
/*
* The tuple may also be a member of at most one CatCList. (If a single
@@ -189,6 +192,22 @@ typedef struct catcacheheader
/* this extern duplicates utils/memutils.h... */
extern PGDLLIMPORT MemoryContext CacheMemoryContext;
+
+/* for guc.c, not PGDLLPMPORT'ed */
+extern int catalog_cache_prune_min_age;
+
+/* source clock for access timestamp of catcache entries */
+extern uint64 catcacheclock;
+
+/* SetCatCacheClock - set catcache timestamp source clock */
+static inline void
+SetCatCacheClock(TimestampTz ts)
+{
+ catcacheclock = (uint64) ts;
+}
+
+extern void assign_catalog_cache_prune_min_age(int newval, void *extra);
+
extern void CreateCacheMemoryContext(void);
extern CatCache *InitCatCache(int id, Oid reloid, Oid indexoid,
--
2.27.0
----Next_Part(Thu_Jan_14_17_32_27_2021_995)--
Content-Type: Text/X-Patch; charset=us-ascii
Content-Transfer-Encoding: 7bit
Content-Disposition: inline;
filename="v6-0002-Remove-dead-flag-from-catcache-tuple.patch"
^ permalink raw reply [nested|flat] 92+ messages in thread
* [PATCH v6 1/3] CatCache expiration feature
@ 2020-11-18 07:54 Kyotaro Horiguchi <[email protected]>
0 siblings, 0 replies; 92+ messages in thread
From: Kyotaro Horiguchi @ 2020-11-18 07:54 UTC (permalink / raw)
---
src/backend/access/transam/xact.c | 3 ++
src/backend/utils/cache/catcache.c | 87 +++++++++++++++++++++++++++++-
src/backend/utils/misc/guc.c | 12 +++++
src/include/utils/catcache.h | 19 +++++++
4 files changed, 120 insertions(+), 1 deletion(-)
diff --git a/src/backend/access/transam/xact.c b/src/backend/access/transam/xact.c
index a2068e3fd4..86888d2409 100644
--- a/src/backend/access/transam/xact.c
+++ b/src/backend/access/transam/xact.c
@@ -1086,6 +1086,9 @@ static void
AtStart_Cache(void)
{
AcceptInvalidationMessages();
+
+ if (xactStartTimestamp != 0)
+ SetCatCacheClock(xactStartTimestamp);
}
/*
diff --git a/src/backend/utils/cache/catcache.c b/src/backend/utils/cache/catcache.c
index fa2b49c676..644d92dd9a 100644
--- a/src/backend/utils/cache/catcache.c
+++ b/src/backend/utils/cache/catcache.c
@@ -38,6 +38,7 @@
#include "utils/rel.h"
#include "utils/resowner_private.h"
#include "utils/syscache.h"
+#include "utils/timestamp.h"
/* #define CACHEDEBUG */ /* turns DEBUG elogs on */
@@ -60,9 +61,19 @@
#define CACHE_elog(...)
#endif
+/*
+ * GUC variable to define the minimum age of entries that will be considered
+ * to be evicted in seconds. -1 to disable the feature.
+ */
+int catalog_cache_prune_min_age = -1;
+uint64 prune_min_age_us;
+
/* Cache management header --- pointer is NULL until created */
static CatCacheHeader *CacheHdr = NULL;
+/* Clock for the last accessed time of a catcache entry. */
+uint64 catcacheclock = 0;
+
static inline HeapTuple SearchCatCacheInternal(CatCache *cache,
int nkeys,
Datum v1, Datum v2,
@@ -74,6 +85,7 @@ static pg_noinline HeapTuple SearchCatCacheMiss(CatCache *cache,
Index hashIndex,
Datum v1, Datum v2,
Datum v3, Datum v4);
+static bool CatCacheCleanupOldEntries(CatCache *cp);
static uint32 CatalogCacheComputeHashValue(CatCache *cache, int nkeys,
Datum v1, Datum v2, Datum v3, Datum v4);
@@ -99,6 +111,15 @@ static void CatCacheFreeKeys(TupleDesc tupdesc, int nkeys, int *attnos,
static void CatCacheCopyKeys(TupleDesc tupdesc, int nkeys, int *attnos,
Datum *srckeys, Datum *dstkeys);
+/* GUC assign function */
+void
+assign_catalog_cache_prune_min_age(int newval, void *extra)
+{
+ if (newval < 0)
+ prune_min_age_us = UINT64_MAX;
+ else
+ prune_min_age_us = ((uint64) newval) * USECS_PER_SEC;
+}
/*
* internal support functions
@@ -1264,6 +1285,9 @@ SearchCatCacheInternal(CatCache *cache,
*/
dlist_move_head(bucket, &ct->cache_elem);
+ /* Record the last access timestamp */
+ ct->lastaccess = catcacheclock;
+
/*
* If it's a positive entry, bump its refcount and return it. If it's
* negative, we can report failure to the caller.
@@ -1425,6 +1449,61 @@ SearchCatCacheMiss(CatCache *cache,
return &ct->tuple;
}
+/*
+ * CatCacheCleanupOldEntries - Remove infrequently-used entries
+ *
+ * Catcache entries happen to be left unused for a long time for several
+ * reasons. Remove such entries to prevent catcache from bloating. It is based
+ * on the similar algorithm with buffer eviction. Entries that are accessed
+ * several times in a certain period live longer than those that have had less
+ * access in the same duration.
+ */
+static bool
+CatCacheCleanupOldEntries(CatCache *cp)
+{
+ int nremoved = 0;
+ int i;
+ long oldest_ts = catcacheclock;
+ uint64 prune_threshold = catcacheclock - prune_min_age_us;
+
+ /* Scan over the whole hash to find entries to remove */
+ for (i = 0 ; i < cp->cc_nbuckets ; i++)
+ {
+ dlist_mutable_iter iter;
+
+ dlist_foreach_modify(iter, &cp->cc_bucket[i])
+ {
+ CatCTup *ct = dlist_container(CatCTup, cache_elem, iter.cur);
+
+ /* Don't remove referenced entries */
+ if (ct->refcount == 0 &&
+ (ct->c_list == NULL || ct->c_list->refcount == 0))
+ {
+ if (ct->lastaccess < prune_threshold)
+ {
+ CatCacheRemoveCTup(cp, ct);
+ nremoved++;
+
+ /* don't let the removed entry update oldest_ts */
+ continue;
+ }
+ }
+
+ /* update the oldest timestamp if the entry remains alive */
+ if (ct->lastaccess < oldest_ts)
+ oldest_ts = ct->lastaccess;
+ }
+ }
+
+ cp->cc_oldest_ts = oldest_ts;
+
+ if (nremoved > 0)
+ elog(DEBUG1, "pruning catalog cache id=%d for %s: removed %d / %d",
+ cp->id, cp->cc_relname, nremoved, cp->cc_ntup + nremoved);
+
+ return nremoved > 0;
+}
+
/*
* ReleaseCatCache
*
@@ -1888,6 +1967,7 @@ CatalogCacheCreateEntry(CatCache *cache, HeapTuple ntp, Datum *arguments,
ct->dead = false;
ct->negative = negative;
ct->hash_value = hashValue;
+ ct->lastaccess = catcacheclock;
dlist_push_head(&cache->cc_bucket[hashIndex], &ct->cache_elem);
@@ -1899,7 +1979,12 @@ CatalogCacheCreateEntry(CatCache *cache, HeapTuple ntp, Datum *arguments,
* arbitrarily, we enlarge when fill factor > 2.
*/
if (cache->cc_ntup > cache->cc_nbuckets * 2)
- RehashCatCache(cache);
+ {
+ /* try removing old entries before expanding hash */
+ if (catcacheclock - cache->cc_oldest_ts < prune_min_age_us ||
+ !CatCacheCleanupOldEntries(cache))
+ RehashCatCache(cache);
+ }
return ct;
}
diff --git a/src/backend/utils/misc/guc.c b/src/backend/utils/misc/guc.c
index 17579eeaca..255e9fa73d 100644
--- a/src/backend/utils/misc/guc.c
+++ b/src/backend/utils/misc/guc.c
@@ -88,6 +88,7 @@
#include "utils/acl.h"
#include "utils/builtins.h"
#include "utils/bytea.h"
+#include "utils/catcache.h"
#include "utils/float.h"
#include "utils/guc_tables.h"
#include "utils/memutils.h"
@@ -3445,6 +3446,17 @@ static struct config_int ConfigureNamesInt[] =
NULL, NULL, NULL
},
+ {
+ {"catalog_cache_prune_min_age", PGC_USERSET, RESOURCES_MEM,
+ gettext_noop("System catalog cache entries that are living unused more than this seconds are considered for removal."),
+ gettext_noop("The value of -1 turns off pruning."),
+ GUC_UNIT_S
+ },
+ &catalog_cache_prune_min_age,
+ -1, -1, INT_MAX,
+ NULL, assign_catalog_cache_prune_min_age, NULL
+ },
+
/* End-of-list marker */
{
{NULL, 0, 0, NULL, NULL}, NULL, 0, 0, 0, NULL, NULL, NULL
diff --git a/src/include/utils/catcache.h b/src/include/utils/catcache.h
index ddc2762eb3..291e857e38 100644
--- a/src/include/utils/catcache.h
+++ b/src/include/utils/catcache.h
@@ -22,6 +22,7 @@
#include "access/htup.h"
#include "access/skey.h"
+#include "datatype/timestamp.h"
#include "lib/ilist.h"
#include "utils/relcache.h"
@@ -61,6 +62,7 @@ typedef struct catcache
slist_node cc_next; /* list link */
ScanKeyData cc_skey[CATCACHE_MAXKEYS]; /* precomputed key info for heap
* scans */
+ uint64 cc_oldest_ts; /* timestamp (us) of the oldest tuple */
/*
* Keep these at the end, so that compiling catcache.c with CATCACHE_STATS
@@ -119,6 +121,7 @@ typedef struct catctup
bool dead; /* dead but not yet removed? */
bool negative; /* negative cache entry? */
HeapTupleData tuple; /* tuple management header */
+ uint64 lastaccess; /* timestamp in us of the last usage */
/*
* The tuple may also be a member of at most one CatCList. (If a single
@@ -189,6 +192,22 @@ typedef struct catcacheheader
/* this extern duplicates utils/memutils.h... */
extern PGDLLIMPORT MemoryContext CacheMemoryContext;
+
+/* for guc.c, not PGDLLPMPORT'ed */
+extern int catalog_cache_prune_min_age;
+
+/* source clock for access timestamp of catcache entries */
+extern uint64 catcacheclock;
+
+/* SetCatCacheClock - set catcache timestamp source clock */
+static inline void
+SetCatCacheClock(TimestampTz ts)
+{
+ catcacheclock = (uint64) ts;
+}
+
+extern void assign_catalog_cache_prune_min_age(int newval, void *extra);
+
extern void CreateCacheMemoryContext(void);
extern CatCache *InitCatCache(int id, Oid reloid, Oid indexoid,
--
2.27.0
----Next_Part(Thu_Jan_14_17_32_27_2021_995)--
Content-Type: Text/X-Patch; charset=us-ascii
Content-Transfer-Encoding: 7bit
Content-Disposition: inline;
filename="v6-0002-Remove-dead-flag-from-catcache-tuple.patch"
^ permalink raw reply [nested|flat] 92+ messages in thread
* [PATCH v6 1/3] CatCache expiration feature
@ 2020-11-18 07:54 Kyotaro Horiguchi <[email protected]>
0 siblings, 0 replies; 92+ messages in thread
From: Kyotaro Horiguchi @ 2020-11-18 07:54 UTC (permalink / raw)
---
src/backend/access/transam/xact.c | 3 ++
src/backend/utils/cache/catcache.c | 87 +++++++++++++++++++++++++++++-
src/backend/utils/misc/guc.c | 12 +++++
src/include/utils/catcache.h | 19 +++++++
4 files changed, 120 insertions(+), 1 deletion(-)
diff --git a/src/backend/access/transam/xact.c b/src/backend/access/transam/xact.c
index a2068e3fd4..86888d2409 100644
--- a/src/backend/access/transam/xact.c
+++ b/src/backend/access/transam/xact.c
@@ -1086,6 +1086,9 @@ static void
AtStart_Cache(void)
{
AcceptInvalidationMessages();
+
+ if (xactStartTimestamp != 0)
+ SetCatCacheClock(xactStartTimestamp);
}
/*
diff --git a/src/backend/utils/cache/catcache.c b/src/backend/utils/cache/catcache.c
index fa2b49c676..644d92dd9a 100644
--- a/src/backend/utils/cache/catcache.c
+++ b/src/backend/utils/cache/catcache.c
@@ -38,6 +38,7 @@
#include "utils/rel.h"
#include "utils/resowner_private.h"
#include "utils/syscache.h"
+#include "utils/timestamp.h"
/* #define CACHEDEBUG */ /* turns DEBUG elogs on */
@@ -60,9 +61,19 @@
#define CACHE_elog(...)
#endif
+/*
+ * GUC variable to define the minimum age of entries that will be considered
+ * to be evicted in seconds. -1 to disable the feature.
+ */
+int catalog_cache_prune_min_age = -1;
+uint64 prune_min_age_us;
+
/* Cache management header --- pointer is NULL until created */
static CatCacheHeader *CacheHdr = NULL;
+/* Clock for the last accessed time of a catcache entry. */
+uint64 catcacheclock = 0;
+
static inline HeapTuple SearchCatCacheInternal(CatCache *cache,
int nkeys,
Datum v1, Datum v2,
@@ -74,6 +85,7 @@ static pg_noinline HeapTuple SearchCatCacheMiss(CatCache *cache,
Index hashIndex,
Datum v1, Datum v2,
Datum v3, Datum v4);
+static bool CatCacheCleanupOldEntries(CatCache *cp);
static uint32 CatalogCacheComputeHashValue(CatCache *cache, int nkeys,
Datum v1, Datum v2, Datum v3, Datum v4);
@@ -99,6 +111,15 @@ static void CatCacheFreeKeys(TupleDesc tupdesc, int nkeys, int *attnos,
static void CatCacheCopyKeys(TupleDesc tupdesc, int nkeys, int *attnos,
Datum *srckeys, Datum *dstkeys);
+/* GUC assign function */
+void
+assign_catalog_cache_prune_min_age(int newval, void *extra)
+{
+ if (newval < 0)
+ prune_min_age_us = UINT64_MAX;
+ else
+ prune_min_age_us = ((uint64) newval) * USECS_PER_SEC;
+}
/*
* internal support functions
@@ -1264,6 +1285,9 @@ SearchCatCacheInternal(CatCache *cache,
*/
dlist_move_head(bucket, &ct->cache_elem);
+ /* Record the last access timestamp */
+ ct->lastaccess = catcacheclock;
+
/*
* If it's a positive entry, bump its refcount and return it. If it's
* negative, we can report failure to the caller.
@@ -1425,6 +1449,61 @@ SearchCatCacheMiss(CatCache *cache,
return &ct->tuple;
}
+/*
+ * CatCacheCleanupOldEntries - Remove infrequently-used entries
+ *
+ * Catcache entries happen to be left unused for a long time for several
+ * reasons. Remove such entries to prevent catcache from bloating. It is based
+ * on the similar algorithm with buffer eviction. Entries that are accessed
+ * several times in a certain period live longer than those that have had less
+ * access in the same duration.
+ */
+static bool
+CatCacheCleanupOldEntries(CatCache *cp)
+{
+ int nremoved = 0;
+ int i;
+ long oldest_ts = catcacheclock;
+ uint64 prune_threshold = catcacheclock - prune_min_age_us;
+
+ /* Scan over the whole hash to find entries to remove */
+ for (i = 0 ; i < cp->cc_nbuckets ; i++)
+ {
+ dlist_mutable_iter iter;
+
+ dlist_foreach_modify(iter, &cp->cc_bucket[i])
+ {
+ CatCTup *ct = dlist_container(CatCTup, cache_elem, iter.cur);
+
+ /* Don't remove referenced entries */
+ if (ct->refcount == 0 &&
+ (ct->c_list == NULL || ct->c_list->refcount == 0))
+ {
+ if (ct->lastaccess < prune_threshold)
+ {
+ CatCacheRemoveCTup(cp, ct);
+ nremoved++;
+
+ /* don't let the removed entry update oldest_ts */
+ continue;
+ }
+ }
+
+ /* update the oldest timestamp if the entry remains alive */
+ if (ct->lastaccess < oldest_ts)
+ oldest_ts = ct->lastaccess;
+ }
+ }
+
+ cp->cc_oldest_ts = oldest_ts;
+
+ if (nremoved > 0)
+ elog(DEBUG1, "pruning catalog cache id=%d for %s: removed %d / %d",
+ cp->id, cp->cc_relname, nremoved, cp->cc_ntup + nremoved);
+
+ return nremoved > 0;
+}
+
/*
* ReleaseCatCache
*
@@ -1888,6 +1967,7 @@ CatalogCacheCreateEntry(CatCache *cache, HeapTuple ntp, Datum *arguments,
ct->dead = false;
ct->negative = negative;
ct->hash_value = hashValue;
+ ct->lastaccess = catcacheclock;
dlist_push_head(&cache->cc_bucket[hashIndex], &ct->cache_elem);
@@ -1899,7 +1979,12 @@ CatalogCacheCreateEntry(CatCache *cache, HeapTuple ntp, Datum *arguments,
* arbitrarily, we enlarge when fill factor > 2.
*/
if (cache->cc_ntup > cache->cc_nbuckets * 2)
- RehashCatCache(cache);
+ {
+ /* try removing old entries before expanding hash */
+ if (catcacheclock - cache->cc_oldest_ts < prune_min_age_us ||
+ !CatCacheCleanupOldEntries(cache))
+ RehashCatCache(cache);
+ }
return ct;
}
diff --git a/src/backend/utils/misc/guc.c b/src/backend/utils/misc/guc.c
index 17579eeaca..255e9fa73d 100644
--- a/src/backend/utils/misc/guc.c
+++ b/src/backend/utils/misc/guc.c
@@ -88,6 +88,7 @@
#include "utils/acl.h"
#include "utils/builtins.h"
#include "utils/bytea.h"
+#include "utils/catcache.h"
#include "utils/float.h"
#include "utils/guc_tables.h"
#include "utils/memutils.h"
@@ -3445,6 +3446,17 @@ static struct config_int ConfigureNamesInt[] =
NULL, NULL, NULL
},
+ {
+ {"catalog_cache_prune_min_age", PGC_USERSET, RESOURCES_MEM,
+ gettext_noop("System catalog cache entries that are living unused more than this seconds are considered for removal."),
+ gettext_noop("The value of -1 turns off pruning."),
+ GUC_UNIT_S
+ },
+ &catalog_cache_prune_min_age,
+ -1, -1, INT_MAX,
+ NULL, assign_catalog_cache_prune_min_age, NULL
+ },
+
/* End-of-list marker */
{
{NULL, 0, 0, NULL, NULL}, NULL, 0, 0, 0, NULL, NULL, NULL
diff --git a/src/include/utils/catcache.h b/src/include/utils/catcache.h
index ddc2762eb3..291e857e38 100644
--- a/src/include/utils/catcache.h
+++ b/src/include/utils/catcache.h
@@ -22,6 +22,7 @@
#include "access/htup.h"
#include "access/skey.h"
+#include "datatype/timestamp.h"
#include "lib/ilist.h"
#include "utils/relcache.h"
@@ -61,6 +62,7 @@ typedef struct catcache
slist_node cc_next; /* list link */
ScanKeyData cc_skey[CATCACHE_MAXKEYS]; /* precomputed key info for heap
* scans */
+ uint64 cc_oldest_ts; /* timestamp (us) of the oldest tuple */
/*
* Keep these at the end, so that compiling catcache.c with CATCACHE_STATS
@@ -119,6 +121,7 @@ typedef struct catctup
bool dead; /* dead but not yet removed? */
bool negative; /* negative cache entry? */
HeapTupleData tuple; /* tuple management header */
+ uint64 lastaccess; /* timestamp in us of the last usage */
/*
* The tuple may also be a member of at most one CatCList. (If a single
@@ -189,6 +192,22 @@ typedef struct catcacheheader
/* this extern duplicates utils/memutils.h... */
extern PGDLLIMPORT MemoryContext CacheMemoryContext;
+
+/* for guc.c, not PGDLLPMPORT'ed */
+extern int catalog_cache_prune_min_age;
+
+/* source clock for access timestamp of catcache entries */
+extern uint64 catcacheclock;
+
+/* SetCatCacheClock - set catcache timestamp source clock */
+static inline void
+SetCatCacheClock(TimestampTz ts)
+{
+ catcacheclock = (uint64) ts;
+}
+
+extern void assign_catalog_cache_prune_min_age(int newval, void *extra);
+
extern void CreateCacheMemoryContext(void);
extern CatCache *InitCatCache(int id, Oid reloid, Oid indexoid,
--
2.27.0
----Next_Part(Thu_Jan_14_17_32_27_2021_995)--
Content-Type: Text/X-Patch; charset=us-ascii
Content-Transfer-Encoding: 7bit
Content-Disposition: inline;
filename="v6-0002-Remove-dead-flag-from-catcache-tuple.patch"
^ permalink raw reply [nested|flat] 92+ messages in thread
* [PATCH v7 1/3] CatCache expiration feature
@ 2020-11-18 07:54 Kyotaro Horiguchi <[email protected]>
0 siblings, 0 replies; 92+ messages in thread
From: Kyotaro Horiguchi @ 2020-11-18 07:54 UTC (permalink / raw)
---
src/backend/access/transam/xact.c | 3 ++
src/backend/utils/cache/catcache.c | 87 +++++++++++++++++++++++++++++-
src/backend/utils/misc/guc.c | 12 +++++
src/include/utils/catcache.h | 19 +++++++
4 files changed, 120 insertions(+), 1 deletion(-)
diff --git a/src/backend/access/transam/xact.c b/src/backend/access/transam/xact.c
index a2068e3fd4..86888d2409 100644
--- a/src/backend/access/transam/xact.c
+++ b/src/backend/access/transam/xact.c
@@ -1086,6 +1086,9 @@ static void
AtStart_Cache(void)
{
AcceptInvalidationMessages();
+
+ if (xactStartTimestamp != 0)
+ SetCatCacheClock(xactStartTimestamp);
}
/*
diff --git a/src/backend/utils/cache/catcache.c b/src/backend/utils/cache/catcache.c
index fa2b49c676..644d92dd9a 100644
--- a/src/backend/utils/cache/catcache.c
+++ b/src/backend/utils/cache/catcache.c
@@ -38,6 +38,7 @@
#include "utils/rel.h"
#include "utils/resowner_private.h"
#include "utils/syscache.h"
+#include "utils/timestamp.h"
/* #define CACHEDEBUG */ /* turns DEBUG elogs on */
@@ -60,9 +61,19 @@
#define CACHE_elog(...)
#endif
+/*
+ * GUC variable to define the minimum age of entries that will be considered
+ * to be evicted in seconds. -1 to disable the feature.
+ */
+int catalog_cache_prune_min_age = -1;
+uint64 prune_min_age_us;
+
/* Cache management header --- pointer is NULL until created */
static CatCacheHeader *CacheHdr = NULL;
+/* Clock for the last accessed time of a catcache entry. */
+uint64 catcacheclock = 0;
+
static inline HeapTuple SearchCatCacheInternal(CatCache *cache,
int nkeys,
Datum v1, Datum v2,
@@ -74,6 +85,7 @@ static pg_noinline HeapTuple SearchCatCacheMiss(CatCache *cache,
Index hashIndex,
Datum v1, Datum v2,
Datum v3, Datum v4);
+static bool CatCacheCleanupOldEntries(CatCache *cp);
static uint32 CatalogCacheComputeHashValue(CatCache *cache, int nkeys,
Datum v1, Datum v2, Datum v3, Datum v4);
@@ -99,6 +111,15 @@ static void CatCacheFreeKeys(TupleDesc tupdesc, int nkeys, int *attnos,
static void CatCacheCopyKeys(TupleDesc tupdesc, int nkeys, int *attnos,
Datum *srckeys, Datum *dstkeys);
+/* GUC assign function */
+void
+assign_catalog_cache_prune_min_age(int newval, void *extra)
+{
+ if (newval < 0)
+ prune_min_age_us = UINT64_MAX;
+ else
+ prune_min_age_us = ((uint64) newval) * USECS_PER_SEC;
+}
/*
* internal support functions
@@ -1264,6 +1285,9 @@ SearchCatCacheInternal(CatCache *cache,
*/
dlist_move_head(bucket, &ct->cache_elem);
+ /* Record the last access timestamp */
+ ct->lastaccess = catcacheclock;
+
/*
* If it's a positive entry, bump its refcount and return it. If it's
* negative, we can report failure to the caller.
@@ -1425,6 +1449,61 @@ SearchCatCacheMiss(CatCache *cache,
return &ct->tuple;
}
+/*
+ * CatCacheCleanupOldEntries - Remove infrequently-used entries
+ *
+ * Catcache entries happen to be left unused for a long time for several
+ * reasons. Remove such entries to prevent catcache from bloating. It is based
+ * on the similar algorithm with buffer eviction. Entries that are accessed
+ * several times in a certain period live longer than those that have had less
+ * access in the same duration.
+ */
+static bool
+CatCacheCleanupOldEntries(CatCache *cp)
+{
+ int nremoved = 0;
+ int i;
+ long oldest_ts = catcacheclock;
+ uint64 prune_threshold = catcacheclock - prune_min_age_us;
+
+ /* Scan over the whole hash to find entries to remove */
+ for (i = 0 ; i < cp->cc_nbuckets ; i++)
+ {
+ dlist_mutable_iter iter;
+
+ dlist_foreach_modify(iter, &cp->cc_bucket[i])
+ {
+ CatCTup *ct = dlist_container(CatCTup, cache_elem, iter.cur);
+
+ /* Don't remove referenced entries */
+ if (ct->refcount == 0 &&
+ (ct->c_list == NULL || ct->c_list->refcount == 0))
+ {
+ if (ct->lastaccess < prune_threshold)
+ {
+ CatCacheRemoveCTup(cp, ct);
+ nremoved++;
+
+ /* don't let the removed entry update oldest_ts */
+ continue;
+ }
+ }
+
+ /* update the oldest timestamp if the entry remains alive */
+ if (ct->lastaccess < oldest_ts)
+ oldest_ts = ct->lastaccess;
+ }
+ }
+
+ cp->cc_oldest_ts = oldest_ts;
+
+ if (nremoved > 0)
+ elog(DEBUG1, "pruning catalog cache id=%d for %s: removed %d / %d",
+ cp->id, cp->cc_relname, nremoved, cp->cc_ntup + nremoved);
+
+ return nremoved > 0;
+}
+
/*
* ReleaseCatCache
*
@@ -1888,6 +1967,7 @@ CatalogCacheCreateEntry(CatCache *cache, HeapTuple ntp, Datum *arguments,
ct->dead = false;
ct->negative = negative;
ct->hash_value = hashValue;
+ ct->lastaccess = catcacheclock;
dlist_push_head(&cache->cc_bucket[hashIndex], &ct->cache_elem);
@@ -1899,7 +1979,12 @@ CatalogCacheCreateEntry(CatCache *cache, HeapTuple ntp, Datum *arguments,
* arbitrarily, we enlarge when fill factor > 2.
*/
if (cache->cc_ntup > cache->cc_nbuckets * 2)
- RehashCatCache(cache);
+ {
+ /* try removing old entries before expanding hash */
+ if (catcacheclock - cache->cc_oldest_ts < prune_min_age_us ||
+ !CatCacheCleanupOldEntries(cache))
+ RehashCatCache(cache);
+ }
return ct;
}
diff --git a/src/backend/utils/misc/guc.c b/src/backend/utils/misc/guc.c
index 17579eeaca..255e9fa73d 100644
--- a/src/backend/utils/misc/guc.c
+++ b/src/backend/utils/misc/guc.c
@@ -88,6 +88,7 @@
#include "utils/acl.h"
#include "utils/builtins.h"
#include "utils/bytea.h"
+#include "utils/catcache.h"
#include "utils/float.h"
#include "utils/guc_tables.h"
#include "utils/memutils.h"
@@ -3445,6 +3446,17 @@ static struct config_int ConfigureNamesInt[] =
NULL, NULL, NULL
},
+ {
+ {"catalog_cache_prune_min_age", PGC_USERSET, RESOURCES_MEM,
+ gettext_noop("System catalog cache entries that are living unused more than this seconds are considered for removal."),
+ gettext_noop("The value of -1 turns off pruning."),
+ GUC_UNIT_S
+ },
+ &catalog_cache_prune_min_age,
+ -1, -1, INT_MAX,
+ NULL, assign_catalog_cache_prune_min_age, NULL
+ },
+
/* End-of-list marker */
{
{NULL, 0, 0, NULL, NULL}, NULL, 0, 0, 0, NULL, NULL, NULL
diff --git a/src/include/utils/catcache.h b/src/include/utils/catcache.h
index ddc2762eb3..291e857e38 100644
--- a/src/include/utils/catcache.h
+++ b/src/include/utils/catcache.h
@@ -22,6 +22,7 @@
#include "access/htup.h"
#include "access/skey.h"
+#include "datatype/timestamp.h"
#include "lib/ilist.h"
#include "utils/relcache.h"
@@ -61,6 +62,7 @@ typedef struct catcache
slist_node cc_next; /* list link */
ScanKeyData cc_skey[CATCACHE_MAXKEYS]; /* precomputed key info for heap
* scans */
+ uint64 cc_oldest_ts; /* timestamp (us) of the oldest tuple */
/*
* Keep these at the end, so that compiling catcache.c with CATCACHE_STATS
@@ -119,6 +121,7 @@ typedef struct catctup
bool dead; /* dead but not yet removed? */
bool negative; /* negative cache entry? */
HeapTupleData tuple; /* tuple management header */
+ uint64 lastaccess; /* timestamp in us of the last usage */
/*
* The tuple may also be a member of at most one CatCList. (If a single
@@ -189,6 +192,22 @@ typedef struct catcacheheader
/* this extern duplicates utils/memutils.h... */
extern PGDLLIMPORT MemoryContext CacheMemoryContext;
+
+/* for guc.c, not PGDLLPMPORT'ed */
+extern int catalog_cache_prune_min_age;
+
+/* source clock for access timestamp of catcache entries */
+extern uint64 catcacheclock;
+
+/* SetCatCacheClock - set catcache timestamp source clock */
+static inline void
+SetCatCacheClock(TimestampTz ts)
+{
+ catcacheclock = (uint64) ts;
+}
+
+extern void assign_catalog_cache_prune_min_age(int newval, void *extra);
+
extern void CreateCacheMemoryContext(void);
extern CatCache *InitCatCache(int id, Oid reloid, Oid indexoid,
--
2.27.0
----Next_Part(Wed_Jan_27_10_13_08_2021_482)--
Content-Type: Text/X-Patch; charset=us-ascii
Content-Transfer-Encoding: 7bit
Content-Disposition: inline;
filename="v7-0002-Remove-dead-flag-from-catcache-tuple.patch"
^ permalink raw reply [nested|flat] 92+ messages in thread
* [PATCH v6 1/3] CatCache expiration feature
@ 2020-11-18 07:54 Kyotaro Horiguchi <[email protected]>
0 siblings, 0 replies; 92+ messages in thread
From: Kyotaro Horiguchi @ 2020-11-18 07:54 UTC (permalink / raw)
---
src/backend/access/transam/xact.c | 3 ++
src/backend/utils/cache/catcache.c | 87 +++++++++++++++++++++++++++++-
src/backend/utils/misc/guc.c | 12 +++++
src/include/utils/catcache.h | 19 +++++++
4 files changed, 120 insertions(+), 1 deletion(-)
diff --git a/src/backend/access/transam/xact.c b/src/backend/access/transam/xact.c
index a2068e3fd4..86888d2409 100644
--- a/src/backend/access/transam/xact.c
+++ b/src/backend/access/transam/xact.c
@@ -1086,6 +1086,9 @@ static void
AtStart_Cache(void)
{
AcceptInvalidationMessages();
+
+ if (xactStartTimestamp != 0)
+ SetCatCacheClock(xactStartTimestamp);
}
/*
diff --git a/src/backend/utils/cache/catcache.c b/src/backend/utils/cache/catcache.c
index fa2b49c676..644d92dd9a 100644
--- a/src/backend/utils/cache/catcache.c
+++ b/src/backend/utils/cache/catcache.c
@@ -38,6 +38,7 @@
#include "utils/rel.h"
#include "utils/resowner_private.h"
#include "utils/syscache.h"
+#include "utils/timestamp.h"
/* #define CACHEDEBUG */ /* turns DEBUG elogs on */
@@ -60,9 +61,19 @@
#define CACHE_elog(...)
#endif
+/*
+ * GUC variable to define the minimum age of entries that will be considered
+ * to be evicted in seconds. -1 to disable the feature.
+ */
+int catalog_cache_prune_min_age = -1;
+uint64 prune_min_age_us;
+
/* Cache management header --- pointer is NULL until created */
static CatCacheHeader *CacheHdr = NULL;
+/* Clock for the last accessed time of a catcache entry. */
+uint64 catcacheclock = 0;
+
static inline HeapTuple SearchCatCacheInternal(CatCache *cache,
int nkeys,
Datum v1, Datum v2,
@@ -74,6 +85,7 @@ static pg_noinline HeapTuple SearchCatCacheMiss(CatCache *cache,
Index hashIndex,
Datum v1, Datum v2,
Datum v3, Datum v4);
+static bool CatCacheCleanupOldEntries(CatCache *cp);
static uint32 CatalogCacheComputeHashValue(CatCache *cache, int nkeys,
Datum v1, Datum v2, Datum v3, Datum v4);
@@ -99,6 +111,15 @@ static void CatCacheFreeKeys(TupleDesc tupdesc, int nkeys, int *attnos,
static void CatCacheCopyKeys(TupleDesc tupdesc, int nkeys, int *attnos,
Datum *srckeys, Datum *dstkeys);
+/* GUC assign function */
+void
+assign_catalog_cache_prune_min_age(int newval, void *extra)
+{
+ if (newval < 0)
+ prune_min_age_us = UINT64_MAX;
+ else
+ prune_min_age_us = ((uint64) newval) * USECS_PER_SEC;
+}
/*
* internal support functions
@@ -1264,6 +1285,9 @@ SearchCatCacheInternal(CatCache *cache,
*/
dlist_move_head(bucket, &ct->cache_elem);
+ /* Record the last access timestamp */
+ ct->lastaccess = catcacheclock;
+
/*
* If it's a positive entry, bump its refcount and return it. If it's
* negative, we can report failure to the caller.
@@ -1425,6 +1449,61 @@ SearchCatCacheMiss(CatCache *cache,
return &ct->tuple;
}
+/*
+ * CatCacheCleanupOldEntries - Remove infrequently-used entries
+ *
+ * Catcache entries happen to be left unused for a long time for several
+ * reasons. Remove such entries to prevent catcache from bloating. It is based
+ * on the similar algorithm with buffer eviction. Entries that are accessed
+ * several times in a certain period live longer than those that have had less
+ * access in the same duration.
+ */
+static bool
+CatCacheCleanupOldEntries(CatCache *cp)
+{
+ int nremoved = 0;
+ int i;
+ long oldest_ts = catcacheclock;
+ uint64 prune_threshold = catcacheclock - prune_min_age_us;
+
+ /* Scan over the whole hash to find entries to remove */
+ for (i = 0 ; i < cp->cc_nbuckets ; i++)
+ {
+ dlist_mutable_iter iter;
+
+ dlist_foreach_modify(iter, &cp->cc_bucket[i])
+ {
+ CatCTup *ct = dlist_container(CatCTup, cache_elem, iter.cur);
+
+ /* Don't remove referenced entries */
+ if (ct->refcount == 0 &&
+ (ct->c_list == NULL || ct->c_list->refcount == 0))
+ {
+ if (ct->lastaccess < prune_threshold)
+ {
+ CatCacheRemoveCTup(cp, ct);
+ nremoved++;
+
+ /* don't let the removed entry update oldest_ts */
+ continue;
+ }
+ }
+
+ /* update the oldest timestamp if the entry remains alive */
+ if (ct->lastaccess < oldest_ts)
+ oldest_ts = ct->lastaccess;
+ }
+ }
+
+ cp->cc_oldest_ts = oldest_ts;
+
+ if (nremoved > 0)
+ elog(DEBUG1, "pruning catalog cache id=%d for %s: removed %d / %d",
+ cp->id, cp->cc_relname, nremoved, cp->cc_ntup + nremoved);
+
+ return nremoved > 0;
+}
+
/*
* ReleaseCatCache
*
@@ -1888,6 +1967,7 @@ CatalogCacheCreateEntry(CatCache *cache, HeapTuple ntp, Datum *arguments,
ct->dead = false;
ct->negative = negative;
ct->hash_value = hashValue;
+ ct->lastaccess = catcacheclock;
dlist_push_head(&cache->cc_bucket[hashIndex], &ct->cache_elem);
@@ -1899,7 +1979,12 @@ CatalogCacheCreateEntry(CatCache *cache, HeapTuple ntp, Datum *arguments,
* arbitrarily, we enlarge when fill factor > 2.
*/
if (cache->cc_ntup > cache->cc_nbuckets * 2)
- RehashCatCache(cache);
+ {
+ /* try removing old entries before expanding hash */
+ if (catcacheclock - cache->cc_oldest_ts < prune_min_age_us ||
+ !CatCacheCleanupOldEntries(cache))
+ RehashCatCache(cache);
+ }
return ct;
}
diff --git a/src/backend/utils/misc/guc.c b/src/backend/utils/misc/guc.c
index 17579eeaca..255e9fa73d 100644
--- a/src/backend/utils/misc/guc.c
+++ b/src/backend/utils/misc/guc.c
@@ -88,6 +88,7 @@
#include "utils/acl.h"
#include "utils/builtins.h"
#include "utils/bytea.h"
+#include "utils/catcache.h"
#include "utils/float.h"
#include "utils/guc_tables.h"
#include "utils/memutils.h"
@@ -3445,6 +3446,17 @@ static struct config_int ConfigureNamesInt[] =
NULL, NULL, NULL
},
+ {
+ {"catalog_cache_prune_min_age", PGC_USERSET, RESOURCES_MEM,
+ gettext_noop("System catalog cache entries that are living unused more than this seconds are considered for removal."),
+ gettext_noop("The value of -1 turns off pruning."),
+ GUC_UNIT_S
+ },
+ &catalog_cache_prune_min_age,
+ -1, -1, INT_MAX,
+ NULL, assign_catalog_cache_prune_min_age, NULL
+ },
+
/* End-of-list marker */
{
{NULL, 0, 0, NULL, NULL}, NULL, 0, 0, 0, NULL, NULL, NULL
diff --git a/src/include/utils/catcache.h b/src/include/utils/catcache.h
index ddc2762eb3..291e857e38 100644
--- a/src/include/utils/catcache.h
+++ b/src/include/utils/catcache.h
@@ -22,6 +22,7 @@
#include "access/htup.h"
#include "access/skey.h"
+#include "datatype/timestamp.h"
#include "lib/ilist.h"
#include "utils/relcache.h"
@@ -61,6 +62,7 @@ typedef struct catcache
slist_node cc_next; /* list link */
ScanKeyData cc_skey[CATCACHE_MAXKEYS]; /* precomputed key info for heap
* scans */
+ uint64 cc_oldest_ts; /* timestamp (us) of the oldest tuple */
/*
* Keep these at the end, so that compiling catcache.c with CATCACHE_STATS
@@ -119,6 +121,7 @@ typedef struct catctup
bool dead; /* dead but not yet removed? */
bool negative; /* negative cache entry? */
HeapTupleData tuple; /* tuple management header */
+ uint64 lastaccess; /* timestamp in us of the last usage */
/*
* The tuple may also be a member of at most one CatCList. (If a single
@@ -189,6 +192,22 @@ typedef struct catcacheheader
/* this extern duplicates utils/memutils.h... */
extern PGDLLIMPORT MemoryContext CacheMemoryContext;
+
+/* for guc.c, not PGDLLPMPORT'ed */
+extern int catalog_cache_prune_min_age;
+
+/* source clock for access timestamp of catcache entries */
+extern uint64 catcacheclock;
+
+/* SetCatCacheClock - set catcache timestamp source clock */
+static inline void
+SetCatCacheClock(TimestampTz ts)
+{
+ catcacheclock = (uint64) ts;
+}
+
+extern void assign_catalog_cache_prune_min_age(int newval, void *extra);
+
extern void CreateCacheMemoryContext(void);
extern CatCache *InitCatCache(int id, Oid reloid, Oid indexoid,
--
2.27.0
----Next_Part(Thu_Jan_14_17_32_27_2021_995)--
Content-Type: Text/X-Patch; charset=us-ascii
Content-Transfer-Encoding: 7bit
Content-Disposition: inline;
filename="v6-0002-Remove-dead-flag-from-catcache-tuple.patch"
^ permalink raw reply [nested|flat] 92+ messages in thread
* [PATCH v7 1/3] CatCache expiration feature
@ 2020-11-18 07:54 Kyotaro Horiguchi <[email protected]>
0 siblings, 0 replies; 92+ messages in thread
From: Kyotaro Horiguchi @ 2020-11-18 07:54 UTC (permalink / raw)
---
src/backend/access/transam/xact.c | 3 ++
src/backend/utils/cache/catcache.c | 87 +++++++++++++++++++++++++++++-
src/backend/utils/misc/guc.c | 12 +++++
src/include/utils/catcache.h | 19 +++++++
4 files changed, 120 insertions(+), 1 deletion(-)
diff --git a/src/backend/access/transam/xact.c b/src/backend/access/transam/xact.c
index a2068e3fd4..86888d2409 100644
--- a/src/backend/access/transam/xact.c
+++ b/src/backend/access/transam/xact.c
@@ -1086,6 +1086,9 @@ static void
AtStart_Cache(void)
{
AcceptInvalidationMessages();
+
+ if (xactStartTimestamp != 0)
+ SetCatCacheClock(xactStartTimestamp);
}
/*
diff --git a/src/backend/utils/cache/catcache.c b/src/backend/utils/cache/catcache.c
index fa2b49c676..644d92dd9a 100644
--- a/src/backend/utils/cache/catcache.c
+++ b/src/backend/utils/cache/catcache.c
@@ -38,6 +38,7 @@
#include "utils/rel.h"
#include "utils/resowner_private.h"
#include "utils/syscache.h"
+#include "utils/timestamp.h"
/* #define CACHEDEBUG */ /* turns DEBUG elogs on */
@@ -60,9 +61,19 @@
#define CACHE_elog(...)
#endif
+/*
+ * GUC variable to define the minimum age of entries that will be considered
+ * to be evicted in seconds. -1 to disable the feature.
+ */
+int catalog_cache_prune_min_age = -1;
+uint64 prune_min_age_us;
+
/* Cache management header --- pointer is NULL until created */
static CatCacheHeader *CacheHdr = NULL;
+/* Clock for the last accessed time of a catcache entry. */
+uint64 catcacheclock = 0;
+
static inline HeapTuple SearchCatCacheInternal(CatCache *cache,
int nkeys,
Datum v1, Datum v2,
@@ -74,6 +85,7 @@ static pg_noinline HeapTuple SearchCatCacheMiss(CatCache *cache,
Index hashIndex,
Datum v1, Datum v2,
Datum v3, Datum v4);
+static bool CatCacheCleanupOldEntries(CatCache *cp);
static uint32 CatalogCacheComputeHashValue(CatCache *cache, int nkeys,
Datum v1, Datum v2, Datum v3, Datum v4);
@@ -99,6 +111,15 @@ static void CatCacheFreeKeys(TupleDesc tupdesc, int nkeys, int *attnos,
static void CatCacheCopyKeys(TupleDesc tupdesc, int nkeys, int *attnos,
Datum *srckeys, Datum *dstkeys);
+/* GUC assign function */
+void
+assign_catalog_cache_prune_min_age(int newval, void *extra)
+{
+ if (newval < 0)
+ prune_min_age_us = UINT64_MAX;
+ else
+ prune_min_age_us = ((uint64) newval) * USECS_PER_SEC;
+}
/*
* internal support functions
@@ -1264,6 +1285,9 @@ SearchCatCacheInternal(CatCache *cache,
*/
dlist_move_head(bucket, &ct->cache_elem);
+ /* Record the last access timestamp */
+ ct->lastaccess = catcacheclock;
+
/*
* If it's a positive entry, bump its refcount and return it. If it's
* negative, we can report failure to the caller.
@@ -1425,6 +1449,61 @@ SearchCatCacheMiss(CatCache *cache,
return &ct->tuple;
}
+/*
+ * CatCacheCleanupOldEntries - Remove infrequently-used entries
+ *
+ * Catcache entries happen to be left unused for a long time for several
+ * reasons. Remove such entries to prevent catcache from bloating. It is based
+ * on the similar algorithm with buffer eviction. Entries that are accessed
+ * several times in a certain period live longer than those that have had less
+ * access in the same duration.
+ */
+static bool
+CatCacheCleanupOldEntries(CatCache *cp)
+{
+ int nremoved = 0;
+ int i;
+ long oldest_ts = catcacheclock;
+ uint64 prune_threshold = catcacheclock - prune_min_age_us;
+
+ /* Scan over the whole hash to find entries to remove */
+ for (i = 0 ; i < cp->cc_nbuckets ; i++)
+ {
+ dlist_mutable_iter iter;
+
+ dlist_foreach_modify(iter, &cp->cc_bucket[i])
+ {
+ CatCTup *ct = dlist_container(CatCTup, cache_elem, iter.cur);
+
+ /* Don't remove referenced entries */
+ if (ct->refcount == 0 &&
+ (ct->c_list == NULL || ct->c_list->refcount == 0))
+ {
+ if (ct->lastaccess < prune_threshold)
+ {
+ CatCacheRemoveCTup(cp, ct);
+ nremoved++;
+
+ /* don't let the removed entry update oldest_ts */
+ continue;
+ }
+ }
+
+ /* update the oldest timestamp if the entry remains alive */
+ if (ct->lastaccess < oldest_ts)
+ oldest_ts = ct->lastaccess;
+ }
+ }
+
+ cp->cc_oldest_ts = oldest_ts;
+
+ if (nremoved > 0)
+ elog(DEBUG1, "pruning catalog cache id=%d for %s: removed %d / %d",
+ cp->id, cp->cc_relname, nremoved, cp->cc_ntup + nremoved);
+
+ return nremoved > 0;
+}
+
/*
* ReleaseCatCache
*
@@ -1888,6 +1967,7 @@ CatalogCacheCreateEntry(CatCache *cache, HeapTuple ntp, Datum *arguments,
ct->dead = false;
ct->negative = negative;
ct->hash_value = hashValue;
+ ct->lastaccess = catcacheclock;
dlist_push_head(&cache->cc_bucket[hashIndex], &ct->cache_elem);
@@ -1899,7 +1979,12 @@ CatalogCacheCreateEntry(CatCache *cache, HeapTuple ntp, Datum *arguments,
* arbitrarily, we enlarge when fill factor > 2.
*/
if (cache->cc_ntup > cache->cc_nbuckets * 2)
- RehashCatCache(cache);
+ {
+ /* try removing old entries before expanding hash */
+ if (catcacheclock - cache->cc_oldest_ts < prune_min_age_us ||
+ !CatCacheCleanupOldEntries(cache))
+ RehashCatCache(cache);
+ }
return ct;
}
diff --git a/src/backend/utils/misc/guc.c b/src/backend/utils/misc/guc.c
index 17579eeaca..255e9fa73d 100644
--- a/src/backend/utils/misc/guc.c
+++ b/src/backend/utils/misc/guc.c
@@ -88,6 +88,7 @@
#include "utils/acl.h"
#include "utils/builtins.h"
#include "utils/bytea.h"
+#include "utils/catcache.h"
#include "utils/float.h"
#include "utils/guc_tables.h"
#include "utils/memutils.h"
@@ -3445,6 +3446,17 @@ static struct config_int ConfigureNamesInt[] =
NULL, NULL, NULL
},
+ {
+ {"catalog_cache_prune_min_age", PGC_USERSET, RESOURCES_MEM,
+ gettext_noop("System catalog cache entries that are living unused more than this seconds are considered for removal."),
+ gettext_noop("The value of -1 turns off pruning."),
+ GUC_UNIT_S
+ },
+ &catalog_cache_prune_min_age,
+ -1, -1, INT_MAX,
+ NULL, assign_catalog_cache_prune_min_age, NULL
+ },
+
/* End-of-list marker */
{
{NULL, 0, 0, NULL, NULL}, NULL, 0, 0, 0, NULL, NULL, NULL
diff --git a/src/include/utils/catcache.h b/src/include/utils/catcache.h
index ddc2762eb3..291e857e38 100644
--- a/src/include/utils/catcache.h
+++ b/src/include/utils/catcache.h
@@ -22,6 +22,7 @@
#include "access/htup.h"
#include "access/skey.h"
+#include "datatype/timestamp.h"
#include "lib/ilist.h"
#include "utils/relcache.h"
@@ -61,6 +62,7 @@ typedef struct catcache
slist_node cc_next; /* list link */
ScanKeyData cc_skey[CATCACHE_MAXKEYS]; /* precomputed key info for heap
* scans */
+ uint64 cc_oldest_ts; /* timestamp (us) of the oldest tuple */
/*
* Keep these at the end, so that compiling catcache.c with CATCACHE_STATS
@@ -119,6 +121,7 @@ typedef struct catctup
bool dead; /* dead but not yet removed? */
bool negative; /* negative cache entry? */
HeapTupleData tuple; /* tuple management header */
+ uint64 lastaccess; /* timestamp in us of the last usage */
/*
* The tuple may also be a member of at most one CatCList. (If a single
@@ -189,6 +192,22 @@ typedef struct catcacheheader
/* this extern duplicates utils/memutils.h... */
extern PGDLLIMPORT MemoryContext CacheMemoryContext;
+
+/* for guc.c, not PGDLLPMPORT'ed */
+extern int catalog_cache_prune_min_age;
+
+/* source clock for access timestamp of catcache entries */
+extern uint64 catcacheclock;
+
+/* SetCatCacheClock - set catcache timestamp source clock */
+static inline void
+SetCatCacheClock(TimestampTz ts)
+{
+ catcacheclock = (uint64) ts;
+}
+
+extern void assign_catalog_cache_prune_min_age(int newval, void *extra);
+
extern void CreateCacheMemoryContext(void);
extern CatCache *InitCatCache(int id, Oid reloid, Oid indexoid,
--
2.27.0
----Next_Part(Wed_Jan_27_10_13_08_2021_482)--
Content-Type: Text/X-Patch; charset=us-ascii
Content-Transfer-Encoding: 7bit
Content-Disposition: inline;
filename="v7-0002-Remove-dead-flag-from-catcache-tuple.patch"
^ permalink raw reply [nested|flat] 92+ messages in thread
* [PATCH v6 1/3] CatCache expiration feature
@ 2020-11-18 07:54 Kyotaro Horiguchi <[email protected]>
0 siblings, 0 replies; 92+ messages in thread
From: Kyotaro Horiguchi @ 2020-11-18 07:54 UTC (permalink / raw)
---
src/backend/access/transam/xact.c | 3 ++
src/backend/utils/cache/catcache.c | 87 +++++++++++++++++++++++++++++-
src/backend/utils/misc/guc.c | 12 +++++
src/include/utils/catcache.h | 19 +++++++
4 files changed, 120 insertions(+), 1 deletion(-)
diff --git a/src/backend/access/transam/xact.c b/src/backend/access/transam/xact.c
index a2068e3fd4..86888d2409 100644
--- a/src/backend/access/transam/xact.c
+++ b/src/backend/access/transam/xact.c
@@ -1086,6 +1086,9 @@ static void
AtStart_Cache(void)
{
AcceptInvalidationMessages();
+
+ if (xactStartTimestamp != 0)
+ SetCatCacheClock(xactStartTimestamp);
}
/*
diff --git a/src/backend/utils/cache/catcache.c b/src/backend/utils/cache/catcache.c
index fa2b49c676..644d92dd9a 100644
--- a/src/backend/utils/cache/catcache.c
+++ b/src/backend/utils/cache/catcache.c
@@ -38,6 +38,7 @@
#include "utils/rel.h"
#include "utils/resowner_private.h"
#include "utils/syscache.h"
+#include "utils/timestamp.h"
/* #define CACHEDEBUG */ /* turns DEBUG elogs on */
@@ -60,9 +61,19 @@
#define CACHE_elog(...)
#endif
+/*
+ * GUC variable to define the minimum age of entries that will be considered
+ * to be evicted in seconds. -1 to disable the feature.
+ */
+int catalog_cache_prune_min_age = -1;
+uint64 prune_min_age_us;
+
/* Cache management header --- pointer is NULL until created */
static CatCacheHeader *CacheHdr = NULL;
+/* Clock for the last accessed time of a catcache entry. */
+uint64 catcacheclock = 0;
+
static inline HeapTuple SearchCatCacheInternal(CatCache *cache,
int nkeys,
Datum v1, Datum v2,
@@ -74,6 +85,7 @@ static pg_noinline HeapTuple SearchCatCacheMiss(CatCache *cache,
Index hashIndex,
Datum v1, Datum v2,
Datum v3, Datum v4);
+static bool CatCacheCleanupOldEntries(CatCache *cp);
static uint32 CatalogCacheComputeHashValue(CatCache *cache, int nkeys,
Datum v1, Datum v2, Datum v3, Datum v4);
@@ -99,6 +111,15 @@ static void CatCacheFreeKeys(TupleDesc tupdesc, int nkeys, int *attnos,
static void CatCacheCopyKeys(TupleDesc tupdesc, int nkeys, int *attnos,
Datum *srckeys, Datum *dstkeys);
+/* GUC assign function */
+void
+assign_catalog_cache_prune_min_age(int newval, void *extra)
+{
+ if (newval < 0)
+ prune_min_age_us = UINT64_MAX;
+ else
+ prune_min_age_us = ((uint64) newval) * USECS_PER_SEC;
+}
/*
* internal support functions
@@ -1264,6 +1285,9 @@ SearchCatCacheInternal(CatCache *cache,
*/
dlist_move_head(bucket, &ct->cache_elem);
+ /* Record the last access timestamp */
+ ct->lastaccess = catcacheclock;
+
/*
* If it's a positive entry, bump its refcount and return it. If it's
* negative, we can report failure to the caller.
@@ -1425,6 +1449,61 @@ SearchCatCacheMiss(CatCache *cache,
return &ct->tuple;
}
+/*
+ * CatCacheCleanupOldEntries - Remove infrequently-used entries
+ *
+ * Catcache entries happen to be left unused for a long time for several
+ * reasons. Remove such entries to prevent catcache from bloating. It is based
+ * on the similar algorithm with buffer eviction. Entries that are accessed
+ * several times in a certain period live longer than those that have had less
+ * access in the same duration.
+ */
+static bool
+CatCacheCleanupOldEntries(CatCache *cp)
+{
+ int nremoved = 0;
+ int i;
+ long oldest_ts = catcacheclock;
+ uint64 prune_threshold = catcacheclock - prune_min_age_us;
+
+ /* Scan over the whole hash to find entries to remove */
+ for (i = 0 ; i < cp->cc_nbuckets ; i++)
+ {
+ dlist_mutable_iter iter;
+
+ dlist_foreach_modify(iter, &cp->cc_bucket[i])
+ {
+ CatCTup *ct = dlist_container(CatCTup, cache_elem, iter.cur);
+
+ /* Don't remove referenced entries */
+ if (ct->refcount == 0 &&
+ (ct->c_list == NULL || ct->c_list->refcount == 0))
+ {
+ if (ct->lastaccess < prune_threshold)
+ {
+ CatCacheRemoveCTup(cp, ct);
+ nremoved++;
+
+ /* don't let the removed entry update oldest_ts */
+ continue;
+ }
+ }
+
+ /* update the oldest timestamp if the entry remains alive */
+ if (ct->lastaccess < oldest_ts)
+ oldest_ts = ct->lastaccess;
+ }
+ }
+
+ cp->cc_oldest_ts = oldest_ts;
+
+ if (nremoved > 0)
+ elog(DEBUG1, "pruning catalog cache id=%d for %s: removed %d / %d",
+ cp->id, cp->cc_relname, nremoved, cp->cc_ntup + nremoved);
+
+ return nremoved > 0;
+}
+
/*
* ReleaseCatCache
*
@@ -1888,6 +1967,7 @@ CatalogCacheCreateEntry(CatCache *cache, HeapTuple ntp, Datum *arguments,
ct->dead = false;
ct->negative = negative;
ct->hash_value = hashValue;
+ ct->lastaccess = catcacheclock;
dlist_push_head(&cache->cc_bucket[hashIndex], &ct->cache_elem);
@@ -1899,7 +1979,12 @@ CatalogCacheCreateEntry(CatCache *cache, HeapTuple ntp, Datum *arguments,
* arbitrarily, we enlarge when fill factor > 2.
*/
if (cache->cc_ntup > cache->cc_nbuckets * 2)
- RehashCatCache(cache);
+ {
+ /* try removing old entries before expanding hash */
+ if (catcacheclock - cache->cc_oldest_ts < prune_min_age_us ||
+ !CatCacheCleanupOldEntries(cache))
+ RehashCatCache(cache);
+ }
return ct;
}
diff --git a/src/backend/utils/misc/guc.c b/src/backend/utils/misc/guc.c
index 17579eeaca..255e9fa73d 100644
--- a/src/backend/utils/misc/guc.c
+++ b/src/backend/utils/misc/guc.c
@@ -88,6 +88,7 @@
#include "utils/acl.h"
#include "utils/builtins.h"
#include "utils/bytea.h"
+#include "utils/catcache.h"
#include "utils/float.h"
#include "utils/guc_tables.h"
#include "utils/memutils.h"
@@ -3445,6 +3446,17 @@ static struct config_int ConfigureNamesInt[] =
NULL, NULL, NULL
},
+ {
+ {"catalog_cache_prune_min_age", PGC_USERSET, RESOURCES_MEM,
+ gettext_noop("System catalog cache entries that are living unused more than this seconds are considered for removal."),
+ gettext_noop("The value of -1 turns off pruning."),
+ GUC_UNIT_S
+ },
+ &catalog_cache_prune_min_age,
+ -1, -1, INT_MAX,
+ NULL, assign_catalog_cache_prune_min_age, NULL
+ },
+
/* End-of-list marker */
{
{NULL, 0, 0, NULL, NULL}, NULL, 0, 0, 0, NULL, NULL, NULL
diff --git a/src/include/utils/catcache.h b/src/include/utils/catcache.h
index ddc2762eb3..291e857e38 100644
--- a/src/include/utils/catcache.h
+++ b/src/include/utils/catcache.h
@@ -22,6 +22,7 @@
#include "access/htup.h"
#include "access/skey.h"
+#include "datatype/timestamp.h"
#include "lib/ilist.h"
#include "utils/relcache.h"
@@ -61,6 +62,7 @@ typedef struct catcache
slist_node cc_next; /* list link */
ScanKeyData cc_skey[CATCACHE_MAXKEYS]; /* precomputed key info for heap
* scans */
+ uint64 cc_oldest_ts; /* timestamp (us) of the oldest tuple */
/*
* Keep these at the end, so that compiling catcache.c with CATCACHE_STATS
@@ -119,6 +121,7 @@ typedef struct catctup
bool dead; /* dead but not yet removed? */
bool negative; /* negative cache entry? */
HeapTupleData tuple; /* tuple management header */
+ uint64 lastaccess; /* timestamp in us of the last usage */
/*
* The tuple may also be a member of at most one CatCList. (If a single
@@ -189,6 +192,22 @@ typedef struct catcacheheader
/* this extern duplicates utils/memutils.h... */
extern PGDLLIMPORT MemoryContext CacheMemoryContext;
+
+/* for guc.c, not PGDLLPMPORT'ed */
+extern int catalog_cache_prune_min_age;
+
+/* source clock for access timestamp of catcache entries */
+extern uint64 catcacheclock;
+
+/* SetCatCacheClock - set catcache timestamp source clock */
+static inline void
+SetCatCacheClock(TimestampTz ts)
+{
+ catcacheclock = (uint64) ts;
+}
+
+extern void assign_catalog_cache_prune_min_age(int newval, void *extra);
+
extern void CreateCacheMemoryContext(void);
extern CatCache *InitCatCache(int id, Oid reloid, Oid indexoid,
--
2.27.0
----Next_Part(Thu_Jan_14_17_32_27_2021_995)--
Content-Type: Text/X-Patch; charset=us-ascii
Content-Transfer-Encoding: 7bit
Content-Disposition: inline;
filename="v6-0002-Remove-dead-flag-from-catcache-tuple.patch"
^ permalink raw reply [nested|flat] 92+ messages in thread
* [PATCH v7 1/3] CatCache expiration feature
@ 2020-11-18 07:54 Kyotaro Horiguchi <[email protected]>
0 siblings, 0 replies; 92+ messages in thread
From: Kyotaro Horiguchi @ 2020-11-18 07:54 UTC (permalink / raw)
---
src/backend/access/transam/xact.c | 3 ++
src/backend/utils/cache/catcache.c | 87 +++++++++++++++++++++++++++++-
src/backend/utils/misc/guc.c | 12 +++++
src/include/utils/catcache.h | 19 +++++++
4 files changed, 120 insertions(+), 1 deletion(-)
diff --git a/src/backend/access/transam/xact.c b/src/backend/access/transam/xact.c
index a2068e3fd4..86888d2409 100644
--- a/src/backend/access/transam/xact.c
+++ b/src/backend/access/transam/xact.c
@@ -1086,6 +1086,9 @@ static void
AtStart_Cache(void)
{
AcceptInvalidationMessages();
+
+ if (xactStartTimestamp != 0)
+ SetCatCacheClock(xactStartTimestamp);
}
/*
diff --git a/src/backend/utils/cache/catcache.c b/src/backend/utils/cache/catcache.c
index fa2b49c676..644d92dd9a 100644
--- a/src/backend/utils/cache/catcache.c
+++ b/src/backend/utils/cache/catcache.c
@@ -38,6 +38,7 @@
#include "utils/rel.h"
#include "utils/resowner_private.h"
#include "utils/syscache.h"
+#include "utils/timestamp.h"
/* #define CACHEDEBUG */ /* turns DEBUG elogs on */
@@ -60,9 +61,19 @@
#define CACHE_elog(...)
#endif
+/*
+ * GUC variable to define the minimum age of entries that will be considered
+ * to be evicted in seconds. -1 to disable the feature.
+ */
+int catalog_cache_prune_min_age = -1;
+uint64 prune_min_age_us;
+
/* Cache management header --- pointer is NULL until created */
static CatCacheHeader *CacheHdr = NULL;
+/* Clock for the last accessed time of a catcache entry. */
+uint64 catcacheclock = 0;
+
static inline HeapTuple SearchCatCacheInternal(CatCache *cache,
int nkeys,
Datum v1, Datum v2,
@@ -74,6 +85,7 @@ static pg_noinline HeapTuple SearchCatCacheMiss(CatCache *cache,
Index hashIndex,
Datum v1, Datum v2,
Datum v3, Datum v4);
+static bool CatCacheCleanupOldEntries(CatCache *cp);
static uint32 CatalogCacheComputeHashValue(CatCache *cache, int nkeys,
Datum v1, Datum v2, Datum v3, Datum v4);
@@ -99,6 +111,15 @@ static void CatCacheFreeKeys(TupleDesc tupdesc, int nkeys, int *attnos,
static void CatCacheCopyKeys(TupleDesc tupdesc, int nkeys, int *attnos,
Datum *srckeys, Datum *dstkeys);
+/* GUC assign function */
+void
+assign_catalog_cache_prune_min_age(int newval, void *extra)
+{
+ if (newval < 0)
+ prune_min_age_us = UINT64_MAX;
+ else
+ prune_min_age_us = ((uint64) newval) * USECS_PER_SEC;
+}
/*
* internal support functions
@@ -1264,6 +1285,9 @@ SearchCatCacheInternal(CatCache *cache,
*/
dlist_move_head(bucket, &ct->cache_elem);
+ /* Record the last access timestamp */
+ ct->lastaccess = catcacheclock;
+
/*
* If it's a positive entry, bump its refcount and return it. If it's
* negative, we can report failure to the caller.
@@ -1425,6 +1449,61 @@ SearchCatCacheMiss(CatCache *cache,
return &ct->tuple;
}
+/*
+ * CatCacheCleanupOldEntries - Remove infrequently-used entries
+ *
+ * Catcache entries happen to be left unused for a long time for several
+ * reasons. Remove such entries to prevent catcache from bloating. It is based
+ * on the similar algorithm with buffer eviction. Entries that are accessed
+ * several times in a certain period live longer than those that have had less
+ * access in the same duration.
+ */
+static bool
+CatCacheCleanupOldEntries(CatCache *cp)
+{
+ int nremoved = 0;
+ int i;
+ long oldest_ts = catcacheclock;
+ uint64 prune_threshold = catcacheclock - prune_min_age_us;
+
+ /* Scan over the whole hash to find entries to remove */
+ for (i = 0 ; i < cp->cc_nbuckets ; i++)
+ {
+ dlist_mutable_iter iter;
+
+ dlist_foreach_modify(iter, &cp->cc_bucket[i])
+ {
+ CatCTup *ct = dlist_container(CatCTup, cache_elem, iter.cur);
+
+ /* Don't remove referenced entries */
+ if (ct->refcount == 0 &&
+ (ct->c_list == NULL || ct->c_list->refcount == 0))
+ {
+ if (ct->lastaccess < prune_threshold)
+ {
+ CatCacheRemoveCTup(cp, ct);
+ nremoved++;
+
+ /* don't let the removed entry update oldest_ts */
+ continue;
+ }
+ }
+
+ /* update the oldest timestamp if the entry remains alive */
+ if (ct->lastaccess < oldest_ts)
+ oldest_ts = ct->lastaccess;
+ }
+ }
+
+ cp->cc_oldest_ts = oldest_ts;
+
+ if (nremoved > 0)
+ elog(DEBUG1, "pruning catalog cache id=%d for %s: removed %d / %d",
+ cp->id, cp->cc_relname, nremoved, cp->cc_ntup + nremoved);
+
+ return nremoved > 0;
+}
+
/*
* ReleaseCatCache
*
@@ -1888,6 +1967,7 @@ CatalogCacheCreateEntry(CatCache *cache, HeapTuple ntp, Datum *arguments,
ct->dead = false;
ct->negative = negative;
ct->hash_value = hashValue;
+ ct->lastaccess = catcacheclock;
dlist_push_head(&cache->cc_bucket[hashIndex], &ct->cache_elem);
@@ -1899,7 +1979,12 @@ CatalogCacheCreateEntry(CatCache *cache, HeapTuple ntp, Datum *arguments,
* arbitrarily, we enlarge when fill factor > 2.
*/
if (cache->cc_ntup > cache->cc_nbuckets * 2)
- RehashCatCache(cache);
+ {
+ /* try removing old entries before expanding hash */
+ if (catcacheclock - cache->cc_oldest_ts < prune_min_age_us ||
+ !CatCacheCleanupOldEntries(cache))
+ RehashCatCache(cache);
+ }
return ct;
}
diff --git a/src/backend/utils/misc/guc.c b/src/backend/utils/misc/guc.c
index 17579eeaca..255e9fa73d 100644
--- a/src/backend/utils/misc/guc.c
+++ b/src/backend/utils/misc/guc.c
@@ -88,6 +88,7 @@
#include "utils/acl.h"
#include "utils/builtins.h"
#include "utils/bytea.h"
+#include "utils/catcache.h"
#include "utils/float.h"
#include "utils/guc_tables.h"
#include "utils/memutils.h"
@@ -3445,6 +3446,17 @@ static struct config_int ConfigureNamesInt[] =
NULL, NULL, NULL
},
+ {
+ {"catalog_cache_prune_min_age", PGC_USERSET, RESOURCES_MEM,
+ gettext_noop("System catalog cache entries that are living unused more than this seconds are considered for removal."),
+ gettext_noop("The value of -1 turns off pruning."),
+ GUC_UNIT_S
+ },
+ &catalog_cache_prune_min_age,
+ -1, -1, INT_MAX,
+ NULL, assign_catalog_cache_prune_min_age, NULL
+ },
+
/* End-of-list marker */
{
{NULL, 0, 0, NULL, NULL}, NULL, 0, 0, 0, NULL, NULL, NULL
diff --git a/src/include/utils/catcache.h b/src/include/utils/catcache.h
index ddc2762eb3..291e857e38 100644
--- a/src/include/utils/catcache.h
+++ b/src/include/utils/catcache.h
@@ -22,6 +22,7 @@
#include "access/htup.h"
#include "access/skey.h"
+#include "datatype/timestamp.h"
#include "lib/ilist.h"
#include "utils/relcache.h"
@@ -61,6 +62,7 @@ typedef struct catcache
slist_node cc_next; /* list link */
ScanKeyData cc_skey[CATCACHE_MAXKEYS]; /* precomputed key info for heap
* scans */
+ uint64 cc_oldest_ts; /* timestamp (us) of the oldest tuple */
/*
* Keep these at the end, so that compiling catcache.c with CATCACHE_STATS
@@ -119,6 +121,7 @@ typedef struct catctup
bool dead; /* dead but not yet removed? */
bool negative; /* negative cache entry? */
HeapTupleData tuple; /* tuple management header */
+ uint64 lastaccess; /* timestamp in us of the last usage */
/*
* The tuple may also be a member of at most one CatCList. (If a single
@@ -189,6 +192,22 @@ typedef struct catcacheheader
/* this extern duplicates utils/memutils.h... */
extern PGDLLIMPORT MemoryContext CacheMemoryContext;
+
+/* for guc.c, not PGDLLPMPORT'ed */
+extern int catalog_cache_prune_min_age;
+
+/* source clock for access timestamp of catcache entries */
+extern uint64 catcacheclock;
+
+/* SetCatCacheClock - set catcache timestamp source clock */
+static inline void
+SetCatCacheClock(TimestampTz ts)
+{
+ catcacheclock = (uint64) ts;
+}
+
+extern void assign_catalog_cache_prune_min_age(int newval, void *extra);
+
extern void CreateCacheMemoryContext(void);
extern CatCache *InitCatCache(int id, Oid reloid, Oid indexoid,
--
2.27.0
----Next_Part(Wed_Jan_27_10_13_08_2021_482)--
Content-Type: Text/X-Patch; charset=us-ascii
Content-Transfer-Encoding: 7bit
Content-Disposition: inline;
filename="v7-0002-Remove-dead-flag-from-catcache-tuple.patch"
^ permalink raw reply [nested|flat] 92+ messages in thread
* [PATCH v7 1/3] CatCache expiration feature
@ 2020-11-18 07:54 Kyotaro Horiguchi <[email protected]>
0 siblings, 0 replies; 92+ messages in thread
From: Kyotaro Horiguchi @ 2020-11-18 07:54 UTC (permalink / raw)
---
src/backend/access/transam/xact.c | 3 ++
src/backend/utils/cache/catcache.c | 87 +++++++++++++++++++++++++++++-
src/backend/utils/misc/guc.c | 12 +++++
src/include/utils/catcache.h | 19 +++++++
4 files changed, 120 insertions(+), 1 deletion(-)
diff --git a/src/backend/access/transam/xact.c b/src/backend/access/transam/xact.c
index a2068e3fd4..86888d2409 100644
--- a/src/backend/access/transam/xact.c
+++ b/src/backend/access/transam/xact.c
@@ -1086,6 +1086,9 @@ static void
AtStart_Cache(void)
{
AcceptInvalidationMessages();
+
+ if (xactStartTimestamp != 0)
+ SetCatCacheClock(xactStartTimestamp);
}
/*
diff --git a/src/backend/utils/cache/catcache.c b/src/backend/utils/cache/catcache.c
index fa2b49c676..644d92dd9a 100644
--- a/src/backend/utils/cache/catcache.c
+++ b/src/backend/utils/cache/catcache.c
@@ -38,6 +38,7 @@
#include "utils/rel.h"
#include "utils/resowner_private.h"
#include "utils/syscache.h"
+#include "utils/timestamp.h"
/* #define CACHEDEBUG */ /* turns DEBUG elogs on */
@@ -60,9 +61,19 @@
#define CACHE_elog(...)
#endif
+/*
+ * GUC variable to define the minimum age of entries that will be considered
+ * to be evicted in seconds. -1 to disable the feature.
+ */
+int catalog_cache_prune_min_age = -1;
+uint64 prune_min_age_us;
+
/* Cache management header --- pointer is NULL until created */
static CatCacheHeader *CacheHdr = NULL;
+/* Clock for the last accessed time of a catcache entry. */
+uint64 catcacheclock = 0;
+
static inline HeapTuple SearchCatCacheInternal(CatCache *cache,
int nkeys,
Datum v1, Datum v2,
@@ -74,6 +85,7 @@ static pg_noinline HeapTuple SearchCatCacheMiss(CatCache *cache,
Index hashIndex,
Datum v1, Datum v2,
Datum v3, Datum v4);
+static bool CatCacheCleanupOldEntries(CatCache *cp);
static uint32 CatalogCacheComputeHashValue(CatCache *cache, int nkeys,
Datum v1, Datum v2, Datum v3, Datum v4);
@@ -99,6 +111,15 @@ static void CatCacheFreeKeys(TupleDesc tupdesc, int nkeys, int *attnos,
static void CatCacheCopyKeys(TupleDesc tupdesc, int nkeys, int *attnos,
Datum *srckeys, Datum *dstkeys);
+/* GUC assign function */
+void
+assign_catalog_cache_prune_min_age(int newval, void *extra)
+{
+ if (newval < 0)
+ prune_min_age_us = UINT64_MAX;
+ else
+ prune_min_age_us = ((uint64) newval) * USECS_PER_SEC;
+}
/*
* internal support functions
@@ -1264,6 +1285,9 @@ SearchCatCacheInternal(CatCache *cache,
*/
dlist_move_head(bucket, &ct->cache_elem);
+ /* Record the last access timestamp */
+ ct->lastaccess = catcacheclock;
+
/*
* If it's a positive entry, bump its refcount and return it. If it's
* negative, we can report failure to the caller.
@@ -1425,6 +1449,61 @@ SearchCatCacheMiss(CatCache *cache,
return &ct->tuple;
}
+/*
+ * CatCacheCleanupOldEntries - Remove infrequently-used entries
+ *
+ * Catcache entries happen to be left unused for a long time for several
+ * reasons. Remove such entries to prevent catcache from bloating. It is based
+ * on the similar algorithm with buffer eviction. Entries that are accessed
+ * several times in a certain period live longer than those that have had less
+ * access in the same duration.
+ */
+static bool
+CatCacheCleanupOldEntries(CatCache *cp)
+{
+ int nremoved = 0;
+ int i;
+ long oldest_ts = catcacheclock;
+ uint64 prune_threshold = catcacheclock - prune_min_age_us;
+
+ /* Scan over the whole hash to find entries to remove */
+ for (i = 0 ; i < cp->cc_nbuckets ; i++)
+ {
+ dlist_mutable_iter iter;
+
+ dlist_foreach_modify(iter, &cp->cc_bucket[i])
+ {
+ CatCTup *ct = dlist_container(CatCTup, cache_elem, iter.cur);
+
+ /* Don't remove referenced entries */
+ if (ct->refcount == 0 &&
+ (ct->c_list == NULL || ct->c_list->refcount == 0))
+ {
+ if (ct->lastaccess < prune_threshold)
+ {
+ CatCacheRemoveCTup(cp, ct);
+ nremoved++;
+
+ /* don't let the removed entry update oldest_ts */
+ continue;
+ }
+ }
+
+ /* update the oldest timestamp if the entry remains alive */
+ if (ct->lastaccess < oldest_ts)
+ oldest_ts = ct->lastaccess;
+ }
+ }
+
+ cp->cc_oldest_ts = oldest_ts;
+
+ if (nremoved > 0)
+ elog(DEBUG1, "pruning catalog cache id=%d for %s: removed %d / %d",
+ cp->id, cp->cc_relname, nremoved, cp->cc_ntup + nremoved);
+
+ return nremoved > 0;
+}
+
/*
* ReleaseCatCache
*
@@ -1888,6 +1967,7 @@ CatalogCacheCreateEntry(CatCache *cache, HeapTuple ntp, Datum *arguments,
ct->dead = false;
ct->negative = negative;
ct->hash_value = hashValue;
+ ct->lastaccess = catcacheclock;
dlist_push_head(&cache->cc_bucket[hashIndex], &ct->cache_elem);
@@ -1899,7 +1979,12 @@ CatalogCacheCreateEntry(CatCache *cache, HeapTuple ntp, Datum *arguments,
* arbitrarily, we enlarge when fill factor > 2.
*/
if (cache->cc_ntup > cache->cc_nbuckets * 2)
- RehashCatCache(cache);
+ {
+ /* try removing old entries before expanding hash */
+ if (catcacheclock - cache->cc_oldest_ts < prune_min_age_us ||
+ !CatCacheCleanupOldEntries(cache))
+ RehashCatCache(cache);
+ }
return ct;
}
diff --git a/src/backend/utils/misc/guc.c b/src/backend/utils/misc/guc.c
index 17579eeaca..255e9fa73d 100644
--- a/src/backend/utils/misc/guc.c
+++ b/src/backend/utils/misc/guc.c
@@ -88,6 +88,7 @@
#include "utils/acl.h"
#include "utils/builtins.h"
#include "utils/bytea.h"
+#include "utils/catcache.h"
#include "utils/float.h"
#include "utils/guc_tables.h"
#include "utils/memutils.h"
@@ -3445,6 +3446,17 @@ static struct config_int ConfigureNamesInt[] =
NULL, NULL, NULL
},
+ {
+ {"catalog_cache_prune_min_age", PGC_USERSET, RESOURCES_MEM,
+ gettext_noop("System catalog cache entries that are living unused more than this seconds are considered for removal."),
+ gettext_noop("The value of -1 turns off pruning."),
+ GUC_UNIT_S
+ },
+ &catalog_cache_prune_min_age,
+ -1, -1, INT_MAX,
+ NULL, assign_catalog_cache_prune_min_age, NULL
+ },
+
/* End-of-list marker */
{
{NULL, 0, 0, NULL, NULL}, NULL, 0, 0, 0, NULL, NULL, NULL
diff --git a/src/include/utils/catcache.h b/src/include/utils/catcache.h
index ddc2762eb3..291e857e38 100644
--- a/src/include/utils/catcache.h
+++ b/src/include/utils/catcache.h
@@ -22,6 +22,7 @@
#include "access/htup.h"
#include "access/skey.h"
+#include "datatype/timestamp.h"
#include "lib/ilist.h"
#include "utils/relcache.h"
@@ -61,6 +62,7 @@ typedef struct catcache
slist_node cc_next; /* list link */
ScanKeyData cc_skey[CATCACHE_MAXKEYS]; /* precomputed key info for heap
* scans */
+ uint64 cc_oldest_ts; /* timestamp (us) of the oldest tuple */
/*
* Keep these at the end, so that compiling catcache.c with CATCACHE_STATS
@@ -119,6 +121,7 @@ typedef struct catctup
bool dead; /* dead but not yet removed? */
bool negative; /* negative cache entry? */
HeapTupleData tuple; /* tuple management header */
+ uint64 lastaccess; /* timestamp in us of the last usage */
/*
* The tuple may also be a member of at most one CatCList. (If a single
@@ -189,6 +192,22 @@ typedef struct catcacheheader
/* this extern duplicates utils/memutils.h... */
extern PGDLLIMPORT MemoryContext CacheMemoryContext;
+
+/* for guc.c, not PGDLLPMPORT'ed */
+extern int catalog_cache_prune_min_age;
+
+/* source clock for access timestamp of catcache entries */
+extern uint64 catcacheclock;
+
+/* SetCatCacheClock - set catcache timestamp source clock */
+static inline void
+SetCatCacheClock(TimestampTz ts)
+{
+ catcacheclock = (uint64) ts;
+}
+
+extern void assign_catalog_cache_prune_min_age(int newval, void *extra);
+
extern void CreateCacheMemoryContext(void);
extern CatCache *InitCatCache(int id, Oid reloid, Oid indexoid,
--
2.27.0
----Next_Part(Wed_Jan_27_10_13_08_2021_482)--
Content-Type: Text/X-Patch; charset=us-ascii
Content-Transfer-Encoding: 7bit
Content-Disposition: inline;
filename="v7-0002-Remove-dead-flag-from-catcache-tuple.patch"
^ permalink raw reply [nested|flat] 92+ messages in thread
* [PATCH v6 1/3] CatCache expiration feature
@ 2020-11-18 07:54 Kyotaro Horiguchi <[email protected]>
0 siblings, 0 replies; 92+ messages in thread
From: Kyotaro Horiguchi @ 2020-11-18 07:54 UTC (permalink / raw)
---
src/backend/access/transam/xact.c | 3 ++
src/backend/utils/cache/catcache.c | 87 +++++++++++++++++++++++++++++-
src/backend/utils/misc/guc.c | 12 +++++
src/include/utils/catcache.h | 19 +++++++
4 files changed, 120 insertions(+), 1 deletion(-)
diff --git a/src/backend/access/transam/xact.c b/src/backend/access/transam/xact.c
index a2068e3fd4..86888d2409 100644
--- a/src/backend/access/transam/xact.c
+++ b/src/backend/access/transam/xact.c
@@ -1086,6 +1086,9 @@ static void
AtStart_Cache(void)
{
AcceptInvalidationMessages();
+
+ if (xactStartTimestamp != 0)
+ SetCatCacheClock(xactStartTimestamp);
}
/*
diff --git a/src/backend/utils/cache/catcache.c b/src/backend/utils/cache/catcache.c
index fa2b49c676..644d92dd9a 100644
--- a/src/backend/utils/cache/catcache.c
+++ b/src/backend/utils/cache/catcache.c
@@ -38,6 +38,7 @@
#include "utils/rel.h"
#include "utils/resowner_private.h"
#include "utils/syscache.h"
+#include "utils/timestamp.h"
/* #define CACHEDEBUG */ /* turns DEBUG elogs on */
@@ -60,9 +61,19 @@
#define CACHE_elog(...)
#endif
+/*
+ * GUC variable to define the minimum age of entries that will be considered
+ * to be evicted in seconds. -1 to disable the feature.
+ */
+int catalog_cache_prune_min_age = -1;
+uint64 prune_min_age_us;
+
/* Cache management header --- pointer is NULL until created */
static CatCacheHeader *CacheHdr = NULL;
+/* Clock for the last accessed time of a catcache entry. */
+uint64 catcacheclock = 0;
+
static inline HeapTuple SearchCatCacheInternal(CatCache *cache,
int nkeys,
Datum v1, Datum v2,
@@ -74,6 +85,7 @@ static pg_noinline HeapTuple SearchCatCacheMiss(CatCache *cache,
Index hashIndex,
Datum v1, Datum v2,
Datum v3, Datum v4);
+static bool CatCacheCleanupOldEntries(CatCache *cp);
static uint32 CatalogCacheComputeHashValue(CatCache *cache, int nkeys,
Datum v1, Datum v2, Datum v3, Datum v4);
@@ -99,6 +111,15 @@ static void CatCacheFreeKeys(TupleDesc tupdesc, int nkeys, int *attnos,
static void CatCacheCopyKeys(TupleDesc tupdesc, int nkeys, int *attnos,
Datum *srckeys, Datum *dstkeys);
+/* GUC assign function */
+void
+assign_catalog_cache_prune_min_age(int newval, void *extra)
+{
+ if (newval < 0)
+ prune_min_age_us = UINT64_MAX;
+ else
+ prune_min_age_us = ((uint64) newval) * USECS_PER_SEC;
+}
/*
* internal support functions
@@ -1264,6 +1285,9 @@ SearchCatCacheInternal(CatCache *cache,
*/
dlist_move_head(bucket, &ct->cache_elem);
+ /* Record the last access timestamp */
+ ct->lastaccess = catcacheclock;
+
/*
* If it's a positive entry, bump its refcount and return it. If it's
* negative, we can report failure to the caller.
@@ -1425,6 +1449,61 @@ SearchCatCacheMiss(CatCache *cache,
return &ct->tuple;
}
+/*
+ * CatCacheCleanupOldEntries - Remove infrequently-used entries
+ *
+ * Catcache entries happen to be left unused for a long time for several
+ * reasons. Remove such entries to prevent catcache from bloating. It is based
+ * on the similar algorithm with buffer eviction. Entries that are accessed
+ * several times in a certain period live longer than those that have had less
+ * access in the same duration.
+ */
+static bool
+CatCacheCleanupOldEntries(CatCache *cp)
+{
+ int nremoved = 0;
+ int i;
+ long oldest_ts = catcacheclock;
+ uint64 prune_threshold = catcacheclock - prune_min_age_us;
+
+ /* Scan over the whole hash to find entries to remove */
+ for (i = 0 ; i < cp->cc_nbuckets ; i++)
+ {
+ dlist_mutable_iter iter;
+
+ dlist_foreach_modify(iter, &cp->cc_bucket[i])
+ {
+ CatCTup *ct = dlist_container(CatCTup, cache_elem, iter.cur);
+
+ /* Don't remove referenced entries */
+ if (ct->refcount == 0 &&
+ (ct->c_list == NULL || ct->c_list->refcount == 0))
+ {
+ if (ct->lastaccess < prune_threshold)
+ {
+ CatCacheRemoveCTup(cp, ct);
+ nremoved++;
+
+ /* don't let the removed entry update oldest_ts */
+ continue;
+ }
+ }
+
+ /* update the oldest timestamp if the entry remains alive */
+ if (ct->lastaccess < oldest_ts)
+ oldest_ts = ct->lastaccess;
+ }
+ }
+
+ cp->cc_oldest_ts = oldest_ts;
+
+ if (nremoved > 0)
+ elog(DEBUG1, "pruning catalog cache id=%d for %s: removed %d / %d",
+ cp->id, cp->cc_relname, nremoved, cp->cc_ntup + nremoved);
+
+ return nremoved > 0;
+}
+
/*
* ReleaseCatCache
*
@@ -1888,6 +1967,7 @@ CatalogCacheCreateEntry(CatCache *cache, HeapTuple ntp, Datum *arguments,
ct->dead = false;
ct->negative = negative;
ct->hash_value = hashValue;
+ ct->lastaccess = catcacheclock;
dlist_push_head(&cache->cc_bucket[hashIndex], &ct->cache_elem);
@@ -1899,7 +1979,12 @@ CatalogCacheCreateEntry(CatCache *cache, HeapTuple ntp, Datum *arguments,
* arbitrarily, we enlarge when fill factor > 2.
*/
if (cache->cc_ntup > cache->cc_nbuckets * 2)
- RehashCatCache(cache);
+ {
+ /* try removing old entries before expanding hash */
+ if (catcacheclock - cache->cc_oldest_ts < prune_min_age_us ||
+ !CatCacheCleanupOldEntries(cache))
+ RehashCatCache(cache);
+ }
return ct;
}
diff --git a/src/backend/utils/misc/guc.c b/src/backend/utils/misc/guc.c
index 17579eeaca..255e9fa73d 100644
--- a/src/backend/utils/misc/guc.c
+++ b/src/backend/utils/misc/guc.c
@@ -88,6 +88,7 @@
#include "utils/acl.h"
#include "utils/builtins.h"
#include "utils/bytea.h"
+#include "utils/catcache.h"
#include "utils/float.h"
#include "utils/guc_tables.h"
#include "utils/memutils.h"
@@ -3445,6 +3446,17 @@ static struct config_int ConfigureNamesInt[] =
NULL, NULL, NULL
},
+ {
+ {"catalog_cache_prune_min_age", PGC_USERSET, RESOURCES_MEM,
+ gettext_noop("System catalog cache entries that are living unused more than this seconds are considered for removal."),
+ gettext_noop("The value of -1 turns off pruning."),
+ GUC_UNIT_S
+ },
+ &catalog_cache_prune_min_age,
+ -1, -1, INT_MAX,
+ NULL, assign_catalog_cache_prune_min_age, NULL
+ },
+
/* End-of-list marker */
{
{NULL, 0, 0, NULL, NULL}, NULL, 0, 0, 0, NULL, NULL, NULL
diff --git a/src/include/utils/catcache.h b/src/include/utils/catcache.h
index ddc2762eb3..291e857e38 100644
--- a/src/include/utils/catcache.h
+++ b/src/include/utils/catcache.h
@@ -22,6 +22,7 @@
#include "access/htup.h"
#include "access/skey.h"
+#include "datatype/timestamp.h"
#include "lib/ilist.h"
#include "utils/relcache.h"
@@ -61,6 +62,7 @@ typedef struct catcache
slist_node cc_next; /* list link */
ScanKeyData cc_skey[CATCACHE_MAXKEYS]; /* precomputed key info for heap
* scans */
+ uint64 cc_oldest_ts; /* timestamp (us) of the oldest tuple */
/*
* Keep these at the end, so that compiling catcache.c with CATCACHE_STATS
@@ -119,6 +121,7 @@ typedef struct catctup
bool dead; /* dead but not yet removed? */
bool negative; /* negative cache entry? */
HeapTupleData tuple; /* tuple management header */
+ uint64 lastaccess; /* timestamp in us of the last usage */
/*
* The tuple may also be a member of at most one CatCList. (If a single
@@ -189,6 +192,22 @@ typedef struct catcacheheader
/* this extern duplicates utils/memutils.h... */
extern PGDLLIMPORT MemoryContext CacheMemoryContext;
+
+/* for guc.c, not PGDLLPMPORT'ed */
+extern int catalog_cache_prune_min_age;
+
+/* source clock for access timestamp of catcache entries */
+extern uint64 catcacheclock;
+
+/* SetCatCacheClock - set catcache timestamp source clock */
+static inline void
+SetCatCacheClock(TimestampTz ts)
+{
+ catcacheclock = (uint64) ts;
+}
+
+extern void assign_catalog_cache_prune_min_age(int newval, void *extra);
+
extern void CreateCacheMemoryContext(void);
extern CatCache *InitCatCache(int id, Oid reloid, Oid indexoid,
--
2.27.0
----Next_Part(Thu_Jan_14_17_32_27_2021_995)--
Content-Type: Text/X-Patch; charset=us-ascii
Content-Transfer-Encoding: 7bit
Content-Disposition: inline;
filename="v6-0002-Remove-dead-flag-from-catcache-tuple.patch"
^ permalink raw reply [nested|flat] 92+ messages in thread
* [PATCH v6 1/3] CatCache expiration feature
@ 2020-11-18 07:54 Kyotaro Horiguchi <[email protected]>
0 siblings, 0 replies; 92+ messages in thread
From: Kyotaro Horiguchi @ 2020-11-18 07:54 UTC (permalink / raw)
---
src/backend/access/transam/xact.c | 3 ++
src/backend/utils/cache/catcache.c | 87 +++++++++++++++++++++++++++++-
src/backend/utils/misc/guc.c | 12 +++++
src/include/utils/catcache.h | 19 +++++++
4 files changed, 120 insertions(+), 1 deletion(-)
diff --git a/src/backend/access/transam/xact.c b/src/backend/access/transam/xact.c
index a2068e3fd4..86888d2409 100644
--- a/src/backend/access/transam/xact.c
+++ b/src/backend/access/transam/xact.c
@@ -1086,6 +1086,9 @@ static void
AtStart_Cache(void)
{
AcceptInvalidationMessages();
+
+ if (xactStartTimestamp != 0)
+ SetCatCacheClock(xactStartTimestamp);
}
/*
diff --git a/src/backend/utils/cache/catcache.c b/src/backend/utils/cache/catcache.c
index fa2b49c676..644d92dd9a 100644
--- a/src/backend/utils/cache/catcache.c
+++ b/src/backend/utils/cache/catcache.c
@@ -38,6 +38,7 @@
#include "utils/rel.h"
#include "utils/resowner_private.h"
#include "utils/syscache.h"
+#include "utils/timestamp.h"
/* #define CACHEDEBUG */ /* turns DEBUG elogs on */
@@ -60,9 +61,19 @@
#define CACHE_elog(...)
#endif
+/*
+ * GUC variable to define the minimum age of entries that will be considered
+ * to be evicted in seconds. -1 to disable the feature.
+ */
+int catalog_cache_prune_min_age = -1;
+uint64 prune_min_age_us;
+
/* Cache management header --- pointer is NULL until created */
static CatCacheHeader *CacheHdr = NULL;
+/* Clock for the last accessed time of a catcache entry. */
+uint64 catcacheclock = 0;
+
static inline HeapTuple SearchCatCacheInternal(CatCache *cache,
int nkeys,
Datum v1, Datum v2,
@@ -74,6 +85,7 @@ static pg_noinline HeapTuple SearchCatCacheMiss(CatCache *cache,
Index hashIndex,
Datum v1, Datum v2,
Datum v3, Datum v4);
+static bool CatCacheCleanupOldEntries(CatCache *cp);
static uint32 CatalogCacheComputeHashValue(CatCache *cache, int nkeys,
Datum v1, Datum v2, Datum v3, Datum v4);
@@ -99,6 +111,15 @@ static void CatCacheFreeKeys(TupleDesc tupdesc, int nkeys, int *attnos,
static void CatCacheCopyKeys(TupleDesc tupdesc, int nkeys, int *attnos,
Datum *srckeys, Datum *dstkeys);
+/* GUC assign function */
+void
+assign_catalog_cache_prune_min_age(int newval, void *extra)
+{
+ if (newval < 0)
+ prune_min_age_us = UINT64_MAX;
+ else
+ prune_min_age_us = ((uint64) newval) * USECS_PER_SEC;
+}
/*
* internal support functions
@@ -1264,6 +1285,9 @@ SearchCatCacheInternal(CatCache *cache,
*/
dlist_move_head(bucket, &ct->cache_elem);
+ /* Record the last access timestamp */
+ ct->lastaccess = catcacheclock;
+
/*
* If it's a positive entry, bump its refcount and return it. If it's
* negative, we can report failure to the caller.
@@ -1425,6 +1449,61 @@ SearchCatCacheMiss(CatCache *cache,
return &ct->tuple;
}
+/*
+ * CatCacheCleanupOldEntries - Remove infrequently-used entries
+ *
+ * Catcache entries happen to be left unused for a long time for several
+ * reasons. Remove such entries to prevent catcache from bloating. It is based
+ * on the similar algorithm with buffer eviction. Entries that are accessed
+ * several times in a certain period live longer than those that have had less
+ * access in the same duration.
+ */
+static bool
+CatCacheCleanupOldEntries(CatCache *cp)
+{
+ int nremoved = 0;
+ int i;
+ long oldest_ts = catcacheclock;
+ uint64 prune_threshold = catcacheclock - prune_min_age_us;
+
+ /* Scan over the whole hash to find entries to remove */
+ for (i = 0 ; i < cp->cc_nbuckets ; i++)
+ {
+ dlist_mutable_iter iter;
+
+ dlist_foreach_modify(iter, &cp->cc_bucket[i])
+ {
+ CatCTup *ct = dlist_container(CatCTup, cache_elem, iter.cur);
+
+ /* Don't remove referenced entries */
+ if (ct->refcount == 0 &&
+ (ct->c_list == NULL || ct->c_list->refcount == 0))
+ {
+ if (ct->lastaccess < prune_threshold)
+ {
+ CatCacheRemoveCTup(cp, ct);
+ nremoved++;
+
+ /* don't let the removed entry update oldest_ts */
+ continue;
+ }
+ }
+
+ /* update the oldest timestamp if the entry remains alive */
+ if (ct->lastaccess < oldest_ts)
+ oldest_ts = ct->lastaccess;
+ }
+ }
+
+ cp->cc_oldest_ts = oldest_ts;
+
+ if (nremoved > 0)
+ elog(DEBUG1, "pruning catalog cache id=%d for %s: removed %d / %d",
+ cp->id, cp->cc_relname, nremoved, cp->cc_ntup + nremoved);
+
+ return nremoved > 0;
+}
+
/*
* ReleaseCatCache
*
@@ -1888,6 +1967,7 @@ CatalogCacheCreateEntry(CatCache *cache, HeapTuple ntp, Datum *arguments,
ct->dead = false;
ct->negative = negative;
ct->hash_value = hashValue;
+ ct->lastaccess = catcacheclock;
dlist_push_head(&cache->cc_bucket[hashIndex], &ct->cache_elem);
@@ -1899,7 +1979,12 @@ CatalogCacheCreateEntry(CatCache *cache, HeapTuple ntp, Datum *arguments,
* arbitrarily, we enlarge when fill factor > 2.
*/
if (cache->cc_ntup > cache->cc_nbuckets * 2)
- RehashCatCache(cache);
+ {
+ /* try removing old entries before expanding hash */
+ if (catcacheclock - cache->cc_oldest_ts < prune_min_age_us ||
+ !CatCacheCleanupOldEntries(cache))
+ RehashCatCache(cache);
+ }
return ct;
}
diff --git a/src/backend/utils/misc/guc.c b/src/backend/utils/misc/guc.c
index 17579eeaca..255e9fa73d 100644
--- a/src/backend/utils/misc/guc.c
+++ b/src/backend/utils/misc/guc.c
@@ -88,6 +88,7 @@
#include "utils/acl.h"
#include "utils/builtins.h"
#include "utils/bytea.h"
+#include "utils/catcache.h"
#include "utils/float.h"
#include "utils/guc_tables.h"
#include "utils/memutils.h"
@@ -3445,6 +3446,17 @@ static struct config_int ConfigureNamesInt[] =
NULL, NULL, NULL
},
+ {
+ {"catalog_cache_prune_min_age", PGC_USERSET, RESOURCES_MEM,
+ gettext_noop("System catalog cache entries that are living unused more than this seconds are considered for removal."),
+ gettext_noop("The value of -1 turns off pruning."),
+ GUC_UNIT_S
+ },
+ &catalog_cache_prune_min_age,
+ -1, -1, INT_MAX,
+ NULL, assign_catalog_cache_prune_min_age, NULL
+ },
+
/* End-of-list marker */
{
{NULL, 0, 0, NULL, NULL}, NULL, 0, 0, 0, NULL, NULL, NULL
diff --git a/src/include/utils/catcache.h b/src/include/utils/catcache.h
index ddc2762eb3..291e857e38 100644
--- a/src/include/utils/catcache.h
+++ b/src/include/utils/catcache.h
@@ -22,6 +22,7 @@
#include "access/htup.h"
#include "access/skey.h"
+#include "datatype/timestamp.h"
#include "lib/ilist.h"
#include "utils/relcache.h"
@@ -61,6 +62,7 @@ typedef struct catcache
slist_node cc_next; /* list link */
ScanKeyData cc_skey[CATCACHE_MAXKEYS]; /* precomputed key info for heap
* scans */
+ uint64 cc_oldest_ts; /* timestamp (us) of the oldest tuple */
/*
* Keep these at the end, so that compiling catcache.c with CATCACHE_STATS
@@ -119,6 +121,7 @@ typedef struct catctup
bool dead; /* dead but not yet removed? */
bool negative; /* negative cache entry? */
HeapTupleData tuple; /* tuple management header */
+ uint64 lastaccess; /* timestamp in us of the last usage */
/*
* The tuple may also be a member of at most one CatCList. (If a single
@@ -189,6 +192,22 @@ typedef struct catcacheheader
/* this extern duplicates utils/memutils.h... */
extern PGDLLIMPORT MemoryContext CacheMemoryContext;
+
+/* for guc.c, not PGDLLPMPORT'ed */
+extern int catalog_cache_prune_min_age;
+
+/* source clock for access timestamp of catcache entries */
+extern uint64 catcacheclock;
+
+/* SetCatCacheClock - set catcache timestamp source clock */
+static inline void
+SetCatCacheClock(TimestampTz ts)
+{
+ catcacheclock = (uint64) ts;
+}
+
+extern void assign_catalog_cache_prune_min_age(int newval, void *extra);
+
extern void CreateCacheMemoryContext(void);
extern CatCache *InitCatCache(int id, Oid reloid, Oid indexoid,
--
2.27.0
----Next_Part(Thu_Jan_14_17_32_27_2021_995)--
Content-Type: Text/X-Patch; charset=us-ascii
Content-Transfer-Encoding: 7bit
Content-Disposition: inline;
filename="v6-0002-Remove-dead-flag-from-catcache-tuple.patch"
^ permalink raw reply [nested|flat] 92+ messages in thread
* [PATCH v7 1/3] CatCache expiration feature
@ 2020-11-18 07:54 Kyotaro Horiguchi <[email protected]>
0 siblings, 0 replies; 92+ messages in thread
From: Kyotaro Horiguchi @ 2020-11-18 07:54 UTC (permalink / raw)
---
src/backend/access/transam/xact.c | 3 ++
src/backend/utils/cache/catcache.c | 87 +++++++++++++++++++++++++++++-
src/backend/utils/misc/guc.c | 12 +++++
src/include/utils/catcache.h | 19 +++++++
4 files changed, 120 insertions(+), 1 deletion(-)
diff --git a/src/backend/access/transam/xact.c b/src/backend/access/transam/xact.c
index a2068e3fd4..86888d2409 100644
--- a/src/backend/access/transam/xact.c
+++ b/src/backend/access/transam/xact.c
@@ -1086,6 +1086,9 @@ static void
AtStart_Cache(void)
{
AcceptInvalidationMessages();
+
+ if (xactStartTimestamp != 0)
+ SetCatCacheClock(xactStartTimestamp);
}
/*
diff --git a/src/backend/utils/cache/catcache.c b/src/backend/utils/cache/catcache.c
index fa2b49c676..644d92dd9a 100644
--- a/src/backend/utils/cache/catcache.c
+++ b/src/backend/utils/cache/catcache.c
@@ -38,6 +38,7 @@
#include "utils/rel.h"
#include "utils/resowner_private.h"
#include "utils/syscache.h"
+#include "utils/timestamp.h"
/* #define CACHEDEBUG */ /* turns DEBUG elogs on */
@@ -60,9 +61,19 @@
#define CACHE_elog(...)
#endif
+/*
+ * GUC variable to define the minimum age of entries that will be considered
+ * to be evicted in seconds. -1 to disable the feature.
+ */
+int catalog_cache_prune_min_age = -1;
+uint64 prune_min_age_us;
+
/* Cache management header --- pointer is NULL until created */
static CatCacheHeader *CacheHdr = NULL;
+/* Clock for the last accessed time of a catcache entry. */
+uint64 catcacheclock = 0;
+
static inline HeapTuple SearchCatCacheInternal(CatCache *cache,
int nkeys,
Datum v1, Datum v2,
@@ -74,6 +85,7 @@ static pg_noinline HeapTuple SearchCatCacheMiss(CatCache *cache,
Index hashIndex,
Datum v1, Datum v2,
Datum v3, Datum v4);
+static bool CatCacheCleanupOldEntries(CatCache *cp);
static uint32 CatalogCacheComputeHashValue(CatCache *cache, int nkeys,
Datum v1, Datum v2, Datum v3, Datum v4);
@@ -99,6 +111,15 @@ static void CatCacheFreeKeys(TupleDesc tupdesc, int nkeys, int *attnos,
static void CatCacheCopyKeys(TupleDesc tupdesc, int nkeys, int *attnos,
Datum *srckeys, Datum *dstkeys);
+/* GUC assign function */
+void
+assign_catalog_cache_prune_min_age(int newval, void *extra)
+{
+ if (newval < 0)
+ prune_min_age_us = UINT64_MAX;
+ else
+ prune_min_age_us = ((uint64) newval) * USECS_PER_SEC;
+}
/*
* internal support functions
@@ -1264,6 +1285,9 @@ SearchCatCacheInternal(CatCache *cache,
*/
dlist_move_head(bucket, &ct->cache_elem);
+ /* Record the last access timestamp */
+ ct->lastaccess = catcacheclock;
+
/*
* If it's a positive entry, bump its refcount and return it. If it's
* negative, we can report failure to the caller.
@@ -1425,6 +1449,61 @@ SearchCatCacheMiss(CatCache *cache,
return &ct->tuple;
}
+/*
+ * CatCacheCleanupOldEntries - Remove infrequently-used entries
+ *
+ * Catcache entries happen to be left unused for a long time for several
+ * reasons. Remove such entries to prevent catcache from bloating. It is based
+ * on the similar algorithm with buffer eviction. Entries that are accessed
+ * several times in a certain period live longer than those that have had less
+ * access in the same duration.
+ */
+static bool
+CatCacheCleanupOldEntries(CatCache *cp)
+{
+ int nremoved = 0;
+ int i;
+ long oldest_ts = catcacheclock;
+ uint64 prune_threshold = catcacheclock - prune_min_age_us;
+
+ /* Scan over the whole hash to find entries to remove */
+ for (i = 0 ; i < cp->cc_nbuckets ; i++)
+ {
+ dlist_mutable_iter iter;
+
+ dlist_foreach_modify(iter, &cp->cc_bucket[i])
+ {
+ CatCTup *ct = dlist_container(CatCTup, cache_elem, iter.cur);
+
+ /* Don't remove referenced entries */
+ if (ct->refcount == 0 &&
+ (ct->c_list == NULL || ct->c_list->refcount == 0))
+ {
+ if (ct->lastaccess < prune_threshold)
+ {
+ CatCacheRemoveCTup(cp, ct);
+ nremoved++;
+
+ /* don't let the removed entry update oldest_ts */
+ continue;
+ }
+ }
+
+ /* update the oldest timestamp if the entry remains alive */
+ if (ct->lastaccess < oldest_ts)
+ oldest_ts = ct->lastaccess;
+ }
+ }
+
+ cp->cc_oldest_ts = oldest_ts;
+
+ if (nremoved > 0)
+ elog(DEBUG1, "pruning catalog cache id=%d for %s: removed %d / %d",
+ cp->id, cp->cc_relname, nremoved, cp->cc_ntup + nremoved);
+
+ return nremoved > 0;
+}
+
/*
* ReleaseCatCache
*
@@ -1888,6 +1967,7 @@ CatalogCacheCreateEntry(CatCache *cache, HeapTuple ntp, Datum *arguments,
ct->dead = false;
ct->negative = negative;
ct->hash_value = hashValue;
+ ct->lastaccess = catcacheclock;
dlist_push_head(&cache->cc_bucket[hashIndex], &ct->cache_elem);
@@ -1899,7 +1979,12 @@ CatalogCacheCreateEntry(CatCache *cache, HeapTuple ntp, Datum *arguments,
* arbitrarily, we enlarge when fill factor > 2.
*/
if (cache->cc_ntup > cache->cc_nbuckets * 2)
- RehashCatCache(cache);
+ {
+ /* try removing old entries before expanding hash */
+ if (catcacheclock - cache->cc_oldest_ts < prune_min_age_us ||
+ !CatCacheCleanupOldEntries(cache))
+ RehashCatCache(cache);
+ }
return ct;
}
diff --git a/src/backend/utils/misc/guc.c b/src/backend/utils/misc/guc.c
index 17579eeaca..255e9fa73d 100644
--- a/src/backend/utils/misc/guc.c
+++ b/src/backend/utils/misc/guc.c
@@ -88,6 +88,7 @@
#include "utils/acl.h"
#include "utils/builtins.h"
#include "utils/bytea.h"
+#include "utils/catcache.h"
#include "utils/float.h"
#include "utils/guc_tables.h"
#include "utils/memutils.h"
@@ -3445,6 +3446,17 @@ static struct config_int ConfigureNamesInt[] =
NULL, NULL, NULL
},
+ {
+ {"catalog_cache_prune_min_age", PGC_USERSET, RESOURCES_MEM,
+ gettext_noop("System catalog cache entries that are living unused more than this seconds are considered for removal."),
+ gettext_noop("The value of -1 turns off pruning."),
+ GUC_UNIT_S
+ },
+ &catalog_cache_prune_min_age,
+ -1, -1, INT_MAX,
+ NULL, assign_catalog_cache_prune_min_age, NULL
+ },
+
/* End-of-list marker */
{
{NULL, 0, 0, NULL, NULL}, NULL, 0, 0, 0, NULL, NULL, NULL
diff --git a/src/include/utils/catcache.h b/src/include/utils/catcache.h
index ddc2762eb3..291e857e38 100644
--- a/src/include/utils/catcache.h
+++ b/src/include/utils/catcache.h
@@ -22,6 +22,7 @@
#include "access/htup.h"
#include "access/skey.h"
+#include "datatype/timestamp.h"
#include "lib/ilist.h"
#include "utils/relcache.h"
@@ -61,6 +62,7 @@ typedef struct catcache
slist_node cc_next; /* list link */
ScanKeyData cc_skey[CATCACHE_MAXKEYS]; /* precomputed key info for heap
* scans */
+ uint64 cc_oldest_ts; /* timestamp (us) of the oldest tuple */
/*
* Keep these at the end, so that compiling catcache.c with CATCACHE_STATS
@@ -119,6 +121,7 @@ typedef struct catctup
bool dead; /* dead but not yet removed? */
bool negative; /* negative cache entry? */
HeapTupleData tuple; /* tuple management header */
+ uint64 lastaccess; /* timestamp in us of the last usage */
/*
* The tuple may also be a member of at most one CatCList. (If a single
@@ -189,6 +192,22 @@ typedef struct catcacheheader
/* this extern duplicates utils/memutils.h... */
extern PGDLLIMPORT MemoryContext CacheMemoryContext;
+
+/* for guc.c, not PGDLLPMPORT'ed */
+extern int catalog_cache_prune_min_age;
+
+/* source clock for access timestamp of catcache entries */
+extern uint64 catcacheclock;
+
+/* SetCatCacheClock - set catcache timestamp source clock */
+static inline void
+SetCatCacheClock(TimestampTz ts)
+{
+ catcacheclock = (uint64) ts;
+}
+
+extern void assign_catalog_cache_prune_min_age(int newval, void *extra);
+
extern void CreateCacheMemoryContext(void);
extern CatCache *InitCatCache(int id, Oid reloid, Oid indexoid,
--
2.27.0
----Next_Part(Wed_Jan_27_10_13_08_2021_482)--
Content-Type: Text/X-Patch; charset=us-ascii
Content-Transfer-Encoding: 7bit
Content-Disposition: inline;
filename="v7-0002-Remove-dead-flag-from-catcache-tuple.patch"
^ permalink raw reply [nested|flat] 92+ messages in thread
* [PATCH v6 1/3] CatCache expiration feature
@ 2020-11-18 07:54 Kyotaro Horiguchi <[email protected]>
0 siblings, 0 replies; 92+ messages in thread
From: Kyotaro Horiguchi @ 2020-11-18 07:54 UTC (permalink / raw)
---
src/backend/access/transam/xact.c | 3 ++
src/backend/utils/cache/catcache.c | 87 +++++++++++++++++++++++++++++-
src/backend/utils/misc/guc.c | 12 +++++
src/include/utils/catcache.h | 19 +++++++
4 files changed, 120 insertions(+), 1 deletion(-)
diff --git a/src/backend/access/transam/xact.c b/src/backend/access/transam/xact.c
index a2068e3fd4..86888d2409 100644
--- a/src/backend/access/transam/xact.c
+++ b/src/backend/access/transam/xact.c
@@ -1086,6 +1086,9 @@ static void
AtStart_Cache(void)
{
AcceptInvalidationMessages();
+
+ if (xactStartTimestamp != 0)
+ SetCatCacheClock(xactStartTimestamp);
}
/*
diff --git a/src/backend/utils/cache/catcache.c b/src/backend/utils/cache/catcache.c
index fa2b49c676..644d92dd9a 100644
--- a/src/backend/utils/cache/catcache.c
+++ b/src/backend/utils/cache/catcache.c
@@ -38,6 +38,7 @@
#include "utils/rel.h"
#include "utils/resowner_private.h"
#include "utils/syscache.h"
+#include "utils/timestamp.h"
/* #define CACHEDEBUG */ /* turns DEBUG elogs on */
@@ -60,9 +61,19 @@
#define CACHE_elog(...)
#endif
+/*
+ * GUC variable to define the minimum age of entries that will be considered
+ * to be evicted in seconds. -1 to disable the feature.
+ */
+int catalog_cache_prune_min_age = -1;
+uint64 prune_min_age_us;
+
/* Cache management header --- pointer is NULL until created */
static CatCacheHeader *CacheHdr = NULL;
+/* Clock for the last accessed time of a catcache entry. */
+uint64 catcacheclock = 0;
+
static inline HeapTuple SearchCatCacheInternal(CatCache *cache,
int nkeys,
Datum v1, Datum v2,
@@ -74,6 +85,7 @@ static pg_noinline HeapTuple SearchCatCacheMiss(CatCache *cache,
Index hashIndex,
Datum v1, Datum v2,
Datum v3, Datum v4);
+static bool CatCacheCleanupOldEntries(CatCache *cp);
static uint32 CatalogCacheComputeHashValue(CatCache *cache, int nkeys,
Datum v1, Datum v2, Datum v3, Datum v4);
@@ -99,6 +111,15 @@ static void CatCacheFreeKeys(TupleDesc tupdesc, int nkeys, int *attnos,
static void CatCacheCopyKeys(TupleDesc tupdesc, int nkeys, int *attnos,
Datum *srckeys, Datum *dstkeys);
+/* GUC assign function */
+void
+assign_catalog_cache_prune_min_age(int newval, void *extra)
+{
+ if (newval < 0)
+ prune_min_age_us = UINT64_MAX;
+ else
+ prune_min_age_us = ((uint64) newval) * USECS_PER_SEC;
+}
/*
* internal support functions
@@ -1264,6 +1285,9 @@ SearchCatCacheInternal(CatCache *cache,
*/
dlist_move_head(bucket, &ct->cache_elem);
+ /* Record the last access timestamp */
+ ct->lastaccess = catcacheclock;
+
/*
* If it's a positive entry, bump its refcount and return it. If it's
* negative, we can report failure to the caller.
@@ -1425,6 +1449,61 @@ SearchCatCacheMiss(CatCache *cache,
return &ct->tuple;
}
+/*
+ * CatCacheCleanupOldEntries - Remove infrequently-used entries
+ *
+ * Catcache entries happen to be left unused for a long time for several
+ * reasons. Remove such entries to prevent catcache from bloating. It is based
+ * on the similar algorithm with buffer eviction. Entries that are accessed
+ * several times in a certain period live longer than those that have had less
+ * access in the same duration.
+ */
+static bool
+CatCacheCleanupOldEntries(CatCache *cp)
+{
+ int nremoved = 0;
+ int i;
+ long oldest_ts = catcacheclock;
+ uint64 prune_threshold = catcacheclock - prune_min_age_us;
+
+ /* Scan over the whole hash to find entries to remove */
+ for (i = 0 ; i < cp->cc_nbuckets ; i++)
+ {
+ dlist_mutable_iter iter;
+
+ dlist_foreach_modify(iter, &cp->cc_bucket[i])
+ {
+ CatCTup *ct = dlist_container(CatCTup, cache_elem, iter.cur);
+
+ /* Don't remove referenced entries */
+ if (ct->refcount == 0 &&
+ (ct->c_list == NULL || ct->c_list->refcount == 0))
+ {
+ if (ct->lastaccess < prune_threshold)
+ {
+ CatCacheRemoveCTup(cp, ct);
+ nremoved++;
+
+ /* don't let the removed entry update oldest_ts */
+ continue;
+ }
+ }
+
+ /* update the oldest timestamp if the entry remains alive */
+ if (ct->lastaccess < oldest_ts)
+ oldest_ts = ct->lastaccess;
+ }
+ }
+
+ cp->cc_oldest_ts = oldest_ts;
+
+ if (nremoved > 0)
+ elog(DEBUG1, "pruning catalog cache id=%d for %s: removed %d / %d",
+ cp->id, cp->cc_relname, nremoved, cp->cc_ntup + nremoved);
+
+ return nremoved > 0;
+}
+
/*
* ReleaseCatCache
*
@@ -1888,6 +1967,7 @@ CatalogCacheCreateEntry(CatCache *cache, HeapTuple ntp, Datum *arguments,
ct->dead = false;
ct->negative = negative;
ct->hash_value = hashValue;
+ ct->lastaccess = catcacheclock;
dlist_push_head(&cache->cc_bucket[hashIndex], &ct->cache_elem);
@@ -1899,7 +1979,12 @@ CatalogCacheCreateEntry(CatCache *cache, HeapTuple ntp, Datum *arguments,
* arbitrarily, we enlarge when fill factor > 2.
*/
if (cache->cc_ntup > cache->cc_nbuckets * 2)
- RehashCatCache(cache);
+ {
+ /* try removing old entries before expanding hash */
+ if (catcacheclock - cache->cc_oldest_ts < prune_min_age_us ||
+ !CatCacheCleanupOldEntries(cache))
+ RehashCatCache(cache);
+ }
return ct;
}
diff --git a/src/backend/utils/misc/guc.c b/src/backend/utils/misc/guc.c
index 17579eeaca..255e9fa73d 100644
--- a/src/backend/utils/misc/guc.c
+++ b/src/backend/utils/misc/guc.c
@@ -88,6 +88,7 @@
#include "utils/acl.h"
#include "utils/builtins.h"
#include "utils/bytea.h"
+#include "utils/catcache.h"
#include "utils/float.h"
#include "utils/guc_tables.h"
#include "utils/memutils.h"
@@ -3445,6 +3446,17 @@ static struct config_int ConfigureNamesInt[] =
NULL, NULL, NULL
},
+ {
+ {"catalog_cache_prune_min_age", PGC_USERSET, RESOURCES_MEM,
+ gettext_noop("System catalog cache entries that are living unused more than this seconds are considered for removal."),
+ gettext_noop("The value of -1 turns off pruning."),
+ GUC_UNIT_S
+ },
+ &catalog_cache_prune_min_age,
+ -1, -1, INT_MAX,
+ NULL, assign_catalog_cache_prune_min_age, NULL
+ },
+
/* End-of-list marker */
{
{NULL, 0, 0, NULL, NULL}, NULL, 0, 0, 0, NULL, NULL, NULL
diff --git a/src/include/utils/catcache.h b/src/include/utils/catcache.h
index ddc2762eb3..291e857e38 100644
--- a/src/include/utils/catcache.h
+++ b/src/include/utils/catcache.h
@@ -22,6 +22,7 @@
#include "access/htup.h"
#include "access/skey.h"
+#include "datatype/timestamp.h"
#include "lib/ilist.h"
#include "utils/relcache.h"
@@ -61,6 +62,7 @@ typedef struct catcache
slist_node cc_next; /* list link */
ScanKeyData cc_skey[CATCACHE_MAXKEYS]; /* precomputed key info for heap
* scans */
+ uint64 cc_oldest_ts; /* timestamp (us) of the oldest tuple */
/*
* Keep these at the end, so that compiling catcache.c with CATCACHE_STATS
@@ -119,6 +121,7 @@ typedef struct catctup
bool dead; /* dead but not yet removed? */
bool negative; /* negative cache entry? */
HeapTupleData tuple; /* tuple management header */
+ uint64 lastaccess; /* timestamp in us of the last usage */
/*
* The tuple may also be a member of at most one CatCList. (If a single
@@ -189,6 +192,22 @@ typedef struct catcacheheader
/* this extern duplicates utils/memutils.h... */
extern PGDLLIMPORT MemoryContext CacheMemoryContext;
+
+/* for guc.c, not PGDLLPMPORT'ed */
+extern int catalog_cache_prune_min_age;
+
+/* source clock for access timestamp of catcache entries */
+extern uint64 catcacheclock;
+
+/* SetCatCacheClock - set catcache timestamp source clock */
+static inline void
+SetCatCacheClock(TimestampTz ts)
+{
+ catcacheclock = (uint64) ts;
+}
+
+extern void assign_catalog_cache_prune_min_age(int newval, void *extra);
+
extern void CreateCacheMemoryContext(void);
extern CatCache *InitCatCache(int id, Oid reloid, Oid indexoid,
--
2.27.0
----Next_Part(Thu_Jan_14_17_32_27_2021_995)--
Content-Type: Text/X-Patch; charset=us-ascii
Content-Transfer-Encoding: 7bit
Content-Disposition: inline;
filename="v6-0002-Remove-dead-flag-from-catcache-tuple.patch"
^ permalink raw reply [nested|flat] 92+ messages in thread
* [PATCH v6 1/3] CatCache expiration feature
@ 2020-11-18 07:54 Kyotaro Horiguchi <[email protected]>
0 siblings, 0 replies; 92+ messages in thread
From: Kyotaro Horiguchi @ 2020-11-18 07:54 UTC (permalink / raw)
---
src/backend/access/transam/xact.c | 3 ++
src/backend/utils/cache/catcache.c | 87 +++++++++++++++++++++++++++++-
src/backend/utils/misc/guc.c | 12 +++++
src/include/utils/catcache.h | 19 +++++++
4 files changed, 120 insertions(+), 1 deletion(-)
diff --git a/src/backend/access/transam/xact.c b/src/backend/access/transam/xact.c
index a2068e3fd4..86888d2409 100644
--- a/src/backend/access/transam/xact.c
+++ b/src/backend/access/transam/xact.c
@@ -1086,6 +1086,9 @@ static void
AtStart_Cache(void)
{
AcceptInvalidationMessages();
+
+ if (xactStartTimestamp != 0)
+ SetCatCacheClock(xactStartTimestamp);
}
/*
diff --git a/src/backend/utils/cache/catcache.c b/src/backend/utils/cache/catcache.c
index fa2b49c676..644d92dd9a 100644
--- a/src/backend/utils/cache/catcache.c
+++ b/src/backend/utils/cache/catcache.c
@@ -38,6 +38,7 @@
#include "utils/rel.h"
#include "utils/resowner_private.h"
#include "utils/syscache.h"
+#include "utils/timestamp.h"
/* #define CACHEDEBUG */ /* turns DEBUG elogs on */
@@ -60,9 +61,19 @@
#define CACHE_elog(...)
#endif
+/*
+ * GUC variable to define the minimum age of entries that will be considered
+ * to be evicted in seconds. -1 to disable the feature.
+ */
+int catalog_cache_prune_min_age = -1;
+uint64 prune_min_age_us;
+
/* Cache management header --- pointer is NULL until created */
static CatCacheHeader *CacheHdr = NULL;
+/* Clock for the last accessed time of a catcache entry. */
+uint64 catcacheclock = 0;
+
static inline HeapTuple SearchCatCacheInternal(CatCache *cache,
int nkeys,
Datum v1, Datum v2,
@@ -74,6 +85,7 @@ static pg_noinline HeapTuple SearchCatCacheMiss(CatCache *cache,
Index hashIndex,
Datum v1, Datum v2,
Datum v3, Datum v4);
+static bool CatCacheCleanupOldEntries(CatCache *cp);
static uint32 CatalogCacheComputeHashValue(CatCache *cache, int nkeys,
Datum v1, Datum v2, Datum v3, Datum v4);
@@ -99,6 +111,15 @@ static void CatCacheFreeKeys(TupleDesc tupdesc, int nkeys, int *attnos,
static void CatCacheCopyKeys(TupleDesc tupdesc, int nkeys, int *attnos,
Datum *srckeys, Datum *dstkeys);
+/* GUC assign function */
+void
+assign_catalog_cache_prune_min_age(int newval, void *extra)
+{
+ if (newval < 0)
+ prune_min_age_us = UINT64_MAX;
+ else
+ prune_min_age_us = ((uint64) newval) * USECS_PER_SEC;
+}
/*
* internal support functions
@@ -1264,6 +1285,9 @@ SearchCatCacheInternal(CatCache *cache,
*/
dlist_move_head(bucket, &ct->cache_elem);
+ /* Record the last access timestamp */
+ ct->lastaccess = catcacheclock;
+
/*
* If it's a positive entry, bump its refcount and return it. If it's
* negative, we can report failure to the caller.
@@ -1425,6 +1449,61 @@ SearchCatCacheMiss(CatCache *cache,
return &ct->tuple;
}
+/*
+ * CatCacheCleanupOldEntries - Remove infrequently-used entries
+ *
+ * Catcache entries happen to be left unused for a long time for several
+ * reasons. Remove such entries to prevent catcache from bloating. It is based
+ * on the similar algorithm with buffer eviction. Entries that are accessed
+ * several times in a certain period live longer than those that have had less
+ * access in the same duration.
+ */
+static bool
+CatCacheCleanupOldEntries(CatCache *cp)
+{
+ int nremoved = 0;
+ int i;
+ long oldest_ts = catcacheclock;
+ uint64 prune_threshold = catcacheclock - prune_min_age_us;
+
+ /* Scan over the whole hash to find entries to remove */
+ for (i = 0 ; i < cp->cc_nbuckets ; i++)
+ {
+ dlist_mutable_iter iter;
+
+ dlist_foreach_modify(iter, &cp->cc_bucket[i])
+ {
+ CatCTup *ct = dlist_container(CatCTup, cache_elem, iter.cur);
+
+ /* Don't remove referenced entries */
+ if (ct->refcount == 0 &&
+ (ct->c_list == NULL || ct->c_list->refcount == 0))
+ {
+ if (ct->lastaccess < prune_threshold)
+ {
+ CatCacheRemoveCTup(cp, ct);
+ nremoved++;
+
+ /* don't let the removed entry update oldest_ts */
+ continue;
+ }
+ }
+
+ /* update the oldest timestamp if the entry remains alive */
+ if (ct->lastaccess < oldest_ts)
+ oldest_ts = ct->lastaccess;
+ }
+ }
+
+ cp->cc_oldest_ts = oldest_ts;
+
+ if (nremoved > 0)
+ elog(DEBUG1, "pruning catalog cache id=%d for %s: removed %d / %d",
+ cp->id, cp->cc_relname, nremoved, cp->cc_ntup + nremoved);
+
+ return nremoved > 0;
+}
+
/*
* ReleaseCatCache
*
@@ -1888,6 +1967,7 @@ CatalogCacheCreateEntry(CatCache *cache, HeapTuple ntp, Datum *arguments,
ct->dead = false;
ct->negative = negative;
ct->hash_value = hashValue;
+ ct->lastaccess = catcacheclock;
dlist_push_head(&cache->cc_bucket[hashIndex], &ct->cache_elem);
@@ -1899,7 +1979,12 @@ CatalogCacheCreateEntry(CatCache *cache, HeapTuple ntp, Datum *arguments,
* arbitrarily, we enlarge when fill factor > 2.
*/
if (cache->cc_ntup > cache->cc_nbuckets * 2)
- RehashCatCache(cache);
+ {
+ /* try removing old entries before expanding hash */
+ if (catcacheclock - cache->cc_oldest_ts < prune_min_age_us ||
+ !CatCacheCleanupOldEntries(cache))
+ RehashCatCache(cache);
+ }
return ct;
}
diff --git a/src/backend/utils/misc/guc.c b/src/backend/utils/misc/guc.c
index 17579eeaca..255e9fa73d 100644
--- a/src/backend/utils/misc/guc.c
+++ b/src/backend/utils/misc/guc.c
@@ -88,6 +88,7 @@
#include "utils/acl.h"
#include "utils/builtins.h"
#include "utils/bytea.h"
+#include "utils/catcache.h"
#include "utils/float.h"
#include "utils/guc_tables.h"
#include "utils/memutils.h"
@@ -3445,6 +3446,17 @@ static struct config_int ConfigureNamesInt[] =
NULL, NULL, NULL
},
+ {
+ {"catalog_cache_prune_min_age", PGC_USERSET, RESOURCES_MEM,
+ gettext_noop("System catalog cache entries that are living unused more than this seconds are considered for removal."),
+ gettext_noop("The value of -1 turns off pruning."),
+ GUC_UNIT_S
+ },
+ &catalog_cache_prune_min_age,
+ -1, -1, INT_MAX,
+ NULL, assign_catalog_cache_prune_min_age, NULL
+ },
+
/* End-of-list marker */
{
{NULL, 0, 0, NULL, NULL}, NULL, 0, 0, 0, NULL, NULL, NULL
diff --git a/src/include/utils/catcache.h b/src/include/utils/catcache.h
index ddc2762eb3..291e857e38 100644
--- a/src/include/utils/catcache.h
+++ b/src/include/utils/catcache.h
@@ -22,6 +22,7 @@
#include "access/htup.h"
#include "access/skey.h"
+#include "datatype/timestamp.h"
#include "lib/ilist.h"
#include "utils/relcache.h"
@@ -61,6 +62,7 @@ typedef struct catcache
slist_node cc_next; /* list link */
ScanKeyData cc_skey[CATCACHE_MAXKEYS]; /* precomputed key info for heap
* scans */
+ uint64 cc_oldest_ts; /* timestamp (us) of the oldest tuple */
/*
* Keep these at the end, so that compiling catcache.c with CATCACHE_STATS
@@ -119,6 +121,7 @@ typedef struct catctup
bool dead; /* dead but not yet removed? */
bool negative; /* negative cache entry? */
HeapTupleData tuple; /* tuple management header */
+ uint64 lastaccess; /* timestamp in us of the last usage */
/*
* The tuple may also be a member of at most one CatCList. (If a single
@@ -189,6 +192,22 @@ typedef struct catcacheheader
/* this extern duplicates utils/memutils.h... */
extern PGDLLIMPORT MemoryContext CacheMemoryContext;
+
+/* for guc.c, not PGDLLPMPORT'ed */
+extern int catalog_cache_prune_min_age;
+
+/* source clock for access timestamp of catcache entries */
+extern uint64 catcacheclock;
+
+/* SetCatCacheClock - set catcache timestamp source clock */
+static inline void
+SetCatCacheClock(TimestampTz ts)
+{
+ catcacheclock = (uint64) ts;
+}
+
+extern void assign_catalog_cache_prune_min_age(int newval, void *extra);
+
extern void CreateCacheMemoryContext(void);
extern CatCache *InitCatCache(int id, Oid reloid, Oid indexoid,
--
2.27.0
----Next_Part(Thu_Jan_14_17_32_27_2021_995)--
Content-Type: Text/X-Patch; charset=us-ascii
Content-Transfer-Encoding: 7bit
Content-Disposition: inline;
filename="v6-0002-Remove-dead-flag-from-catcache-tuple.patch"
^ permalink raw reply [nested|flat] 92+ messages in thread
* [PATCH v7 1/3] CatCache expiration feature
@ 2020-11-18 07:54 Kyotaro Horiguchi <[email protected]>
0 siblings, 0 replies; 92+ messages in thread
From: Kyotaro Horiguchi @ 2020-11-18 07:54 UTC (permalink / raw)
---
src/backend/access/transam/xact.c | 3 ++
src/backend/utils/cache/catcache.c | 87 +++++++++++++++++++++++++++++-
src/backend/utils/misc/guc.c | 12 +++++
src/include/utils/catcache.h | 19 +++++++
4 files changed, 120 insertions(+), 1 deletion(-)
diff --git a/src/backend/access/transam/xact.c b/src/backend/access/transam/xact.c
index a2068e3fd4..86888d2409 100644
--- a/src/backend/access/transam/xact.c
+++ b/src/backend/access/transam/xact.c
@@ -1086,6 +1086,9 @@ static void
AtStart_Cache(void)
{
AcceptInvalidationMessages();
+
+ if (xactStartTimestamp != 0)
+ SetCatCacheClock(xactStartTimestamp);
}
/*
diff --git a/src/backend/utils/cache/catcache.c b/src/backend/utils/cache/catcache.c
index fa2b49c676..644d92dd9a 100644
--- a/src/backend/utils/cache/catcache.c
+++ b/src/backend/utils/cache/catcache.c
@@ -38,6 +38,7 @@
#include "utils/rel.h"
#include "utils/resowner_private.h"
#include "utils/syscache.h"
+#include "utils/timestamp.h"
/* #define CACHEDEBUG */ /* turns DEBUG elogs on */
@@ -60,9 +61,19 @@
#define CACHE_elog(...)
#endif
+/*
+ * GUC variable to define the minimum age of entries that will be considered
+ * to be evicted in seconds. -1 to disable the feature.
+ */
+int catalog_cache_prune_min_age = -1;
+uint64 prune_min_age_us;
+
/* Cache management header --- pointer is NULL until created */
static CatCacheHeader *CacheHdr = NULL;
+/* Clock for the last accessed time of a catcache entry. */
+uint64 catcacheclock = 0;
+
static inline HeapTuple SearchCatCacheInternal(CatCache *cache,
int nkeys,
Datum v1, Datum v2,
@@ -74,6 +85,7 @@ static pg_noinline HeapTuple SearchCatCacheMiss(CatCache *cache,
Index hashIndex,
Datum v1, Datum v2,
Datum v3, Datum v4);
+static bool CatCacheCleanupOldEntries(CatCache *cp);
static uint32 CatalogCacheComputeHashValue(CatCache *cache, int nkeys,
Datum v1, Datum v2, Datum v3, Datum v4);
@@ -99,6 +111,15 @@ static void CatCacheFreeKeys(TupleDesc tupdesc, int nkeys, int *attnos,
static void CatCacheCopyKeys(TupleDesc tupdesc, int nkeys, int *attnos,
Datum *srckeys, Datum *dstkeys);
+/* GUC assign function */
+void
+assign_catalog_cache_prune_min_age(int newval, void *extra)
+{
+ if (newval < 0)
+ prune_min_age_us = UINT64_MAX;
+ else
+ prune_min_age_us = ((uint64) newval) * USECS_PER_SEC;
+}
/*
* internal support functions
@@ -1264,6 +1285,9 @@ SearchCatCacheInternal(CatCache *cache,
*/
dlist_move_head(bucket, &ct->cache_elem);
+ /* Record the last access timestamp */
+ ct->lastaccess = catcacheclock;
+
/*
* If it's a positive entry, bump its refcount and return it. If it's
* negative, we can report failure to the caller.
@@ -1425,6 +1449,61 @@ SearchCatCacheMiss(CatCache *cache,
return &ct->tuple;
}
+/*
+ * CatCacheCleanupOldEntries - Remove infrequently-used entries
+ *
+ * Catcache entries happen to be left unused for a long time for several
+ * reasons. Remove such entries to prevent catcache from bloating. It is based
+ * on the similar algorithm with buffer eviction. Entries that are accessed
+ * several times in a certain period live longer than those that have had less
+ * access in the same duration.
+ */
+static bool
+CatCacheCleanupOldEntries(CatCache *cp)
+{
+ int nremoved = 0;
+ int i;
+ long oldest_ts = catcacheclock;
+ uint64 prune_threshold = catcacheclock - prune_min_age_us;
+
+ /* Scan over the whole hash to find entries to remove */
+ for (i = 0 ; i < cp->cc_nbuckets ; i++)
+ {
+ dlist_mutable_iter iter;
+
+ dlist_foreach_modify(iter, &cp->cc_bucket[i])
+ {
+ CatCTup *ct = dlist_container(CatCTup, cache_elem, iter.cur);
+
+ /* Don't remove referenced entries */
+ if (ct->refcount == 0 &&
+ (ct->c_list == NULL || ct->c_list->refcount == 0))
+ {
+ if (ct->lastaccess < prune_threshold)
+ {
+ CatCacheRemoveCTup(cp, ct);
+ nremoved++;
+
+ /* don't let the removed entry update oldest_ts */
+ continue;
+ }
+ }
+
+ /* update the oldest timestamp if the entry remains alive */
+ if (ct->lastaccess < oldest_ts)
+ oldest_ts = ct->lastaccess;
+ }
+ }
+
+ cp->cc_oldest_ts = oldest_ts;
+
+ if (nremoved > 0)
+ elog(DEBUG1, "pruning catalog cache id=%d for %s: removed %d / %d",
+ cp->id, cp->cc_relname, nremoved, cp->cc_ntup + nremoved);
+
+ return nremoved > 0;
+}
+
/*
* ReleaseCatCache
*
@@ -1888,6 +1967,7 @@ CatalogCacheCreateEntry(CatCache *cache, HeapTuple ntp, Datum *arguments,
ct->dead = false;
ct->negative = negative;
ct->hash_value = hashValue;
+ ct->lastaccess = catcacheclock;
dlist_push_head(&cache->cc_bucket[hashIndex], &ct->cache_elem);
@@ -1899,7 +1979,12 @@ CatalogCacheCreateEntry(CatCache *cache, HeapTuple ntp, Datum *arguments,
* arbitrarily, we enlarge when fill factor > 2.
*/
if (cache->cc_ntup > cache->cc_nbuckets * 2)
- RehashCatCache(cache);
+ {
+ /* try removing old entries before expanding hash */
+ if (catcacheclock - cache->cc_oldest_ts < prune_min_age_us ||
+ !CatCacheCleanupOldEntries(cache))
+ RehashCatCache(cache);
+ }
return ct;
}
diff --git a/src/backend/utils/misc/guc.c b/src/backend/utils/misc/guc.c
index 17579eeaca..255e9fa73d 100644
--- a/src/backend/utils/misc/guc.c
+++ b/src/backend/utils/misc/guc.c
@@ -88,6 +88,7 @@
#include "utils/acl.h"
#include "utils/builtins.h"
#include "utils/bytea.h"
+#include "utils/catcache.h"
#include "utils/float.h"
#include "utils/guc_tables.h"
#include "utils/memutils.h"
@@ -3445,6 +3446,17 @@ static struct config_int ConfigureNamesInt[] =
NULL, NULL, NULL
},
+ {
+ {"catalog_cache_prune_min_age", PGC_USERSET, RESOURCES_MEM,
+ gettext_noop("System catalog cache entries that are living unused more than this seconds are considered for removal."),
+ gettext_noop("The value of -1 turns off pruning."),
+ GUC_UNIT_S
+ },
+ &catalog_cache_prune_min_age,
+ -1, -1, INT_MAX,
+ NULL, assign_catalog_cache_prune_min_age, NULL
+ },
+
/* End-of-list marker */
{
{NULL, 0, 0, NULL, NULL}, NULL, 0, 0, 0, NULL, NULL, NULL
diff --git a/src/include/utils/catcache.h b/src/include/utils/catcache.h
index ddc2762eb3..291e857e38 100644
--- a/src/include/utils/catcache.h
+++ b/src/include/utils/catcache.h
@@ -22,6 +22,7 @@
#include "access/htup.h"
#include "access/skey.h"
+#include "datatype/timestamp.h"
#include "lib/ilist.h"
#include "utils/relcache.h"
@@ -61,6 +62,7 @@ typedef struct catcache
slist_node cc_next; /* list link */
ScanKeyData cc_skey[CATCACHE_MAXKEYS]; /* precomputed key info for heap
* scans */
+ uint64 cc_oldest_ts; /* timestamp (us) of the oldest tuple */
/*
* Keep these at the end, so that compiling catcache.c with CATCACHE_STATS
@@ -119,6 +121,7 @@ typedef struct catctup
bool dead; /* dead but not yet removed? */
bool negative; /* negative cache entry? */
HeapTupleData tuple; /* tuple management header */
+ uint64 lastaccess; /* timestamp in us of the last usage */
/*
* The tuple may also be a member of at most one CatCList. (If a single
@@ -189,6 +192,22 @@ typedef struct catcacheheader
/* this extern duplicates utils/memutils.h... */
extern PGDLLIMPORT MemoryContext CacheMemoryContext;
+
+/* for guc.c, not PGDLLPMPORT'ed */
+extern int catalog_cache_prune_min_age;
+
+/* source clock for access timestamp of catcache entries */
+extern uint64 catcacheclock;
+
+/* SetCatCacheClock - set catcache timestamp source clock */
+static inline void
+SetCatCacheClock(TimestampTz ts)
+{
+ catcacheclock = (uint64) ts;
+}
+
+extern void assign_catalog_cache_prune_min_age(int newval, void *extra);
+
extern void CreateCacheMemoryContext(void);
extern CatCache *InitCatCache(int id, Oid reloid, Oid indexoid,
--
2.27.0
----Next_Part(Wed_Jan_27_10_13_08_2021_482)--
Content-Type: Text/X-Patch; charset=us-ascii
Content-Transfer-Encoding: 7bit
Content-Disposition: inline;
filename="v7-0002-Remove-dead-flag-from-catcache-tuple.patch"
^ permalink raw reply [nested|flat] 92+ messages in thread
* [PATCH v6 1/3] CatCache expiration feature
@ 2020-11-18 07:54 Kyotaro Horiguchi <[email protected]>
0 siblings, 0 replies; 92+ messages in thread
From: Kyotaro Horiguchi @ 2020-11-18 07:54 UTC (permalink / raw)
---
src/backend/access/transam/xact.c | 3 ++
src/backend/utils/cache/catcache.c | 87 +++++++++++++++++++++++++++++-
src/backend/utils/misc/guc.c | 12 +++++
src/include/utils/catcache.h | 19 +++++++
4 files changed, 120 insertions(+), 1 deletion(-)
diff --git a/src/backend/access/transam/xact.c b/src/backend/access/transam/xact.c
index a2068e3fd4..86888d2409 100644
--- a/src/backend/access/transam/xact.c
+++ b/src/backend/access/transam/xact.c
@@ -1086,6 +1086,9 @@ static void
AtStart_Cache(void)
{
AcceptInvalidationMessages();
+
+ if (xactStartTimestamp != 0)
+ SetCatCacheClock(xactStartTimestamp);
}
/*
diff --git a/src/backend/utils/cache/catcache.c b/src/backend/utils/cache/catcache.c
index fa2b49c676..644d92dd9a 100644
--- a/src/backend/utils/cache/catcache.c
+++ b/src/backend/utils/cache/catcache.c
@@ -38,6 +38,7 @@
#include "utils/rel.h"
#include "utils/resowner_private.h"
#include "utils/syscache.h"
+#include "utils/timestamp.h"
/* #define CACHEDEBUG */ /* turns DEBUG elogs on */
@@ -60,9 +61,19 @@
#define CACHE_elog(...)
#endif
+/*
+ * GUC variable to define the minimum age of entries that will be considered
+ * to be evicted in seconds. -1 to disable the feature.
+ */
+int catalog_cache_prune_min_age = -1;
+uint64 prune_min_age_us;
+
/* Cache management header --- pointer is NULL until created */
static CatCacheHeader *CacheHdr = NULL;
+/* Clock for the last accessed time of a catcache entry. */
+uint64 catcacheclock = 0;
+
static inline HeapTuple SearchCatCacheInternal(CatCache *cache,
int nkeys,
Datum v1, Datum v2,
@@ -74,6 +85,7 @@ static pg_noinline HeapTuple SearchCatCacheMiss(CatCache *cache,
Index hashIndex,
Datum v1, Datum v2,
Datum v3, Datum v4);
+static bool CatCacheCleanupOldEntries(CatCache *cp);
static uint32 CatalogCacheComputeHashValue(CatCache *cache, int nkeys,
Datum v1, Datum v2, Datum v3, Datum v4);
@@ -99,6 +111,15 @@ static void CatCacheFreeKeys(TupleDesc tupdesc, int nkeys, int *attnos,
static void CatCacheCopyKeys(TupleDesc tupdesc, int nkeys, int *attnos,
Datum *srckeys, Datum *dstkeys);
+/* GUC assign function */
+void
+assign_catalog_cache_prune_min_age(int newval, void *extra)
+{
+ if (newval < 0)
+ prune_min_age_us = UINT64_MAX;
+ else
+ prune_min_age_us = ((uint64) newval) * USECS_PER_SEC;
+}
/*
* internal support functions
@@ -1264,6 +1285,9 @@ SearchCatCacheInternal(CatCache *cache,
*/
dlist_move_head(bucket, &ct->cache_elem);
+ /* Record the last access timestamp */
+ ct->lastaccess = catcacheclock;
+
/*
* If it's a positive entry, bump its refcount and return it. If it's
* negative, we can report failure to the caller.
@@ -1425,6 +1449,61 @@ SearchCatCacheMiss(CatCache *cache,
return &ct->tuple;
}
+/*
+ * CatCacheCleanupOldEntries - Remove infrequently-used entries
+ *
+ * Catcache entries happen to be left unused for a long time for several
+ * reasons. Remove such entries to prevent catcache from bloating. It is based
+ * on the similar algorithm with buffer eviction. Entries that are accessed
+ * several times in a certain period live longer than those that have had less
+ * access in the same duration.
+ */
+static bool
+CatCacheCleanupOldEntries(CatCache *cp)
+{
+ int nremoved = 0;
+ int i;
+ long oldest_ts = catcacheclock;
+ uint64 prune_threshold = catcacheclock - prune_min_age_us;
+
+ /* Scan over the whole hash to find entries to remove */
+ for (i = 0 ; i < cp->cc_nbuckets ; i++)
+ {
+ dlist_mutable_iter iter;
+
+ dlist_foreach_modify(iter, &cp->cc_bucket[i])
+ {
+ CatCTup *ct = dlist_container(CatCTup, cache_elem, iter.cur);
+
+ /* Don't remove referenced entries */
+ if (ct->refcount == 0 &&
+ (ct->c_list == NULL || ct->c_list->refcount == 0))
+ {
+ if (ct->lastaccess < prune_threshold)
+ {
+ CatCacheRemoveCTup(cp, ct);
+ nremoved++;
+
+ /* don't let the removed entry update oldest_ts */
+ continue;
+ }
+ }
+
+ /* update the oldest timestamp if the entry remains alive */
+ if (ct->lastaccess < oldest_ts)
+ oldest_ts = ct->lastaccess;
+ }
+ }
+
+ cp->cc_oldest_ts = oldest_ts;
+
+ if (nremoved > 0)
+ elog(DEBUG1, "pruning catalog cache id=%d for %s: removed %d / %d",
+ cp->id, cp->cc_relname, nremoved, cp->cc_ntup + nremoved);
+
+ return nremoved > 0;
+}
+
/*
* ReleaseCatCache
*
@@ -1888,6 +1967,7 @@ CatalogCacheCreateEntry(CatCache *cache, HeapTuple ntp, Datum *arguments,
ct->dead = false;
ct->negative = negative;
ct->hash_value = hashValue;
+ ct->lastaccess = catcacheclock;
dlist_push_head(&cache->cc_bucket[hashIndex], &ct->cache_elem);
@@ -1899,7 +1979,12 @@ CatalogCacheCreateEntry(CatCache *cache, HeapTuple ntp, Datum *arguments,
* arbitrarily, we enlarge when fill factor > 2.
*/
if (cache->cc_ntup > cache->cc_nbuckets * 2)
- RehashCatCache(cache);
+ {
+ /* try removing old entries before expanding hash */
+ if (catcacheclock - cache->cc_oldest_ts < prune_min_age_us ||
+ !CatCacheCleanupOldEntries(cache))
+ RehashCatCache(cache);
+ }
return ct;
}
diff --git a/src/backend/utils/misc/guc.c b/src/backend/utils/misc/guc.c
index 17579eeaca..255e9fa73d 100644
--- a/src/backend/utils/misc/guc.c
+++ b/src/backend/utils/misc/guc.c
@@ -88,6 +88,7 @@
#include "utils/acl.h"
#include "utils/builtins.h"
#include "utils/bytea.h"
+#include "utils/catcache.h"
#include "utils/float.h"
#include "utils/guc_tables.h"
#include "utils/memutils.h"
@@ -3445,6 +3446,17 @@ static struct config_int ConfigureNamesInt[] =
NULL, NULL, NULL
},
+ {
+ {"catalog_cache_prune_min_age", PGC_USERSET, RESOURCES_MEM,
+ gettext_noop("System catalog cache entries that are living unused more than this seconds are considered for removal."),
+ gettext_noop("The value of -1 turns off pruning."),
+ GUC_UNIT_S
+ },
+ &catalog_cache_prune_min_age,
+ -1, -1, INT_MAX,
+ NULL, assign_catalog_cache_prune_min_age, NULL
+ },
+
/* End-of-list marker */
{
{NULL, 0, 0, NULL, NULL}, NULL, 0, 0, 0, NULL, NULL, NULL
diff --git a/src/include/utils/catcache.h b/src/include/utils/catcache.h
index ddc2762eb3..291e857e38 100644
--- a/src/include/utils/catcache.h
+++ b/src/include/utils/catcache.h
@@ -22,6 +22,7 @@
#include "access/htup.h"
#include "access/skey.h"
+#include "datatype/timestamp.h"
#include "lib/ilist.h"
#include "utils/relcache.h"
@@ -61,6 +62,7 @@ typedef struct catcache
slist_node cc_next; /* list link */
ScanKeyData cc_skey[CATCACHE_MAXKEYS]; /* precomputed key info for heap
* scans */
+ uint64 cc_oldest_ts; /* timestamp (us) of the oldest tuple */
/*
* Keep these at the end, so that compiling catcache.c with CATCACHE_STATS
@@ -119,6 +121,7 @@ typedef struct catctup
bool dead; /* dead but not yet removed? */
bool negative; /* negative cache entry? */
HeapTupleData tuple; /* tuple management header */
+ uint64 lastaccess; /* timestamp in us of the last usage */
/*
* The tuple may also be a member of at most one CatCList. (If a single
@@ -189,6 +192,22 @@ typedef struct catcacheheader
/* this extern duplicates utils/memutils.h... */
extern PGDLLIMPORT MemoryContext CacheMemoryContext;
+
+/* for guc.c, not PGDLLPMPORT'ed */
+extern int catalog_cache_prune_min_age;
+
+/* source clock for access timestamp of catcache entries */
+extern uint64 catcacheclock;
+
+/* SetCatCacheClock - set catcache timestamp source clock */
+static inline void
+SetCatCacheClock(TimestampTz ts)
+{
+ catcacheclock = (uint64) ts;
+}
+
+extern void assign_catalog_cache_prune_min_age(int newval, void *extra);
+
extern void CreateCacheMemoryContext(void);
extern CatCache *InitCatCache(int id, Oid reloid, Oid indexoid,
--
2.27.0
----Next_Part(Thu_Jan_14_17_32_27_2021_995)--
Content-Type: Text/X-Patch; charset=us-ascii
Content-Transfer-Encoding: 7bit
Content-Disposition: inline;
filename="v6-0002-Remove-dead-flag-from-catcache-tuple.patch"
^ permalink raw reply [nested|flat] 92+ messages in thread
* [PATCH v7 1/3] CatCache expiration feature
@ 2020-11-18 07:54 Kyotaro Horiguchi <[email protected]>
0 siblings, 0 replies; 92+ messages in thread
From: Kyotaro Horiguchi @ 2020-11-18 07:54 UTC (permalink / raw)
---
src/backend/access/transam/xact.c | 3 ++
src/backend/utils/cache/catcache.c | 87 +++++++++++++++++++++++++++++-
src/backend/utils/misc/guc.c | 12 +++++
src/include/utils/catcache.h | 19 +++++++
4 files changed, 120 insertions(+), 1 deletion(-)
diff --git a/src/backend/access/transam/xact.c b/src/backend/access/transam/xact.c
index a2068e3fd4..86888d2409 100644
--- a/src/backend/access/transam/xact.c
+++ b/src/backend/access/transam/xact.c
@@ -1086,6 +1086,9 @@ static void
AtStart_Cache(void)
{
AcceptInvalidationMessages();
+
+ if (xactStartTimestamp != 0)
+ SetCatCacheClock(xactStartTimestamp);
}
/*
diff --git a/src/backend/utils/cache/catcache.c b/src/backend/utils/cache/catcache.c
index fa2b49c676..644d92dd9a 100644
--- a/src/backend/utils/cache/catcache.c
+++ b/src/backend/utils/cache/catcache.c
@@ -38,6 +38,7 @@
#include "utils/rel.h"
#include "utils/resowner_private.h"
#include "utils/syscache.h"
+#include "utils/timestamp.h"
/* #define CACHEDEBUG */ /* turns DEBUG elogs on */
@@ -60,9 +61,19 @@
#define CACHE_elog(...)
#endif
+/*
+ * GUC variable to define the minimum age of entries that will be considered
+ * to be evicted in seconds. -1 to disable the feature.
+ */
+int catalog_cache_prune_min_age = -1;
+uint64 prune_min_age_us;
+
/* Cache management header --- pointer is NULL until created */
static CatCacheHeader *CacheHdr = NULL;
+/* Clock for the last accessed time of a catcache entry. */
+uint64 catcacheclock = 0;
+
static inline HeapTuple SearchCatCacheInternal(CatCache *cache,
int nkeys,
Datum v1, Datum v2,
@@ -74,6 +85,7 @@ static pg_noinline HeapTuple SearchCatCacheMiss(CatCache *cache,
Index hashIndex,
Datum v1, Datum v2,
Datum v3, Datum v4);
+static bool CatCacheCleanupOldEntries(CatCache *cp);
static uint32 CatalogCacheComputeHashValue(CatCache *cache, int nkeys,
Datum v1, Datum v2, Datum v3, Datum v4);
@@ -99,6 +111,15 @@ static void CatCacheFreeKeys(TupleDesc tupdesc, int nkeys, int *attnos,
static void CatCacheCopyKeys(TupleDesc tupdesc, int nkeys, int *attnos,
Datum *srckeys, Datum *dstkeys);
+/* GUC assign function */
+void
+assign_catalog_cache_prune_min_age(int newval, void *extra)
+{
+ if (newval < 0)
+ prune_min_age_us = UINT64_MAX;
+ else
+ prune_min_age_us = ((uint64) newval) * USECS_PER_SEC;
+}
/*
* internal support functions
@@ -1264,6 +1285,9 @@ SearchCatCacheInternal(CatCache *cache,
*/
dlist_move_head(bucket, &ct->cache_elem);
+ /* Record the last access timestamp */
+ ct->lastaccess = catcacheclock;
+
/*
* If it's a positive entry, bump its refcount and return it. If it's
* negative, we can report failure to the caller.
@@ -1425,6 +1449,61 @@ SearchCatCacheMiss(CatCache *cache,
return &ct->tuple;
}
+/*
+ * CatCacheCleanupOldEntries - Remove infrequently-used entries
+ *
+ * Catcache entries happen to be left unused for a long time for several
+ * reasons. Remove such entries to prevent catcache from bloating. It is based
+ * on the similar algorithm with buffer eviction. Entries that are accessed
+ * several times in a certain period live longer than those that have had less
+ * access in the same duration.
+ */
+static bool
+CatCacheCleanupOldEntries(CatCache *cp)
+{
+ int nremoved = 0;
+ int i;
+ long oldest_ts = catcacheclock;
+ uint64 prune_threshold = catcacheclock - prune_min_age_us;
+
+ /* Scan over the whole hash to find entries to remove */
+ for (i = 0 ; i < cp->cc_nbuckets ; i++)
+ {
+ dlist_mutable_iter iter;
+
+ dlist_foreach_modify(iter, &cp->cc_bucket[i])
+ {
+ CatCTup *ct = dlist_container(CatCTup, cache_elem, iter.cur);
+
+ /* Don't remove referenced entries */
+ if (ct->refcount == 0 &&
+ (ct->c_list == NULL || ct->c_list->refcount == 0))
+ {
+ if (ct->lastaccess < prune_threshold)
+ {
+ CatCacheRemoveCTup(cp, ct);
+ nremoved++;
+
+ /* don't let the removed entry update oldest_ts */
+ continue;
+ }
+ }
+
+ /* update the oldest timestamp if the entry remains alive */
+ if (ct->lastaccess < oldest_ts)
+ oldest_ts = ct->lastaccess;
+ }
+ }
+
+ cp->cc_oldest_ts = oldest_ts;
+
+ if (nremoved > 0)
+ elog(DEBUG1, "pruning catalog cache id=%d for %s: removed %d / %d",
+ cp->id, cp->cc_relname, nremoved, cp->cc_ntup + nremoved);
+
+ return nremoved > 0;
+}
+
/*
* ReleaseCatCache
*
@@ -1888,6 +1967,7 @@ CatalogCacheCreateEntry(CatCache *cache, HeapTuple ntp, Datum *arguments,
ct->dead = false;
ct->negative = negative;
ct->hash_value = hashValue;
+ ct->lastaccess = catcacheclock;
dlist_push_head(&cache->cc_bucket[hashIndex], &ct->cache_elem);
@@ -1899,7 +1979,12 @@ CatalogCacheCreateEntry(CatCache *cache, HeapTuple ntp, Datum *arguments,
* arbitrarily, we enlarge when fill factor > 2.
*/
if (cache->cc_ntup > cache->cc_nbuckets * 2)
- RehashCatCache(cache);
+ {
+ /* try removing old entries before expanding hash */
+ if (catcacheclock - cache->cc_oldest_ts < prune_min_age_us ||
+ !CatCacheCleanupOldEntries(cache))
+ RehashCatCache(cache);
+ }
return ct;
}
diff --git a/src/backend/utils/misc/guc.c b/src/backend/utils/misc/guc.c
index 17579eeaca..255e9fa73d 100644
--- a/src/backend/utils/misc/guc.c
+++ b/src/backend/utils/misc/guc.c
@@ -88,6 +88,7 @@
#include "utils/acl.h"
#include "utils/builtins.h"
#include "utils/bytea.h"
+#include "utils/catcache.h"
#include "utils/float.h"
#include "utils/guc_tables.h"
#include "utils/memutils.h"
@@ -3445,6 +3446,17 @@ static struct config_int ConfigureNamesInt[] =
NULL, NULL, NULL
},
+ {
+ {"catalog_cache_prune_min_age", PGC_USERSET, RESOURCES_MEM,
+ gettext_noop("System catalog cache entries that are living unused more than this seconds are considered for removal."),
+ gettext_noop("The value of -1 turns off pruning."),
+ GUC_UNIT_S
+ },
+ &catalog_cache_prune_min_age,
+ -1, -1, INT_MAX,
+ NULL, assign_catalog_cache_prune_min_age, NULL
+ },
+
/* End-of-list marker */
{
{NULL, 0, 0, NULL, NULL}, NULL, 0, 0, 0, NULL, NULL, NULL
diff --git a/src/include/utils/catcache.h b/src/include/utils/catcache.h
index ddc2762eb3..291e857e38 100644
--- a/src/include/utils/catcache.h
+++ b/src/include/utils/catcache.h
@@ -22,6 +22,7 @@
#include "access/htup.h"
#include "access/skey.h"
+#include "datatype/timestamp.h"
#include "lib/ilist.h"
#include "utils/relcache.h"
@@ -61,6 +62,7 @@ typedef struct catcache
slist_node cc_next; /* list link */
ScanKeyData cc_skey[CATCACHE_MAXKEYS]; /* precomputed key info for heap
* scans */
+ uint64 cc_oldest_ts; /* timestamp (us) of the oldest tuple */
/*
* Keep these at the end, so that compiling catcache.c with CATCACHE_STATS
@@ -119,6 +121,7 @@ typedef struct catctup
bool dead; /* dead but not yet removed? */
bool negative; /* negative cache entry? */
HeapTupleData tuple; /* tuple management header */
+ uint64 lastaccess; /* timestamp in us of the last usage */
/*
* The tuple may also be a member of at most one CatCList. (If a single
@@ -189,6 +192,22 @@ typedef struct catcacheheader
/* this extern duplicates utils/memutils.h... */
extern PGDLLIMPORT MemoryContext CacheMemoryContext;
+
+/* for guc.c, not PGDLLPMPORT'ed */
+extern int catalog_cache_prune_min_age;
+
+/* source clock for access timestamp of catcache entries */
+extern uint64 catcacheclock;
+
+/* SetCatCacheClock - set catcache timestamp source clock */
+static inline void
+SetCatCacheClock(TimestampTz ts)
+{
+ catcacheclock = (uint64) ts;
+}
+
+extern void assign_catalog_cache_prune_min_age(int newval, void *extra);
+
extern void CreateCacheMemoryContext(void);
extern CatCache *InitCatCache(int id, Oid reloid, Oid indexoid,
--
2.27.0
----Next_Part(Wed_Jan_27_10_13_08_2021_482)--
Content-Type: Text/X-Patch; charset=us-ascii
Content-Transfer-Encoding: 7bit
Content-Disposition: inline;
filename="v7-0002-Remove-dead-flag-from-catcache-tuple.patch"
^ permalink raw reply [nested|flat] 92+ messages in thread
* [PATCH v7 1/3] CatCache expiration feature
@ 2020-11-18 07:54 Kyotaro Horiguchi <[email protected]>
0 siblings, 0 replies; 92+ messages in thread
From: Kyotaro Horiguchi @ 2020-11-18 07:54 UTC (permalink / raw)
---
src/backend/access/transam/xact.c | 3 ++
src/backend/utils/cache/catcache.c | 87 +++++++++++++++++++++++++++++-
src/backend/utils/misc/guc.c | 12 +++++
src/include/utils/catcache.h | 19 +++++++
4 files changed, 120 insertions(+), 1 deletion(-)
diff --git a/src/backend/access/transam/xact.c b/src/backend/access/transam/xact.c
index a2068e3fd4..86888d2409 100644
--- a/src/backend/access/transam/xact.c
+++ b/src/backend/access/transam/xact.c
@@ -1086,6 +1086,9 @@ static void
AtStart_Cache(void)
{
AcceptInvalidationMessages();
+
+ if (xactStartTimestamp != 0)
+ SetCatCacheClock(xactStartTimestamp);
}
/*
diff --git a/src/backend/utils/cache/catcache.c b/src/backend/utils/cache/catcache.c
index fa2b49c676..644d92dd9a 100644
--- a/src/backend/utils/cache/catcache.c
+++ b/src/backend/utils/cache/catcache.c
@@ -38,6 +38,7 @@
#include "utils/rel.h"
#include "utils/resowner_private.h"
#include "utils/syscache.h"
+#include "utils/timestamp.h"
/* #define CACHEDEBUG */ /* turns DEBUG elogs on */
@@ -60,9 +61,19 @@
#define CACHE_elog(...)
#endif
+/*
+ * GUC variable to define the minimum age of entries that will be considered
+ * to be evicted in seconds. -1 to disable the feature.
+ */
+int catalog_cache_prune_min_age = -1;
+uint64 prune_min_age_us;
+
/* Cache management header --- pointer is NULL until created */
static CatCacheHeader *CacheHdr = NULL;
+/* Clock for the last accessed time of a catcache entry. */
+uint64 catcacheclock = 0;
+
static inline HeapTuple SearchCatCacheInternal(CatCache *cache,
int nkeys,
Datum v1, Datum v2,
@@ -74,6 +85,7 @@ static pg_noinline HeapTuple SearchCatCacheMiss(CatCache *cache,
Index hashIndex,
Datum v1, Datum v2,
Datum v3, Datum v4);
+static bool CatCacheCleanupOldEntries(CatCache *cp);
static uint32 CatalogCacheComputeHashValue(CatCache *cache, int nkeys,
Datum v1, Datum v2, Datum v3, Datum v4);
@@ -99,6 +111,15 @@ static void CatCacheFreeKeys(TupleDesc tupdesc, int nkeys, int *attnos,
static void CatCacheCopyKeys(TupleDesc tupdesc, int nkeys, int *attnos,
Datum *srckeys, Datum *dstkeys);
+/* GUC assign function */
+void
+assign_catalog_cache_prune_min_age(int newval, void *extra)
+{
+ if (newval < 0)
+ prune_min_age_us = UINT64_MAX;
+ else
+ prune_min_age_us = ((uint64) newval) * USECS_PER_SEC;
+}
/*
* internal support functions
@@ -1264,6 +1285,9 @@ SearchCatCacheInternal(CatCache *cache,
*/
dlist_move_head(bucket, &ct->cache_elem);
+ /* Record the last access timestamp */
+ ct->lastaccess = catcacheclock;
+
/*
* If it's a positive entry, bump its refcount and return it. If it's
* negative, we can report failure to the caller.
@@ -1425,6 +1449,61 @@ SearchCatCacheMiss(CatCache *cache,
return &ct->tuple;
}
+/*
+ * CatCacheCleanupOldEntries - Remove infrequently-used entries
+ *
+ * Catcache entries happen to be left unused for a long time for several
+ * reasons. Remove such entries to prevent catcache from bloating. It is based
+ * on the similar algorithm with buffer eviction. Entries that are accessed
+ * several times in a certain period live longer than those that have had less
+ * access in the same duration.
+ */
+static bool
+CatCacheCleanupOldEntries(CatCache *cp)
+{
+ int nremoved = 0;
+ int i;
+ long oldest_ts = catcacheclock;
+ uint64 prune_threshold = catcacheclock - prune_min_age_us;
+
+ /* Scan over the whole hash to find entries to remove */
+ for (i = 0 ; i < cp->cc_nbuckets ; i++)
+ {
+ dlist_mutable_iter iter;
+
+ dlist_foreach_modify(iter, &cp->cc_bucket[i])
+ {
+ CatCTup *ct = dlist_container(CatCTup, cache_elem, iter.cur);
+
+ /* Don't remove referenced entries */
+ if (ct->refcount == 0 &&
+ (ct->c_list == NULL || ct->c_list->refcount == 0))
+ {
+ if (ct->lastaccess < prune_threshold)
+ {
+ CatCacheRemoveCTup(cp, ct);
+ nremoved++;
+
+ /* don't let the removed entry update oldest_ts */
+ continue;
+ }
+ }
+
+ /* update the oldest timestamp if the entry remains alive */
+ if (ct->lastaccess < oldest_ts)
+ oldest_ts = ct->lastaccess;
+ }
+ }
+
+ cp->cc_oldest_ts = oldest_ts;
+
+ if (nremoved > 0)
+ elog(DEBUG1, "pruning catalog cache id=%d for %s: removed %d / %d",
+ cp->id, cp->cc_relname, nremoved, cp->cc_ntup + nremoved);
+
+ return nremoved > 0;
+}
+
/*
* ReleaseCatCache
*
@@ -1888,6 +1967,7 @@ CatalogCacheCreateEntry(CatCache *cache, HeapTuple ntp, Datum *arguments,
ct->dead = false;
ct->negative = negative;
ct->hash_value = hashValue;
+ ct->lastaccess = catcacheclock;
dlist_push_head(&cache->cc_bucket[hashIndex], &ct->cache_elem);
@@ -1899,7 +1979,12 @@ CatalogCacheCreateEntry(CatCache *cache, HeapTuple ntp, Datum *arguments,
* arbitrarily, we enlarge when fill factor > 2.
*/
if (cache->cc_ntup > cache->cc_nbuckets * 2)
- RehashCatCache(cache);
+ {
+ /* try removing old entries before expanding hash */
+ if (catcacheclock - cache->cc_oldest_ts < prune_min_age_us ||
+ !CatCacheCleanupOldEntries(cache))
+ RehashCatCache(cache);
+ }
return ct;
}
diff --git a/src/backend/utils/misc/guc.c b/src/backend/utils/misc/guc.c
index 17579eeaca..255e9fa73d 100644
--- a/src/backend/utils/misc/guc.c
+++ b/src/backend/utils/misc/guc.c
@@ -88,6 +88,7 @@
#include "utils/acl.h"
#include "utils/builtins.h"
#include "utils/bytea.h"
+#include "utils/catcache.h"
#include "utils/float.h"
#include "utils/guc_tables.h"
#include "utils/memutils.h"
@@ -3445,6 +3446,17 @@ static struct config_int ConfigureNamesInt[] =
NULL, NULL, NULL
},
+ {
+ {"catalog_cache_prune_min_age", PGC_USERSET, RESOURCES_MEM,
+ gettext_noop("System catalog cache entries that are living unused more than this seconds are considered for removal."),
+ gettext_noop("The value of -1 turns off pruning."),
+ GUC_UNIT_S
+ },
+ &catalog_cache_prune_min_age,
+ -1, -1, INT_MAX,
+ NULL, assign_catalog_cache_prune_min_age, NULL
+ },
+
/* End-of-list marker */
{
{NULL, 0, 0, NULL, NULL}, NULL, 0, 0, 0, NULL, NULL, NULL
diff --git a/src/include/utils/catcache.h b/src/include/utils/catcache.h
index ddc2762eb3..291e857e38 100644
--- a/src/include/utils/catcache.h
+++ b/src/include/utils/catcache.h
@@ -22,6 +22,7 @@
#include "access/htup.h"
#include "access/skey.h"
+#include "datatype/timestamp.h"
#include "lib/ilist.h"
#include "utils/relcache.h"
@@ -61,6 +62,7 @@ typedef struct catcache
slist_node cc_next; /* list link */
ScanKeyData cc_skey[CATCACHE_MAXKEYS]; /* precomputed key info for heap
* scans */
+ uint64 cc_oldest_ts; /* timestamp (us) of the oldest tuple */
/*
* Keep these at the end, so that compiling catcache.c with CATCACHE_STATS
@@ -119,6 +121,7 @@ typedef struct catctup
bool dead; /* dead but not yet removed? */
bool negative; /* negative cache entry? */
HeapTupleData tuple; /* tuple management header */
+ uint64 lastaccess; /* timestamp in us of the last usage */
/*
* The tuple may also be a member of at most one CatCList. (If a single
@@ -189,6 +192,22 @@ typedef struct catcacheheader
/* this extern duplicates utils/memutils.h... */
extern PGDLLIMPORT MemoryContext CacheMemoryContext;
+
+/* for guc.c, not PGDLLPMPORT'ed */
+extern int catalog_cache_prune_min_age;
+
+/* source clock for access timestamp of catcache entries */
+extern uint64 catcacheclock;
+
+/* SetCatCacheClock - set catcache timestamp source clock */
+static inline void
+SetCatCacheClock(TimestampTz ts)
+{
+ catcacheclock = (uint64) ts;
+}
+
+extern void assign_catalog_cache_prune_min_age(int newval, void *extra);
+
extern void CreateCacheMemoryContext(void);
extern CatCache *InitCatCache(int id, Oid reloid, Oid indexoid,
--
2.27.0
----Next_Part(Wed_Jan_27_10_13_08_2021_482)--
Content-Type: Text/X-Patch; charset=us-ascii
Content-Transfer-Encoding: 7bit
Content-Disposition: inline;
filename="v7-0002-Remove-dead-flag-from-catcache-tuple.patch"
^ permalink raw reply [nested|flat] 92+ messages in thread
* [PATCH v6 1/3] CatCache expiration feature
@ 2020-11-18 07:54 Kyotaro Horiguchi <[email protected]>
0 siblings, 0 replies; 92+ messages in thread
From: Kyotaro Horiguchi @ 2020-11-18 07:54 UTC (permalink / raw)
---
src/backend/access/transam/xact.c | 3 ++
src/backend/utils/cache/catcache.c | 87 +++++++++++++++++++++++++++++-
src/backend/utils/misc/guc.c | 12 +++++
src/include/utils/catcache.h | 19 +++++++
4 files changed, 120 insertions(+), 1 deletion(-)
diff --git a/src/backend/access/transam/xact.c b/src/backend/access/transam/xact.c
index a2068e3fd4..86888d2409 100644
--- a/src/backend/access/transam/xact.c
+++ b/src/backend/access/transam/xact.c
@@ -1086,6 +1086,9 @@ static void
AtStart_Cache(void)
{
AcceptInvalidationMessages();
+
+ if (xactStartTimestamp != 0)
+ SetCatCacheClock(xactStartTimestamp);
}
/*
diff --git a/src/backend/utils/cache/catcache.c b/src/backend/utils/cache/catcache.c
index fa2b49c676..644d92dd9a 100644
--- a/src/backend/utils/cache/catcache.c
+++ b/src/backend/utils/cache/catcache.c
@@ -38,6 +38,7 @@
#include "utils/rel.h"
#include "utils/resowner_private.h"
#include "utils/syscache.h"
+#include "utils/timestamp.h"
/* #define CACHEDEBUG */ /* turns DEBUG elogs on */
@@ -60,9 +61,19 @@
#define CACHE_elog(...)
#endif
+/*
+ * GUC variable to define the minimum age of entries that will be considered
+ * to be evicted in seconds. -1 to disable the feature.
+ */
+int catalog_cache_prune_min_age = -1;
+uint64 prune_min_age_us;
+
/* Cache management header --- pointer is NULL until created */
static CatCacheHeader *CacheHdr = NULL;
+/* Clock for the last accessed time of a catcache entry. */
+uint64 catcacheclock = 0;
+
static inline HeapTuple SearchCatCacheInternal(CatCache *cache,
int nkeys,
Datum v1, Datum v2,
@@ -74,6 +85,7 @@ static pg_noinline HeapTuple SearchCatCacheMiss(CatCache *cache,
Index hashIndex,
Datum v1, Datum v2,
Datum v3, Datum v4);
+static bool CatCacheCleanupOldEntries(CatCache *cp);
static uint32 CatalogCacheComputeHashValue(CatCache *cache, int nkeys,
Datum v1, Datum v2, Datum v3, Datum v4);
@@ -99,6 +111,15 @@ static void CatCacheFreeKeys(TupleDesc tupdesc, int nkeys, int *attnos,
static void CatCacheCopyKeys(TupleDesc tupdesc, int nkeys, int *attnos,
Datum *srckeys, Datum *dstkeys);
+/* GUC assign function */
+void
+assign_catalog_cache_prune_min_age(int newval, void *extra)
+{
+ if (newval < 0)
+ prune_min_age_us = UINT64_MAX;
+ else
+ prune_min_age_us = ((uint64) newval) * USECS_PER_SEC;
+}
/*
* internal support functions
@@ -1264,6 +1285,9 @@ SearchCatCacheInternal(CatCache *cache,
*/
dlist_move_head(bucket, &ct->cache_elem);
+ /* Record the last access timestamp */
+ ct->lastaccess = catcacheclock;
+
/*
* If it's a positive entry, bump its refcount and return it. If it's
* negative, we can report failure to the caller.
@@ -1425,6 +1449,61 @@ SearchCatCacheMiss(CatCache *cache,
return &ct->tuple;
}
+/*
+ * CatCacheCleanupOldEntries - Remove infrequently-used entries
+ *
+ * Catcache entries happen to be left unused for a long time for several
+ * reasons. Remove such entries to prevent catcache from bloating. It is based
+ * on the similar algorithm with buffer eviction. Entries that are accessed
+ * several times in a certain period live longer than those that have had less
+ * access in the same duration.
+ */
+static bool
+CatCacheCleanupOldEntries(CatCache *cp)
+{
+ int nremoved = 0;
+ int i;
+ long oldest_ts = catcacheclock;
+ uint64 prune_threshold = catcacheclock - prune_min_age_us;
+
+ /* Scan over the whole hash to find entries to remove */
+ for (i = 0 ; i < cp->cc_nbuckets ; i++)
+ {
+ dlist_mutable_iter iter;
+
+ dlist_foreach_modify(iter, &cp->cc_bucket[i])
+ {
+ CatCTup *ct = dlist_container(CatCTup, cache_elem, iter.cur);
+
+ /* Don't remove referenced entries */
+ if (ct->refcount == 0 &&
+ (ct->c_list == NULL || ct->c_list->refcount == 0))
+ {
+ if (ct->lastaccess < prune_threshold)
+ {
+ CatCacheRemoveCTup(cp, ct);
+ nremoved++;
+
+ /* don't let the removed entry update oldest_ts */
+ continue;
+ }
+ }
+
+ /* update the oldest timestamp if the entry remains alive */
+ if (ct->lastaccess < oldest_ts)
+ oldest_ts = ct->lastaccess;
+ }
+ }
+
+ cp->cc_oldest_ts = oldest_ts;
+
+ if (nremoved > 0)
+ elog(DEBUG1, "pruning catalog cache id=%d for %s: removed %d / %d",
+ cp->id, cp->cc_relname, nremoved, cp->cc_ntup + nremoved);
+
+ return nremoved > 0;
+}
+
/*
* ReleaseCatCache
*
@@ -1888,6 +1967,7 @@ CatalogCacheCreateEntry(CatCache *cache, HeapTuple ntp, Datum *arguments,
ct->dead = false;
ct->negative = negative;
ct->hash_value = hashValue;
+ ct->lastaccess = catcacheclock;
dlist_push_head(&cache->cc_bucket[hashIndex], &ct->cache_elem);
@@ -1899,7 +1979,12 @@ CatalogCacheCreateEntry(CatCache *cache, HeapTuple ntp, Datum *arguments,
* arbitrarily, we enlarge when fill factor > 2.
*/
if (cache->cc_ntup > cache->cc_nbuckets * 2)
- RehashCatCache(cache);
+ {
+ /* try removing old entries before expanding hash */
+ if (catcacheclock - cache->cc_oldest_ts < prune_min_age_us ||
+ !CatCacheCleanupOldEntries(cache))
+ RehashCatCache(cache);
+ }
return ct;
}
diff --git a/src/backend/utils/misc/guc.c b/src/backend/utils/misc/guc.c
index 17579eeaca..255e9fa73d 100644
--- a/src/backend/utils/misc/guc.c
+++ b/src/backend/utils/misc/guc.c
@@ -88,6 +88,7 @@
#include "utils/acl.h"
#include "utils/builtins.h"
#include "utils/bytea.h"
+#include "utils/catcache.h"
#include "utils/float.h"
#include "utils/guc_tables.h"
#include "utils/memutils.h"
@@ -3445,6 +3446,17 @@ static struct config_int ConfigureNamesInt[] =
NULL, NULL, NULL
},
+ {
+ {"catalog_cache_prune_min_age", PGC_USERSET, RESOURCES_MEM,
+ gettext_noop("System catalog cache entries that are living unused more than this seconds are considered for removal."),
+ gettext_noop("The value of -1 turns off pruning."),
+ GUC_UNIT_S
+ },
+ &catalog_cache_prune_min_age,
+ -1, -1, INT_MAX,
+ NULL, assign_catalog_cache_prune_min_age, NULL
+ },
+
/* End-of-list marker */
{
{NULL, 0, 0, NULL, NULL}, NULL, 0, 0, 0, NULL, NULL, NULL
diff --git a/src/include/utils/catcache.h b/src/include/utils/catcache.h
index ddc2762eb3..291e857e38 100644
--- a/src/include/utils/catcache.h
+++ b/src/include/utils/catcache.h
@@ -22,6 +22,7 @@
#include "access/htup.h"
#include "access/skey.h"
+#include "datatype/timestamp.h"
#include "lib/ilist.h"
#include "utils/relcache.h"
@@ -61,6 +62,7 @@ typedef struct catcache
slist_node cc_next; /* list link */
ScanKeyData cc_skey[CATCACHE_MAXKEYS]; /* precomputed key info for heap
* scans */
+ uint64 cc_oldest_ts; /* timestamp (us) of the oldest tuple */
/*
* Keep these at the end, so that compiling catcache.c with CATCACHE_STATS
@@ -119,6 +121,7 @@ typedef struct catctup
bool dead; /* dead but not yet removed? */
bool negative; /* negative cache entry? */
HeapTupleData tuple; /* tuple management header */
+ uint64 lastaccess; /* timestamp in us of the last usage */
/*
* The tuple may also be a member of at most one CatCList. (If a single
@@ -189,6 +192,22 @@ typedef struct catcacheheader
/* this extern duplicates utils/memutils.h... */
extern PGDLLIMPORT MemoryContext CacheMemoryContext;
+
+/* for guc.c, not PGDLLPMPORT'ed */
+extern int catalog_cache_prune_min_age;
+
+/* source clock for access timestamp of catcache entries */
+extern uint64 catcacheclock;
+
+/* SetCatCacheClock - set catcache timestamp source clock */
+static inline void
+SetCatCacheClock(TimestampTz ts)
+{
+ catcacheclock = (uint64) ts;
+}
+
+extern void assign_catalog_cache_prune_min_age(int newval, void *extra);
+
extern void CreateCacheMemoryContext(void);
extern CatCache *InitCatCache(int id, Oid reloid, Oid indexoid,
--
2.27.0
----Next_Part(Thu_Jan_14_17_32_27_2021_995)--
Content-Type: Text/X-Patch; charset=us-ascii
Content-Transfer-Encoding: 7bit
Content-Disposition: inline;
filename="v6-0002-Remove-dead-flag-from-catcache-tuple.patch"
^ permalink raw reply [nested|flat] 92+ messages in thread
* [PATCH v7 1/3] CatCache expiration feature
@ 2020-11-18 07:54 Kyotaro Horiguchi <[email protected]>
0 siblings, 0 replies; 92+ messages in thread
From: Kyotaro Horiguchi @ 2020-11-18 07:54 UTC (permalink / raw)
---
src/backend/access/transam/xact.c | 3 ++
src/backend/utils/cache/catcache.c | 87 +++++++++++++++++++++++++++++-
src/backend/utils/misc/guc.c | 12 +++++
src/include/utils/catcache.h | 19 +++++++
4 files changed, 120 insertions(+), 1 deletion(-)
diff --git a/src/backend/access/transam/xact.c b/src/backend/access/transam/xact.c
index a2068e3fd4..86888d2409 100644
--- a/src/backend/access/transam/xact.c
+++ b/src/backend/access/transam/xact.c
@@ -1086,6 +1086,9 @@ static void
AtStart_Cache(void)
{
AcceptInvalidationMessages();
+
+ if (xactStartTimestamp != 0)
+ SetCatCacheClock(xactStartTimestamp);
}
/*
diff --git a/src/backend/utils/cache/catcache.c b/src/backend/utils/cache/catcache.c
index fa2b49c676..644d92dd9a 100644
--- a/src/backend/utils/cache/catcache.c
+++ b/src/backend/utils/cache/catcache.c
@@ -38,6 +38,7 @@
#include "utils/rel.h"
#include "utils/resowner_private.h"
#include "utils/syscache.h"
+#include "utils/timestamp.h"
/* #define CACHEDEBUG */ /* turns DEBUG elogs on */
@@ -60,9 +61,19 @@
#define CACHE_elog(...)
#endif
+/*
+ * GUC variable to define the minimum age of entries that will be considered
+ * to be evicted in seconds. -1 to disable the feature.
+ */
+int catalog_cache_prune_min_age = -1;
+uint64 prune_min_age_us;
+
/* Cache management header --- pointer is NULL until created */
static CatCacheHeader *CacheHdr = NULL;
+/* Clock for the last accessed time of a catcache entry. */
+uint64 catcacheclock = 0;
+
static inline HeapTuple SearchCatCacheInternal(CatCache *cache,
int nkeys,
Datum v1, Datum v2,
@@ -74,6 +85,7 @@ static pg_noinline HeapTuple SearchCatCacheMiss(CatCache *cache,
Index hashIndex,
Datum v1, Datum v2,
Datum v3, Datum v4);
+static bool CatCacheCleanupOldEntries(CatCache *cp);
static uint32 CatalogCacheComputeHashValue(CatCache *cache, int nkeys,
Datum v1, Datum v2, Datum v3, Datum v4);
@@ -99,6 +111,15 @@ static void CatCacheFreeKeys(TupleDesc tupdesc, int nkeys, int *attnos,
static void CatCacheCopyKeys(TupleDesc tupdesc, int nkeys, int *attnos,
Datum *srckeys, Datum *dstkeys);
+/* GUC assign function */
+void
+assign_catalog_cache_prune_min_age(int newval, void *extra)
+{
+ if (newval < 0)
+ prune_min_age_us = UINT64_MAX;
+ else
+ prune_min_age_us = ((uint64) newval) * USECS_PER_SEC;
+}
/*
* internal support functions
@@ -1264,6 +1285,9 @@ SearchCatCacheInternal(CatCache *cache,
*/
dlist_move_head(bucket, &ct->cache_elem);
+ /* Record the last access timestamp */
+ ct->lastaccess = catcacheclock;
+
/*
* If it's a positive entry, bump its refcount and return it. If it's
* negative, we can report failure to the caller.
@@ -1425,6 +1449,61 @@ SearchCatCacheMiss(CatCache *cache,
return &ct->tuple;
}
+/*
+ * CatCacheCleanupOldEntries - Remove infrequently-used entries
+ *
+ * Catcache entries happen to be left unused for a long time for several
+ * reasons. Remove such entries to prevent catcache from bloating. It is based
+ * on the similar algorithm with buffer eviction. Entries that are accessed
+ * several times in a certain period live longer than those that have had less
+ * access in the same duration.
+ */
+static bool
+CatCacheCleanupOldEntries(CatCache *cp)
+{
+ int nremoved = 0;
+ int i;
+ long oldest_ts = catcacheclock;
+ uint64 prune_threshold = catcacheclock - prune_min_age_us;
+
+ /* Scan over the whole hash to find entries to remove */
+ for (i = 0 ; i < cp->cc_nbuckets ; i++)
+ {
+ dlist_mutable_iter iter;
+
+ dlist_foreach_modify(iter, &cp->cc_bucket[i])
+ {
+ CatCTup *ct = dlist_container(CatCTup, cache_elem, iter.cur);
+
+ /* Don't remove referenced entries */
+ if (ct->refcount == 0 &&
+ (ct->c_list == NULL || ct->c_list->refcount == 0))
+ {
+ if (ct->lastaccess < prune_threshold)
+ {
+ CatCacheRemoveCTup(cp, ct);
+ nremoved++;
+
+ /* don't let the removed entry update oldest_ts */
+ continue;
+ }
+ }
+
+ /* update the oldest timestamp if the entry remains alive */
+ if (ct->lastaccess < oldest_ts)
+ oldest_ts = ct->lastaccess;
+ }
+ }
+
+ cp->cc_oldest_ts = oldest_ts;
+
+ if (nremoved > 0)
+ elog(DEBUG1, "pruning catalog cache id=%d for %s: removed %d / %d",
+ cp->id, cp->cc_relname, nremoved, cp->cc_ntup + nremoved);
+
+ return nremoved > 0;
+}
+
/*
* ReleaseCatCache
*
@@ -1888,6 +1967,7 @@ CatalogCacheCreateEntry(CatCache *cache, HeapTuple ntp, Datum *arguments,
ct->dead = false;
ct->negative = negative;
ct->hash_value = hashValue;
+ ct->lastaccess = catcacheclock;
dlist_push_head(&cache->cc_bucket[hashIndex], &ct->cache_elem);
@@ -1899,7 +1979,12 @@ CatalogCacheCreateEntry(CatCache *cache, HeapTuple ntp, Datum *arguments,
* arbitrarily, we enlarge when fill factor > 2.
*/
if (cache->cc_ntup > cache->cc_nbuckets * 2)
- RehashCatCache(cache);
+ {
+ /* try removing old entries before expanding hash */
+ if (catcacheclock - cache->cc_oldest_ts < prune_min_age_us ||
+ !CatCacheCleanupOldEntries(cache))
+ RehashCatCache(cache);
+ }
return ct;
}
diff --git a/src/backend/utils/misc/guc.c b/src/backend/utils/misc/guc.c
index 17579eeaca..255e9fa73d 100644
--- a/src/backend/utils/misc/guc.c
+++ b/src/backend/utils/misc/guc.c
@@ -88,6 +88,7 @@
#include "utils/acl.h"
#include "utils/builtins.h"
#include "utils/bytea.h"
+#include "utils/catcache.h"
#include "utils/float.h"
#include "utils/guc_tables.h"
#include "utils/memutils.h"
@@ -3445,6 +3446,17 @@ static struct config_int ConfigureNamesInt[] =
NULL, NULL, NULL
},
+ {
+ {"catalog_cache_prune_min_age", PGC_USERSET, RESOURCES_MEM,
+ gettext_noop("System catalog cache entries that are living unused more than this seconds are considered for removal."),
+ gettext_noop("The value of -1 turns off pruning."),
+ GUC_UNIT_S
+ },
+ &catalog_cache_prune_min_age,
+ -1, -1, INT_MAX,
+ NULL, assign_catalog_cache_prune_min_age, NULL
+ },
+
/* End-of-list marker */
{
{NULL, 0, 0, NULL, NULL}, NULL, 0, 0, 0, NULL, NULL, NULL
diff --git a/src/include/utils/catcache.h b/src/include/utils/catcache.h
index ddc2762eb3..291e857e38 100644
--- a/src/include/utils/catcache.h
+++ b/src/include/utils/catcache.h
@@ -22,6 +22,7 @@
#include "access/htup.h"
#include "access/skey.h"
+#include "datatype/timestamp.h"
#include "lib/ilist.h"
#include "utils/relcache.h"
@@ -61,6 +62,7 @@ typedef struct catcache
slist_node cc_next; /* list link */
ScanKeyData cc_skey[CATCACHE_MAXKEYS]; /* precomputed key info for heap
* scans */
+ uint64 cc_oldest_ts; /* timestamp (us) of the oldest tuple */
/*
* Keep these at the end, so that compiling catcache.c with CATCACHE_STATS
@@ -119,6 +121,7 @@ typedef struct catctup
bool dead; /* dead but not yet removed? */
bool negative; /* negative cache entry? */
HeapTupleData tuple; /* tuple management header */
+ uint64 lastaccess; /* timestamp in us of the last usage */
/*
* The tuple may also be a member of at most one CatCList. (If a single
@@ -189,6 +192,22 @@ typedef struct catcacheheader
/* this extern duplicates utils/memutils.h... */
extern PGDLLIMPORT MemoryContext CacheMemoryContext;
+
+/* for guc.c, not PGDLLPMPORT'ed */
+extern int catalog_cache_prune_min_age;
+
+/* source clock for access timestamp of catcache entries */
+extern uint64 catcacheclock;
+
+/* SetCatCacheClock - set catcache timestamp source clock */
+static inline void
+SetCatCacheClock(TimestampTz ts)
+{
+ catcacheclock = (uint64) ts;
+}
+
+extern void assign_catalog_cache_prune_min_age(int newval, void *extra);
+
extern void CreateCacheMemoryContext(void);
extern CatCache *InitCatCache(int id, Oid reloid, Oid indexoid,
--
2.27.0
----Next_Part(Wed_Jan_27_10_13_08_2021_482)--
Content-Type: Text/X-Patch; charset=us-ascii
Content-Transfer-Encoding: 7bit
Content-Disposition: inline;
filename="v7-0002-Remove-dead-flag-from-catcache-tuple.patch"
^ permalink raw reply [nested|flat] 92+ messages in thread
* [PATCH v6 1/3] CatCache expiration feature
@ 2020-11-18 07:54 Kyotaro Horiguchi <[email protected]>
0 siblings, 0 replies; 92+ messages in thread
From: Kyotaro Horiguchi @ 2020-11-18 07:54 UTC (permalink / raw)
---
src/backend/access/transam/xact.c | 3 ++
src/backend/utils/cache/catcache.c | 87 +++++++++++++++++++++++++++++-
src/backend/utils/misc/guc.c | 12 +++++
src/include/utils/catcache.h | 19 +++++++
4 files changed, 120 insertions(+), 1 deletion(-)
diff --git a/src/backend/access/transam/xact.c b/src/backend/access/transam/xact.c
index a2068e3fd4..86888d2409 100644
--- a/src/backend/access/transam/xact.c
+++ b/src/backend/access/transam/xact.c
@@ -1086,6 +1086,9 @@ static void
AtStart_Cache(void)
{
AcceptInvalidationMessages();
+
+ if (xactStartTimestamp != 0)
+ SetCatCacheClock(xactStartTimestamp);
}
/*
diff --git a/src/backend/utils/cache/catcache.c b/src/backend/utils/cache/catcache.c
index fa2b49c676..644d92dd9a 100644
--- a/src/backend/utils/cache/catcache.c
+++ b/src/backend/utils/cache/catcache.c
@@ -38,6 +38,7 @@
#include "utils/rel.h"
#include "utils/resowner_private.h"
#include "utils/syscache.h"
+#include "utils/timestamp.h"
/* #define CACHEDEBUG */ /* turns DEBUG elogs on */
@@ -60,9 +61,19 @@
#define CACHE_elog(...)
#endif
+/*
+ * GUC variable to define the minimum age of entries that will be considered
+ * to be evicted in seconds. -1 to disable the feature.
+ */
+int catalog_cache_prune_min_age = -1;
+uint64 prune_min_age_us;
+
/* Cache management header --- pointer is NULL until created */
static CatCacheHeader *CacheHdr = NULL;
+/* Clock for the last accessed time of a catcache entry. */
+uint64 catcacheclock = 0;
+
static inline HeapTuple SearchCatCacheInternal(CatCache *cache,
int nkeys,
Datum v1, Datum v2,
@@ -74,6 +85,7 @@ static pg_noinline HeapTuple SearchCatCacheMiss(CatCache *cache,
Index hashIndex,
Datum v1, Datum v2,
Datum v3, Datum v4);
+static bool CatCacheCleanupOldEntries(CatCache *cp);
static uint32 CatalogCacheComputeHashValue(CatCache *cache, int nkeys,
Datum v1, Datum v2, Datum v3, Datum v4);
@@ -99,6 +111,15 @@ static void CatCacheFreeKeys(TupleDesc tupdesc, int nkeys, int *attnos,
static void CatCacheCopyKeys(TupleDesc tupdesc, int nkeys, int *attnos,
Datum *srckeys, Datum *dstkeys);
+/* GUC assign function */
+void
+assign_catalog_cache_prune_min_age(int newval, void *extra)
+{
+ if (newval < 0)
+ prune_min_age_us = UINT64_MAX;
+ else
+ prune_min_age_us = ((uint64) newval) * USECS_PER_SEC;
+}
/*
* internal support functions
@@ -1264,6 +1285,9 @@ SearchCatCacheInternal(CatCache *cache,
*/
dlist_move_head(bucket, &ct->cache_elem);
+ /* Record the last access timestamp */
+ ct->lastaccess = catcacheclock;
+
/*
* If it's a positive entry, bump its refcount and return it. If it's
* negative, we can report failure to the caller.
@@ -1425,6 +1449,61 @@ SearchCatCacheMiss(CatCache *cache,
return &ct->tuple;
}
+/*
+ * CatCacheCleanupOldEntries - Remove infrequently-used entries
+ *
+ * Catcache entries happen to be left unused for a long time for several
+ * reasons. Remove such entries to prevent catcache from bloating. It is based
+ * on the similar algorithm with buffer eviction. Entries that are accessed
+ * several times in a certain period live longer than those that have had less
+ * access in the same duration.
+ */
+static bool
+CatCacheCleanupOldEntries(CatCache *cp)
+{
+ int nremoved = 0;
+ int i;
+ long oldest_ts = catcacheclock;
+ uint64 prune_threshold = catcacheclock - prune_min_age_us;
+
+ /* Scan over the whole hash to find entries to remove */
+ for (i = 0 ; i < cp->cc_nbuckets ; i++)
+ {
+ dlist_mutable_iter iter;
+
+ dlist_foreach_modify(iter, &cp->cc_bucket[i])
+ {
+ CatCTup *ct = dlist_container(CatCTup, cache_elem, iter.cur);
+
+ /* Don't remove referenced entries */
+ if (ct->refcount == 0 &&
+ (ct->c_list == NULL || ct->c_list->refcount == 0))
+ {
+ if (ct->lastaccess < prune_threshold)
+ {
+ CatCacheRemoveCTup(cp, ct);
+ nremoved++;
+
+ /* don't let the removed entry update oldest_ts */
+ continue;
+ }
+ }
+
+ /* update the oldest timestamp if the entry remains alive */
+ if (ct->lastaccess < oldest_ts)
+ oldest_ts = ct->lastaccess;
+ }
+ }
+
+ cp->cc_oldest_ts = oldest_ts;
+
+ if (nremoved > 0)
+ elog(DEBUG1, "pruning catalog cache id=%d for %s: removed %d / %d",
+ cp->id, cp->cc_relname, nremoved, cp->cc_ntup + nremoved);
+
+ return nremoved > 0;
+}
+
/*
* ReleaseCatCache
*
@@ -1888,6 +1967,7 @@ CatalogCacheCreateEntry(CatCache *cache, HeapTuple ntp, Datum *arguments,
ct->dead = false;
ct->negative = negative;
ct->hash_value = hashValue;
+ ct->lastaccess = catcacheclock;
dlist_push_head(&cache->cc_bucket[hashIndex], &ct->cache_elem);
@@ -1899,7 +1979,12 @@ CatalogCacheCreateEntry(CatCache *cache, HeapTuple ntp, Datum *arguments,
* arbitrarily, we enlarge when fill factor > 2.
*/
if (cache->cc_ntup > cache->cc_nbuckets * 2)
- RehashCatCache(cache);
+ {
+ /* try removing old entries before expanding hash */
+ if (catcacheclock - cache->cc_oldest_ts < prune_min_age_us ||
+ !CatCacheCleanupOldEntries(cache))
+ RehashCatCache(cache);
+ }
return ct;
}
diff --git a/src/backend/utils/misc/guc.c b/src/backend/utils/misc/guc.c
index 17579eeaca..255e9fa73d 100644
--- a/src/backend/utils/misc/guc.c
+++ b/src/backend/utils/misc/guc.c
@@ -88,6 +88,7 @@
#include "utils/acl.h"
#include "utils/builtins.h"
#include "utils/bytea.h"
+#include "utils/catcache.h"
#include "utils/float.h"
#include "utils/guc_tables.h"
#include "utils/memutils.h"
@@ -3445,6 +3446,17 @@ static struct config_int ConfigureNamesInt[] =
NULL, NULL, NULL
},
+ {
+ {"catalog_cache_prune_min_age", PGC_USERSET, RESOURCES_MEM,
+ gettext_noop("System catalog cache entries that are living unused more than this seconds are considered for removal."),
+ gettext_noop("The value of -1 turns off pruning."),
+ GUC_UNIT_S
+ },
+ &catalog_cache_prune_min_age,
+ -1, -1, INT_MAX,
+ NULL, assign_catalog_cache_prune_min_age, NULL
+ },
+
/* End-of-list marker */
{
{NULL, 0, 0, NULL, NULL}, NULL, 0, 0, 0, NULL, NULL, NULL
diff --git a/src/include/utils/catcache.h b/src/include/utils/catcache.h
index ddc2762eb3..291e857e38 100644
--- a/src/include/utils/catcache.h
+++ b/src/include/utils/catcache.h
@@ -22,6 +22,7 @@
#include "access/htup.h"
#include "access/skey.h"
+#include "datatype/timestamp.h"
#include "lib/ilist.h"
#include "utils/relcache.h"
@@ -61,6 +62,7 @@ typedef struct catcache
slist_node cc_next; /* list link */
ScanKeyData cc_skey[CATCACHE_MAXKEYS]; /* precomputed key info for heap
* scans */
+ uint64 cc_oldest_ts; /* timestamp (us) of the oldest tuple */
/*
* Keep these at the end, so that compiling catcache.c with CATCACHE_STATS
@@ -119,6 +121,7 @@ typedef struct catctup
bool dead; /* dead but not yet removed? */
bool negative; /* negative cache entry? */
HeapTupleData tuple; /* tuple management header */
+ uint64 lastaccess; /* timestamp in us of the last usage */
/*
* The tuple may also be a member of at most one CatCList. (If a single
@@ -189,6 +192,22 @@ typedef struct catcacheheader
/* this extern duplicates utils/memutils.h... */
extern PGDLLIMPORT MemoryContext CacheMemoryContext;
+
+/* for guc.c, not PGDLLPMPORT'ed */
+extern int catalog_cache_prune_min_age;
+
+/* source clock for access timestamp of catcache entries */
+extern uint64 catcacheclock;
+
+/* SetCatCacheClock - set catcache timestamp source clock */
+static inline void
+SetCatCacheClock(TimestampTz ts)
+{
+ catcacheclock = (uint64) ts;
+}
+
+extern void assign_catalog_cache_prune_min_age(int newval, void *extra);
+
extern void CreateCacheMemoryContext(void);
extern CatCache *InitCatCache(int id, Oid reloid, Oid indexoid,
--
2.27.0
----Next_Part(Thu_Jan_14_17_32_27_2021_995)--
Content-Type: Text/X-Patch; charset=us-ascii
Content-Transfer-Encoding: 7bit
Content-Disposition: inline;
filename="v6-0002-Remove-dead-flag-from-catcache-tuple.patch"
^ permalink raw reply [nested|flat] 92+ messages in thread
* [PATCH v8 1/1] CatCache expiration feature
@ 2021-01-27 11:08 Heikki Linnakangas <[email protected]>
0 siblings, 0 replies; 92+ messages in thread
From: Heikki Linnakangas @ 2021-01-27 11:08 UTC (permalink / raw)
Author: Kyotaro Horiguchi
---
src/backend/access/transam/xact.c | 3 ++
src/backend/utils/cache/catcache.c | 82 +++++++++++++++++++++++++++++-
src/backend/utils/misc/guc.c | 12 +++++
src/include/utils/catcache.h | 17 +++++++
4 files changed, 112 insertions(+), 2 deletions(-)
diff --git a/src/backend/access/transam/xact.c b/src/backend/access/transam/xact.c
index a2068e3fd45..86888d24091 100644
--- a/src/backend/access/transam/xact.c
+++ b/src/backend/access/transam/xact.c
@@ -1086,6 +1086,9 @@ static void
AtStart_Cache(void)
{
AcceptInvalidationMessages();
+
+ if (xactStartTimestamp != 0)
+ SetCatCacheClock(xactStartTimestamp);
}
/*
diff --git a/src/backend/utils/cache/catcache.c b/src/backend/utils/cache/catcache.c
index fa2b49c676e..e29f825687a 100644
--- a/src/backend/utils/cache/catcache.c
+++ b/src/backend/utils/cache/catcache.c
@@ -38,6 +38,7 @@
#include "utils/rel.h"
#include "utils/resowner_private.h"
#include "utils/syscache.h"
+#include "utils/timestamp.h"
/* #define CACHEDEBUG */ /* turns DEBUG elogs on */
@@ -60,9 +61,18 @@
#define CACHE_elog(...)
#endif
+/*
+ * GUC variable to define the minimum age of entries that will be considered
+ * to be evicted in seconds. -1 to disable the feature.
+ */
+int catalog_cache_prune_min_age = -1;
+
/* Cache management header --- pointer is NULL until created */
static CatCacheHeader *CacheHdr = NULL;
+/* Clock for the last accessed time of a catcache entry. */
+TimestampTz catcacheclock = 0;
+
static inline HeapTuple SearchCatCacheInternal(CatCache *cache,
int nkeys,
Datum v1, Datum v2,
@@ -74,6 +84,7 @@ static pg_noinline HeapTuple SearchCatCacheMiss(CatCache *cache,
Index hashIndex,
Datum v1, Datum v2,
Datum v3, Datum v4);
+static bool CatCacheCleanupOldEntries(CatCache *cp);
static uint32 CatalogCacheComputeHashValue(CatCache *cache, int nkeys,
Datum v1, Datum v2, Datum v3, Datum v4);
@@ -99,7 +110,6 @@ static void CatCacheFreeKeys(TupleDesc tupdesc, int nkeys, int *attnos,
static void CatCacheCopyKeys(TupleDesc tupdesc, int nkeys, int *attnos,
Datum *srckeys, Datum *dstkeys);
-
/*
* internal support functions
*/
@@ -1264,6 +1274,9 @@ SearchCatCacheInternal(CatCache *cache,
*/
dlist_move_head(bucket, &ct->cache_elem);
+ /* Record the last access timestamp */
+ ct->lastaccess = catcacheclock;
+
/*
* If it's a positive entry, bump its refcount and return it. If it's
* negative, we can report failure to the caller.
@@ -1425,6 +1438,66 @@ SearchCatCacheMiss(CatCache *cache,
return &ct->tuple;
}
+/*
+ * CatCacheCleanupOldEntries - Remove infrequently-used entries
+ *
+ * Catcache entries happen to be left unused for a long time for several
+ * reasons. Remove such entries to prevent catcache from bloating. It is based
+ * on the similar algorithm with buffer eviction. Entries that are accessed
+ * several times in a certain period live longer than those that have had less
+ * access in the same duration.
+ */
+static bool
+CatCacheCleanupOldEntries(CatCache *cp)
+{
+ int nremoved = 0;
+ int i;
+ TimestampTz oldest_ts = catcacheclock;
+ TimestampTz prune_threshold;
+
+ if (catalog_cache_prune_min_age < 0)
+ return false;
+
+ prune_threshold = catcacheclock - ((int64) catalog_cache_prune_min_age) * USECS_PER_SEC;
+
+ /* Scan over the whole hash to find entries to remove */
+ for (i = 0 ; i < cp->cc_nbuckets ; i++)
+ {
+ dlist_mutable_iter iter;
+
+ dlist_foreach_modify(iter, &cp->cc_bucket[i])
+ {
+ CatCTup *ct = dlist_container(CatCTup, cache_elem, iter.cur);
+
+ /* Don't remove referenced entries */
+ if (ct->refcount == 0 &&
+ (ct->c_list == NULL || ct->c_list->refcount == 0))
+ {
+ if (ct->lastaccess < prune_threshold)
+ {
+ CatCacheRemoveCTup(cp, ct);
+ nremoved++;
+
+ /* don't let the removed entry update oldest_ts */
+ continue;
+ }
+ }
+
+ /* update the oldest timestamp if the entry remains alive */
+ if (ct->lastaccess < oldest_ts)
+ oldest_ts = ct->lastaccess;
+ }
+ }
+
+ cp->cc_oldest_ts = oldest_ts;
+
+ if (nremoved > 0)
+ elog(DEBUG1, "pruning catalog cache id=%d for %s: removed %d / %d",
+ cp->id, cp->cc_relname, nremoved, cp->cc_ntup + nremoved);
+
+ return nremoved > 0;
+}
+
/*
* ReleaseCatCache
*
@@ -1888,6 +1961,7 @@ CatalogCacheCreateEntry(CatCache *cache, HeapTuple ntp, Datum *arguments,
ct->dead = false;
ct->negative = negative;
ct->hash_value = hashValue;
+ ct->lastaccess = catcacheclock;
dlist_push_head(&cache->cc_bucket[hashIndex], &ct->cache_elem);
@@ -1899,7 +1973,11 @@ CatalogCacheCreateEntry(CatCache *cache, HeapTuple ntp, Datum *arguments,
* arbitrarily, we enlarge when fill factor > 2.
*/
if (cache->cc_ntup > cache->cc_nbuckets * 2)
- RehashCatCache(cache);
+ {
+ /* try removing old entries before expanding hash */
+ if (!CatCacheCleanupOldEntries(cache))
+ RehashCatCache(cache);
+ }
return ct;
}
diff --git a/src/backend/utils/misc/guc.c b/src/backend/utils/misc/guc.c
index 17579eeaca9..f92f0513a6f 100644
--- a/src/backend/utils/misc/guc.c
+++ b/src/backend/utils/misc/guc.c
@@ -88,6 +88,7 @@
#include "utils/acl.h"
#include "utils/builtins.h"
#include "utils/bytea.h"
+#include "utils/catcache.h"
#include "utils/float.h"
#include "utils/guc_tables.h"
#include "utils/memutils.h"
@@ -3445,6 +3446,17 @@ static struct config_int ConfigureNamesInt[] =
NULL, NULL, NULL
},
+ {
+ {"catalog_cache_prune_min_age", PGC_USERSET, RESOURCES_MEM,
+ gettext_noop("System catalog cache entries that are living unused more than this seconds are considered for removal."),
+ gettext_noop("The value of -1 turns off pruning."),
+ GUC_UNIT_S
+ },
+ &catalog_cache_prune_min_age,
+ -1, -1, INT_MAX,
+ NULL, NULL, NULL
+ },
+
/* End-of-list marker */
{
{NULL, 0, 0, NULL, NULL}, NULL, 0, 0, 0, NULL, NULL, NULL
diff --git a/src/include/utils/catcache.h b/src/include/utils/catcache.h
index ddc2762eb3f..57a15bfec22 100644
--- a/src/include/utils/catcache.h
+++ b/src/include/utils/catcache.h
@@ -22,6 +22,7 @@
#include "access/htup.h"
#include "access/skey.h"
+#include "datatype/timestamp.h"
#include "lib/ilist.h"
#include "utils/relcache.h"
@@ -61,6 +62,7 @@ typedef struct catcache
slist_node cc_next; /* list link */
ScanKeyData cc_skey[CATCACHE_MAXKEYS]; /* precomputed key info for heap
* scans */
+ TimestampTz cc_oldest_ts; /* timestamp (us) of the oldest tuple */
/*
* Keep these at the end, so that compiling catcache.c with CATCACHE_STATS
@@ -119,6 +121,7 @@ typedef struct catctup
bool dead; /* dead but not yet removed? */
bool negative; /* negative cache entry? */
HeapTupleData tuple; /* tuple management header */
+ TimestampTz lastaccess; /* timestamp in us of the last usage */
/*
* The tuple may also be a member of at most one CatCList. (If a single
@@ -189,6 +192,20 @@ typedef struct catcacheheader
/* this extern duplicates utils/memutils.h... */
extern PGDLLIMPORT MemoryContext CacheMemoryContext;
+
+/* for guc.c, not PGDLLPMPORT'ed */
+extern int catalog_cache_prune_min_age;
+
+/* source clock for access timestamp of catcache entries */
+extern TimestampTz catcacheclock;
+
+/* SetCatCacheClock - set catcache timestamp source clock */
+static inline void
+SetCatCacheClock(TimestampTz ts)
+{
+ catcacheclock = ts;
+}
+
extern void CreateCacheMemoryContext(void);
extern CatCache *InitCatCache(int id, Oid reloid, Oid indexoid,
--
2.29.2
--------------38A9F5DCD32945DE1430BC1D--
^ permalink raw reply [nested|flat] 92+ messages in thread
* [PATCH v8 1/1] CatCache expiration feature
@ 2021-01-27 11:08 Heikki Linnakangas <[email protected]>
0 siblings, 0 replies; 92+ messages in thread
From: Heikki Linnakangas @ 2021-01-27 11:08 UTC (permalink / raw)
Author: Kyotaro Horiguchi
---
src/backend/access/transam/xact.c | 3 ++
src/backend/utils/cache/catcache.c | 82 +++++++++++++++++++++++++++++-
src/backend/utils/misc/guc.c | 12 +++++
src/include/utils/catcache.h | 17 +++++++
4 files changed, 112 insertions(+), 2 deletions(-)
diff --git a/src/backend/access/transam/xact.c b/src/backend/access/transam/xact.c
index a2068e3fd45..86888d24091 100644
--- a/src/backend/access/transam/xact.c
+++ b/src/backend/access/transam/xact.c
@@ -1086,6 +1086,9 @@ static void
AtStart_Cache(void)
{
AcceptInvalidationMessages();
+
+ if (xactStartTimestamp != 0)
+ SetCatCacheClock(xactStartTimestamp);
}
/*
diff --git a/src/backend/utils/cache/catcache.c b/src/backend/utils/cache/catcache.c
index fa2b49c676e..e29f825687a 100644
--- a/src/backend/utils/cache/catcache.c
+++ b/src/backend/utils/cache/catcache.c
@@ -38,6 +38,7 @@
#include "utils/rel.h"
#include "utils/resowner_private.h"
#include "utils/syscache.h"
+#include "utils/timestamp.h"
/* #define CACHEDEBUG */ /* turns DEBUG elogs on */
@@ -60,9 +61,18 @@
#define CACHE_elog(...)
#endif
+/*
+ * GUC variable to define the minimum age of entries that will be considered
+ * to be evicted in seconds. -1 to disable the feature.
+ */
+int catalog_cache_prune_min_age = -1;
+
/* Cache management header --- pointer is NULL until created */
static CatCacheHeader *CacheHdr = NULL;
+/* Clock for the last accessed time of a catcache entry. */
+TimestampTz catcacheclock = 0;
+
static inline HeapTuple SearchCatCacheInternal(CatCache *cache,
int nkeys,
Datum v1, Datum v2,
@@ -74,6 +84,7 @@ static pg_noinline HeapTuple SearchCatCacheMiss(CatCache *cache,
Index hashIndex,
Datum v1, Datum v2,
Datum v3, Datum v4);
+static bool CatCacheCleanupOldEntries(CatCache *cp);
static uint32 CatalogCacheComputeHashValue(CatCache *cache, int nkeys,
Datum v1, Datum v2, Datum v3, Datum v4);
@@ -99,7 +110,6 @@ static void CatCacheFreeKeys(TupleDesc tupdesc, int nkeys, int *attnos,
static void CatCacheCopyKeys(TupleDesc tupdesc, int nkeys, int *attnos,
Datum *srckeys, Datum *dstkeys);
-
/*
* internal support functions
*/
@@ -1264,6 +1274,9 @@ SearchCatCacheInternal(CatCache *cache,
*/
dlist_move_head(bucket, &ct->cache_elem);
+ /* Record the last access timestamp */
+ ct->lastaccess = catcacheclock;
+
/*
* If it's a positive entry, bump its refcount and return it. If it's
* negative, we can report failure to the caller.
@@ -1425,6 +1438,66 @@ SearchCatCacheMiss(CatCache *cache,
return &ct->tuple;
}
+/*
+ * CatCacheCleanupOldEntries - Remove infrequently-used entries
+ *
+ * Catcache entries happen to be left unused for a long time for several
+ * reasons. Remove such entries to prevent catcache from bloating. It is based
+ * on the similar algorithm with buffer eviction. Entries that are accessed
+ * several times in a certain period live longer than those that have had less
+ * access in the same duration.
+ */
+static bool
+CatCacheCleanupOldEntries(CatCache *cp)
+{
+ int nremoved = 0;
+ int i;
+ TimestampTz oldest_ts = catcacheclock;
+ TimestampTz prune_threshold;
+
+ if (catalog_cache_prune_min_age < 0)
+ return false;
+
+ prune_threshold = catcacheclock - ((int64) catalog_cache_prune_min_age) * USECS_PER_SEC;
+
+ /* Scan over the whole hash to find entries to remove */
+ for (i = 0 ; i < cp->cc_nbuckets ; i++)
+ {
+ dlist_mutable_iter iter;
+
+ dlist_foreach_modify(iter, &cp->cc_bucket[i])
+ {
+ CatCTup *ct = dlist_container(CatCTup, cache_elem, iter.cur);
+
+ /* Don't remove referenced entries */
+ if (ct->refcount == 0 &&
+ (ct->c_list == NULL || ct->c_list->refcount == 0))
+ {
+ if (ct->lastaccess < prune_threshold)
+ {
+ CatCacheRemoveCTup(cp, ct);
+ nremoved++;
+
+ /* don't let the removed entry update oldest_ts */
+ continue;
+ }
+ }
+
+ /* update the oldest timestamp if the entry remains alive */
+ if (ct->lastaccess < oldest_ts)
+ oldest_ts = ct->lastaccess;
+ }
+ }
+
+ cp->cc_oldest_ts = oldest_ts;
+
+ if (nremoved > 0)
+ elog(DEBUG1, "pruning catalog cache id=%d for %s: removed %d / %d",
+ cp->id, cp->cc_relname, nremoved, cp->cc_ntup + nremoved);
+
+ return nremoved > 0;
+}
+
/*
* ReleaseCatCache
*
@@ -1888,6 +1961,7 @@ CatalogCacheCreateEntry(CatCache *cache, HeapTuple ntp, Datum *arguments,
ct->dead = false;
ct->negative = negative;
ct->hash_value = hashValue;
+ ct->lastaccess = catcacheclock;
dlist_push_head(&cache->cc_bucket[hashIndex], &ct->cache_elem);
@@ -1899,7 +1973,11 @@ CatalogCacheCreateEntry(CatCache *cache, HeapTuple ntp, Datum *arguments,
* arbitrarily, we enlarge when fill factor > 2.
*/
if (cache->cc_ntup > cache->cc_nbuckets * 2)
- RehashCatCache(cache);
+ {
+ /* try removing old entries before expanding hash */
+ if (!CatCacheCleanupOldEntries(cache))
+ RehashCatCache(cache);
+ }
return ct;
}
diff --git a/src/backend/utils/misc/guc.c b/src/backend/utils/misc/guc.c
index 17579eeaca9..f92f0513a6f 100644
--- a/src/backend/utils/misc/guc.c
+++ b/src/backend/utils/misc/guc.c
@@ -88,6 +88,7 @@
#include "utils/acl.h"
#include "utils/builtins.h"
#include "utils/bytea.h"
+#include "utils/catcache.h"
#include "utils/float.h"
#include "utils/guc_tables.h"
#include "utils/memutils.h"
@@ -3445,6 +3446,17 @@ static struct config_int ConfigureNamesInt[] =
NULL, NULL, NULL
},
+ {
+ {"catalog_cache_prune_min_age", PGC_USERSET, RESOURCES_MEM,
+ gettext_noop("System catalog cache entries that are living unused more than this seconds are considered for removal."),
+ gettext_noop("The value of -1 turns off pruning."),
+ GUC_UNIT_S
+ },
+ &catalog_cache_prune_min_age,
+ -1, -1, INT_MAX,
+ NULL, NULL, NULL
+ },
+
/* End-of-list marker */
{
{NULL, 0, 0, NULL, NULL}, NULL, 0, 0, 0, NULL, NULL, NULL
diff --git a/src/include/utils/catcache.h b/src/include/utils/catcache.h
index ddc2762eb3f..57a15bfec22 100644
--- a/src/include/utils/catcache.h
+++ b/src/include/utils/catcache.h
@@ -22,6 +22,7 @@
#include "access/htup.h"
#include "access/skey.h"
+#include "datatype/timestamp.h"
#include "lib/ilist.h"
#include "utils/relcache.h"
@@ -61,6 +62,7 @@ typedef struct catcache
slist_node cc_next; /* list link */
ScanKeyData cc_skey[CATCACHE_MAXKEYS]; /* precomputed key info for heap
* scans */
+ TimestampTz cc_oldest_ts; /* timestamp (us) of the oldest tuple */
/*
* Keep these at the end, so that compiling catcache.c with CATCACHE_STATS
@@ -119,6 +121,7 @@ typedef struct catctup
bool dead; /* dead but not yet removed? */
bool negative; /* negative cache entry? */
HeapTupleData tuple; /* tuple management header */
+ TimestampTz lastaccess; /* timestamp in us of the last usage */
/*
* The tuple may also be a member of at most one CatCList. (If a single
@@ -189,6 +192,20 @@ typedef struct catcacheheader
/* this extern duplicates utils/memutils.h... */
extern PGDLLIMPORT MemoryContext CacheMemoryContext;
+
+/* for guc.c, not PGDLLPMPORT'ed */
+extern int catalog_cache_prune_min_age;
+
+/* source clock for access timestamp of catcache entries */
+extern TimestampTz catcacheclock;
+
+/* SetCatCacheClock - set catcache timestamp source clock */
+static inline void
+SetCatCacheClock(TimestampTz ts)
+{
+ catcacheclock = ts;
+}
+
extern void CreateCacheMemoryContext(void);
extern CatCache *InitCatCache(int id, Oid reloid, Oid indexoid,
--
2.29.2
--------------38A9F5DCD32945DE1430BC1D--
^ permalink raw reply [nested|flat] 92+ messages in thread
* [PATCH v8 1/1] CatCache expiration feature
@ 2021-01-27 11:08 Heikki Linnakangas <[email protected]>
0 siblings, 0 replies; 92+ messages in thread
From: Heikki Linnakangas @ 2021-01-27 11:08 UTC (permalink / raw)
Author: Kyotaro Horiguchi
---
src/backend/access/transam/xact.c | 3 ++
src/backend/utils/cache/catcache.c | 82 +++++++++++++++++++++++++++++-
src/backend/utils/misc/guc.c | 12 +++++
src/include/utils/catcache.h | 17 +++++++
4 files changed, 112 insertions(+), 2 deletions(-)
diff --git a/src/backend/access/transam/xact.c b/src/backend/access/transam/xact.c
index a2068e3fd45..86888d24091 100644
--- a/src/backend/access/transam/xact.c
+++ b/src/backend/access/transam/xact.c
@@ -1086,6 +1086,9 @@ static void
AtStart_Cache(void)
{
AcceptInvalidationMessages();
+
+ if (xactStartTimestamp != 0)
+ SetCatCacheClock(xactStartTimestamp);
}
/*
diff --git a/src/backend/utils/cache/catcache.c b/src/backend/utils/cache/catcache.c
index fa2b49c676e..e29f825687a 100644
--- a/src/backend/utils/cache/catcache.c
+++ b/src/backend/utils/cache/catcache.c
@@ -38,6 +38,7 @@
#include "utils/rel.h"
#include "utils/resowner_private.h"
#include "utils/syscache.h"
+#include "utils/timestamp.h"
/* #define CACHEDEBUG */ /* turns DEBUG elogs on */
@@ -60,9 +61,18 @@
#define CACHE_elog(...)
#endif
+/*
+ * GUC variable to define the minimum age of entries that will be considered
+ * to be evicted in seconds. -1 to disable the feature.
+ */
+int catalog_cache_prune_min_age = -1;
+
/* Cache management header --- pointer is NULL until created */
static CatCacheHeader *CacheHdr = NULL;
+/* Clock for the last accessed time of a catcache entry. */
+TimestampTz catcacheclock = 0;
+
static inline HeapTuple SearchCatCacheInternal(CatCache *cache,
int nkeys,
Datum v1, Datum v2,
@@ -74,6 +84,7 @@ static pg_noinline HeapTuple SearchCatCacheMiss(CatCache *cache,
Index hashIndex,
Datum v1, Datum v2,
Datum v3, Datum v4);
+static bool CatCacheCleanupOldEntries(CatCache *cp);
static uint32 CatalogCacheComputeHashValue(CatCache *cache, int nkeys,
Datum v1, Datum v2, Datum v3, Datum v4);
@@ -99,7 +110,6 @@ static void CatCacheFreeKeys(TupleDesc tupdesc, int nkeys, int *attnos,
static void CatCacheCopyKeys(TupleDesc tupdesc, int nkeys, int *attnos,
Datum *srckeys, Datum *dstkeys);
-
/*
* internal support functions
*/
@@ -1264,6 +1274,9 @@ SearchCatCacheInternal(CatCache *cache,
*/
dlist_move_head(bucket, &ct->cache_elem);
+ /* Record the last access timestamp */
+ ct->lastaccess = catcacheclock;
+
/*
* If it's a positive entry, bump its refcount and return it. If it's
* negative, we can report failure to the caller.
@@ -1425,6 +1438,66 @@ SearchCatCacheMiss(CatCache *cache,
return &ct->tuple;
}
+/*
+ * CatCacheCleanupOldEntries - Remove infrequently-used entries
+ *
+ * Catcache entries happen to be left unused for a long time for several
+ * reasons. Remove such entries to prevent catcache from bloating. It is based
+ * on the similar algorithm with buffer eviction. Entries that are accessed
+ * several times in a certain period live longer than those that have had less
+ * access in the same duration.
+ */
+static bool
+CatCacheCleanupOldEntries(CatCache *cp)
+{
+ int nremoved = 0;
+ int i;
+ TimestampTz oldest_ts = catcacheclock;
+ TimestampTz prune_threshold;
+
+ if (catalog_cache_prune_min_age < 0)
+ return false;
+
+ prune_threshold = catcacheclock - ((int64) catalog_cache_prune_min_age) * USECS_PER_SEC;
+
+ /* Scan over the whole hash to find entries to remove */
+ for (i = 0 ; i < cp->cc_nbuckets ; i++)
+ {
+ dlist_mutable_iter iter;
+
+ dlist_foreach_modify(iter, &cp->cc_bucket[i])
+ {
+ CatCTup *ct = dlist_container(CatCTup, cache_elem, iter.cur);
+
+ /* Don't remove referenced entries */
+ if (ct->refcount == 0 &&
+ (ct->c_list == NULL || ct->c_list->refcount == 0))
+ {
+ if (ct->lastaccess < prune_threshold)
+ {
+ CatCacheRemoveCTup(cp, ct);
+ nremoved++;
+
+ /* don't let the removed entry update oldest_ts */
+ continue;
+ }
+ }
+
+ /* update the oldest timestamp if the entry remains alive */
+ if (ct->lastaccess < oldest_ts)
+ oldest_ts = ct->lastaccess;
+ }
+ }
+
+ cp->cc_oldest_ts = oldest_ts;
+
+ if (nremoved > 0)
+ elog(DEBUG1, "pruning catalog cache id=%d for %s: removed %d / %d",
+ cp->id, cp->cc_relname, nremoved, cp->cc_ntup + nremoved);
+
+ return nremoved > 0;
+}
+
/*
* ReleaseCatCache
*
@@ -1888,6 +1961,7 @@ CatalogCacheCreateEntry(CatCache *cache, HeapTuple ntp, Datum *arguments,
ct->dead = false;
ct->negative = negative;
ct->hash_value = hashValue;
+ ct->lastaccess = catcacheclock;
dlist_push_head(&cache->cc_bucket[hashIndex], &ct->cache_elem);
@@ -1899,7 +1973,11 @@ CatalogCacheCreateEntry(CatCache *cache, HeapTuple ntp, Datum *arguments,
* arbitrarily, we enlarge when fill factor > 2.
*/
if (cache->cc_ntup > cache->cc_nbuckets * 2)
- RehashCatCache(cache);
+ {
+ /* try removing old entries before expanding hash */
+ if (!CatCacheCleanupOldEntries(cache))
+ RehashCatCache(cache);
+ }
return ct;
}
diff --git a/src/backend/utils/misc/guc.c b/src/backend/utils/misc/guc.c
index 17579eeaca9..f92f0513a6f 100644
--- a/src/backend/utils/misc/guc.c
+++ b/src/backend/utils/misc/guc.c
@@ -88,6 +88,7 @@
#include "utils/acl.h"
#include "utils/builtins.h"
#include "utils/bytea.h"
+#include "utils/catcache.h"
#include "utils/float.h"
#include "utils/guc_tables.h"
#include "utils/memutils.h"
@@ -3445,6 +3446,17 @@ static struct config_int ConfigureNamesInt[] =
NULL, NULL, NULL
},
+ {
+ {"catalog_cache_prune_min_age", PGC_USERSET, RESOURCES_MEM,
+ gettext_noop("System catalog cache entries that are living unused more than this seconds are considered for removal."),
+ gettext_noop("The value of -1 turns off pruning."),
+ GUC_UNIT_S
+ },
+ &catalog_cache_prune_min_age,
+ -1, -1, INT_MAX,
+ NULL, NULL, NULL
+ },
+
/* End-of-list marker */
{
{NULL, 0, 0, NULL, NULL}, NULL, 0, 0, 0, NULL, NULL, NULL
diff --git a/src/include/utils/catcache.h b/src/include/utils/catcache.h
index ddc2762eb3f..57a15bfec22 100644
--- a/src/include/utils/catcache.h
+++ b/src/include/utils/catcache.h
@@ -22,6 +22,7 @@
#include "access/htup.h"
#include "access/skey.h"
+#include "datatype/timestamp.h"
#include "lib/ilist.h"
#include "utils/relcache.h"
@@ -61,6 +62,7 @@ typedef struct catcache
slist_node cc_next; /* list link */
ScanKeyData cc_skey[CATCACHE_MAXKEYS]; /* precomputed key info for heap
* scans */
+ TimestampTz cc_oldest_ts; /* timestamp (us) of the oldest tuple */
/*
* Keep these at the end, so that compiling catcache.c with CATCACHE_STATS
@@ -119,6 +121,7 @@ typedef struct catctup
bool dead; /* dead but not yet removed? */
bool negative; /* negative cache entry? */
HeapTupleData tuple; /* tuple management header */
+ TimestampTz lastaccess; /* timestamp in us of the last usage */
/*
* The tuple may also be a member of at most one CatCList. (If a single
@@ -189,6 +192,20 @@ typedef struct catcacheheader
/* this extern duplicates utils/memutils.h... */
extern PGDLLIMPORT MemoryContext CacheMemoryContext;
+
+/* for guc.c, not PGDLLPMPORT'ed */
+extern int catalog_cache_prune_min_age;
+
+/* source clock for access timestamp of catcache entries */
+extern TimestampTz catcacheclock;
+
+/* SetCatCacheClock - set catcache timestamp source clock */
+static inline void
+SetCatCacheClock(TimestampTz ts)
+{
+ catcacheclock = ts;
+}
+
extern void CreateCacheMemoryContext(void);
extern CatCache *InitCatCache(int id, Oid reloid, Oid indexoid,
--
2.29.2
--------------38A9F5DCD32945DE1430BC1D--
^ permalink raw reply [nested|flat] 92+ messages in thread
* [PATCH v8 1/1] CatCache expiration feature
@ 2021-01-27 11:08 Heikki Linnakangas <[email protected]>
0 siblings, 0 replies; 92+ messages in thread
From: Heikki Linnakangas @ 2021-01-27 11:08 UTC (permalink / raw)
Author: Kyotaro Horiguchi
---
src/backend/access/transam/xact.c | 3 ++
src/backend/utils/cache/catcache.c | 82 +++++++++++++++++++++++++++++-
src/backend/utils/misc/guc.c | 12 +++++
src/include/utils/catcache.h | 17 +++++++
4 files changed, 112 insertions(+), 2 deletions(-)
diff --git a/src/backend/access/transam/xact.c b/src/backend/access/transam/xact.c
index a2068e3fd45..86888d24091 100644
--- a/src/backend/access/transam/xact.c
+++ b/src/backend/access/transam/xact.c
@@ -1086,6 +1086,9 @@ static void
AtStart_Cache(void)
{
AcceptInvalidationMessages();
+
+ if (xactStartTimestamp != 0)
+ SetCatCacheClock(xactStartTimestamp);
}
/*
diff --git a/src/backend/utils/cache/catcache.c b/src/backend/utils/cache/catcache.c
index fa2b49c676e..e29f825687a 100644
--- a/src/backend/utils/cache/catcache.c
+++ b/src/backend/utils/cache/catcache.c
@@ -38,6 +38,7 @@
#include "utils/rel.h"
#include "utils/resowner_private.h"
#include "utils/syscache.h"
+#include "utils/timestamp.h"
/* #define CACHEDEBUG */ /* turns DEBUG elogs on */
@@ -60,9 +61,18 @@
#define CACHE_elog(...)
#endif
+/*
+ * GUC variable to define the minimum age of entries that will be considered
+ * to be evicted in seconds. -1 to disable the feature.
+ */
+int catalog_cache_prune_min_age = -1;
+
/* Cache management header --- pointer is NULL until created */
static CatCacheHeader *CacheHdr = NULL;
+/* Clock for the last accessed time of a catcache entry. */
+TimestampTz catcacheclock = 0;
+
static inline HeapTuple SearchCatCacheInternal(CatCache *cache,
int nkeys,
Datum v1, Datum v2,
@@ -74,6 +84,7 @@ static pg_noinline HeapTuple SearchCatCacheMiss(CatCache *cache,
Index hashIndex,
Datum v1, Datum v2,
Datum v3, Datum v4);
+static bool CatCacheCleanupOldEntries(CatCache *cp);
static uint32 CatalogCacheComputeHashValue(CatCache *cache, int nkeys,
Datum v1, Datum v2, Datum v3, Datum v4);
@@ -99,7 +110,6 @@ static void CatCacheFreeKeys(TupleDesc tupdesc, int nkeys, int *attnos,
static void CatCacheCopyKeys(TupleDesc tupdesc, int nkeys, int *attnos,
Datum *srckeys, Datum *dstkeys);
-
/*
* internal support functions
*/
@@ -1264,6 +1274,9 @@ SearchCatCacheInternal(CatCache *cache,
*/
dlist_move_head(bucket, &ct->cache_elem);
+ /* Record the last access timestamp */
+ ct->lastaccess = catcacheclock;
+
/*
* If it's a positive entry, bump its refcount and return it. If it's
* negative, we can report failure to the caller.
@@ -1425,6 +1438,66 @@ SearchCatCacheMiss(CatCache *cache,
return &ct->tuple;
}
+/*
+ * CatCacheCleanupOldEntries - Remove infrequently-used entries
+ *
+ * Catcache entries happen to be left unused for a long time for several
+ * reasons. Remove such entries to prevent catcache from bloating. It is based
+ * on the similar algorithm with buffer eviction. Entries that are accessed
+ * several times in a certain period live longer than those that have had less
+ * access in the same duration.
+ */
+static bool
+CatCacheCleanupOldEntries(CatCache *cp)
+{
+ int nremoved = 0;
+ int i;
+ TimestampTz oldest_ts = catcacheclock;
+ TimestampTz prune_threshold;
+
+ if (catalog_cache_prune_min_age < 0)
+ return false;
+
+ prune_threshold = catcacheclock - ((int64) catalog_cache_prune_min_age) * USECS_PER_SEC;
+
+ /* Scan over the whole hash to find entries to remove */
+ for (i = 0 ; i < cp->cc_nbuckets ; i++)
+ {
+ dlist_mutable_iter iter;
+
+ dlist_foreach_modify(iter, &cp->cc_bucket[i])
+ {
+ CatCTup *ct = dlist_container(CatCTup, cache_elem, iter.cur);
+
+ /* Don't remove referenced entries */
+ if (ct->refcount == 0 &&
+ (ct->c_list == NULL || ct->c_list->refcount == 0))
+ {
+ if (ct->lastaccess < prune_threshold)
+ {
+ CatCacheRemoveCTup(cp, ct);
+ nremoved++;
+
+ /* don't let the removed entry update oldest_ts */
+ continue;
+ }
+ }
+
+ /* update the oldest timestamp if the entry remains alive */
+ if (ct->lastaccess < oldest_ts)
+ oldest_ts = ct->lastaccess;
+ }
+ }
+
+ cp->cc_oldest_ts = oldest_ts;
+
+ if (nremoved > 0)
+ elog(DEBUG1, "pruning catalog cache id=%d for %s: removed %d / %d",
+ cp->id, cp->cc_relname, nremoved, cp->cc_ntup + nremoved);
+
+ return nremoved > 0;
+}
+
/*
* ReleaseCatCache
*
@@ -1888,6 +1961,7 @@ CatalogCacheCreateEntry(CatCache *cache, HeapTuple ntp, Datum *arguments,
ct->dead = false;
ct->negative = negative;
ct->hash_value = hashValue;
+ ct->lastaccess = catcacheclock;
dlist_push_head(&cache->cc_bucket[hashIndex], &ct->cache_elem);
@@ -1899,7 +1973,11 @@ CatalogCacheCreateEntry(CatCache *cache, HeapTuple ntp, Datum *arguments,
* arbitrarily, we enlarge when fill factor > 2.
*/
if (cache->cc_ntup > cache->cc_nbuckets * 2)
- RehashCatCache(cache);
+ {
+ /* try removing old entries before expanding hash */
+ if (!CatCacheCleanupOldEntries(cache))
+ RehashCatCache(cache);
+ }
return ct;
}
diff --git a/src/backend/utils/misc/guc.c b/src/backend/utils/misc/guc.c
index 17579eeaca9..f92f0513a6f 100644
--- a/src/backend/utils/misc/guc.c
+++ b/src/backend/utils/misc/guc.c
@@ -88,6 +88,7 @@
#include "utils/acl.h"
#include "utils/builtins.h"
#include "utils/bytea.h"
+#include "utils/catcache.h"
#include "utils/float.h"
#include "utils/guc_tables.h"
#include "utils/memutils.h"
@@ -3445,6 +3446,17 @@ static struct config_int ConfigureNamesInt[] =
NULL, NULL, NULL
},
+ {
+ {"catalog_cache_prune_min_age", PGC_USERSET, RESOURCES_MEM,
+ gettext_noop("System catalog cache entries that are living unused more than this seconds are considered for removal."),
+ gettext_noop("The value of -1 turns off pruning."),
+ GUC_UNIT_S
+ },
+ &catalog_cache_prune_min_age,
+ -1, -1, INT_MAX,
+ NULL, NULL, NULL
+ },
+
/* End-of-list marker */
{
{NULL, 0, 0, NULL, NULL}, NULL, 0, 0, 0, NULL, NULL, NULL
diff --git a/src/include/utils/catcache.h b/src/include/utils/catcache.h
index ddc2762eb3f..57a15bfec22 100644
--- a/src/include/utils/catcache.h
+++ b/src/include/utils/catcache.h
@@ -22,6 +22,7 @@
#include "access/htup.h"
#include "access/skey.h"
+#include "datatype/timestamp.h"
#include "lib/ilist.h"
#include "utils/relcache.h"
@@ -61,6 +62,7 @@ typedef struct catcache
slist_node cc_next; /* list link */
ScanKeyData cc_skey[CATCACHE_MAXKEYS]; /* precomputed key info for heap
* scans */
+ TimestampTz cc_oldest_ts; /* timestamp (us) of the oldest tuple */
/*
* Keep these at the end, so that compiling catcache.c with CATCACHE_STATS
@@ -119,6 +121,7 @@ typedef struct catctup
bool dead; /* dead but not yet removed? */
bool negative; /* negative cache entry? */
HeapTupleData tuple; /* tuple management header */
+ TimestampTz lastaccess; /* timestamp in us of the last usage */
/*
* The tuple may also be a member of at most one CatCList. (If a single
@@ -189,6 +192,20 @@ typedef struct catcacheheader
/* this extern duplicates utils/memutils.h... */
extern PGDLLIMPORT MemoryContext CacheMemoryContext;
+
+/* for guc.c, not PGDLLPMPORT'ed */
+extern int catalog_cache_prune_min_age;
+
+/* source clock for access timestamp of catcache entries */
+extern TimestampTz catcacheclock;
+
+/* SetCatCacheClock - set catcache timestamp source clock */
+static inline void
+SetCatCacheClock(TimestampTz ts)
+{
+ catcacheclock = ts;
+}
+
extern void CreateCacheMemoryContext(void);
extern CatCache *InitCatCache(int id, Oid reloid, Oid indexoid,
--
2.29.2
--------------38A9F5DCD32945DE1430BC1D--
^ permalink raw reply [nested|flat] 92+ messages in thread
* [PATCH v8 1/1] CatCache expiration feature
@ 2021-01-27 11:08 Heikki Linnakangas <[email protected]>
0 siblings, 0 replies; 92+ messages in thread
From: Heikki Linnakangas @ 2021-01-27 11:08 UTC (permalink / raw)
Author: Kyotaro Horiguchi
---
src/backend/access/transam/xact.c | 3 ++
src/backend/utils/cache/catcache.c | 82 +++++++++++++++++++++++++++++-
src/backend/utils/misc/guc.c | 12 +++++
src/include/utils/catcache.h | 17 +++++++
4 files changed, 112 insertions(+), 2 deletions(-)
diff --git a/src/backend/access/transam/xact.c b/src/backend/access/transam/xact.c
index a2068e3fd45..86888d24091 100644
--- a/src/backend/access/transam/xact.c
+++ b/src/backend/access/transam/xact.c
@@ -1086,6 +1086,9 @@ static void
AtStart_Cache(void)
{
AcceptInvalidationMessages();
+
+ if (xactStartTimestamp != 0)
+ SetCatCacheClock(xactStartTimestamp);
}
/*
diff --git a/src/backend/utils/cache/catcache.c b/src/backend/utils/cache/catcache.c
index fa2b49c676e..e29f825687a 100644
--- a/src/backend/utils/cache/catcache.c
+++ b/src/backend/utils/cache/catcache.c
@@ -38,6 +38,7 @@
#include "utils/rel.h"
#include "utils/resowner_private.h"
#include "utils/syscache.h"
+#include "utils/timestamp.h"
/* #define CACHEDEBUG */ /* turns DEBUG elogs on */
@@ -60,9 +61,18 @@
#define CACHE_elog(...)
#endif
+/*
+ * GUC variable to define the minimum age of entries that will be considered
+ * to be evicted in seconds. -1 to disable the feature.
+ */
+int catalog_cache_prune_min_age = -1;
+
/* Cache management header --- pointer is NULL until created */
static CatCacheHeader *CacheHdr = NULL;
+/* Clock for the last accessed time of a catcache entry. */
+TimestampTz catcacheclock = 0;
+
static inline HeapTuple SearchCatCacheInternal(CatCache *cache,
int nkeys,
Datum v1, Datum v2,
@@ -74,6 +84,7 @@ static pg_noinline HeapTuple SearchCatCacheMiss(CatCache *cache,
Index hashIndex,
Datum v1, Datum v2,
Datum v3, Datum v4);
+static bool CatCacheCleanupOldEntries(CatCache *cp);
static uint32 CatalogCacheComputeHashValue(CatCache *cache, int nkeys,
Datum v1, Datum v2, Datum v3, Datum v4);
@@ -99,7 +110,6 @@ static void CatCacheFreeKeys(TupleDesc tupdesc, int nkeys, int *attnos,
static void CatCacheCopyKeys(TupleDesc tupdesc, int nkeys, int *attnos,
Datum *srckeys, Datum *dstkeys);
-
/*
* internal support functions
*/
@@ -1264,6 +1274,9 @@ SearchCatCacheInternal(CatCache *cache,
*/
dlist_move_head(bucket, &ct->cache_elem);
+ /* Record the last access timestamp */
+ ct->lastaccess = catcacheclock;
+
/*
* If it's a positive entry, bump its refcount and return it. If it's
* negative, we can report failure to the caller.
@@ -1425,6 +1438,66 @@ SearchCatCacheMiss(CatCache *cache,
return &ct->tuple;
}
+/*
+ * CatCacheCleanupOldEntries - Remove infrequently-used entries
+ *
+ * Catcache entries happen to be left unused for a long time for several
+ * reasons. Remove such entries to prevent catcache from bloating. It is based
+ * on the similar algorithm with buffer eviction. Entries that are accessed
+ * several times in a certain period live longer than those that have had less
+ * access in the same duration.
+ */
+static bool
+CatCacheCleanupOldEntries(CatCache *cp)
+{
+ int nremoved = 0;
+ int i;
+ TimestampTz oldest_ts = catcacheclock;
+ TimestampTz prune_threshold;
+
+ if (catalog_cache_prune_min_age < 0)
+ return false;
+
+ prune_threshold = catcacheclock - ((int64) catalog_cache_prune_min_age) * USECS_PER_SEC;
+
+ /* Scan over the whole hash to find entries to remove */
+ for (i = 0 ; i < cp->cc_nbuckets ; i++)
+ {
+ dlist_mutable_iter iter;
+
+ dlist_foreach_modify(iter, &cp->cc_bucket[i])
+ {
+ CatCTup *ct = dlist_container(CatCTup, cache_elem, iter.cur);
+
+ /* Don't remove referenced entries */
+ if (ct->refcount == 0 &&
+ (ct->c_list == NULL || ct->c_list->refcount == 0))
+ {
+ if (ct->lastaccess < prune_threshold)
+ {
+ CatCacheRemoveCTup(cp, ct);
+ nremoved++;
+
+ /* don't let the removed entry update oldest_ts */
+ continue;
+ }
+ }
+
+ /* update the oldest timestamp if the entry remains alive */
+ if (ct->lastaccess < oldest_ts)
+ oldest_ts = ct->lastaccess;
+ }
+ }
+
+ cp->cc_oldest_ts = oldest_ts;
+
+ if (nremoved > 0)
+ elog(DEBUG1, "pruning catalog cache id=%d for %s: removed %d / %d",
+ cp->id, cp->cc_relname, nremoved, cp->cc_ntup + nremoved);
+
+ return nremoved > 0;
+}
+
/*
* ReleaseCatCache
*
@@ -1888,6 +1961,7 @@ CatalogCacheCreateEntry(CatCache *cache, HeapTuple ntp, Datum *arguments,
ct->dead = false;
ct->negative = negative;
ct->hash_value = hashValue;
+ ct->lastaccess = catcacheclock;
dlist_push_head(&cache->cc_bucket[hashIndex], &ct->cache_elem);
@@ -1899,7 +1973,11 @@ CatalogCacheCreateEntry(CatCache *cache, HeapTuple ntp, Datum *arguments,
* arbitrarily, we enlarge when fill factor > 2.
*/
if (cache->cc_ntup > cache->cc_nbuckets * 2)
- RehashCatCache(cache);
+ {
+ /* try removing old entries before expanding hash */
+ if (!CatCacheCleanupOldEntries(cache))
+ RehashCatCache(cache);
+ }
return ct;
}
diff --git a/src/backend/utils/misc/guc.c b/src/backend/utils/misc/guc.c
index 17579eeaca9..f92f0513a6f 100644
--- a/src/backend/utils/misc/guc.c
+++ b/src/backend/utils/misc/guc.c
@@ -88,6 +88,7 @@
#include "utils/acl.h"
#include "utils/builtins.h"
#include "utils/bytea.h"
+#include "utils/catcache.h"
#include "utils/float.h"
#include "utils/guc_tables.h"
#include "utils/memutils.h"
@@ -3445,6 +3446,17 @@ static struct config_int ConfigureNamesInt[] =
NULL, NULL, NULL
},
+ {
+ {"catalog_cache_prune_min_age", PGC_USERSET, RESOURCES_MEM,
+ gettext_noop("System catalog cache entries that are living unused more than this seconds are considered for removal."),
+ gettext_noop("The value of -1 turns off pruning."),
+ GUC_UNIT_S
+ },
+ &catalog_cache_prune_min_age,
+ -1, -1, INT_MAX,
+ NULL, NULL, NULL
+ },
+
/* End-of-list marker */
{
{NULL, 0, 0, NULL, NULL}, NULL, 0, 0, 0, NULL, NULL, NULL
diff --git a/src/include/utils/catcache.h b/src/include/utils/catcache.h
index ddc2762eb3f..57a15bfec22 100644
--- a/src/include/utils/catcache.h
+++ b/src/include/utils/catcache.h
@@ -22,6 +22,7 @@
#include "access/htup.h"
#include "access/skey.h"
+#include "datatype/timestamp.h"
#include "lib/ilist.h"
#include "utils/relcache.h"
@@ -61,6 +62,7 @@ typedef struct catcache
slist_node cc_next; /* list link */
ScanKeyData cc_skey[CATCACHE_MAXKEYS]; /* precomputed key info for heap
* scans */
+ TimestampTz cc_oldest_ts; /* timestamp (us) of the oldest tuple */
/*
* Keep these at the end, so that compiling catcache.c with CATCACHE_STATS
@@ -119,6 +121,7 @@ typedef struct catctup
bool dead; /* dead but not yet removed? */
bool negative; /* negative cache entry? */
HeapTupleData tuple; /* tuple management header */
+ TimestampTz lastaccess; /* timestamp in us of the last usage */
/*
* The tuple may also be a member of at most one CatCList. (If a single
@@ -189,6 +192,20 @@ typedef struct catcacheheader
/* this extern duplicates utils/memutils.h... */
extern PGDLLIMPORT MemoryContext CacheMemoryContext;
+
+/* for guc.c, not PGDLLPMPORT'ed */
+extern int catalog_cache_prune_min_age;
+
+/* source clock for access timestamp of catcache entries */
+extern TimestampTz catcacheclock;
+
+/* SetCatCacheClock - set catcache timestamp source clock */
+static inline void
+SetCatCacheClock(TimestampTz ts)
+{
+ catcacheclock = ts;
+}
+
extern void CreateCacheMemoryContext(void);
extern CatCache *InitCatCache(int id, Oid reloid, Oid indexoid,
--
2.29.2
--------------38A9F5DCD32945DE1430BC1D--
^ permalink raw reply [nested|flat] 92+ messages in thread
* [PATCH v8 1/1] CatCache expiration feature
@ 2021-01-27 11:08 Heikki Linnakangas <[email protected]>
0 siblings, 0 replies; 92+ messages in thread
From: Heikki Linnakangas @ 2021-01-27 11:08 UTC (permalink / raw)
Author: Kyotaro Horiguchi
---
src/backend/access/transam/xact.c | 3 ++
src/backend/utils/cache/catcache.c | 82 +++++++++++++++++++++++++++++-
src/backend/utils/misc/guc.c | 12 +++++
src/include/utils/catcache.h | 17 +++++++
4 files changed, 112 insertions(+), 2 deletions(-)
diff --git a/src/backend/access/transam/xact.c b/src/backend/access/transam/xact.c
index a2068e3fd45..86888d24091 100644
--- a/src/backend/access/transam/xact.c
+++ b/src/backend/access/transam/xact.c
@@ -1086,6 +1086,9 @@ static void
AtStart_Cache(void)
{
AcceptInvalidationMessages();
+
+ if (xactStartTimestamp != 0)
+ SetCatCacheClock(xactStartTimestamp);
}
/*
diff --git a/src/backend/utils/cache/catcache.c b/src/backend/utils/cache/catcache.c
index fa2b49c676e..e29f825687a 100644
--- a/src/backend/utils/cache/catcache.c
+++ b/src/backend/utils/cache/catcache.c
@@ -38,6 +38,7 @@
#include "utils/rel.h"
#include "utils/resowner_private.h"
#include "utils/syscache.h"
+#include "utils/timestamp.h"
/* #define CACHEDEBUG */ /* turns DEBUG elogs on */
@@ -60,9 +61,18 @@
#define CACHE_elog(...)
#endif
+/*
+ * GUC variable to define the minimum age of entries that will be considered
+ * to be evicted in seconds. -1 to disable the feature.
+ */
+int catalog_cache_prune_min_age = -1;
+
/* Cache management header --- pointer is NULL until created */
static CatCacheHeader *CacheHdr = NULL;
+/* Clock for the last accessed time of a catcache entry. */
+TimestampTz catcacheclock = 0;
+
static inline HeapTuple SearchCatCacheInternal(CatCache *cache,
int nkeys,
Datum v1, Datum v2,
@@ -74,6 +84,7 @@ static pg_noinline HeapTuple SearchCatCacheMiss(CatCache *cache,
Index hashIndex,
Datum v1, Datum v2,
Datum v3, Datum v4);
+static bool CatCacheCleanupOldEntries(CatCache *cp);
static uint32 CatalogCacheComputeHashValue(CatCache *cache, int nkeys,
Datum v1, Datum v2, Datum v3, Datum v4);
@@ -99,7 +110,6 @@ static void CatCacheFreeKeys(TupleDesc tupdesc, int nkeys, int *attnos,
static void CatCacheCopyKeys(TupleDesc tupdesc, int nkeys, int *attnos,
Datum *srckeys, Datum *dstkeys);
-
/*
* internal support functions
*/
@@ -1264,6 +1274,9 @@ SearchCatCacheInternal(CatCache *cache,
*/
dlist_move_head(bucket, &ct->cache_elem);
+ /* Record the last access timestamp */
+ ct->lastaccess = catcacheclock;
+
/*
* If it's a positive entry, bump its refcount and return it. If it's
* negative, we can report failure to the caller.
@@ -1425,6 +1438,66 @@ SearchCatCacheMiss(CatCache *cache,
return &ct->tuple;
}
+/*
+ * CatCacheCleanupOldEntries - Remove infrequently-used entries
+ *
+ * Catcache entries happen to be left unused for a long time for several
+ * reasons. Remove such entries to prevent catcache from bloating. It is based
+ * on the similar algorithm with buffer eviction. Entries that are accessed
+ * several times in a certain period live longer than those that have had less
+ * access in the same duration.
+ */
+static bool
+CatCacheCleanupOldEntries(CatCache *cp)
+{
+ int nremoved = 0;
+ int i;
+ TimestampTz oldest_ts = catcacheclock;
+ TimestampTz prune_threshold;
+
+ if (catalog_cache_prune_min_age < 0)
+ return false;
+
+ prune_threshold = catcacheclock - ((int64) catalog_cache_prune_min_age) * USECS_PER_SEC;
+
+ /* Scan over the whole hash to find entries to remove */
+ for (i = 0 ; i < cp->cc_nbuckets ; i++)
+ {
+ dlist_mutable_iter iter;
+
+ dlist_foreach_modify(iter, &cp->cc_bucket[i])
+ {
+ CatCTup *ct = dlist_container(CatCTup, cache_elem, iter.cur);
+
+ /* Don't remove referenced entries */
+ if (ct->refcount == 0 &&
+ (ct->c_list == NULL || ct->c_list->refcount == 0))
+ {
+ if (ct->lastaccess < prune_threshold)
+ {
+ CatCacheRemoveCTup(cp, ct);
+ nremoved++;
+
+ /* don't let the removed entry update oldest_ts */
+ continue;
+ }
+ }
+
+ /* update the oldest timestamp if the entry remains alive */
+ if (ct->lastaccess < oldest_ts)
+ oldest_ts = ct->lastaccess;
+ }
+ }
+
+ cp->cc_oldest_ts = oldest_ts;
+
+ if (nremoved > 0)
+ elog(DEBUG1, "pruning catalog cache id=%d for %s: removed %d / %d",
+ cp->id, cp->cc_relname, nremoved, cp->cc_ntup + nremoved);
+
+ return nremoved > 0;
+}
+
/*
* ReleaseCatCache
*
@@ -1888,6 +1961,7 @@ CatalogCacheCreateEntry(CatCache *cache, HeapTuple ntp, Datum *arguments,
ct->dead = false;
ct->negative = negative;
ct->hash_value = hashValue;
+ ct->lastaccess = catcacheclock;
dlist_push_head(&cache->cc_bucket[hashIndex], &ct->cache_elem);
@@ -1899,7 +1973,11 @@ CatalogCacheCreateEntry(CatCache *cache, HeapTuple ntp, Datum *arguments,
* arbitrarily, we enlarge when fill factor > 2.
*/
if (cache->cc_ntup > cache->cc_nbuckets * 2)
- RehashCatCache(cache);
+ {
+ /* try removing old entries before expanding hash */
+ if (!CatCacheCleanupOldEntries(cache))
+ RehashCatCache(cache);
+ }
return ct;
}
diff --git a/src/backend/utils/misc/guc.c b/src/backend/utils/misc/guc.c
index 17579eeaca9..f92f0513a6f 100644
--- a/src/backend/utils/misc/guc.c
+++ b/src/backend/utils/misc/guc.c
@@ -88,6 +88,7 @@
#include "utils/acl.h"
#include "utils/builtins.h"
#include "utils/bytea.h"
+#include "utils/catcache.h"
#include "utils/float.h"
#include "utils/guc_tables.h"
#include "utils/memutils.h"
@@ -3445,6 +3446,17 @@ static struct config_int ConfigureNamesInt[] =
NULL, NULL, NULL
},
+ {
+ {"catalog_cache_prune_min_age", PGC_USERSET, RESOURCES_MEM,
+ gettext_noop("System catalog cache entries that are living unused more than this seconds are considered for removal."),
+ gettext_noop("The value of -1 turns off pruning."),
+ GUC_UNIT_S
+ },
+ &catalog_cache_prune_min_age,
+ -1, -1, INT_MAX,
+ NULL, NULL, NULL
+ },
+
/* End-of-list marker */
{
{NULL, 0, 0, NULL, NULL}, NULL, 0, 0, 0, NULL, NULL, NULL
diff --git a/src/include/utils/catcache.h b/src/include/utils/catcache.h
index ddc2762eb3f..57a15bfec22 100644
--- a/src/include/utils/catcache.h
+++ b/src/include/utils/catcache.h
@@ -22,6 +22,7 @@
#include "access/htup.h"
#include "access/skey.h"
+#include "datatype/timestamp.h"
#include "lib/ilist.h"
#include "utils/relcache.h"
@@ -61,6 +62,7 @@ typedef struct catcache
slist_node cc_next; /* list link */
ScanKeyData cc_skey[CATCACHE_MAXKEYS]; /* precomputed key info for heap
* scans */
+ TimestampTz cc_oldest_ts; /* timestamp (us) of the oldest tuple */
/*
* Keep these at the end, so that compiling catcache.c with CATCACHE_STATS
@@ -119,6 +121,7 @@ typedef struct catctup
bool dead; /* dead but not yet removed? */
bool negative; /* negative cache entry? */
HeapTupleData tuple; /* tuple management header */
+ TimestampTz lastaccess; /* timestamp in us of the last usage */
/*
* The tuple may also be a member of at most one CatCList. (If a single
@@ -189,6 +192,20 @@ typedef struct catcacheheader
/* this extern duplicates utils/memutils.h... */
extern PGDLLIMPORT MemoryContext CacheMemoryContext;
+
+/* for guc.c, not PGDLLPMPORT'ed */
+extern int catalog_cache_prune_min_age;
+
+/* source clock for access timestamp of catcache entries */
+extern TimestampTz catcacheclock;
+
+/* SetCatCacheClock - set catcache timestamp source clock */
+static inline void
+SetCatCacheClock(TimestampTz ts)
+{
+ catcacheclock = ts;
+}
+
extern void CreateCacheMemoryContext(void);
extern CatCache *InitCatCache(int id, Oid reloid, Oid indexoid,
--
2.29.2
--------------38A9F5DCD32945DE1430BC1D--
^ permalink raw reply [nested|flat] 92+ messages in thread
* [PATCH v8 1/1] CatCache expiration feature
@ 2021-01-27 11:08 Heikki Linnakangas <[email protected]>
0 siblings, 0 replies; 92+ messages in thread
From: Heikki Linnakangas @ 2021-01-27 11:08 UTC (permalink / raw)
Author: Kyotaro Horiguchi
---
src/backend/access/transam/xact.c | 3 ++
src/backend/utils/cache/catcache.c | 82 +++++++++++++++++++++++++++++-
src/backend/utils/misc/guc.c | 12 +++++
src/include/utils/catcache.h | 17 +++++++
4 files changed, 112 insertions(+), 2 deletions(-)
diff --git a/src/backend/access/transam/xact.c b/src/backend/access/transam/xact.c
index a2068e3fd45..86888d24091 100644
--- a/src/backend/access/transam/xact.c
+++ b/src/backend/access/transam/xact.c
@@ -1086,6 +1086,9 @@ static void
AtStart_Cache(void)
{
AcceptInvalidationMessages();
+
+ if (xactStartTimestamp != 0)
+ SetCatCacheClock(xactStartTimestamp);
}
/*
diff --git a/src/backend/utils/cache/catcache.c b/src/backend/utils/cache/catcache.c
index fa2b49c676e..e29f825687a 100644
--- a/src/backend/utils/cache/catcache.c
+++ b/src/backend/utils/cache/catcache.c
@@ -38,6 +38,7 @@
#include "utils/rel.h"
#include "utils/resowner_private.h"
#include "utils/syscache.h"
+#include "utils/timestamp.h"
/* #define CACHEDEBUG */ /* turns DEBUG elogs on */
@@ -60,9 +61,18 @@
#define CACHE_elog(...)
#endif
+/*
+ * GUC variable to define the minimum age of entries that will be considered
+ * to be evicted in seconds. -1 to disable the feature.
+ */
+int catalog_cache_prune_min_age = -1;
+
/* Cache management header --- pointer is NULL until created */
static CatCacheHeader *CacheHdr = NULL;
+/* Clock for the last accessed time of a catcache entry. */
+TimestampTz catcacheclock = 0;
+
static inline HeapTuple SearchCatCacheInternal(CatCache *cache,
int nkeys,
Datum v1, Datum v2,
@@ -74,6 +84,7 @@ static pg_noinline HeapTuple SearchCatCacheMiss(CatCache *cache,
Index hashIndex,
Datum v1, Datum v2,
Datum v3, Datum v4);
+static bool CatCacheCleanupOldEntries(CatCache *cp);
static uint32 CatalogCacheComputeHashValue(CatCache *cache, int nkeys,
Datum v1, Datum v2, Datum v3, Datum v4);
@@ -99,7 +110,6 @@ static void CatCacheFreeKeys(TupleDesc tupdesc, int nkeys, int *attnos,
static void CatCacheCopyKeys(TupleDesc tupdesc, int nkeys, int *attnos,
Datum *srckeys, Datum *dstkeys);
-
/*
* internal support functions
*/
@@ -1264,6 +1274,9 @@ SearchCatCacheInternal(CatCache *cache,
*/
dlist_move_head(bucket, &ct->cache_elem);
+ /* Record the last access timestamp */
+ ct->lastaccess = catcacheclock;
+
/*
* If it's a positive entry, bump its refcount and return it. If it's
* negative, we can report failure to the caller.
@@ -1425,6 +1438,66 @@ SearchCatCacheMiss(CatCache *cache,
return &ct->tuple;
}
+/*
+ * CatCacheCleanupOldEntries - Remove infrequently-used entries
+ *
+ * Catcache entries happen to be left unused for a long time for several
+ * reasons. Remove such entries to prevent catcache from bloating. It is based
+ * on the similar algorithm with buffer eviction. Entries that are accessed
+ * several times in a certain period live longer than those that have had less
+ * access in the same duration.
+ */
+static bool
+CatCacheCleanupOldEntries(CatCache *cp)
+{
+ int nremoved = 0;
+ int i;
+ TimestampTz oldest_ts = catcacheclock;
+ TimestampTz prune_threshold;
+
+ if (catalog_cache_prune_min_age < 0)
+ return false;
+
+ prune_threshold = catcacheclock - ((int64) catalog_cache_prune_min_age) * USECS_PER_SEC;
+
+ /* Scan over the whole hash to find entries to remove */
+ for (i = 0 ; i < cp->cc_nbuckets ; i++)
+ {
+ dlist_mutable_iter iter;
+
+ dlist_foreach_modify(iter, &cp->cc_bucket[i])
+ {
+ CatCTup *ct = dlist_container(CatCTup, cache_elem, iter.cur);
+
+ /* Don't remove referenced entries */
+ if (ct->refcount == 0 &&
+ (ct->c_list == NULL || ct->c_list->refcount == 0))
+ {
+ if (ct->lastaccess < prune_threshold)
+ {
+ CatCacheRemoveCTup(cp, ct);
+ nremoved++;
+
+ /* don't let the removed entry update oldest_ts */
+ continue;
+ }
+ }
+
+ /* update the oldest timestamp if the entry remains alive */
+ if (ct->lastaccess < oldest_ts)
+ oldest_ts = ct->lastaccess;
+ }
+ }
+
+ cp->cc_oldest_ts = oldest_ts;
+
+ if (nremoved > 0)
+ elog(DEBUG1, "pruning catalog cache id=%d for %s: removed %d / %d",
+ cp->id, cp->cc_relname, nremoved, cp->cc_ntup + nremoved);
+
+ return nremoved > 0;
+}
+
/*
* ReleaseCatCache
*
@@ -1888,6 +1961,7 @@ CatalogCacheCreateEntry(CatCache *cache, HeapTuple ntp, Datum *arguments,
ct->dead = false;
ct->negative = negative;
ct->hash_value = hashValue;
+ ct->lastaccess = catcacheclock;
dlist_push_head(&cache->cc_bucket[hashIndex], &ct->cache_elem);
@@ -1899,7 +1973,11 @@ CatalogCacheCreateEntry(CatCache *cache, HeapTuple ntp, Datum *arguments,
* arbitrarily, we enlarge when fill factor > 2.
*/
if (cache->cc_ntup > cache->cc_nbuckets * 2)
- RehashCatCache(cache);
+ {
+ /* try removing old entries before expanding hash */
+ if (!CatCacheCleanupOldEntries(cache))
+ RehashCatCache(cache);
+ }
return ct;
}
diff --git a/src/backend/utils/misc/guc.c b/src/backend/utils/misc/guc.c
index 17579eeaca9..f92f0513a6f 100644
--- a/src/backend/utils/misc/guc.c
+++ b/src/backend/utils/misc/guc.c
@@ -88,6 +88,7 @@
#include "utils/acl.h"
#include "utils/builtins.h"
#include "utils/bytea.h"
+#include "utils/catcache.h"
#include "utils/float.h"
#include "utils/guc_tables.h"
#include "utils/memutils.h"
@@ -3445,6 +3446,17 @@ static struct config_int ConfigureNamesInt[] =
NULL, NULL, NULL
},
+ {
+ {"catalog_cache_prune_min_age", PGC_USERSET, RESOURCES_MEM,
+ gettext_noop("System catalog cache entries that are living unused more than this seconds are considered for removal."),
+ gettext_noop("The value of -1 turns off pruning."),
+ GUC_UNIT_S
+ },
+ &catalog_cache_prune_min_age,
+ -1, -1, INT_MAX,
+ NULL, NULL, NULL
+ },
+
/* End-of-list marker */
{
{NULL, 0, 0, NULL, NULL}, NULL, 0, 0, 0, NULL, NULL, NULL
diff --git a/src/include/utils/catcache.h b/src/include/utils/catcache.h
index ddc2762eb3f..57a15bfec22 100644
--- a/src/include/utils/catcache.h
+++ b/src/include/utils/catcache.h
@@ -22,6 +22,7 @@
#include "access/htup.h"
#include "access/skey.h"
+#include "datatype/timestamp.h"
#include "lib/ilist.h"
#include "utils/relcache.h"
@@ -61,6 +62,7 @@ typedef struct catcache
slist_node cc_next; /* list link */
ScanKeyData cc_skey[CATCACHE_MAXKEYS]; /* precomputed key info for heap
* scans */
+ TimestampTz cc_oldest_ts; /* timestamp (us) of the oldest tuple */
/*
* Keep these at the end, so that compiling catcache.c with CATCACHE_STATS
@@ -119,6 +121,7 @@ typedef struct catctup
bool dead; /* dead but not yet removed? */
bool negative; /* negative cache entry? */
HeapTupleData tuple; /* tuple management header */
+ TimestampTz lastaccess; /* timestamp in us of the last usage */
/*
* The tuple may also be a member of at most one CatCList. (If a single
@@ -189,6 +192,20 @@ typedef struct catcacheheader
/* this extern duplicates utils/memutils.h... */
extern PGDLLIMPORT MemoryContext CacheMemoryContext;
+
+/* for guc.c, not PGDLLPMPORT'ed */
+extern int catalog_cache_prune_min_age;
+
+/* source clock for access timestamp of catcache entries */
+extern TimestampTz catcacheclock;
+
+/* SetCatCacheClock - set catcache timestamp source clock */
+static inline void
+SetCatCacheClock(TimestampTz ts)
+{
+ catcacheclock = ts;
+}
+
extern void CreateCacheMemoryContext(void);
extern CatCache *InitCatCache(int id, Oid reloid, Oid indexoid,
--
2.29.2
--------------38A9F5DCD32945DE1430BC1D--
^ permalink raw reply [nested|flat] 92+ messages in thread
* [PATCH v8 1/1] CatCache expiration feature
@ 2021-01-27 11:08 Heikki Linnakangas <[email protected]>
0 siblings, 0 replies; 92+ messages in thread
From: Heikki Linnakangas @ 2021-01-27 11:08 UTC (permalink / raw)
Author: Kyotaro Horiguchi
---
src/backend/access/transam/xact.c | 3 ++
src/backend/utils/cache/catcache.c | 82 +++++++++++++++++++++++++++++-
src/backend/utils/misc/guc.c | 12 +++++
src/include/utils/catcache.h | 17 +++++++
4 files changed, 112 insertions(+), 2 deletions(-)
diff --git a/src/backend/access/transam/xact.c b/src/backend/access/transam/xact.c
index a2068e3fd45..86888d24091 100644
--- a/src/backend/access/transam/xact.c
+++ b/src/backend/access/transam/xact.c
@@ -1086,6 +1086,9 @@ static void
AtStart_Cache(void)
{
AcceptInvalidationMessages();
+
+ if (xactStartTimestamp != 0)
+ SetCatCacheClock(xactStartTimestamp);
}
/*
diff --git a/src/backend/utils/cache/catcache.c b/src/backend/utils/cache/catcache.c
index fa2b49c676e..e29f825687a 100644
--- a/src/backend/utils/cache/catcache.c
+++ b/src/backend/utils/cache/catcache.c
@@ -38,6 +38,7 @@
#include "utils/rel.h"
#include "utils/resowner_private.h"
#include "utils/syscache.h"
+#include "utils/timestamp.h"
/* #define CACHEDEBUG */ /* turns DEBUG elogs on */
@@ -60,9 +61,18 @@
#define CACHE_elog(...)
#endif
+/*
+ * GUC variable to define the minimum age of entries that will be considered
+ * to be evicted in seconds. -1 to disable the feature.
+ */
+int catalog_cache_prune_min_age = -1;
+
/* Cache management header --- pointer is NULL until created */
static CatCacheHeader *CacheHdr = NULL;
+/* Clock for the last accessed time of a catcache entry. */
+TimestampTz catcacheclock = 0;
+
static inline HeapTuple SearchCatCacheInternal(CatCache *cache,
int nkeys,
Datum v1, Datum v2,
@@ -74,6 +84,7 @@ static pg_noinline HeapTuple SearchCatCacheMiss(CatCache *cache,
Index hashIndex,
Datum v1, Datum v2,
Datum v3, Datum v4);
+static bool CatCacheCleanupOldEntries(CatCache *cp);
static uint32 CatalogCacheComputeHashValue(CatCache *cache, int nkeys,
Datum v1, Datum v2, Datum v3, Datum v4);
@@ -99,7 +110,6 @@ static void CatCacheFreeKeys(TupleDesc tupdesc, int nkeys, int *attnos,
static void CatCacheCopyKeys(TupleDesc tupdesc, int nkeys, int *attnos,
Datum *srckeys, Datum *dstkeys);
-
/*
* internal support functions
*/
@@ -1264,6 +1274,9 @@ SearchCatCacheInternal(CatCache *cache,
*/
dlist_move_head(bucket, &ct->cache_elem);
+ /* Record the last access timestamp */
+ ct->lastaccess = catcacheclock;
+
/*
* If it's a positive entry, bump its refcount and return it. If it's
* negative, we can report failure to the caller.
@@ -1425,6 +1438,66 @@ SearchCatCacheMiss(CatCache *cache,
return &ct->tuple;
}
+/*
+ * CatCacheCleanupOldEntries - Remove infrequently-used entries
+ *
+ * Catcache entries happen to be left unused for a long time for several
+ * reasons. Remove such entries to prevent catcache from bloating. It is based
+ * on the similar algorithm with buffer eviction. Entries that are accessed
+ * several times in a certain period live longer than those that have had less
+ * access in the same duration.
+ */
+static bool
+CatCacheCleanupOldEntries(CatCache *cp)
+{
+ int nremoved = 0;
+ int i;
+ TimestampTz oldest_ts = catcacheclock;
+ TimestampTz prune_threshold;
+
+ if (catalog_cache_prune_min_age < 0)
+ return false;
+
+ prune_threshold = catcacheclock - ((int64) catalog_cache_prune_min_age) * USECS_PER_SEC;
+
+ /* Scan over the whole hash to find entries to remove */
+ for (i = 0 ; i < cp->cc_nbuckets ; i++)
+ {
+ dlist_mutable_iter iter;
+
+ dlist_foreach_modify(iter, &cp->cc_bucket[i])
+ {
+ CatCTup *ct = dlist_container(CatCTup, cache_elem, iter.cur);
+
+ /* Don't remove referenced entries */
+ if (ct->refcount == 0 &&
+ (ct->c_list == NULL || ct->c_list->refcount == 0))
+ {
+ if (ct->lastaccess < prune_threshold)
+ {
+ CatCacheRemoveCTup(cp, ct);
+ nremoved++;
+
+ /* don't let the removed entry update oldest_ts */
+ continue;
+ }
+ }
+
+ /* update the oldest timestamp if the entry remains alive */
+ if (ct->lastaccess < oldest_ts)
+ oldest_ts = ct->lastaccess;
+ }
+ }
+
+ cp->cc_oldest_ts = oldest_ts;
+
+ if (nremoved > 0)
+ elog(DEBUG1, "pruning catalog cache id=%d for %s: removed %d / %d",
+ cp->id, cp->cc_relname, nremoved, cp->cc_ntup + nremoved);
+
+ return nremoved > 0;
+}
+
/*
* ReleaseCatCache
*
@@ -1888,6 +1961,7 @@ CatalogCacheCreateEntry(CatCache *cache, HeapTuple ntp, Datum *arguments,
ct->dead = false;
ct->negative = negative;
ct->hash_value = hashValue;
+ ct->lastaccess = catcacheclock;
dlist_push_head(&cache->cc_bucket[hashIndex], &ct->cache_elem);
@@ -1899,7 +1973,11 @@ CatalogCacheCreateEntry(CatCache *cache, HeapTuple ntp, Datum *arguments,
* arbitrarily, we enlarge when fill factor > 2.
*/
if (cache->cc_ntup > cache->cc_nbuckets * 2)
- RehashCatCache(cache);
+ {
+ /* try removing old entries before expanding hash */
+ if (!CatCacheCleanupOldEntries(cache))
+ RehashCatCache(cache);
+ }
return ct;
}
diff --git a/src/backend/utils/misc/guc.c b/src/backend/utils/misc/guc.c
index 17579eeaca9..f92f0513a6f 100644
--- a/src/backend/utils/misc/guc.c
+++ b/src/backend/utils/misc/guc.c
@@ -88,6 +88,7 @@
#include "utils/acl.h"
#include "utils/builtins.h"
#include "utils/bytea.h"
+#include "utils/catcache.h"
#include "utils/float.h"
#include "utils/guc_tables.h"
#include "utils/memutils.h"
@@ -3445,6 +3446,17 @@ static struct config_int ConfigureNamesInt[] =
NULL, NULL, NULL
},
+ {
+ {"catalog_cache_prune_min_age", PGC_USERSET, RESOURCES_MEM,
+ gettext_noop("System catalog cache entries that are living unused more than this seconds are considered for removal."),
+ gettext_noop("The value of -1 turns off pruning."),
+ GUC_UNIT_S
+ },
+ &catalog_cache_prune_min_age,
+ -1, -1, INT_MAX,
+ NULL, NULL, NULL
+ },
+
/* End-of-list marker */
{
{NULL, 0, 0, NULL, NULL}, NULL, 0, 0, 0, NULL, NULL, NULL
diff --git a/src/include/utils/catcache.h b/src/include/utils/catcache.h
index ddc2762eb3f..57a15bfec22 100644
--- a/src/include/utils/catcache.h
+++ b/src/include/utils/catcache.h
@@ -22,6 +22,7 @@
#include "access/htup.h"
#include "access/skey.h"
+#include "datatype/timestamp.h"
#include "lib/ilist.h"
#include "utils/relcache.h"
@@ -61,6 +62,7 @@ typedef struct catcache
slist_node cc_next; /* list link */
ScanKeyData cc_skey[CATCACHE_MAXKEYS]; /* precomputed key info for heap
* scans */
+ TimestampTz cc_oldest_ts; /* timestamp (us) of the oldest tuple */
/*
* Keep these at the end, so that compiling catcache.c with CATCACHE_STATS
@@ -119,6 +121,7 @@ typedef struct catctup
bool dead; /* dead but not yet removed? */
bool negative; /* negative cache entry? */
HeapTupleData tuple; /* tuple management header */
+ TimestampTz lastaccess; /* timestamp in us of the last usage */
/*
* The tuple may also be a member of at most one CatCList. (If a single
@@ -189,6 +192,20 @@ typedef struct catcacheheader
/* this extern duplicates utils/memutils.h... */
extern PGDLLIMPORT MemoryContext CacheMemoryContext;
+
+/* for guc.c, not PGDLLPMPORT'ed */
+extern int catalog_cache_prune_min_age;
+
+/* source clock for access timestamp of catcache entries */
+extern TimestampTz catcacheclock;
+
+/* SetCatCacheClock - set catcache timestamp source clock */
+static inline void
+SetCatCacheClock(TimestampTz ts)
+{
+ catcacheclock = ts;
+}
+
extern void CreateCacheMemoryContext(void);
extern CatCache *InitCatCache(int id, Oid reloid, Oid indexoid,
--
2.29.2
--------------38A9F5DCD32945DE1430BC1D--
^ permalink raw reply [nested|flat] 92+ messages in thread
* [PATCH v8 1/1] CatCache expiration feature
@ 2021-01-27 11:08 Heikki Linnakangas <[email protected]>
0 siblings, 0 replies; 92+ messages in thread
From: Heikki Linnakangas @ 2021-01-27 11:08 UTC (permalink / raw)
Author: Kyotaro Horiguchi
---
src/backend/access/transam/xact.c | 3 ++
src/backend/utils/cache/catcache.c | 82 +++++++++++++++++++++++++++++-
src/backend/utils/misc/guc.c | 12 +++++
src/include/utils/catcache.h | 17 +++++++
4 files changed, 112 insertions(+), 2 deletions(-)
diff --git a/src/backend/access/transam/xact.c b/src/backend/access/transam/xact.c
index a2068e3fd45..86888d24091 100644
--- a/src/backend/access/transam/xact.c
+++ b/src/backend/access/transam/xact.c
@@ -1086,6 +1086,9 @@ static void
AtStart_Cache(void)
{
AcceptInvalidationMessages();
+
+ if (xactStartTimestamp != 0)
+ SetCatCacheClock(xactStartTimestamp);
}
/*
diff --git a/src/backend/utils/cache/catcache.c b/src/backend/utils/cache/catcache.c
index fa2b49c676e..e29f825687a 100644
--- a/src/backend/utils/cache/catcache.c
+++ b/src/backend/utils/cache/catcache.c
@@ -38,6 +38,7 @@
#include "utils/rel.h"
#include "utils/resowner_private.h"
#include "utils/syscache.h"
+#include "utils/timestamp.h"
/* #define CACHEDEBUG */ /* turns DEBUG elogs on */
@@ -60,9 +61,18 @@
#define CACHE_elog(...)
#endif
+/*
+ * GUC variable to define the minimum age of entries that will be considered
+ * to be evicted in seconds. -1 to disable the feature.
+ */
+int catalog_cache_prune_min_age = -1;
+
/* Cache management header --- pointer is NULL until created */
static CatCacheHeader *CacheHdr = NULL;
+/* Clock for the last accessed time of a catcache entry. */
+TimestampTz catcacheclock = 0;
+
static inline HeapTuple SearchCatCacheInternal(CatCache *cache,
int nkeys,
Datum v1, Datum v2,
@@ -74,6 +84,7 @@ static pg_noinline HeapTuple SearchCatCacheMiss(CatCache *cache,
Index hashIndex,
Datum v1, Datum v2,
Datum v3, Datum v4);
+static bool CatCacheCleanupOldEntries(CatCache *cp);
static uint32 CatalogCacheComputeHashValue(CatCache *cache, int nkeys,
Datum v1, Datum v2, Datum v3, Datum v4);
@@ -99,7 +110,6 @@ static void CatCacheFreeKeys(TupleDesc tupdesc, int nkeys, int *attnos,
static void CatCacheCopyKeys(TupleDesc tupdesc, int nkeys, int *attnos,
Datum *srckeys, Datum *dstkeys);
-
/*
* internal support functions
*/
@@ -1264,6 +1274,9 @@ SearchCatCacheInternal(CatCache *cache,
*/
dlist_move_head(bucket, &ct->cache_elem);
+ /* Record the last access timestamp */
+ ct->lastaccess = catcacheclock;
+
/*
* If it's a positive entry, bump its refcount and return it. If it's
* negative, we can report failure to the caller.
@@ -1425,6 +1438,66 @@ SearchCatCacheMiss(CatCache *cache,
return &ct->tuple;
}
+/*
+ * CatCacheCleanupOldEntries - Remove infrequently-used entries
+ *
+ * Catcache entries happen to be left unused for a long time for several
+ * reasons. Remove such entries to prevent catcache from bloating. It is based
+ * on the similar algorithm with buffer eviction. Entries that are accessed
+ * several times in a certain period live longer than those that have had less
+ * access in the same duration.
+ */
+static bool
+CatCacheCleanupOldEntries(CatCache *cp)
+{
+ int nremoved = 0;
+ int i;
+ TimestampTz oldest_ts = catcacheclock;
+ TimestampTz prune_threshold;
+
+ if (catalog_cache_prune_min_age < 0)
+ return false;
+
+ prune_threshold = catcacheclock - ((int64) catalog_cache_prune_min_age) * USECS_PER_SEC;
+
+ /* Scan over the whole hash to find entries to remove */
+ for (i = 0 ; i < cp->cc_nbuckets ; i++)
+ {
+ dlist_mutable_iter iter;
+
+ dlist_foreach_modify(iter, &cp->cc_bucket[i])
+ {
+ CatCTup *ct = dlist_container(CatCTup, cache_elem, iter.cur);
+
+ /* Don't remove referenced entries */
+ if (ct->refcount == 0 &&
+ (ct->c_list == NULL || ct->c_list->refcount == 0))
+ {
+ if (ct->lastaccess < prune_threshold)
+ {
+ CatCacheRemoveCTup(cp, ct);
+ nremoved++;
+
+ /* don't let the removed entry update oldest_ts */
+ continue;
+ }
+ }
+
+ /* update the oldest timestamp if the entry remains alive */
+ if (ct->lastaccess < oldest_ts)
+ oldest_ts = ct->lastaccess;
+ }
+ }
+
+ cp->cc_oldest_ts = oldest_ts;
+
+ if (nremoved > 0)
+ elog(DEBUG1, "pruning catalog cache id=%d for %s: removed %d / %d",
+ cp->id, cp->cc_relname, nremoved, cp->cc_ntup + nremoved);
+
+ return nremoved > 0;
+}
+
/*
* ReleaseCatCache
*
@@ -1888,6 +1961,7 @@ CatalogCacheCreateEntry(CatCache *cache, HeapTuple ntp, Datum *arguments,
ct->dead = false;
ct->negative = negative;
ct->hash_value = hashValue;
+ ct->lastaccess = catcacheclock;
dlist_push_head(&cache->cc_bucket[hashIndex], &ct->cache_elem);
@@ -1899,7 +1973,11 @@ CatalogCacheCreateEntry(CatCache *cache, HeapTuple ntp, Datum *arguments,
* arbitrarily, we enlarge when fill factor > 2.
*/
if (cache->cc_ntup > cache->cc_nbuckets * 2)
- RehashCatCache(cache);
+ {
+ /* try removing old entries before expanding hash */
+ if (!CatCacheCleanupOldEntries(cache))
+ RehashCatCache(cache);
+ }
return ct;
}
diff --git a/src/backend/utils/misc/guc.c b/src/backend/utils/misc/guc.c
index 17579eeaca9..f92f0513a6f 100644
--- a/src/backend/utils/misc/guc.c
+++ b/src/backend/utils/misc/guc.c
@@ -88,6 +88,7 @@
#include "utils/acl.h"
#include "utils/builtins.h"
#include "utils/bytea.h"
+#include "utils/catcache.h"
#include "utils/float.h"
#include "utils/guc_tables.h"
#include "utils/memutils.h"
@@ -3445,6 +3446,17 @@ static struct config_int ConfigureNamesInt[] =
NULL, NULL, NULL
},
+ {
+ {"catalog_cache_prune_min_age", PGC_USERSET, RESOURCES_MEM,
+ gettext_noop("System catalog cache entries that are living unused more than this seconds are considered for removal."),
+ gettext_noop("The value of -1 turns off pruning."),
+ GUC_UNIT_S
+ },
+ &catalog_cache_prune_min_age,
+ -1, -1, INT_MAX,
+ NULL, NULL, NULL
+ },
+
/* End-of-list marker */
{
{NULL, 0, 0, NULL, NULL}, NULL, 0, 0, 0, NULL, NULL, NULL
diff --git a/src/include/utils/catcache.h b/src/include/utils/catcache.h
index ddc2762eb3f..57a15bfec22 100644
--- a/src/include/utils/catcache.h
+++ b/src/include/utils/catcache.h
@@ -22,6 +22,7 @@
#include "access/htup.h"
#include "access/skey.h"
+#include "datatype/timestamp.h"
#include "lib/ilist.h"
#include "utils/relcache.h"
@@ -61,6 +62,7 @@ typedef struct catcache
slist_node cc_next; /* list link */
ScanKeyData cc_skey[CATCACHE_MAXKEYS]; /* precomputed key info for heap
* scans */
+ TimestampTz cc_oldest_ts; /* timestamp (us) of the oldest tuple */
/*
* Keep these at the end, so that compiling catcache.c with CATCACHE_STATS
@@ -119,6 +121,7 @@ typedef struct catctup
bool dead; /* dead but not yet removed? */
bool negative; /* negative cache entry? */
HeapTupleData tuple; /* tuple management header */
+ TimestampTz lastaccess; /* timestamp in us of the last usage */
/*
* The tuple may also be a member of at most one CatCList. (If a single
@@ -189,6 +192,20 @@ typedef struct catcacheheader
/* this extern duplicates utils/memutils.h... */
extern PGDLLIMPORT MemoryContext CacheMemoryContext;
+
+/* for guc.c, not PGDLLPMPORT'ed */
+extern int catalog_cache_prune_min_age;
+
+/* source clock for access timestamp of catcache entries */
+extern TimestampTz catcacheclock;
+
+/* SetCatCacheClock - set catcache timestamp source clock */
+static inline void
+SetCatCacheClock(TimestampTz ts)
+{
+ catcacheclock = ts;
+}
+
extern void CreateCacheMemoryContext(void);
extern CatCache *InitCatCache(int id, Oid reloid, Oid indexoid,
--
2.29.2
--------------38A9F5DCD32945DE1430BC1D--
^ permalink raw reply [nested|flat] 92+ messages in thread
* [PATCH v8 1/1] CatCache expiration feature
@ 2021-01-27 11:08 Heikki Linnakangas <[email protected]>
0 siblings, 0 replies; 92+ messages in thread
From: Heikki Linnakangas @ 2021-01-27 11:08 UTC (permalink / raw)
Author: Kyotaro Horiguchi
---
src/backend/access/transam/xact.c | 3 ++
src/backend/utils/cache/catcache.c | 82 +++++++++++++++++++++++++++++-
src/backend/utils/misc/guc.c | 12 +++++
src/include/utils/catcache.h | 17 +++++++
4 files changed, 112 insertions(+), 2 deletions(-)
diff --git a/src/backend/access/transam/xact.c b/src/backend/access/transam/xact.c
index a2068e3fd45..86888d24091 100644
--- a/src/backend/access/transam/xact.c
+++ b/src/backend/access/transam/xact.c
@@ -1086,6 +1086,9 @@ static void
AtStart_Cache(void)
{
AcceptInvalidationMessages();
+
+ if (xactStartTimestamp != 0)
+ SetCatCacheClock(xactStartTimestamp);
}
/*
diff --git a/src/backend/utils/cache/catcache.c b/src/backend/utils/cache/catcache.c
index fa2b49c676e..e29f825687a 100644
--- a/src/backend/utils/cache/catcache.c
+++ b/src/backend/utils/cache/catcache.c
@@ -38,6 +38,7 @@
#include "utils/rel.h"
#include "utils/resowner_private.h"
#include "utils/syscache.h"
+#include "utils/timestamp.h"
/* #define CACHEDEBUG */ /* turns DEBUG elogs on */
@@ -60,9 +61,18 @@
#define CACHE_elog(...)
#endif
+/*
+ * GUC variable to define the minimum age of entries that will be considered
+ * to be evicted in seconds. -1 to disable the feature.
+ */
+int catalog_cache_prune_min_age = -1;
+
/* Cache management header --- pointer is NULL until created */
static CatCacheHeader *CacheHdr = NULL;
+/* Clock for the last accessed time of a catcache entry. */
+TimestampTz catcacheclock = 0;
+
static inline HeapTuple SearchCatCacheInternal(CatCache *cache,
int nkeys,
Datum v1, Datum v2,
@@ -74,6 +84,7 @@ static pg_noinline HeapTuple SearchCatCacheMiss(CatCache *cache,
Index hashIndex,
Datum v1, Datum v2,
Datum v3, Datum v4);
+static bool CatCacheCleanupOldEntries(CatCache *cp);
static uint32 CatalogCacheComputeHashValue(CatCache *cache, int nkeys,
Datum v1, Datum v2, Datum v3, Datum v4);
@@ -99,7 +110,6 @@ static void CatCacheFreeKeys(TupleDesc tupdesc, int nkeys, int *attnos,
static void CatCacheCopyKeys(TupleDesc tupdesc, int nkeys, int *attnos,
Datum *srckeys, Datum *dstkeys);
-
/*
* internal support functions
*/
@@ -1264,6 +1274,9 @@ SearchCatCacheInternal(CatCache *cache,
*/
dlist_move_head(bucket, &ct->cache_elem);
+ /* Record the last access timestamp */
+ ct->lastaccess = catcacheclock;
+
/*
* If it's a positive entry, bump its refcount and return it. If it's
* negative, we can report failure to the caller.
@@ -1425,6 +1438,66 @@ SearchCatCacheMiss(CatCache *cache,
return &ct->tuple;
}
+/*
+ * CatCacheCleanupOldEntries - Remove infrequently-used entries
+ *
+ * Catcache entries happen to be left unused for a long time for several
+ * reasons. Remove such entries to prevent catcache from bloating. It is based
+ * on the similar algorithm with buffer eviction. Entries that are accessed
+ * several times in a certain period live longer than those that have had less
+ * access in the same duration.
+ */
+static bool
+CatCacheCleanupOldEntries(CatCache *cp)
+{
+ int nremoved = 0;
+ int i;
+ TimestampTz oldest_ts = catcacheclock;
+ TimestampTz prune_threshold;
+
+ if (catalog_cache_prune_min_age < 0)
+ return false;
+
+ prune_threshold = catcacheclock - ((int64) catalog_cache_prune_min_age) * USECS_PER_SEC;
+
+ /* Scan over the whole hash to find entries to remove */
+ for (i = 0 ; i < cp->cc_nbuckets ; i++)
+ {
+ dlist_mutable_iter iter;
+
+ dlist_foreach_modify(iter, &cp->cc_bucket[i])
+ {
+ CatCTup *ct = dlist_container(CatCTup, cache_elem, iter.cur);
+
+ /* Don't remove referenced entries */
+ if (ct->refcount == 0 &&
+ (ct->c_list == NULL || ct->c_list->refcount == 0))
+ {
+ if (ct->lastaccess < prune_threshold)
+ {
+ CatCacheRemoveCTup(cp, ct);
+ nremoved++;
+
+ /* don't let the removed entry update oldest_ts */
+ continue;
+ }
+ }
+
+ /* update the oldest timestamp if the entry remains alive */
+ if (ct->lastaccess < oldest_ts)
+ oldest_ts = ct->lastaccess;
+ }
+ }
+
+ cp->cc_oldest_ts = oldest_ts;
+
+ if (nremoved > 0)
+ elog(DEBUG1, "pruning catalog cache id=%d for %s: removed %d / %d",
+ cp->id, cp->cc_relname, nremoved, cp->cc_ntup + nremoved);
+
+ return nremoved > 0;
+}
+
/*
* ReleaseCatCache
*
@@ -1888,6 +1961,7 @@ CatalogCacheCreateEntry(CatCache *cache, HeapTuple ntp, Datum *arguments,
ct->dead = false;
ct->negative = negative;
ct->hash_value = hashValue;
+ ct->lastaccess = catcacheclock;
dlist_push_head(&cache->cc_bucket[hashIndex], &ct->cache_elem);
@@ -1899,7 +1973,11 @@ CatalogCacheCreateEntry(CatCache *cache, HeapTuple ntp, Datum *arguments,
* arbitrarily, we enlarge when fill factor > 2.
*/
if (cache->cc_ntup > cache->cc_nbuckets * 2)
- RehashCatCache(cache);
+ {
+ /* try removing old entries before expanding hash */
+ if (!CatCacheCleanupOldEntries(cache))
+ RehashCatCache(cache);
+ }
return ct;
}
diff --git a/src/backend/utils/misc/guc.c b/src/backend/utils/misc/guc.c
index 17579eeaca9..f92f0513a6f 100644
--- a/src/backend/utils/misc/guc.c
+++ b/src/backend/utils/misc/guc.c
@@ -88,6 +88,7 @@
#include "utils/acl.h"
#include "utils/builtins.h"
#include "utils/bytea.h"
+#include "utils/catcache.h"
#include "utils/float.h"
#include "utils/guc_tables.h"
#include "utils/memutils.h"
@@ -3445,6 +3446,17 @@ static struct config_int ConfigureNamesInt[] =
NULL, NULL, NULL
},
+ {
+ {"catalog_cache_prune_min_age", PGC_USERSET, RESOURCES_MEM,
+ gettext_noop("System catalog cache entries that are living unused more than this seconds are considered for removal."),
+ gettext_noop("The value of -1 turns off pruning."),
+ GUC_UNIT_S
+ },
+ &catalog_cache_prune_min_age,
+ -1, -1, INT_MAX,
+ NULL, NULL, NULL
+ },
+
/* End-of-list marker */
{
{NULL, 0, 0, NULL, NULL}, NULL, 0, 0, 0, NULL, NULL, NULL
diff --git a/src/include/utils/catcache.h b/src/include/utils/catcache.h
index ddc2762eb3f..57a15bfec22 100644
--- a/src/include/utils/catcache.h
+++ b/src/include/utils/catcache.h
@@ -22,6 +22,7 @@
#include "access/htup.h"
#include "access/skey.h"
+#include "datatype/timestamp.h"
#include "lib/ilist.h"
#include "utils/relcache.h"
@@ -61,6 +62,7 @@ typedef struct catcache
slist_node cc_next; /* list link */
ScanKeyData cc_skey[CATCACHE_MAXKEYS]; /* precomputed key info for heap
* scans */
+ TimestampTz cc_oldest_ts; /* timestamp (us) of the oldest tuple */
/*
* Keep these at the end, so that compiling catcache.c with CATCACHE_STATS
@@ -119,6 +121,7 @@ typedef struct catctup
bool dead; /* dead but not yet removed? */
bool negative; /* negative cache entry? */
HeapTupleData tuple; /* tuple management header */
+ TimestampTz lastaccess; /* timestamp in us of the last usage */
/*
* The tuple may also be a member of at most one CatCList. (If a single
@@ -189,6 +192,20 @@ typedef struct catcacheheader
/* this extern duplicates utils/memutils.h... */
extern PGDLLIMPORT MemoryContext CacheMemoryContext;
+
+/* for guc.c, not PGDLLPMPORT'ed */
+extern int catalog_cache_prune_min_age;
+
+/* source clock for access timestamp of catcache entries */
+extern TimestampTz catcacheclock;
+
+/* SetCatCacheClock - set catcache timestamp source clock */
+static inline void
+SetCatCacheClock(TimestampTz ts)
+{
+ catcacheclock = ts;
+}
+
extern void CreateCacheMemoryContext(void);
extern CatCache *InitCatCache(int id, Oid reloid, Oid indexoid,
--
2.29.2
--------------38A9F5DCD32945DE1430BC1D--
^ permalink raw reply [nested|flat] 92+ messages in thread
* [PATCH v8 1/1] CatCache expiration feature
@ 2021-01-27 11:08 Heikki Linnakangas <[email protected]>
0 siblings, 0 replies; 92+ messages in thread
From: Heikki Linnakangas @ 2021-01-27 11:08 UTC (permalink / raw)
Author: Kyotaro Horiguchi
---
src/backend/access/transam/xact.c | 3 ++
src/backend/utils/cache/catcache.c | 82 +++++++++++++++++++++++++++++-
src/backend/utils/misc/guc.c | 12 +++++
src/include/utils/catcache.h | 17 +++++++
4 files changed, 112 insertions(+), 2 deletions(-)
diff --git a/src/backend/access/transam/xact.c b/src/backend/access/transam/xact.c
index a2068e3fd45..86888d24091 100644
--- a/src/backend/access/transam/xact.c
+++ b/src/backend/access/transam/xact.c
@@ -1086,6 +1086,9 @@ static void
AtStart_Cache(void)
{
AcceptInvalidationMessages();
+
+ if (xactStartTimestamp != 0)
+ SetCatCacheClock(xactStartTimestamp);
}
/*
diff --git a/src/backend/utils/cache/catcache.c b/src/backend/utils/cache/catcache.c
index fa2b49c676e..e29f825687a 100644
--- a/src/backend/utils/cache/catcache.c
+++ b/src/backend/utils/cache/catcache.c
@@ -38,6 +38,7 @@
#include "utils/rel.h"
#include "utils/resowner_private.h"
#include "utils/syscache.h"
+#include "utils/timestamp.h"
/* #define CACHEDEBUG */ /* turns DEBUG elogs on */
@@ -60,9 +61,18 @@
#define CACHE_elog(...)
#endif
+/*
+ * GUC variable to define the minimum age of entries that will be considered
+ * to be evicted in seconds. -1 to disable the feature.
+ */
+int catalog_cache_prune_min_age = -1;
+
/* Cache management header --- pointer is NULL until created */
static CatCacheHeader *CacheHdr = NULL;
+/* Clock for the last accessed time of a catcache entry. */
+TimestampTz catcacheclock = 0;
+
static inline HeapTuple SearchCatCacheInternal(CatCache *cache,
int nkeys,
Datum v1, Datum v2,
@@ -74,6 +84,7 @@ static pg_noinline HeapTuple SearchCatCacheMiss(CatCache *cache,
Index hashIndex,
Datum v1, Datum v2,
Datum v3, Datum v4);
+static bool CatCacheCleanupOldEntries(CatCache *cp);
static uint32 CatalogCacheComputeHashValue(CatCache *cache, int nkeys,
Datum v1, Datum v2, Datum v3, Datum v4);
@@ -99,7 +110,6 @@ static void CatCacheFreeKeys(TupleDesc tupdesc, int nkeys, int *attnos,
static void CatCacheCopyKeys(TupleDesc tupdesc, int nkeys, int *attnos,
Datum *srckeys, Datum *dstkeys);
-
/*
* internal support functions
*/
@@ -1264,6 +1274,9 @@ SearchCatCacheInternal(CatCache *cache,
*/
dlist_move_head(bucket, &ct->cache_elem);
+ /* Record the last access timestamp */
+ ct->lastaccess = catcacheclock;
+
/*
* If it's a positive entry, bump its refcount and return it. If it's
* negative, we can report failure to the caller.
@@ -1425,6 +1438,66 @@ SearchCatCacheMiss(CatCache *cache,
return &ct->tuple;
}
+/*
+ * CatCacheCleanupOldEntries - Remove infrequently-used entries
+ *
+ * Catcache entries happen to be left unused for a long time for several
+ * reasons. Remove such entries to prevent catcache from bloating. It is based
+ * on the similar algorithm with buffer eviction. Entries that are accessed
+ * several times in a certain period live longer than those that have had less
+ * access in the same duration.
+ */
+static bool
+CatCacheCleanupOldEntries(CatCache *cp)
+{
+ int nremoved = 0;
+ int i;
+ TimestampTz oldest_ts = catcacheclock;
+ TimestampTz prune_threshold;
+
+ if (catalog_cache_prune_min_age < 0)
+ return false;
+
+ prune_threshold = catcacheclock - ((int64) catalog_cache_prune_min_age) * USECS_PER_SEC;
+
+ /* Scan over the whole hash to find entries to remove */
+ for (i = 0 ; i < cp->cc_nbuckets ; i++)
+ {
+ dlist_mutable_iter iter;
+
+ dlist_foreach_modify(iter, &cp->cc_bucket[i])
+ {
+ CatCTup *ct = dlist_container(CatCTup, cache_elem, iter.cur);
+
+ /* Don't remove referenced entries */
+ if (ct->refcount == 0 &&
+ (ct->c_list == NULL || ct->c_list->refcount == 0))
+ {
+ if (ct->lastaccess < prune_threshold)
+ {
+ CatCacheRemoveCTup(cp, ct);
+ nremoved++;
+
+ /* don't let the removed entry update oldest_ts */
+ continue;
+ }
+ }
+
+ /* update the oldest timestamp if the entry remains alive */
+ if (ct->lastaccess < oldest_ts)
+ oldest_ts = ct->lastaccess;
+ }
+ }
+
+ cp->cc_oldest_ts = oldest_ts;
+
+ if (nremoved > 0)
+ elog(DEBUG1, "pruning catalog cache id=%d for %s: removed %d / %d",
+ cp->id, cp->cc_relname, nremoved, cp->cc_ntup + nremoved);
+
+ return nremoved > 0;
+}
+
/*
* ReleaseCatCache
*
@@ -1888,6 +1961,7 @@ CatalogCacheCreateEntry(CatCache *cache, HeapTuple ntp, Datum *arguments,
ct->dead = false;
ct->negative = negative;
ct->hash_value = hashValue;
+ ct->lastaccess = catcacheclock;
dlist_push_head(&cache->cc_bucket[hashIndex], &ct->cache_elem);
@@ -1899,7 +1973,11 @@ CatalogCacheCreateEntry(CatCache *cache, HeapTuple ntp, Datum *arguments,
* arbitrarily, we enlarge when fill factor > 2.
*/
if (cache->cc_ntup > cache->cc_nbuckets * 2)
- RehashCatCache(cache);
+ {
+ /* try removing old entries before expanding hash */
+ if (!CatCacheCleanupOldEntries(cache))
+ RehashCatCache(cache);
+ }
return ct;
}
diff --git a/src/backend/utils/misc/guc.c b/src/backend/utils/misc/guc.c
index 17579eeaca9..f92f0513a6f 100644
--- a/src/backend/utils/misc/guc.c
+++ b/src/backend/utils/misc/guc.c
@@ -88,6 +88,7 @@
#include "utils/acl.h"
#include "utils/builtins.h"
#include "utils/bytea.h"
+#include "utils/catcache.h"
#include "utils/float.h"
#include "utils/guc_tables.h"
#include "utils/memutils.h"
@@ -3445,6 +3446,17 @@ static struct config_int ConfigureNamesInt[] =
NULL, NULL, NULL
},
+ {
+ {"catalog_cache_prune_min_age", PGC_USERSET, RESOURCES_MEM,
+ gettext_noop("System catalog cache entries that are living unused more than this seconds are considered for removal."),
+ gettext_noop("The value of -1 turns off pruning."),
+ GUC_UNIT_S
+ },
+ &catalog_cache_prune_min_age,
+ -1, -1, INT_MAX,
+ NULL, NULL, NULL
+ },
+
/* End-of-list marker */
{
{NULL, 0, 0, NULL, NULL}, NULL, 0, 0, 0, NULL, NULL, NULL
diff --git a/src/include/utils/catcache.h b/src/include/utils/catcache.h
index ddc2762eb3f..57a15bfec22 100644
--- a/src/include/utils/catcache.h
+++ b/src/include/utils/catcache.h
@@ -22,6 +22,7 @@
#include "access/htup.h"
#include "access/skey.h"
+#include "datatype/timestamp.h"
#include "lib/ilist.h"
#include "utils/relcache.h"
@@ -61,6 +62,7 @@ typedef struct catcache
slist_node cc_next; /* list link */
ScanKeyData cc_skey[CATCACHE_MAXKEYS]; /* precomputed key info for heap
* scans */
+ TimestampTz cc_oldest_ts; /* timestamp (us) of the oldest tuple */
/*
* Keep these at the end, so that compiling catcache.c with CATCACHE_STATS
@@ -119,6 +121,7 @@ typedef struct catctup
bool dead; /* dead but not yet removed? */
bool negative; /* negative cache entry? */
HeapTupleData tuple; /* tuple management header */
+ TimestampTz lastaccess; /* timestamp in us of the last usage */
/*
* The tuple may also be a member of at most one CatCList. (If a single
@@ -189,6 +192,20 @@ typedef struct catcacheheader
/* this extern duplicates utils/memutils.h... */
extern PGDLLIMPORT MemoryContext CacheMemoryContext;
+
+/* for guc.c, not PGDLLPMPORT'ed */
+extern int catalog_cache_prune_min_age;
+
+/* source clock for access timestamp of catcache entries */
+extern TimestampTz catcacheclock;
+
+/* SetCatCacheClock - set catcache timestamp source clock */
+static inline void
+SetCatCacheClock(TimestampTz ts)
+{
+ catcacheclock = ts;
+}
+
extern void CreateCacheMemoryContext(void);
extern CatCache *InitCatCache(int id, Oid reloid, Oid indexoid,
--
2.29.2
--------------38A9F5DCD32945DE1430BC1D--
^ permalink raw reply [nested|flat] 92+ messages in thread
* [PATCH v8 1/1] CatCache expiration feature
@ 2021-01-27 11:08 Heikki Linnakangas <[email protected]>
0 siblings, 0 replies; 92+ messages in thread
From: Heikki Linnakangas @ 2021-01-27 11:08 UTC (permalink / raw)
Author: Kyotaro Horiguchi
---
src/backend/access/transam/xact.c | 3 ++
src/backend/utils/cache/catcache.c | 82 +++++++++++++++++++++++++++++-
src/backend/utils/misc/guc.c | 12 +++++
src/include/utils/catcache.h | 17 +++++++
4 files changed, 112 insertions(+), 2 deletions(-)
diff --git a/src/backend/access/transam/xact.c b/src/backend/access/transam/xact.c
index a2068e3fd45..86888d24091 100644
--- a/src/backend/access/transam/xact.c
+++ b/src/backend/access/transam/xact.c
@@ -1086,6 +1086,9 @@ static void
AtStart_Cache(void)
{
AcceptInvalidationMessages();
+
+ if (xactStartTimestamp != 0)
+ SetCatCacheClock(xactStartTimestamp);
}
/*
diff --git a/src/backend/utils/cache/catcache.c b/src/backend/utils/cache/catcache.c
index fa2b49c676e..e29f825687a 100644
--- a/src/backend/utils/cache/catcache.c
+++ b/src/backend/utils/cache/catcache.c
@@ -38,6 +38,7 @@
#include "utils/rel.h"
#include "utils/resowner_private.h"
#include "utils/syscache.h"
+#include "utils/timestamp.h"
/* #define CACHEDEBUG */ /* turns DEBUG elogs on */
@@ -60,9 +61,18 @@
#define CACHE_elog(...)
#endif
+/*
+ * GUC variable to define the minimum age of entries that will be considered
+ * to be evicted in seconds. -1 to disable the feature.
+ */
+int catalog_cache_prune_min_age = -1;
+
/* Cache management header --- pointer is NULL until created */
static CatCacheHeader *CacheHdr = NULL;
+/* Clock for the last accessed time of a catcache entry. */
+TimestampTz catcacheclock = 0;
+
static inline HeapTuple SearchCatCacheInternal(CatCache *cache,
int nkeys,
Datum v1, Datum v2,
@@ -74,6 +84,7 @@ static pg_noinline HeapTuple SearchCatCacheMiss(CatCache *cache,
Index hashIndex,
Datum v1, Datum v2,
Datum v3, Datum v4);
+static bool CatCacheCleanupOldEntries(CatCache *cp);
static uint32 CatalogCacheComputeHashValue(CatCache *cache, int nkeys,
Datum v1, Datum v2, Datum v3, Datum v4);
@@ -99,7 +110,6 @@ static void CatCacheFreeKeys(TupleDesc tupdesc, int nkeys, int *attnos,
static void CatCacheCopyKeys(TupleDesc tupdesc, int nkeys, int *attnos,
Datum *srckeys, Datum *dstkeys);
-
/*
* internal support functions
*/
@@ -1264,6 +1274,9 @@ SearchCatCacheInternal(CatCache *cache,
*/
dlist_move_head(bucket, &ct->cache_elem);
+ /* Record the last access timestamp */
+ ct->lastaccess = catcacheclock;
+
/*
* If it's a positive entry, bump its refcount and return it. If it's
* negative, we can report failure to the caller.
@@ -1425,6 +1438,66 @@ SearchCatCacheMiss(CatCache *cache,
return &ct->tuple;
}
+/*
+ * CatCacheCleanupOldEntries - Remove infrequently-used entries
+ *
+ * Catcache entries happen to be left unused for a long time for several
+ * reasons. Remove such entries to prevent catcache from bloating. It is based
+ * on the similar algorithm with buffer eviction. Entries that are accessed
+ * several times in a certain period live longer than those that have had less
+ * access in the same duration.
+ */
+static bool
+CatCacheCleanupOldEntries(CatCache *cp)
+{
+ int nremoved = 0;
+ int i;
+ TimestampTz oldest_ts = catcacheclock;
+ TimestampTz prune_threshold;
+
+ if (catalog_cache_prune_min_age < 0)
+ return false;
+
+ prune_threshold = catcacheclock - ((int64) catalog_cache_prune_min_age) * USECS_PER_SEC;
+
+ /* Scan over the whole hash to find entries to remove */
+ for (i = 0 ; i < cp->cc_nbuckets ; i++)
+ {
+ dlist_mutable_iter iter;
+
+ dlist_foreach_modify(iter, &cp->cc_bucket[i])
+ {
+ CatCTup *ct = dlist_container(CatCTup, cache_elem, iter.cur);
+
+ /* Don't remove referenced entries */
+ if (ct->refcount == 0 &&
+ (ct->c_list == NULL || ct->c_list->refcount == 0))
+ {
+ if (ct->lastaccess < prune_threshold)
+ {
+ CatCacheRemoveCTup(cp, ct);
+ nremoved++;
+
+ /* don't let the removed entry update oldest_ts */
+ continue;
+ }
+ }
+
+ /* update the oldest timestamp if the entry remains alive */
+ if (ct->lastaccess < oldest_ts)
+ oldest_ts = ct->lastaccess;
+ }
+ }
+
+ cp->cc_oldest_ts = oldest_ts;
+
+ if (nremoved > 0)
+ elog(DEBUG1, "pruning catalog cache id=%d for %s: removed %d / %d",
+ cp->id, cp->cc_relname, nremoved, cp->cc_ntup + nremoved);
+
+ return nremoved > 0;
+}
+
/*
* ReleaseCatCache
*
@@ -1888,6 +1961,7 @@ CatalogCacheCreateEntry(CatCache *cache, HeapTuple ntp, Datum *arguments,
ct->dead = false;
ct->negative = negative;
ct->hash_value = hashValue;
+ ct->lastaccess = catcacheclock;
dlist_push_head(&cache->cc_bucket[hashIndex], &ct->cache_elem);
@@ -1899,7 +1973,11 @@ CatalogCacheCreateEntry(CatCache *cache, HeapTuple ntp, Datum *arguments,
* arbitrarily, we enlarge when fill factor > 2.
*/
if (cache->cc_ntup > cache->cc_nbuckets * 2)
- RehashCatCache(cache);
+ {
+ /* try removing old entries before expanding hash */
+ if (!CatCacheCleanupOldEntries(cache))
+ RehashCatCache(cache);
+ }
return ct;
}
diff --git a/src/backend/utils/misc/guc.c b/src/backend/utils/misc/guc.c
index 17579eeaca9..f92f0513a6f 100644
--- a/src/backend/utils/misc/guc.c
+++ b/src/backend/utils/misc/guc.c
@@ -88,6 +88,7 @@
#include "utils/acl.h"
#include "utils/builtins.h"
#include "utils/bytea.h"
+#include "utils/catcache.h"
#include "utils/float.h"
#include "utils/guc_tables.h"
#include "utils/memutils.h"
@@ -3445,6 +3446,17 @@ static struct config_int ConfigureNamesInt[] =
NULL, NULL, NULL
},
+ {
+ {"catalog_cache_prune_min_age", PGC_USERSET, RESOURCES_MEM,
+ gettext_noop("System catalog cache entries that are living unused more than this seconds are considered for removal."),
+ gettext_noop("The value of -1 turns off pruning."),
+ GUC_UNIT_S
+ },
+ &catalog_cache_prune_min_age,
+ -1, -1, INT_MAX,
+ NULL, NULL, NULL
+ },
+
/* End-of-list marker */
{
{NULL, 0, 0, NULL, NULL}, NULL, 0, 0, 0, NULL, NULL, NULL
diff --git a/src/include/utils/catcache.h b/src/include/utils/catcache.h
index ddc2762eb3f..57a15bfec22 100644
--- a/src/include/utils/catcache.h
+++ b/src/include/utils/catcache.h
@@ -22,6 +22,7 @@
#include "access/htup.h"
#include "access/skey.h"
+#include "datatype/timestamp.h"
#include "lib/ilist.h"
#include "utils/relcache.h"
@@ -61,6 +62,7 @@ typedef struct catcache
slist_node cc_next; /* list link */
ScanKeyData cc_skey[CATCACHE_MAXKEYS]; /* precomputed key info for heap
* scans */
+ TimestampTz cc_oldest_ts; /* timestamp (us) of the oldest tuple */
/*
* Keep these at the end, so that compiling catcache.c with CATCACHE_STATS
@@ -119,6 +121,7 @@ typedef struct catctup
bool dead; /* dead but not yet removed? */
bool negative; /* negative cache entry? */
HeapTupleData tuple; /* tuple management header */
+ TimestampTz lastaccess; /* timestamp in us of the last usage */
/*
* The tuple may also be a member of at most one CatCList. (If a single
@@ -189,6 +192,20 @@ typedef struct catcacheheader
/* this extern duplicates utils/memutils.h... */
extern PGDLLIMPORT MemoryContext CacheMemoryContext;
+
+/* for guc.c, not PGDLLPMPORT'ed */
+extern int catalog_cache_prune_min_age;
+
+/* source clock for access timestamp of catcache entries */
+extern TimestampTz catcacheclock;
+
+/* SetCatCacheClock - set catcache timestamp source clock */
+static inline void
+SetCatCacheClock(TimestampTz ts)
+{
+ catcacheclock = ts;
+}
+
extern void CreateCacheMemoryContext(void);
extern CatCache *InitCatCache(int id, Oid reloid, Oid indexoid,
--
2.29.2
--------------38A9F5DCD32945DE1430BC1D--
^ permalink raw reply [nested|flat] 92+ messages in thread
* [PATCH v8 1/1] CatCache expiration feature
@ 2021-01-27 11:08 Heikki Linnakangas <[email protected]>
0 siblings, 0 replies; 92+ messages in thread
From: Heikki Linnakangas @ 2021-01-27 11:08 UTC (permalink / raw)
Author: Kyotaro Horiguchi
---
src/backend/access/transam/xact.c | 3 ++
src/backend/utils/cache/catcache.c | 82 +++++++++++++++++++++++++++++-
src/backend/utils/misc/guc.c | 12 +++++
src/include/utils/catcache.h | 17 +++++++
4 files changed, 112 insertions(+), 2 deletions(-)
diff --git a/src/backend/access/transam/xact.c b/src/backend/access/transam/xact.c
index a2068e3fd45..86888d24091 100644
--- a/src/backend/access/transam/xact.c
+++ b/src/backend/access/transam/xact.c
@@ -1086,6 +1086,9 @@ static void
AtStart_Cache(void)
{
AcceptInvalidationMessages();
+
+ if (xactStartTimestamp != 0)
+ SetCatCacheClock(xactStartTimestamp);
}
/*
diff --git a/src/backend/utils/cache/catcache.c b/src/backend/utils/cache/catcache.c
index fa2b49c676e..e29f825687a 100644
--- a/src/backend/utils/cache/catcache.c
+++ b/src/backend/utils/cache/catcache.c
@@ -38,6 +38,7 @@
#include "utils/rel.h"
#include "utils/resowner_private.h"
#include "utils/syscache.h"
+#include "utils/timestamp.h"
/* #define CACHEDEBUG */ /* turns DEBUG elogs on */
@@ -60,9 +61,18 @@
#define CACHE_elog(...)
#endif
+/*
+ * GUC variable to define the minimum age of entries that will be considered
+ * to be evicted in seconds. -1 to disable the feature.
+ */
+int catalog_cache_prune_min_age = -1;
+
/* Cache management header --- pointer is NULL until created */
static CatCacheHeader *CacheHdr = NULL;
+/* Clock for the last accessed time of a catcache entry. */
+TimestampTz catcacheclock = 0;
+
static inline HeapTuple SearchCatCacheInternal(CatCache *cache,
int nkeys,
Datum v1, Datum v2,
@@ -74,6 +84,7 @@ static pg_noinline HeapTuple SearchCatCacheMiss(CatCache *cache,
Index hashIndex,
Datum v1, Datum v2,
Datum v3, Datum v4);
+static bool CatCacheCleanupOldEntries(CatCache *cp);
static uint32 CatalogCacheComputeHashValue(CatCache *cache, int nkeys,
Datum v1, Datum v2, Datum v3, Datum v4);
@@ -99,7 +110,6 @@ static void CatCacheFreeKeys(TupleDesc tupdesc, int nkeys, int *attnos,
static void CatCacheCopyKeys(TupleDesc tupdesc, int nkeys, int *attnos,
Datum *srckeys, Datum *dstkeys);
-
/*
* internal support functions
*/
@@ -1264,6 +1274,9 @@ SearchCatCacheInternal(CatCache *cache,
*/
dlist_move_head(bucket, &ct->cache_elem);
+ /* Record the last access timestamp */
+ ct->lastaccess = catcacheclock;
+
/*
* If it's a positive entry, bump its refcount and return it. If it's
* negative, we can report failure to the caller.
@@ -1425,6 +1438,66 @@ SearchCatCacheMiss(CatCache *cache,
return &ct->tuple;
}
+/*
+ * CatCacheCleanupOldEntries - Remove infrequently-used entries
+ *
+ * Catcache entries happen to be left unused for a long time for several
+ * reasons. Remove such entries to prevent catcache from bloating. It is based
+ * on the similar algorithm with buffer eviction. Entries that are accessed
+ * several times in a certain period live longer than those that have had less
+ * access in the same duration.
+ */
+static bool
+CatCacheCleanupOldEntries(CatCache *cp)
+{
+ int nremoved = 0;
+ int i;
+ TimestampTz oldest_ts = catcacheclock;
+ TimestampTz prune_threshold;
+
+ if (catalog_cache_prune_min_age < 0)
+ return false;
+
+ prune_threshold = catcacheclock - ((int64) catalog_cache_prune_min_age) * USECS_PER_SEC;
+
+ /* Scan over the whole hash to find entries to remove */
+ for (i = 0 ; i < cp->cc_nbuckets ; i++)
+ {
+ dlist_mutable_iter iter;
+
+ dlist_foreach_modify(iter, &cp->cc_bucket[i])
+ {
+ CatCTup *ct = dlist_container(CatCTup, cache_elem, iter.cur);
+
+ /* Don't remove referenced entries */
+ if (ct->refcount == 0 &&
+ (ct->c_list == NULL || ct->c_list->refcount == 0))
+ {
+ if (ct->lastaccess < prune_threshold)
+ {
+ CatCacheRemoveCTup(cp, ct);
+ nremoved++;
+
+ /* don't let the removed entry update oldest_ts */
+ continue;
+ }
+ }
+
+ /* update the oldest timestamp if the entry remains alive */
+ if (ct->lastaccess < oldest_ts)
+ oldest_ts = ct->lastaccess;
+ }
+ }
+
+ cp->cc_oldest_ts = oldest_ts;
+
+ if (nremoved > 0)
+ elog(DEBUG1, "pruning catalog cache id=%d for %s: removed %d / %d",
+ cp->id, cp->cc_relname, nremoved, cp->cc_ntup + nremoved);
+
+ return nremoved > 0;
+}
+
/*
* ReleaseCatCache
*
@@ -1888,6 +1961,7 @@ CatalogCacheCreateEntry(CatCache *cache, HeapTuple ntp, Datum *arguments,
ct->dead = false;
ct->negative = negative;
ct->hash_value = hashValue;
+ ct->lastaccess = catcacheclock;
dlist_push_head(&cache->cc_bucket[hashIndex], &ct->cache_elem);
@@ -1899,7 +1973,11 @@ CatalogCacheCreateEntry(CatCache *cache, HeapTuple ntp, Datum *arguments,
* arbitrarily, we enlarge when fill factor > 2.
*/
if (cache->cc_ntup > cache->cc_nbuckets * 2)
- RehashCatCache(cache);
+ {
+ /* try removing old entries before expanding hash */
+ if (!CatCacheCleanupOldEntries(cache))
+ RehashCatCache(cache);
+ }
return ct;
}
diff --git a/src/backend/utils/misc/guc.c b/src/backend/utils/misc/guc.c
index 17579eeaca9..f92f0513a6f 100644
--- a/src/backend/utils/misc/guc.c
+++ b/src/backend/utils/misc/guc.c
@@ -88,6 +88,7 @@
#include "utils/acl.h"
#include "utils/builtins.h"
#include "utils/bytea.h"
+#include "utils/catcache.h"
#include "utils/float.h"
#include "utils/guc_tables.h"
#include "utils/memutils.h"
@@ -3445,6 +3446,17 @@ static struct config_int ConfigureNamesInt[] =
NULL, NULL, NULL
},
+ {
+ {"catalog_cache_prune_min_age", PGC_USERSET, RESOURCES_MEM,
+ gettext_noop("System catalog cache entries that are living unused more than this seconds are considered for removal."),
+ gettext_noop("The value of -1 turns off pruning."),
+ GUC_UNIT_S
+ },
+ &catalog_cache_prune_min_age,
+ -1, -1, INT_MAX,
+ NULL, NULL, NULL
+ },
+
/* End-of-list marker */
{
{NULL, 0, 0, NULL, NULL}, NULL, 0, 0, 0, NULL, NULL, NULL
diff --git a/src/include/utils/catcache.h b/src/include/utils/catcache.h
index ddc2762eb3f..57a15bfec22 100644
--- a/src/include/utils/catcache.h
+++ b/src/include/utils/catcache.h
@@ -22,6 +22,7 @@
#include "access/htup.h"
#include "access/skey.h"
+#include "datatype/timestamp.h"
#include "lib/ilist.h"
#include "utils/relcache.h"
@@ -61,6 +62,7 @@ typedef struct catcache
slist_node cc_next; /* list link */
ScanKeyData cc_skey[CATCACHE_MAXKEYS]; /* precomputed key info for heap
* scans */
+ TimestampTz cc_oldest_ts; /* timestamp (us) of the oldest tuple */
/*
* Keep these at the end, so that compiling catcache.c with CATCACHE_STATS
@@ -119,6 +121,7 @@ typedef struct catctup
bool dead; /* dead but not yet removed? */
bool negative; /* negative cache entry? */
HeapTupleData tuple; /* tuple management header */
+ TimestampTz lastaccess; /* timestamp in us of the last usage */
/*
* The tuple may also be a member of at most one CatCList. (If a single
@@ -189,6 +192,20 @@ typedef struct catcacheheader
/* this extern duplicates utils/memutils.h... */
extern PGDLLIMPORT MemoryContext CacheMemoryContext;
+
+/* for guc.c, not PGDLLPMPORT'ed */
+extern int catalog_cache_prune_min_age;
+
+/* source clock for access timestamp of catcache entries */
+extern TimestampTz catcacheclock;
+
+/* SetCatCacheClock - set catcache timestamp source clock */
+static inline void
+SetCatCacheClock(TimestampTz ts)
+{
+ catcacheclock = ts;
+}
+
extern void CreateCacheMemoryContext(void);
extern CatCache *InitCatCache(int id, Oid reloid, Oid indexoid,
--
2.29.2
--------------38A9F5DCD32945DE1430BC1D--
^ permalink raw reply [nested|flat] 92+ messages in thread
* [PATCH v8 1/1] CatCache expiration feature
@ 2021-01-27 11:08 Heikki Linnakangas <[email protected]>
0 siblings, 0 replies; 92+ messages in thread
From: Heikki Linnakangas @ 2021-01-27 11:08 UTC (permalink / raw)
Author: Kyotaro Horiguchi
---
src/backend/access/transam/xact.c | 3 ++
src/backend/utils/cache/catcache.c | 82 +++++++++++++++++++++++++++++-
src/backend/utils/misc/guc.c | 12 +++++
src/include/utils/catcache.h | 17 +++++++
4 files changed, 112 insertions(+), 2 deletions(-)
diff --git a/src/backend/access/transam/xact.c b/src/backend/access/transam/xact.c
index a2068e3fd45..86888d24091 100644
--- a/src/backend/access/transam/xact.c
+++ b/src/backend/access/transam/xact.c
@@ -1086,6 +1086,9 @@ static void
AtStart_Cache(void)
{
AcceptInvalidationMessages();
+
+ if (xactStartTimestamp != 0)
+ SetCatCacheClock(xactStartTimestamp);
}
/*
diff --git a/src/backend/utils/cache/catcache.c b/src/backend/utils/cache/catcache.c
index fa2b49c676e..e29f825687a 100644
--- a/src/backend/utils/cache/catcache.c
+++ b/src/backend/utils/cache/catcache.c
@@ -38,6 +38,7 @@
#include "utils/rel.h"
#include "utils/resowner_private.h"
#include "utils/syscache.h"
+#include "utils/timestamp.h"
/* #define CACHEDEBUG */ /* turns DEBUG elogs on */
@@ -60,9 +61,18 @@
#define CACHE_elog(...)
#endif
+/*
+ * GUC variable to define the minimum age of entries that will be considered
+ * to be evicted in seconds. -1 to disable the feature.
+ */
+int catalog_cache_prune_min_age = -1;
+
/* Cache management header --- pointer is NULL until created */
static CatCacheHeader *CacheHdr = NULL;
+/* Clock for the last accessed time of a catcache entry. */
+TimestampTz catcacheclock = 0;
+
static inline HeapTuple SearchCatCacheInternal(CatCache *cache,
int nkeys,
Datum v1, Datum v2,
@@ -74,6 +84,7 @@ static pg_noinline HeapTuple SearchCatCacheMiss(CatCache *cache,
Index hashIndex,
Datum v1, Datum v2,
Datum v3, Datum v4);
+static bool CatCacheCleanupOldEntries(CatCache *cp);
static uint32 CatalogCacheComputeHashValue(CatCache *cache, int nkeys,
Datum v1, Datum v2, Datum v3, Datum v4);
@@ -99,7 +110,6 @@ static void CatCacheFreeKeys(TupleDesc tupdesc, int nkeys, int *attnos,
static void CatCacheCopyKeys(TupleDesc tupdesc, int nkeys, int *attnos,
Datum *srckeys, Datum *dstkeys);
-
/*
* internal support functions
*/
@@ -1264,6 +1274,9 @@ SearchCatCacheInternal(CatCache *cache,
*/
dlist_move_head(bucket, &ct->cache_elem);
+ /* Record the last access timestamp */
+ ct->lastaccess = catcacheclock;
+
/*
* If it's a positive entry, bump its refcount and return it. If it's
* negative, we can report failure to the caller.
@@ -1425,6 +1438,66 @@ SearchCatCacheMiss(CatCache *cache,
return &ct->tuple;
}
+/*
+ * CatCacheCleanupOldEntries - Remove infrequently-used entries
+ *
+ * Catcache entries happen to be left unused for a long time for several
+ * reasons. Remove such entries to prevent catcache from bloating. It is based
+ * on the similar algorithm with buffer eviction. Entries that are accessed
+ * several times in a certain period live longer than those that have had less
+ * access in the same duration.
+ */
+static bool
+CatCacheCleanupOldEntries(CatCache *cp)
+{
+ int nremoved = 0;
+ int i;
+ TimestampTz oldest_ts = catcacheclock;
+ TimestampTz prune_threshold;
+
+ if (catalog_cache_prune_min_age < 0)
+ return false;
+
+ prune_threshold = catcacheclock - ((int64) catalog_cache_prune_min_age) * USECS_PER_SEC;
+
+ /* Scan over the whole hash to find entries to remove */
+ for (i = 0 ; i < cp->cc_nbuckets ; i++)
+ {
+ dlist_mutable_iter iter;
+
+ dlist_foreach_modify(iter, &cp->cc_bucket[i])
+ {
+ CatCTup *ct = dlist_container(CatCTup, cache_elem, iter.cur);
+
+ /* Don't remove referenced entries */
+ if (ct->refcount == 0 &&
+ (ct->c_list == NULL || ct->c_list->refcount == 0))
+ {
+ if (ct->lastaccess < prune_threshold)
+ {
+ CatCacheRemoveCTup(cp, ct);
+ nremoved++;
+
+ /* don't let the removed entry update oldest_ts */
+ continue;
+ }
+ }
+
+ /* update the oldest timestamp if the entry remains alive */
+ if (ct->lastaccess < oldest_ts)
+ oldest_ts = ct->lastaccess;
+ }
+ }
+
+ cp->cc_oldest_ts = oldest_ts;
+
+ if (nremoved > 0)
+ elog(DEBUG1, "pruning catalog cache id=%d for %s: removed %d / %d",
+ cp->id, cp->cc_relname, nremoved, cp->cc_ntup + nremoved);
+
+ return nremoved > 0;
+}
+
/*
* ReleaseCatCache
*
@@ -1888,6 +1961,7 @@ CatalogCacheCreateEntry(CatCache *cache, HeapTuple ntp, Datum *arguments,
ct->dead = false;
ct->negative = negative;
ct->hash_value = hashValue;
+ ct->lastaccess = catcacheclock;
dlist_push_head(&cache->cc_bucket[hashIndex], &ct->cache_elem);
@@ -1899,7 +1973,11 @@ CatalogCacheCreateEntry(CatCache *cache, HeapTuple ntp, Datum *arguments,
* arbitrarily, we enlarge when fill factor > 2.
*/
if (cache->cc_ntup > cache->cc_nbuckets * 2)
- RehashCatCache(cache);
+ {
+ /* try removing old entries before expanding hash */
+ if (!CatCacheCleanupOldEntries(cache))
+ RehashCatCache(cache);
+ }
return ct;
}
diff --git a/src/backend/utils/misc/guc.c b/src/backend/utils/misc/guc.c
index 17579eeaca9..f92f0513a6f 100644
--- a/src/backend/utils/misc/guc.c
+++ b/src/backend/utils/misc/guc.c
@@ -88,6 +88,7 @@
#include "utils/acl.h"
#include "utils/builtins.h"
#include "utils/bytea.h"
+#include "utils/catcache.h"
#include "utils/float.h"
#include "utils/guc_tables.h"
#include "utils/memutils.h"
@@ -3445,6 +3446,17 @@ static struct config_int ConfigureNamesInt[] =
NULL, NULL, NULL
},
+ {
+ {"catalog_cache_prune_min_age", PGC_USERSET, RESOURCES_MEM,
+ gettext_noop("System catalog cache entries that are living unused more than this seconds are considered for removal."),
+ gettext_noop("The value of -1 turns off pruning."),
+ GUC_UNIT_S
+ },
+ &catalog_cache_prune_min_age,
+ -1, -1, INT_MAX,
+ NULL, NULL, NULL
+ },
+
/* End-of-list marker */
{
{NULL, 0, 0, NULL, NULL}, NULL, 0, 0, 0, NULL, NULL, NULL
diff --git a/src/include/utils/catcache.h b/src/include/utils/catcache.h
index ddc2762eb3f..57a15bfec22 100644
--- a/src/include/utils/catcache.h
+++ b/src/include/utils/catcache.h
@@ -22,6 +22,7 @@
#include "access/htup.h"
#include "access/skey.h"
+#include "datatype/timestamp.h"
#include "lib/ilist.h"
#include "utils/relcache.h"
@@ -61,6 +62,7 @@ typedef struct catcache
slist_node cc_next; /* list link */
ScanKeyData cc_skey[CATCACHE_MAXKEYS]; /* precomputed key info for heap
* scans */
+ TimestampTz cc_oldest_ts; /* timestamp (us) of the oldest tuple */
/*
* Keep these at the end, so that compiling catcache.c with CATCACHE_STATS
@@ -119,6 +121,7 @@ typedef struct catctup
bool dead; /* dead but not yet removed? */
bool negative; /* negative cache entry? */
HeapTupleData tuple; /* tuple management header */
+ TimestampTz lastaccess; /* timestamp in us of the last usage */
/*
* The tuple may also be a member of at most one CatCList. (If a single
@@ -189,6 +192,20 @@ typedef struct catcacheheader
/* this extern duplicates utils/memutils.h... */
extern PGDLLIMPORT MemoryContext CacheMemoryContext;
+
+/* for guc.c, not PGDLLPMPORT'ed */
+extern int catalog_cache_prune_min_age;
+
+/* source clock for access timestamp of catcache entries */
+extern TimestampTz catcacheclock;
+
+/* SetCatCacheClock - set catcache timestamp source clock */
+static inline void
+SetCatCacheClock(TimestampTz ts)
+{
+ catcacheclock = ts;
+}
+
extern void CreateCacheMemoryContext(void);
extern CatCache *InitCatCache(int id, Oid reloid, Oid indexoid,
--
2.29.2
--------------38A9F5DCD32945DE1430BC1D--
^ permalink raw reply [nested|flat] 92+ messages in thread
* [PATCH v8 1/1] CatCache expiration feature
@ 2021-01-27 11:08 Heikki Linnakangas <[email protected]>
0 siblings, 0 replies; 92+ messages in thread
From: Heikki Linnakangas @ 2021-01-27 11:08 UTC (permalink / raw)
Author: Kyotaro Horiguchi
---
src/backend/access/transam/xact.c | 3 ++
src/backend/utils/cache/catcache.c | 82 +++++++++++++++++++++++++++++-
src/backend/utils/misc/guc.c | 12 +++++
src/include/utils/catcache.h | 17 +++++++
4 files changed, 112 insertions(+), 2 deletions(-)
diff --git a/src/backend/access/transam/xact.c b/src/backend/access/transam/xact.c
index a2068e3fd45..86888d24091 100644
--- a/src/backend/access/transam/xact.c
+++ b/src/backend/access/transam/xact.c
@@ -1086,6 +1086,9 @@ static void
AtStart_Cache(void)
{
AcceptInvalidationMessages();
+
+ if (xactStartTimestamp != 0)
+ SetCatCacheClock(xactStartTimestamp);
}
/*
diff --git a/src/backend/utils/cache/catcache.c b/src/backend/utils/cache/catcache.c
index fa2b49c676e..e29f825687a 100644
--- a/src/backend/utils/cache/catcache.c
+++ b/src/backend/utils/cache/catcache.c
@@ -38,6 +38,7 @@
#include "utils/rel.h"
#include "utils/resowner_private.h"
#include "utils/syscache.h"
+#include "utils/timestamp.h"
/* #define CACHEDEBUG */ /* turns DEBUG elogs on */
@@ -60,9 +61,18 @@
#define CACHE_elog(...)
#endif
+/*
+ * GUC variable to define the minimum age of entries that will be considered
+ * to be evicted in seconds. -1 to disable the feature.
+ */
+int catalog_cache_prune_min_age = -1;
+
/* Cache management header --- pointer is NULL until created */
static CatCacheHeader *CacheHdr = NULL;
+/* Clock for the last accessed time of a catcache entry. */
+TimestampTz catcacheclock = 0;
+
static inline HeapTuple SearchCatCacheInternal(CatCache *cache,
int nkeys,
Datum v1, Datum v2,
@@ -74,6 +84,7 @@ static pg_noinline HeapTuple SearchCatCacheMiss(CatCache *cache,
Index hashIndex,
Datum v1, Datum v2,
Datum v3, Datum v4);
+static bool CatCacheCleanupOldEntries(CatCache *cp);
static uint32 CatalogCacheComputeHashValue(CatCache *cache, int nkeys,
Datum v1, Datum v2, Datum v3, Datum v4);
@@ -99,7 +110,6 @@ static void CatCacheFreeKeys(TupleDesc tupdesc, int nkeys, int *attnos,
static void CatCacheCopyKeys(TupleDesc tupdesc, int nkeys, int *attnos,
Datum *srckeys, Datum *dstkeys);
-
/*
* internal support functions
*/
@@ -1264,6 +1274,9 @@ SearchCatCacheInternal(CatCache *cache,
*/
dlist_move_head(bucket, &ct->cache_elem);
+ /* Record the last access timestamp */
+ ct->lastaccess = catcacheclock;
+
/*
* If it's a positive entry, bump its refcount and return it. If it's
* negative, we can report failure to the caller.
@@ -1425,6 +1438,66 @@ SearchCatCacheMiss(CatCache *cache,
return &ct->tuple;
}
+/*
+ * CatCacheCleanupOldEntries - Remove infrequently-used entries
+ *
+ * Catcache entries happen to be left unused for a long time for several
+ * reasons. Remove such entries to prevent catcache from bloating. It is based
+ * on the similar algorithm with buffer eviction. Entries that are accessed
+ * several times in a certain period live longer than those that have had less
+ * access in the same duration.
+ */
+static bool
+CatCacheCleanupOldEntries(CatCache *cp)
+{
+ int nremoved = 0;
+ int i;
+ TimestampTz oldest_ts = catcacheclock;
+ TimestampTz prune_threshold;
+
+ if (catalog_cache_prune_min_age < 0)
+ return false;
+
+ prune_threshold = catcacheclock - ((int64) catalog_cache_prune_min_age) * USECS_PER_SEC;
+
+ /* Scan over the whole hash to find entries to remove */
+ for (i = 0 ; i < cp->cc_nbuckets ; i++)
+ {
+ dlist_mutable_iter iter;
+
+ dlist_foreach_modify(iter, &cp->cc_bucket[i])
+ {
+ CatCTup *ct = dlist_container(CatCTup, cache_elem, iter.cur);
+
+ /* Don't remove referenced entries */
+ if (ct->refcount == 0 &&
+ (ct->c_list == NULL || ct->c_list->refcount == 0))
+ {
+ if (ct->lastaccess < prune_threshold)
+ {
+ CatCacheRemoveCTup(cp, ct);
+ nremoved++;
+
+ /* don't let the removed entry update oldest_ts */
+ continue;
+ }
+ }
+
+ /* update the oldest timestamp if the entry remains alive */
+ if (ct->lastaccess < oldest_ts)
+ oldest_ts = ct->lastaccess;
+ }
+ }
+
+ cp->cc_oldest_ts = oldest_ts;
+
+ if (nremoved > 0)
+ elog(DEBUG1, "pruning catalog cache id=%d for %s: removed %d / %d",
+ cp->id, cp->cc_relname, nremoved, cp->cc_ntup + nremoved);
+
+ return nremoved > 0;
+}
+
/*
* ReleaseCatCache
*
@@ -1888,6 +1961,7 @@ CatalogCacheCreateEntry(CatCache *cache, HeapTuple ntp, Datum *arguments,
ct->dead = false;
ct->negative = negative;
ct->hash_value = hashValue;
+ ct->lastaccess = catcacheclock;
dlist_push_head(&cache->cc_bucket[hashIndex], &ct->cache_elem);
@@ -1899,7 +1973,11 @@ CatalogCacheCreateEntry(CatCache *cache, HeapTuple ntp, Datum *arguments,
* arbitrarily, we enlarge when fill factor > 2.
*/
if (cache->cc_ntup > cache->cc_nbuckets * 2)
- RehashCatCache(cache);
+ {
+ /* try removing old entries before expanding hash */
+ if (!CatCacheCleanupOldEntries(cache))
+ RehashCatCache(cache);
+ }
return ct;
}
diff --git a/src/backend/utils/misc/guc.c b/src/backend/utils/misc/guc.c
index 17579eeaca9..f92f0513a6f 100644
--- a/src/backend/utils/misc/guc.c
+++ b/src/backend/utils/misc/guc.c
@@ -88,6 +88,7 @@
#include "utils/acl.h"
#include "utils/builtins.h"
#include "utils/bytea.h"
+#include "utils/catcache.h"
#include "utils/float.h"
#include "utils/guc_tables.h"
#include "utils/memutils.h"
@@ -3445,6 +3446,17 @@ static struct config_int ConfigureNamesInt[] =
NULL, NULL, NULL
},
+ {
+ {"catalog_cache_prune_min_age", PGC_USERSET, RESOURCES_MEM,
+ gettext_noop("System catalog cache entries that are living unused more than this seconds are considered for removal."),
+ gettext_noop("The value of -1 turns off pruning."),
+ GUC_UNIT_S
+ },
+ &catalog_cache_prune_min_age,
+ -1, -1, INT_MAX,
+ NULL, NULL, NULL
+ },
+
/* End-of-list marker */
{
{NULL, 0, 0, NULL, NULL}, NULL, 0, 0, 0, NULL, NULL, NULL
diff --git a/src/include/utils/catcache.h b/src/include/utils/catcache.h
index ddc2762eb3f..57a15bfec22 100644
--- a/src/include/utils/catcache.h
+++ b/src/include/utils/catcache.h
@@ -22,6 +22,7 @@
#include "access/htup.h"
#include "access/skey.h"
+#include "datatype/timestamp.h"
#include "lib/ilist.h"
#include "utils/relcache.h"
@@ -61,6 +62,7 @@ typedef struct catcache
slist_node cc_next; /* list link */
ScanKeyData cc_skey[CATCACHE_MAXKEYS]; /* precomputed key info for heap
* scans */
+ TimestampTz cc_oldest_ts; /* timestamp (us) of the oldest tuple */
/*
* Keep these at the end, so that compiling catcache.c with CATCACHE_STATS
@@ -119,6 +121,7 @@ typedef struct catctup
bool dead; /* dead but not yet removed? */
bool negative; /* negative cache entry? */
HeapTupleData tuple; /* tuple management header */
+ TimestampTz lastaccess; /* timestamp in us of the last usage */
/*
* The tuple may also be a member of at most one CatCList. (If a single
@@ -189,6 +192,20 @@ typedef struct catcacheheader
/* this extern duplicates utils/memutils.h... */
extern PGDLLIMPORT MemoryContext CacheMemoryContext;
+
+/* for guc.c, not PGDLLPMPORT'ed */
+extern int catalog_cache_prune_min_age;
+
+/* source clock for access timestamp of catcache entries */
+extern TimestampTz catcacheclock;
+
+/* SetCatCacheClock - set catcache timestamp source clock */
+static inline void
+SetCatCacheClock(TimestampTz ts)
+{
+ catcacheclock = ts;
+}
+
extern void CreateCacheMemoryContext(void);
extern CatCache *InitCatCache(int id, Oid reloid, Oid indexoid,
--
2.29.2
--------------38A9F5DCD32945DE1430BC1D--
^ permalink raw reply [nested|flat] 92+ messages in thread
* [PATCH v8 1/1] CatCache expiration feature
@ 2021-01-27 11:08 Heikki Linnakangas <[email protected]>
0 siblings, 0 replies; 92+ messages in thread
From: Heikki Linnakangas @ 2021-01-27 11:08 UTC (permalink / raw)
Author: Kyotaro Horiguchi
---
src/backend/access/transam/xact.c | 3 ++
src/backend/utils/cache/catcache.c | 82 +++++++++++++++++++++++++++++-
src/backend/utils/misc/guc.c | 12 +++++
src/include/utils/catcache.h | 17 +++++++
4 files changed, 112 insertions(+), 2 deletions(-)
diff --git a/src/backend/access/transam/xact.c b/src/backend/access/transam/xact.c
index a2068e3fd45..86888d24091 100644
--- a/src/backend/access/transam/xact.c
+++ b/src/backend/access/transam/xact.c
@@ -1086,6 +1086,9 @@ static void
AtStart_Cache(void)
{
AcceptInvalidationMessages();
+
+ if (xactStartTimestamp != 0)
+ SetCatCacheClock(xactStartTimestamp);
}
/*
diff --git a/src/backend/utils/cache/catcache.c b/src/backend/utils/cache/catcache.c
index fa2b49c676e..e29f825687a 100644
--- a/src/backend/utils/cache/catcache.c
+++ b/src/backend/utils/cache/catcache.c
@@ -38,6 +38,7 @@
#include "utils/rel.h"
#include "utils/resowner_private.h"
#include "utils/syscache.h"
+#include "utils/timestamp.h"
/* #define CACHEDEBUG */ /* turns DEBUG elogs on */
@@ -60,9 +61,18 @@
#define CACHE_elog(...)
#endif
+/*
+ * GUC variable to define the minimum age of entries that will be considered
+ * to be evicted in seconds. -1 to disable the feature.
+ */
+int catalog_cache_prune_min_age = -1;
+
/* Cache management header --- pointer is NULL until created */
static CatCacheHeader *CacheHdr = NULL;
+/* Clock for the last accessed time of a catcache entry. */
+TimestampTz catcacheclock = 0;
+
static inline HeapTuple SearchCatCacheInternal(CatCache *cache,
int nkeys,
Datum v1, Datum v2,
@@ -74,6 +84,7 @@ static pg_noinline HeapTuple SearchCatCacheMiss(CatCache *cache,
Index hashIndex,
Datum v1, Datum v2,
Datum v3, Datum v4);
+static bool CatCacheCleanupOldEntries(CatCache *cp);
static uint32 CatalogCacheComputeHashValue(CatCache *cache, int nkeys,
Datum v1, Datum v2, Datum v3, Datum v4);
@@ -99,7 +110,6 @@ static void CatCacheFreeKeys(TupleDesc tupdesc, int nkeys, int *attnos,
static void CatCacheCopyKeys(TupleDesc tupdesc, int nkeys, int *attnos,
Datum *srckeys, Datum *dstkeys);
-
/*
* internal support functions
*/
@@ -1264,6 +1274,9 @@ SearchCatCacheInternal(CatCache *cache,
*/
dlist_move_head(bucket, &ct->cache_elem);
+ /* Record the last access timestamp */
+ ct->lastaccess = catcacheclock;
+
/*
* If it's a positive entry, bump its refcount and return it. If it's
* negative, we can report failure to the caller.
@@ -1425,6 +1438,66 @@ SearchCatCacheMiss(CatCache *cache,
return &ct->tuple;
}
+/*
+ * CatCacheCleanupOldEntries - Remove infrequently-used entries
+ *
+ * Catcache entries happen to be left unused for a long time for several
+ * reasons. Remove such entries to prevent catcache from bloating. It is based
+ * on the similar algorithm with buffer eviction. Entries that are accessed
+ * several times in a certain period live longer than those that have had less
+ * access in the same duration.
+ */
+static bool
+CatCacheCleanupOldEntries(CatCache *cp)
+{
+ int nremoved = 0;
+ int i;
+ TimestampTz oldest_ts = catcacheclock;
+ TimestampTz prune_threshold;
+
+ if (catalog_cache_prune_min_age < 0)
+ return false;
+
+ prune_threshold = catcacheclock - ((int64) catalog_cache_prune_min_age) * USECS_PER_SEC;
+
+ /* Scan over the whole hash to find entries to remove */
+ for (i = 0 ; i < cp->cc_nbuckets ; i++)
+ {
+ dlist_mutable_iter iter;
+
+ dlist_foreach_modify(iter, &cp->cc_bucket[i])
+ {
+ CatCTup *ct = dlist_container(CatCTup, cache_elem, iter.cur);
+
+ /* Don't remove referenced entries */
+ if (ct->refcount == 0 &&
+ (ct->c_list == NULL || ct->c_list->refcount == 0))
+ {
+ if (ct->lastaccess < prune_threshold)
+ {
+ CatCacheRemoveCTup(cp, ct);
+ nremoved++;
+
+ /* don't let the removed entry update oldest_ts */
+ continue;
+ }
+ }
+
+ /* update the oldest timestamp if the entry remains alive */
+ if (ct->lastaccess < oldest_ts)
+ oldest_ts = ct->lastaccess;
+ }
+ }
+
+ cp->cc_oldest_ts = oldest_ts;
+
+ if (nremoved > 0)
+ elog(DEBUG1, "pruning catalog cache id=%d for %s: removed %d / %d",
+ cp->id, cp->cc_relname, nremoved, cp->cc_ntup + nremoved);
+
+ return nremoved > 0;
+}
+
/*
* ReleaseCatCache
*
@@ -1888,6 +1961,7 @@ CatalogCacheCreateEntry(CatCache *cache, HeapTuple ntp, Datum *arguments,
ct->dead = false;
ct->negative = negative;
ct->hash_value = hashValue;
+ ct->lastaccess = catcacheclock;
dlist_push_head(&cache->cc_bucket[hashIndex], &ct->cache_elem);
@@ -1899,7 +1973,11 @@ CatalogCacheCreateEntry(CatCache *cache, HeapTuple ntp, Datum *arguments,
* arbitrarily, we enlarge when fill factor > 2.
*/
if (cache->cc_ntup > cache->cc_nbuckets * 2)
- RehashCatCache(cache);
+ {
+ /* try removing old entries before expanding hash */
+ if (!CatCacheCleanupOldEntries(cache))
+ RehashCatCache(cache);
+ }
return ct;
}
diff --git a/src/backend/utils/misc/guc.c b/src/backend/utils/misc/guc.c
index 17579eeaca9..f92f0513a6f 100644
--- a/src/backend/utils/misc/guc.c
+++ b/src/backend/utils/misc/guc.c
@@ -88,6 +88,7 @@
#include "utils/acl.h"
#include "utils/builtins.h"
#include "utils/bytea.h"
+#include "utils/catcache.h"
#include "utils/float.h"
#include "utils/guc_tables.h"
#include "utils/memutils.h"
@@ -3445,6 +3446,17 @@ static struct config_int ConfigureNamesInt[] =
NULL, NULL, NULL
},
+ {
+ {"catalog_cache_prune_min_age", PGC_USERSET, RESOURCES_MEM,
+ gettext_noop("System catalog cache entries that are living unused more than this seconds are considered for removal."),
+ gettext_noop("The value of -1 turns off pruning."),
+ GUC_UNIT_S
+ },
+ &catalog_cache_prune_min_age,
+ -1, -1, INT_MAX,
+ NULL, NULL, NULL
+ },
+
/* End-of-list marker */
{
{NULL, 0, 0, NULL, NULL}, NULL, 0, 0, 0, NULL, NULL, NULL
diff --git a/src/include/utils/catcache.h b/src/include/utils/catcache.h
index ddc2762eb3f..57a15bfec22 100644
--- a/src/include/utils/catcache.h
+++ b/src/include/utils/catcache.h
@@ -22,6 +22,7 @@
#include "access/htup.h"
#include "access/skey.h"
+#include "datatype/timestamp.h"
#include "lib/ilist.h"
#include "utils/relcache.h"
@@ -61,6 +62,7 @@ typedef struct catcache
slist_node cc_next; /* list link */
ScanKeyData cc_skey[CATCACHE_MAXKEYS]; /* precomputed key info for heap
* scans */
+ TimestampTz cc_oldest_ts; /* timestamp (us) of the oldest tuple */
/*
* Keep these at the end, so that compiling catcache.c with CATCACHE_STATS
@@ -119,6 +121,7 @@ typedef struct catctup
bool dead; /* dead but not yet removed? */
bool negative; /* negative cache entry? */
HeapTupleData tuple; /* tuple management header */
+ TimestampTz lastaccess; /* timestamp in us of the last usage */
/*
* The tuple may also be a member of at most one CatCList. (If a single
@@ -189,6 +192,20 @@ typedef struct catcacheheader
/* this extern duplicates utils/memutils.h... */
extern PGDLLIMPORT MemoryContext CacheMemoryContext;
+
+/* for guc.c, not PGDLLPMPORT'ed */
+extern int catalog_cache_prune_min_age;
+
+/* source clock for access timestamp of catcache entries */
+extern TimestampTz catcacheclock;
+
+/* SetCatCacheClock - set catcache timestamp source clock */
+static inline void
+SetCatCacheClock(TimestampTz ts)
+{
+ catcacheclock = ts;
+}
+
extern void CreateCacheMemoryContext(void);
extern CatCache *InitCatCache(int id, Oid reloid, Oid indexoid,
--
2.29.2
--------------38A9F5DCD32945DE1430BC1D--
^ permalink raw reply [nested|flat] 92+ messages in thread
* [PATCH v8 1/1] CatCache expiration feature
@ 2021-01-27 11:08 Heikki Linnakangas <[email protected]>
0 siblings, 0 replies; 92+ messages in thread
From: Heikki Linnakangas @ 2021-01-27 11:08 UTC (permalink / raw)
Author: Kyotaro Horiguchi
---
src/backend/access/transam/xact.c | 3 ++
src/backend/utils/cache/catcache.c | 82 +++++++++++++++++++++++++++++-
src/backend/utils/misc/guc.c | 12 +++++
src/include/utils/catcache.h | 17 +++++++
4 files changed, 112 insertions(+), 2 deletions(-)
diff --git a/src/backend/access/transam/xact.c b/src/backend/access/transam/xact.c
index a2068e3fd45..86888d24091 100644
--- a/src/backend/access/transam/xact.c
+++ b/src/backend/access/transam/xact.c
@@ -1086,6 +1086,9 @@ static void
AtStart_Cache(void)
{
AcceptInvalidationMessages();
+
+ if (xactStartTimestamp != 0)
+ SetCatCacheClock(xactStartTimestamp);
}
/*
diff --git a/src/backend/utils/cache/catcache.c b/src/backend/utils/cache/catcache.c
index fa2b49c676e..e29f825687a 100644
--- a/src/backend/utils/cache/catcache.c
+++ b/src/backend/utils/cache/catcache.c
@@ -38,6 +38,7 @@
#include "utils/rel.h"
#include "utils/resowner_private.h"
#include "utils/syscache.h"
+#include "utils/timestamp.h"
/* #define CACHEDEBUG */ /* turns DEBUG elogs on */
@@ -60,9 +61,18 @@
#define CACHE_elog(...)
#endif
+/*
+ * GUC variable to define the minimum age of entries that will be considered
+ * to be evicted in seconds. -1 to disable the feature.
+ */
+int catalog_cache_prune_min_age = -1;
+
/* Cache management header --- pointer is NULL until created */
static CatCacheHeader *CacheHdr = NULL;
+/* Clock for the last accessed time of a catcache entry. */
+TimestampTz catcacheclock = 0;
+
static inline HeapTuple SearchCatCacheInternal(CatCache *cache,
int nkeys,
Datum v1, Datum v2,
@@ -74,6 +84,7 @@ static pg_noinline HeapTuple SearchCatCacheMiss(CatCache *cache,
Index hashIndex,
Datum v1, Datum v2,
Datum v3, Datum v4);
+static bool CatCacheCleanupOldEntries(CatCache *cp);
static uint32 CatalogCacheComputeHashValue(CatCache *cache, int nkeys,
Datum v1, Datum v2, Datum v3, Datum v4);
@@ -99,7 +110,6 @@ static void CatCacheFreeKeys(TupleDesc tupdesc, int nkeys, int *attnos,
static void CatCacheCopyKeys(TupleDesc tupdesc, int nkeys, int *attnos,
Datum *srckeys, Datum *dstkeys);
-
/*
* internal support functions
*/
@@ -1264,6 +1274,9 @@ SearchCatCacheInternal(CatCache *cache,
*/
dlist_move_head(bucket, &ct->cache_elem);
+ /* Record the last access timestamp */
+ ct->lastaccess = catcacheclock;
+
/*
* If it's a positive entry, bump its refcount and return it. If it's
* negative, we can report failure to the caller.
@@ -1425,6 +1438,66 @@ SearchCatCacheMiss(CatCache *cache,
return &ct->tuple;
}
+/*
+ * CatCacheCleanupOldEntries - Remove infrequently-used entries
+ *
+ * Catcache entries happen to be left unused for a long time for several
+ * reasons. Remove such entries to prevent catcache from bloating. It is based
+ * on the similar algorithm with buffer eviction. Entries that are accessed
+ * several times in a certain period live longer than those that have had less
+ * access in the same duration.
+ */
+static bool
+CatCacheCleanupOldEntries(CatCache *cp)
+{
+ int nremoved = 0;
+ int i;
+ TimestampTz oldest_ts = catcacheclock;
+ TimestampTz prune_threshold;
+
+ if (catalog_cache_prune_min_age < 0)
+ return false;
+
+ prune_threshold = catcacheclock - ((int64) catalog_cache_prune_min_age) * USECS_PER_SEC;
+
+ /* Scan over the whole hash to find entries to remove */
+ for (i = 0 ; i < cp->cc_nbuckets ; i++)
+ {
+ dlist_mutable_iter iter;
+
+ dlist_foreach_modify(iter, &cp->cc_bucket[i])
+ {
+ CatCTup *ct = dlist_container(CatCTup, cache_elem, iter.cur);
+
+ /* Don't remove referenced entries */
+ if (ct->refcount == 0 &&
+ (ct->c_list == NULL || ct->c_list->refcount == 0))
+ {
+ if (ct->lastaccess < prune_threshold)
+ {
+ CatCacheRemoveCTup(cp, ct);
+ nremoved++;
+
+ /* don't let the removed entry update oldest_ts */
+ continue;
+ }
+ }
+
+ /* update the oldest timestamp if the entry remains alive */
+ if (ct->lastaccess < oldest_ts)
+ oldest_ts = ct->lastaccess;
+ }
+ }
+
+ cp->cc_oldest_ts = oldest_ts;
+
+ if (nremoved > 0)
+ elog(DEBUG1, "pruning catalog cache id=%d for %s: removed %d / %d",
+ cp->id, cp->cc_relname, nremoved, cp->cc_ntup + nremoved);
+
+ return nremoved > 0;
+}
+
/*
* ReleaseCatCache
*
@@ -1888,6 +1961,7 @@ CatalogCacheCreateEntry(CatCache *cache, HeapTuple ntp, Datum *arguments,
ct->dead = false;
ct->negative = negative;
ct->hash_value = hashValue;
+ ct->lastaccess = catcacheclock;
dlist_push_head(&cache->cc_bucket[hashIndex], &ct->cache_elem);
@@ -1899,7 +1973,11 @@ CatalogCacheCreateEntry(CatCache *cache, HeapTuple ntp, Datum *arguments,
* arbitrarily, we enlarge when fill factor > 2.
*/
if (cache->cc_ntup > cache->cc_nbuckets * 2)
- RehashCatCache(cache);
+ {
+ /* try removing old entries before expanding hash */
+ if (!CatCacheCleanupOldEntries(cache))
+ RehashCatCache(cache);
+ }
return ct;
}
diff --git a/src/backend/utils/misc/guc.c b/src/backend/utils/misc/guc.c
index 17579eeaca9..f92f0513a6f 100644
--- a/src/backend/utils/misc/guc.c
+++ b/src/backend/utils/misc/guc.c
@@ -88,6 +88,7 @@
#include "utils/acl.h"
#include "utils/builtins.h"
#include "utils/bytea.h"
+#include "utils/catcache.h"
#include "utils/float.h"
#include "utils/guc_tables.h"
#include "utils/memutils.h"
@@ -3445,6 +3446,17 @@ static struct config_int ConfigureNamesInt[] =
NULL, NULL, NULL
},
+ {
+ {"catalog_cache_prune_min_age", PGC_USERSET, RESOURCES_MEM,
+ gettext_noop("System catalog cache entries that are living unused more than this seconds are considered for removal."),
+ gettext_noop("The value of -1 turns off pruning."),
+ GUC_UNIT_S
+ },
+ &catalog_cache_prune_min_age,
+ -1, -1, INT_MAX,
+ NULL, NULL, NULL
+ },
+
/* End-of-list marker */
{
{NULL, 0, 0, NULL, NULL}, NULL, 0, 0, 0, NULL, NULL, NULL
diff --git a/src/include/utils/catcache.h b/src/include/utils/catcache.h
index ddc2762eb3f..57a15bfec22 100644
--- a/src/include/utils/catcache.h
+++ b/src/include/utils/catcache.h
@@ -22,6 +22,7 @@
#include "access/htup.h"
#include "access/skey.h"
+#include "datatype/timestamp.h"
#include "lib/ilist.h"
#include "utils/relcache.h"
@@ -61,6 +62,7 @@ typedef struct catcache
slist_node cc_next; /* list link */
ScanKeyData cc_skey[CATCACHE_MAXKEYS]; /* precomputed key info for heap
* scans */
+ TimestampTz cc_oldest_ts; /* timestamp (us) of the oldest tuple */
/*
* Keep these at the end, so that compiling catcache.c with CATCACHE_STATS
@@ -119,6 +121,7 @@ typedef struct catctup
bool dead; /* dead but not yet removed? */
bool negative; /* negative cache entry? */
HeapTupleData tuple; /* tuple management header */
+ TimestampTz lastaccess; /* timestamp in us of the last usage */
/*
* The tuple may also be a member of at most one CatCList. (If a single
@@ -189,6 +192,20 @@ typedef struct catcacheheader
/* this extern duplicates utils/memutils.h... */
extern PGDLLIMPORT MemoryContext CacheMemoryContext;
+
+/* for guc.c, not PGDLLPMPORT'ed */
+extern int catalog_cache_prune_min_age;
+
+/* source clock for access timestamp of catcache entries */
+extern TimestampTz catcacheclock;
+
+/* SetCatCacheClock - set catcache timestamp source clock */
+static inline void
+SetCatCacheClock(TimestampTz ts)
+{
+ catcacheclock = ts;
+}
+
extern void CreateCacheMemoryContext(void);
extern CatCache *InitCatCache(int id, Oid reloid, Oid indexoid,
--
2.29.2
--------------38A9F5DCD32945DE1430BC1D--
^ permalink raw reply [nested|flat] 92+ messages in thread
* [PATCH v8 1/1] CatCache expiration feature
@ 2021-01-27 11:08 Heikki Linnakangas <[email protected]>
0 siblings, 0 replies; 92+ messages in thread
From: Heikki Linnakangas @ 2021-01-27 11:08 UTC (permalink / raw)
Author: Kyotaro Horiguchi
---
src/backend/access/transam/xact.c | 3 ++
src/backend/utils/cache/catcache.c | 82 +++++++++++++++++++++++++++++-
src/backend/utils/misc/guc.c | 12 +++++
src/include/utils/catcache.h | 17 +++++++
4 files changed, 112 insertions(+), 2 deletions(-)
diff --git a/src/backend/access/transam/xact.c b/src/backend/access/transam/xact.c
index a2068e3fd45..86888d24091 100644
--- a/src/backend/access/transam/xact.c
+++ b/src/backend/access/transam/xact.c
@@ -1086,6 +1086,9 @@ static void
AtStart_Cache(void)
{
AcceptInvalidationMessages();
+
+ if (xactStartTimestamp != 0)
+ SetCatCacheClock(xactStartTimestamp);
}
/*
diff --git a/src/backend/utils/cache/catcache.c b/src/backend/utils/cache/catcache.c
index fa2b49c676e..e29f825687a 100644
--- a/src/backend/utils/cache/catcache.c
+++ b/src/backend/utils/cache/catcache.c
@@ -38,6 +38,7 @@
#include "utils/rel.h"
#include "utils/resowner_private.h"
#include "utils/syscache.h"
+#include "utils/timestamp.h"
/* #define CACHEDEBUG */ /* turns DEBUG elogs on */
@@ -60,9 +61,18 @@
#define CACHE_elog(...)
#endif
+/*
+ * GUC variable to define the minimum age of entries that will be considered
+ * to be evicted in seconds. -1 to disable the feature.
+ */
+int catalog_cache_prune_min_age = -1;
+
/* Cache management header --- pointer is NULL until created */
static CatCacheHeader *CacheHdr = NULL;
+/* Clock for the last accessed time of a catcache entry. */
+TimestampTz catcacheclock = 0;
+
static inline HeapTuple SearchCatCacheInternal(CatCache *cache,
int nkeys,
Datum v1, Datum v2,
@@ -74,6 +84,7 @@ static pg_noinline HeapTuple SearchCatCacheMiss(CatCache *cache,
Index hashIndex,
Datum v1, Datum v2,
Datum v3, Datum v4);
+static bool CatCacheCleanupOldEntries(CatCache *cp);
static uint32 CatalogCacheComputeHashValue(CatCache *cache, int nkeys,
Datum v1, Datum v2, Datum v3, Datum v4);
@@ -99,7 +110,6 @@ static void CatCacheFreeKeys(TupleDesc tupdesc, int nkeys, int *attnos,
static void CatCacheCopyKeys(TupleDesc tupdesc, int nkeys, int *attnos,
Datum *srckeys, Datum *dstkeys);
-
/*
* internal support functions
*/
@@ -1264,6 +1274,9 @@ SearchCatCacheInternal(CatCache *cache,
*/
dlist_move_head(bucket, &ct->cache_elem);
+ /* Record the last access timestamp */
+ ct->lastaccess = catcacheclock;
+
/*
* If it's a positive entry, bump its refcount and return it. If it's
* negative, we can report failure to the caller.
@@ -1425,6 +1438,66 @@ SearchCatCacheMiss(CatCache *cache,
return &ct->tuple;
}
+/*
+ * CatCacheCleanupOldEntries - Remove infrequently-used entries
+ *
+ * Catcache entries happen to be left unused for a long time for several
+ * reasons. Remove such entries to prevent catcache from bloating. It is based
+ * on the similar algorithm with buffer eviction. Entries that are accessed
+ * several times in a certain period live longer than those that have had less
+ * access in the same duration.
+ */
+static bool
+CatCacheCleanupOldEntries(CatCache *cp)
+{
+ int nremoved = 0;
+ int i;
+ TimestampTz oldest_ts = catcacheclock;
+ TimestampTz prune_threshold;
+
+ if (catalog_cache_prune_min_age < 0)
+ return false;
+
+ prune_threshold = catcacheclock - ((int64) catalog_cache_prune_min_age) * USECS_PER_SEC;
+
+ /* Scan over the whole hash to find entries to remove */
+ for (i = 0 ; i < cp->cc_nbuckets ; i++)
+ {
+ dlist_mutable_iter iter;
+
+ dlist_foreach_modify(iter, &cp->cc_bucket[i])
+ {
+ CatCTup *ct = dlist_container(CatCTup, cache_elem, iter.cur);
+
+ /* Don't remove referenced entries */
+ if (ct->refcount == 0 &&
+ (ct->c_list == NULL || ct->c_list->refcount == 0))
+ {
+ if (ct->lastaccess < prune_threshold)
+ {
+ CatCacheRemoveCTup(cp, ct);
+ nremoved++;
+
+ /* don't let the removed entry update oldest_ts */
+ continue;
+ }
+ }
+
+ /* update the oldest timestamp if the entry remains alive */
+ if (ct->lastaccess < oldest_ts)
+ oldest_ts = ct->lastaccess;
+ }
+ }
+
+ cp->cc_oldest_ts = oldest_ts;
+
+ if (nremoved > 0)
+ elog(DEBUG1, "pruning catalog cache id=%d for %s: removed %d / %d",
+ cp->id, cp->cc_relname, nremoved, cp->cc_ntup + nremoved);
+
+ return nremoved > 0;
+}
+
/*
* ReleaseCatCache
*
@@ -1888,6 +1961,7 @@ CatalogCacheCreateEntry(CatCache *cache, HeapTuple ntp, Datum *arguments,
ct->dead = false;
ct->negative = negative;
ct->hash_value = hashValue;
+ ct->lastaccess = catcacheclock;
dlist_push_head(&cache->cc_bucket[hashIndex], &ct->cache_elem);
@@ -1899,7 +1973,11 @@ CatalogCacheCreateEntry(CatCache *cache, HeapTuple ntp, Datum *arguments,
* arbitrarily, we enlarge when fill factor > 2.
*/
if (cache->cc_ntup > cache->cc_nbuckets * 2)
- RehashCatCache(cache);
+ {
+ /* try removing old entries before expanding hash */
+ if (!CatCacheCleanupOldEntries(cache))
+ RehashCatCache(cache);
+ }
return ct;
}
diff --git a/src/backend/utils/misc/guc.c b/src/backend/utils/misc/guc.c
index 17579eeaca9..f92f0513a6f 100644
--- a/src/backend/utils/misc/guc.c
+++ b/src/backend/utils/misc/guc.c
@@ -88,6 +88,7 @@
#include "utils/acl.h"
#include "utils/builtins.h"
#include "utils/bytea.h"
+#include "utils/catcache.h"
#include "utils/float.h"
#include "utils/guc_tables.h"
#include "utils/memutils.h"
@@ -3445,6 +3446,17 @@ static struct config_int ConfigureNamesInt[] =
NULL, NULL, NULL
},
+ {
+ {"catalog_cache_prune_min_age", PGC_USERSET, RESOURCES_MEM,
+ gettext_noop("System catalog cache entries that are living unused more than this seconds are considered for removal."),
+ gettext_noop("The value of -1 turns off pruning."),
+ GUC_UNIT_S
+ },
+ &catalog_cache_prune_min_age,
+ -1, -1, INT_MAX,
+ NULL, NULL, NULL
+ },
+
/* End-of-list marker */
{
{NULL, 0, 0, NULL, NULL}, NULL, 0, 0, 0, NULL, NULL, NULL
diff --git a/src/include/utils/catcache.h b/src/include/utils/catcache.h
index ddc2762eb3f..57a15bfec22 100644
--- a/src/include/utils/catcache.h
+++ b/src/include/utils/catcache.h
@@ -22,6 +22,7 @@
#include "access/htup.h"
#include "access/skey.h"
+#include "datatype/timestamp.h"
#include "lib/ilist.h"
#include "utils/relcache.h"
@@ -61,6 +62,7 @@ typedef struct catcache
slist_node cc_next; /* list link */
ScanKeyData cc_skey[CATCACHE_MAXKEYS]; /* precomputed key info for heap
* scans */
+ TimestampTz cc_oldest_ts; /* timestamp (us) of the oldest tuple */
/*
* Keep these at the end, so that compiling catcache.c with CATCACHE_STATS
@@ -119,6 +121,7 @@ typedef struct catctup
bool dead; /* dead but not yet removed? */
bool negative; /* negative cache entry? */
HeapTupleData tuple; /* tuple management header */
+ TimestampTz lastaccess; /* timestamp in us of the last usage */
/*
* The tuple may also be a member of at most one CatCList. (If a single
@@ -189,6 +192,20 @@ typedef struct catcacheheader
/* this extern duplicates utils/memutils.h... */
extern PGDLLIMPORT MemoryContext CacheMemoryContext;
+
+/* for guc.c, not PGDLLPMPORT'ed */
+extern int catalog_cache_prune_min_age;
+
+/* source clock for access timestamp of catcache entries */
+extern TimestampTz catcacheclock;
+
+/* SetCatCacheClock - set catcache timestamp source clock */
+static inline void
+SetCatCacheClock(TimestampTz ts)
+{
+ catcacheclock = ts;
+}
+
extern void CreateCacheMemoryContext(void);
extern CatCache *InitCatCache(int id, Oid reloid, Oid indexoid,
--
2.29.2
--------------38A9F5DCD32945DE1430BC1D--
^ permalink raw reply [nested|flat] 92+ messages in thread
* [PATCH v8 1/1] CatCache expiration feature
@ 2021-01-27 11:08 Heikki Linnakangas <[email protected]>
0 siblings, 0 replies; 92+ messages in thread
From: Heikki Linnakangas @ 2021-01-27 11:08 UTC (permalink / raw)
Author: Kyotaro Horiguchi
---
src/backend/access/transam/xact.c | 3 ++
src/backend/utils/cache/catcache.c | 82 +++++++++++++++++++++++++++++-
src/backend/utils/misc/guc.c | 12 +++++
src/include/utils/catcache.h | 17 +++++++
4 files changed, 112 insertions(+), 2 deletions(-)
diff --git a/src/backend/access/transam/xact.c b/src/backend/access/transam/xact.c
index a2068e3fd45..86888d24091 100644
--- a/src/backend/access/transam/xact.c
+++ b/src/backend/access/transam/xact.c
@@ -1086,6 +1086,9 @@ static void
AtStart_Cache(void)
{
AcceptInvalidationMessages();
+
+ if (xactStartTimestamp != 0)
+ SetCatCacheClock(xactStartTimestamp);
}
/*
diff --git a/src/backend/utils/cache/catcache.c b/src/backend/utils/cache/catcache.c
index fa2b49c676e..e29f825687a 100644
--- a/src/backend/utils/cache/catcache.c
+++ b/src/backend/utils/cache/catcache.c
@@ -38,6 +38,7 @@
#include "utils/rel.h"
#include "utils/resowner_private.h"
#include "utils/syscache.h"
+#include "utils/timestamp.h"
/* #define CACHEDEBUG */ /* turns DEBUG elogs on */
@@ -60,9 +61,18 @@
#define CACHE_elog(...)
#endif
+/*
+ * GUC variable to define the minimum age of entries that will be considered
+ * to be evicted in seconds. -1 to disable the feature.
+ */
+int catalog_cache_prune_min_age = -1;
+
/* Cache management header --- pointer is NULL until created */
static CatCacheHeader *CacheHdr = NULL;
+/* Clock for the last accessed time of a catcache entry. */
+TimestampTz catcacheclock = 0;
+
static inline HeapTuple SearchCatCacheInternal(CatCache *cache,
int nkeys,
Datum v1, Datum v2,
@@ -74,6 +84,7 @@ static pg_noinline HeapTuple SearchCatCacheMiss(CatCache *cache,
Index hashIndex,
Datum v1, Datum v2,
Datum v3, Datum v4);
+static bool CatCacheCleanupOldEntries(CatCache *cp);
static uint32 CatalogCacheComputeHashValue(CatCache *cache, int nkeys,
Datum v1, Datum v2, Datum v3, Datum v4);
@@ -99,7 +110,6 @@ static void CatCacheFreeKeys(TupleDesc tupdesc, int nkeys, int *attnos,
static void CatCacheCopyKeys(TupleDesc tupdesc, int nkeys, int *attnos,
Datum *srckeys, Datum *dstkeys);
-
/*
* internal support functions
*/
@@ -1264,6 +1274,9 @@ SearchCatCacheInternal(CatCache *cache,
*/
dlist_move_head(bucket, &ct->cache_elem);
+ /* Record the last access timestamp */
+ ct->lastaccess = catcacheclock;
+
/*
* If it's a positive entry, bump its refcount and return it. If it's
* negative, we can report failure to the caller.
@@ -1425,6 +1438,66 @@ SearchCatCacheMiss(CatCache *cache,
return &ct->tuple;
}
+/*
+ * CatCacheCleanupOldEntries - Remove infrequently-used entries
+ *
+ * Catcache entries happen to be left unused for a long time for several
+ * reasons. Remove such entries to prevent catcache from bloating. It is based
+ * on the similar algorithm with buffer eviction. Entries that are accessed
+ * several times in a certain period live longer than those that have had less
+ * access in the same duration.
+ */
+static bool
+CatCacheCleanupOldEntries(CatCache *cp)
+{
+ int nremoved = 0;
+ int i;
+ TimestampTz oldest_ts = catcacheclock;
+ TimestampTz prune_threshold;
+
+ if (catalog_cache_prune_min_age < 0)
+ return false;
+
+ prune_threshold = catcacheclock - ((int64) catalog_cache_prune_min_age) * USECS_PER_SEC;
+
+ /* Scan over the whole hash to find entries to remove */
+ for (i = 0 ; i < cp->cc_nbuckets ; i++)
+ {
+ dlist_mutable_iter iter;
+
+ dlist_foreach_modify(iter, &cp->cc_bucket[i])
+ {
+ CatCTup *ct = dlist_container(CatCTup, cache_elem, iter.cur);
+
+ /* Don't remove referenced entries */
+ if (ct->refcount == 0 &&
+ (ct->c_list == NULL || ct->c_list->refcount == 0))
+ {
+ if (ct->lastaccess < prune_threshold)
+ {
+ CatCacheRemoveCTup(cp, ct);
+ nremoved++;
+
+ /* don't let the removed entry update oldest_ts */
+ continue;
+ }
+ }
+
+ /* update the oldest timestamp if the entry remains alive */
+ if (ct->lastaccess < oldest_ts)
+ oldest_ts = ct->lastaccess;
+ }
+ }
+
+ cp->cc_oldest_ts = oldest_ts;
+
+ if (nremoved > 0)
+ elog(DEBUG1, "pruning catalog cache id=%d for %s: removed %d / %d",
+ cp->id, cp->cc_relname, nremoved, cp->cc_ntup + nremoved);
+
+ return nremoved > 0;
+}
+
/*
* ReleaseCatCache
*
@@ -1888,6 +1961,7 @@ CatalogCacheCreateEntry(CatCache *cache, HeapTuple ntp, Datum *arguments,
ct->dead = false;
ct->negative = negative;
ct->hash_value = hashValue;
+ ct->lastaccess = catcacheclock;
dlist_push_head(&cache->cc_bucket[hashIndex], &ct->cache_elem);
@@ -1899,7 +1973,11 @@ CatalogCacheCreateEntry(CatCache *cache, HeapTuple ntp, Datum *arguments,
* arbitrarily, we enlarge when fill factor > 2.
*/
if (cache->cc_ntup > cache->cc_nbuckets * 2)
- RehashCatCache(cache);
+ {
+ /* try removing old entries before expanding hash */
+ if (!CatCacheCleanupOldEntries(cache))
+ RehashCatCache(cache);
+ }
return ct;
}
diff --git a/src/backend/utils/misc/guc.c b/src/backend/utils/misc/guc.c
index 17579eeaca9..f92f0513a6f 100644
--- a/src/backend/utils/misc/guc.c
+++ b/src/backend/utils/misc/guc.c
@@ -88,6 +88,7 @@
#include "utils/acl.h"
#include "utils/builtins.h"
#include "utils/bytea.h"
+#include "utils/catcache.h"
#include "utils/float.h"
#include "utils/guc_tables.h"
#include "utils/memutils.h"
@@ -3445,6 +3446,17 @@ static struct config_int ConfigureNamesInt[] =
NULL, NULL, NULL
},
+ {
+ {"catalog_cache_prune_min_age", PGC_USERSET, RESOURCES_MEM,
+ gettext_noop("System catalog cache entries that are living unused more than this seconds are considered for removal."),
+ gettext_noop("The value of -1 turns off pruning."),
+ GUC_UNIT_S
+ },
+ &catalog_cache_prune_min_age,
+ -1, -1, INT_MAX,
+ NULL, NULL, NULL
+ },
+
/* End-of-list marker */
{
{NULL, 0, 0, NULL, NULL}, NULL, 0, 0, 0, NULL, NULL, NULL
diff --git a/src/include/utils/catcache.h b/src/include/utils/catcache.h
index ddc2762eb3f..57a15bfec22 100644
--- a/src/include/utils/catcache.h
+++ b/src/include/utils/catcache.h
@@ -22,6 +22,7 @@
#include "access/htup.h"
#include "access/skey.h"
+#include "datatype/timestamp.h"
#include "lib/ilist.h"
#include "utils/relcache.h"
@@ -61,6 +62,7 @@ typedef struct catcache
slist_node cc_next; /* list link */
ScanKeyData cc_skey[CATCACHE_MAXKEYS]; /* precomputed key info for heap
* scans */
+ TimestampTz cc_oldest_ts; /* timestamp (us) of the oldest tuple */
/*
* Keep these at the end, so that compiling catcache.c with CATCACHE_STATS
@@ -119,6 +121,7 @@ typedef struct catctup
bool dead; /* dead but not yet removed? */
bool negative; /* negative cache entry? */
HeapTupleData tuple; /* tuple management header */
+ TimestampTz lastaccess; /* timestamp in us of the last usage */
/*
* The tuple may also be a member of at most one CatCList. (If a single
@@ -189,6 +192,20 @@ typedef struct catcacheheader
/* this extern duplicates utils/memutils.h... */
extern PGDLLIMPORT MemoryContext CacheMemoryContext;
+
+/* for guc.c, not PGDLLPMPORT'ed */
+extern int catalog_cache_prune_min_age;
+
+/* source clock for access timestamp of catcache entries */
+extern TimestampTz catcacheclock;
+
+/* SetCatCacheClock - set catcache timestamp source clock */
+static inline void
+SetCatCacheClock(TimestampTz ts)
+{
+ catcacheclock = ts;
+}
+
extern void CreateCacheMemoryContext(void);
extern CatCache *InitCatCache(int id, Oid reloid, Oid indexoid,
--
2.29.2
--------------38A9F5DCD32945DE1430BC1D--
^ permalink raw reply [nested|flat] 92+ messages in thread
* [PATCH v8 1/1] CatCache expiration feature
@ 2021-01-27 11:08 Heikki Linnakangas <[email protected]>
0 siblings, 0 replies; 92+ messages in thread
From: Heikki Linnakangas @ 2021-01-27 11:08 UTC (permalink / raw)
Author: Kyotaro Horiguchi
---
src/backend/access/transam/xact.c | 3 ++
src/backend/utils/cache/catcache.c | 82 +++++++++++++++++++++++++++++-
src/backend/utils/misc/guc.c | 12 +++++
src/include/utils/catcache.h | 17 +++++++
4 files changed, 112 insertions(+), 2 deletions(-)
diff --git a/src/backend/access/transam/xact.c b/src/backend/access/transam/xact.c
index a2068e3fd45..86888d24091 100644
--- a/src/backend/access/transam/xact.c
+++ b/src/backend/access/transam/xact.c
@@ -1086,6 +1086,9 @@ static void
AtStart_Cache(void)
{
AcceptInvalidationMessages();
+
+ if (xactStartTimestamp != 0)
+ SetCatCacheClock(xactStartTimestamp);
}
/*
diff --git a/src/backend/utils/cache/catcache.c b/src/backend/utils/cache/catcache.c
index fa2b49c676e..e29f825687a 100644
--- a/src/backend/utils/cache/catcache.c
+++ b/src/backend/utils/cache/catcache.c
@@ -38,6 +38,7 @@
#include "utils/rel.h"
#include "utils/resowner_private.h"
#include "utils/syscache.h"
+#include "utils/timestamp.h"
/* #define CACHEDEBUG */ /* turns DEBUG elogs on */
@@ -60,9 +61,18 @@
#define CACHE_elog(...)
#endif
+/*
+ * GUC variable to define the minimum age of entries that will be considered
+ * to be evicted in seconds. -1 to disable the feature.
+ */
+int catalog_cache_prune_min_age = -1;
+
/* Cache management header --- pointer is NULL until created */
static CatCacheHeader *CacheHdr = NULL;
+/* Clock for the last accessed time of a catcache entry. */
+TimestampTz catcacheclock = 0;
+
static inline HeapTuple SearchCatCacheInternal(CatCache *cache,
int nkeys,
Datum v1, Datum v2,
@@ -74,6 +84,7 @@ static pg_noinline HeapTuple SearchCatCacheMiss(CatCache *cache,
Index hashIndex,
Datum v1, Datum v2,
Datum v3, Datum v4);
+static bool CatCacheCleanupOldEntries(CatCache *cp);
static uint32 CatalogCacheComputeHashValue(CatCache *cache, int nkeys,
Datum v1, Datum v2, Datum v3, Datum v4);
@@ -99,7 +110,6 @@ static void CatCacheFreeKeys(TupleDesc tupdesc, int nkeys, int *attnos,
static void CatCacheCopyKeys(TupleDesc tupdesc, int nkeys, int *attnos,
Datum *srckeys, Datum *dstkeys);
-
/*
* internal support functions
*/
@@ -1264,6 +1274,9 @@ SearchCatCacheInternal(CatCache *cache,
*/
dlist_move_head(bucket, &ct->cache_elem);
+ /* Record the last access timestamp */
+ ct->lastaccess = catcacheclock;
+
/*
* If it's a positive entry, bump its refcount and return it. If it's
* negative, we can report failure to the caller.
@@ -1425,6 +1438,66 @@ SearchCatCacheMiss(CatCache *cache,
return &ct->tuple;
}
+/*
+ * CatCacheCleanupOldEntries - Remove infrequently-used entries
+ *
+ * Catcache entries happen to be left unused for a long time for several
+ * reasons. Remove such entries to prevent catcache from bloating. It is based
+ * on the similar algorithm with buffer eviction. Entries that are accessed
+ * several times in a certain period live longer than those that have had less
+ * access in the same duration.
+ */
+static bool
+CatCacheCleanupOldEntries(CatCache *cp)
+{
+ int nremoved = 0;
+ int i;
+ TimestampTz oldest_ts = catcacheclock;
+ TimestampTz prune_threshold;
+
+ if (catalog_cache_prune_min_age < 0)
+ return false;
+
+ prune_threshold = catcacheclock - ((int64) catalog_cache_prune_min_age) * USECS_PER_SEC;
+
+ /* Scan over the whole hash to find entries to remove */
+ for (i = 0 ; i < cp->cc_nbuckets ; i++)
+ {
+ dlist_mutable_iter iter;
+
+ dlist_foreach_modify(iter, &cp->cc_bucket[i])
+ {
+ CatCTup *ct = dlist_container(CatCTup, cache_elem, iter.cur);
+
+ /* Don't remove referenced entries */
+ if (ct->refcount == 0 &&
+ (ct->c_list == NULL || ct->c_list->refcount == 0))
+ {
+ if (ct->lastaccess < prune_threshold)
+ {
+ CatCacheRemoveCTup(cp, ct);
+ nremoved++;
+
+ /* don't let the removed entry update oldest_ts */
+ continue;
+ }
+ }
+
+ /* update the oldest timestamp if the entry remains alive */
+ if (ct->lastaccess < oldest_ts)
+ oldest_ts = ct->lastaccess;
+ }
+ }
+
+ cp->cc_oldest_ts = oldest_ts;
+
+ if (nremoved > 0)
+ elog(DEBUG1, "pruning catalog cache id=%d for %s: removed %d / %d",
+ cp->id, cp->cc_relname, nremoved, cp->cc_ntup + nremoved);
+
+ return nremoved > 0;
+}
+
/*
* ReleaseCatCache
*
@@ -1888,6 +1961,7 @@ CatalogCacheCreateEntry(CatCache *cache, HeapTuple ntp, Datum *arguments,
ct->dead = false;
ct->negative = negative;
ct->hash_value = hashValue;
+ ct->lastaccess = catcacheclock;
dlist_push_head(&cache->cc_bucket[hashIndex], &ct->cache_elem);
@@ -1899,7 +1973,11 @@ CatalogCacheCreateEntry(CatCache *cache, HeapTuple ntp, Datum *arguments,
* arbitrarily, we enlarge when fill factor > 2.
*/
if (cache->cc_ntup > cache->cc_nbuckets * 2)
- RehashCatCache(cache);
+ {
+ /* try removing old entries before expanding hash */
+ if (!CatCacheCleanupOldEntries(cache))
+ RehashCatCache(cache);
+ }
return ct;
}
diff --git a/src/backend/utils/misc/guc.c b/src/backend/utils/misc/guc.c
index 17579eeaca9..f92f0513a6f 100644
--- a/src/backend/utils/misc/guc.c
+++ b/src/backend/utils/misc/guc.c
@@ -88,6 +88,7 @@
#include "utils/acl.h"
#include "utils/builtins.h"
#include "utils/bytea.h"
+#include "utils/catcache.h"
#include "utils/float.h"
#include "utils/guc_tables.h"
#include "utils/memutils.h"
@@ -3445,6 +3446,17 @@ static struct config_int ConfigureNamesInt[] =
NULL, NULL, NULL
},
+ {
+ {"catalog_cache_prune_min_age", PGC_USERSET, RESOURCES_MEM,
+ gettext_noop("System catalog cache entries that are living unused more than this seconds are considered for removal."),
+ gettext_noop("The value of -1 turns off pruning."),
+ GUC_UNIT_S
+ },
+ &catalog_cache_prune_min_age,
+ -1, -1, INT_MAX,
+ NULL, NULL, NULL
+ },
+
/* End-of-list marker */
{
{NULL, 0, 0, NULL, NULL}, NULL, 0, 0, 0, NULL, NULL, NULL
diff --git a/src/include/utils/catcache.h b/src/include/utils/catcache.h
index ddc2762eb3f..57a15bfec22 100644
--- a/src/include/utils/catcache.h
+++ b/src/include/utils/catcache.h
@@ -22,6 +22,7 @@
#include "access/htup.h"
#include "access/skey.h"
+#include "datatype/timestamp.h"
#include "lib/ilist.h"
#include "utils/relcache.h"
@@ -61,6 +62,7 @@ typedef struct catcache
slist_node cc_next; /* list link */
ScanKeyData cc_skey[CATCACHE_MAXKEYS]; /* precomputed key info for heap
* scans */
+ TimestampTz cc_oldest_ts; /* timestamp (us) of the oldest tuple */
/*
* Keep these at the end, so that compiling catcache.c with CATCACHE_STATS
@@ -119,6 +121,7 @@ typedef struct catctup
bool dead; /* dead but not yet removed? */
bool negative; /* negative cache entry? */
HeapTupleData tuple; /* tuple management header */
+ TimestampTz lastaccess; /* timestamp in us of the last usage */
/*
* The tuple may also be a member of at most one CatCList. (If a single
@@ -189,6 +192,20 @@ typedef struct catcacheheader
/* this extern duplicates utils/memutils.h... */
extern PGDLLIMPORT MemoryContext CacheMemoryContext;
+
+/* for guc.c, not PGDLLPMPORT'ed */
+extern int catalog_cache_prune_min_age;
+
+/* source clock for access timestamp of catcache entries */
+extern TimestampTz catcacheclock;
+
+/* SetCatCacheClock - set catcache timestamp source clock */
+static inline void
+SetCatCacheClock(TimestampTz ts)
+{
+ catcacheclock = ts;
+}
+
extern void CreateCacheMemoryContext(void);
extern CatCache *InitCatCache(int id, Oid reloid, Oid indexoid,
--
2.29.2
--------------38A9F5DCD32945DE1430BC1D--
^ permalink raw reply [nested|flat] 92+ messages in thread
* [PATCH v9] CatCache expiration feature
@ 2021-01-27 11:08 Heikki Linnakangas <[email protected]>
0 siblings, 0 replies; 92+ messages in thread
From: Heikki Linnakangas @ 2021-01-27 11:08 UTC (permalink / raw)
Author: Kyotaro Horiguchi
---
doc/src/sgml/config.sgml | 20 +++++++
src/backend/access/transam/xact.c | 3 ++
src/backend/utils/cache/catcache.c | 85 +++++++++++++++++++++++++++++-
src/backend/utils/misc/guc.c | 12 +++++
src/include/utils/catcache.h | 17 ++++++
5 files changed, 136 insertions(+), 1 deletion(-)
diff --git a/doc/src/sgml/config.sgml b/doc/src/sgml/config.sgml
index f1037df5a9..14be8061ce 100644
--- a/doc/src/sgml/config.sgml
+++ b/doc/src/sgml/config.sgml
@@ -1960,6 +1960,26 @@ include_dir 'conf.d'
</listitem>
</varlistentry>
+ <varlistentry id="guc-catalog-cache-prune-min-age" xreflabel="catalog_cache_prune_min_age">
+ <term><varname>catalog_cache_prune_min_age</varname> (<type>integer</type>)
+ <indexterm>
+ <primary><varname>catalog_cache_prune_min_age</varname> configuration parameter</primary>
+ </indexterm>
+ </term>
+ <listitem>
+ <para>
+ Setting <varname>catalog_cache_prune_min_age</varname> allows catalog
+ cache entries older than this seconds removed. A value
+ of <literal>-1</literal> disables this feature, effectively setting
+ the value to infinity. The default is 600 seconds. You can reduce
+ this value to reduce the amount of memory used by the catalog cache in
+ exchange of possible performance degradation or increase it to gain in
+ performance of intermittently executed queries in exchange of the
+ possible increase of memory usage.
+ </para>
+ </listitem>
+ </varlistentry>
+
</variablelist>
</sect2>
diff --git a/src/backend/access/transam/xact.c b/src/backend/access/transam/xact.c
index a2068e3fd4..86888d2409 100644
--- a/src/backend/access/transam/xact.c
+++ b/src/backend/access/transam/xact.c
@@ -1086,6 +1086,9 @@ static void
AtStart_Cache(void)
{
AcceptInvalidationMessages();
+
+ if (xactStartTimestamp != 0)
+ SetCatCacheClock(xactStartTimestamp);
}
/*
diff --git a/src/backend/utils/cache/catcache.c b/src/backend/utils/cache/catcache.c
index fa2b49c676..3e24a81992 100644
--- a/src/backend/utils/cache/catcache.c
+++ b/src/backend/utils/cache/catcache.c
@@ -38,6 +38,7 @@
#include "utils/rel.h"
#include "utils/resowner_private.h"
#include "utils/syscache.h"
+#include "utils/timestamp.h"
/* #define CACHEDEBUG */ /* turns DEBUG elogs on */
@@ -60,9 +61,18 @@
#define CACHE_elog(...)
#endif
+/*
+ * GUC variable to define the minimum age of entries that are the candidates
+ * for evictetion in seconds. -1 to disable the feature.
+ */
+int catalog_cache_prune_min_age = -1;
+
/* Cache management header --- pointer is NULL until created */
static CatCacheHeader *CacheHdr = NULL;
+/* Clock for the last accessed time of catcache entry. */
+TimestampTz catcacheclock = 0;
+
static inline HeapTuple SearchCatCacheInternal(CatCache *cache,
int nkeys,
Datum v1, Datum v2,
@@ -74,6 +84,7 @@ static pg_noinline HeapTuple SearchCatCacheMiss(CatCache *cache,
Index hashIndex,
Datum v1, Datum v2,
Datum v3, Datum v4);
+static bool CatCacheCleanupOldEntries(CatCache *cp);
static uint32 CatalogCacheComputeHashValue(CatCache *cache, int nkeys,
Datum v1, Datum v2, Datum v3, Datum v4);
@@ -833,6 +844,7 @@ InitCatCache(int id,
cp->cc_nkeys = nkeys;
for (i = 0; i < nkeys; ++i)
cp->cc_keyno[i] = key[i];
+ cp->cc_oldest_ts = catcacheclock;
/*
* new cache is initialized as far as we can go for now. print some
@@ -1264,6 +1276,9 @@ SearchCatCacheInternal(CatCache *cache,
*/
dlist_move_head(bucket, &ct->cache_elem);
+ /* Record the last access timestamp */
+ ct->lastaccess = catcacheclock;
+
/*
* If it's a positive entry, bump its refcount and return it. If it's
* negative, we can report failure to the caller.
@@ -1425,6 +1440,69 @@ SearchCatCacheMiss(CatCache *cache,
return &ct->tuple;
}
+/*
+ * CatCacheCleanupOldEntries - Remove infrequently-used entries
+ *
+ * Removes entries that has been left alone for a certain period to prevent
+ * catcache bloat.
+ */
+static bool
+CatCacheCleanupOldEntries(CatCache *cp)
+{
+ int nremoved = 0;
+ int i;
+ TimestampTz oldest_ts = catcacheclock;
+ TimestampTz prune_threshold;
+
+ if (catalog_cache_prune_min_age < 0)
+ return false;
+
+ /* entries older than this time would be removed */
+ prune_threshold = catcacheclock -
+ ((int64) catalog_cache_prune_min_age) * USECS_PER_SEC;
+
+ /* return if we know we have no entry to remove */
+ if (cp->cc_oldest_ts > prune_threshold)
+ return false;
+
+ /* Scan over the whole hash to find entries to remove */
+ for (i = 0 ; i < cp->cc_nbuckets ; i++)
+ {
+ dlist_mutable_iter iter;
+
+ dlist_foreach_modify(iter, &cp->cc_bucket[i])
+ {
+ CatCTup *ct = dlist_container(CatCTup, cache_elem, iter.cur);
+
+ /* Don't remove referenced entries */
+ if (ct->refcount == 0 &&
+ (ct->c_list == NULL || ct->c_list->refcount == 0))
+ {
+ if (ct->lastaccess <= prune_threshold)
+ {
+ CatCacheRemoveCTup(cp, ct);
+ nremoved++;
+
+ /* don't let the removed entry update oldest_ts */
+ continue;
+ }
+ }
+
+ /* update the oldest timestamp if the entry remains alive */
+ if (ct->lastaccess < oldest_ts)
+ oldest_ts = ct->lastaccess;
+ }
+ }
+
+ cp->cc_oldest_ts = oldest_ts;
+
+ if (nremoved > 0)
+ elog(DEBUG1, "pruning catalog cache id=%d for %s: removed %d / %d",
+ cp->id, cp->cc_relname, nremoved, cp->cc_ntup + nremoved);
+
+ return nremoved > 0;
+}
+
/*
* ReleaseCatCache
*
@@ -1888,6 +1966,7 @@ CatalogCacheCreateEntry(CatCache *cache, HeapTuple ntp, Datum *arguments,
ct->dead = false;
ct->negative = negative;
ct->hash_value = hashValue;
+ ct->lastaccess = catcacheclock;
dlist_push_head(&cache->cc_bucket[hashIndex], &ct->cache_elem);
@@ -1899,7 +1978,11 @@ CatalogCacheCreateEntry(CatCache *cache, HeapTuple ntp, Datum *arguments,
* arbitrarily, we enlarge when fill factor > 2.
*/
if (cache->cc_ntup > cache->cc_nbuckets * 2)
- RehashCatCache(cache);
+ {
+ /* try removing old entries before expanding hash */
+ if (!CatCacheCleanupOldEntries(cache))
+ RehashCatCache(cache);
+ }
return ct;
}
diff --git a/src/backend/utils/misc/guc.c b/src/backend/utils/misc/guc.c
index eafdb1118e..6a1e52911a 100644
--- a/src/backend/utils/misc/guc.c
+++ b/src/backend/utils/misc/guc.c
@@ -88,6 +88,7 @@
#include "utils/acl.h"
#include "utils/builtins.h"
#include "utils/bytea.h"
+#include "utils/catcache.h"
#include "utils/float.h"
#include "utils/guc_tables.h"
#include "utils/memutils.h"
@@ -3445,6 +3446,17 @@ static struct config_int ConfigureNamesInt[] =
NULL, NULL, NULL
},
+ {
+ {"catalog_cache_prune_min_age", PGC_USERSET, RESOURCES_MEM,
+ gettext_noop("System catalog cache entries that are unused more than this seconds are to be removed."),
+ gettext_noop("The value of -1 turns off pruning."),
+ GUC_UNIT_S
+ },
+ &catalog_cache_prune_min_age,
+ 600, -1, INT_MAX,
+ NULL, NULL, NULL
+ },
+
/* End-of-list marker */
{
{NULL, 0, 0, NULL, NULL}, NULL, 0, 0, 0, NULL, NULL, NULL
diff --git a/src/include/utils/catcache.h b/src/include/utils/catcache.h
index ddc2762eb3..786df7aeda 100644
--- a/src/include/utils/catcache.h
+++ b/src/include/utils/catcache.h
@@ -22,6 +22,7 @@
#include "access/htup.h"
#include "access/skey.h"
+#include "datatype/timestamp.h"
#include "lib/ilist.h"
#include "utils/relcache.h"
@@ -61,6 +62,7 @@ typedef struct catcache
slist_node cc_next; /* list link */
ScanKeyData cc_skey[CATCACHE_MAXKEYS]; /* precomputed key info for heap
* scans */
+ TimestampTz cc_oldest_ts; /* timestamp of the oldest tuple */
/*
* Keep these at the end, so that compiling catcache.c with CATCACHE_STATS
@@ -119,6 +121,7 @@ typedef struct catctup
bool dead; /* dead but not yet removed? */
bool negative; /* negative cache entry? */
HeapTupleData tuple; /* tuple management header */
+ TimestampTz lastaccess; /* timestamp of the last use */
/*
* The tuple may also be a member of at most one CatCList. (If a single
@@ -189,6 +192,20 @@ typedef struct catcacheheader
/* this extern duplicates utils/memutils.h... */
extern PGDLLIMPORT MemoryContext CacheMemoryContext;
+
+/* for guc.c, not PGDLLPMPORT'ed */
+extern int catalog_cache_prune_min_age;
+
+/* source clock for access timestamp of catcache entries */
+extern TimestampTz catcacheclock;
+
+/* SetCatCacheClock - set catcache timestamp source clock */
+static inline void
+SetCatCacheClock(TimestampTz ts)
+{
+ catcacheclock = ts;
+}
+
extern void CreateCacheMemoryContext(void);
extern CatCache *InitCatCache(int id, Oid reloid, Oid indexoid,
--
2.27.0
----Next_Part(Thu_Jan_28_16_50_44_2021_768)----
^ permalink raw reply [nested|flat] 92+ messages in thread
* [PATCH v8 1/1] CatCache expiration feature
@ 2021-01-27 11:08 Heikki Linnakangas <[email protected]>
0 siblings, 0 replies; 92+ messages in thread
From: Heikki Linnakangas @ 2021-01-27 11:08 UTC (permalink / raw)
Author: Kyotaro Horiguchi
---
src/backend/access/transam/xact.c | 3 ++
src/backend/utils/cache/catcache.c | 82 +++++++++++++++++++++++++++++-
src/backend/utils/misc/guc.c | 12 +++++
src/include/utils/catcache.h | 17 +++++++
4 files changed, 112 insertions(+), 2 deletions(-)
diff --git a/src/backend/access/transam/xact.c b/src/backend/access/transam/xact.c
index a2068e3fd45..86888d24091 100644
--- a/src/backend/access/transam/xact.c
+++ b/src/backend/access/transam/xact.c
@@ -1086,6 +1086,9 @@ static void
AtStart_Cache(void)
{
AcceptInvalidationMessages();
+
+ if (xactStartTimestamp != 0)
+ SetCatCacheClock(xactStartTimestamp);
}
/*
diff --git a/src/backend/utils/cache/catcache.c b/src/backend/utils/cache/catcache.c
index fa2b49c676e..e29f825687a 100644
--- a/src/backend/utils/cache/catcache.c
+++ b/src/backend/utils/cache/catcache.c
@@ -38,6 +38,7 @@
#include "utils/rel.h"
#include "utils/resowner_private.h"
#include "utils/syscache.h"
+#include "utils/timestamp.h"
/* #define CACHEDEBUG */ /* turns DEBUG elogs on */
@@ -60,9 +61,18 @@
#define CACHE_elog(...)
#endif
+/*
+ * GUC variable to define the minimum age of entries that will be considered
+ * to be evicted in seconds. -1 to disable the feature.
+ */
+int catalog_cache_prune_min_age = -1;
+
/* Cache management header --- pointer is NULL until created */
static CatCacheHeader *CacheHdr = NULL;
+/* Clock for the last accessed time of a catcache entry. */
+TimestampTz catcacheclock = 0;
+
static inline HeapTuple SearchCatCacheInternal(CatCache *cache,
int nkeys,
Datum v1, Datum v2,
@@ -74,6 +84,7 @@ static pg_noinline HeapTuple SearchCatCacheMiss(CatCache *cache,
Index hashIndex,
Datum v1, Datum v2,
Datum v3, Datum v4);
+static bool CatCacheCleanupOldEntries(CatCache *cp);
static uint32 CatalogCacheComputeHashValue(CatCache *cache, int nkeys,
Datum v1, Datum v2, Datum v3, Datum v4);
@@ -99,7 +110,6 @@ static void CatCacheFreeKeys(TupleDesc tupdesc, int nkeys, int *attnos,
static void CatCacheCopyKeys(TupleDesc tupdesc, int nkeys, int *attnos,
Datum *srckeys, Datum *dstkeys);
-
/*
* internal support functions
*/
@@ -1264,6 +1274,9 @@ SearchCatCacheInternal(CatCache *cache,
*/
dlist_move_head(bucket, &ct->cache_elem);
+ /* Record the last access timestamp */
+ ct->lastaccess = catcacheclock;
+
/*
* If it's a positive entry, bump its refcount and return it. If it's
* negative, we can report failure to the caller.
@@ -1425,6 +1438,66 @@ SearchCatCacheMiss(CatCache *cache,
return &ct->tuple;
}
+/*
+ * CatCacheCleanupOldEntries - Remove infrequently-used entries
+ *
+ * Catcache entries happen to be left unused for a long time for several
+ * reasons. Remove such entries to prevent catcache from bloating. It is based
+ * on the similar algorithm with buffer eviction. Entries that are accessed
+ * several times in a certain period live longer than those that have had less
+ * access in the same duration.
+ */
+static bool
+CatCacheCleanupOldEntries(CatCache *cp)
+{
+ int nremoved = 0;
+ int i;
+ TimestampTz oldest_ts = catcacheclock;
+ TimestampTz prune_threshold;
+
+ if (catalog_cache_prune_min_age < 0)
+ return false;
+
+ prune_threshold = catcacheclock - ((int64) catalog_cache_prune_min_age) * USECS_PER_SEC;
+
+ /* Scan over the whole hash to find entries to remove */
+ for (i = 0 ; i < cp->cc_nbuckets ; i++)
+ {
+ dlist_mutable_iter iter;
+
+ dlist_foreach_modify(iter, &cp->cc_bucket[i])
+ {
+ CatCTup *ct = dlist_container(CatCTup, cache_elem, iter.cur);
+
+ /* Don't remove referenced entries */
+ if (ct->refcount == 0 &&
+ (ct->c_list == NULL || ct->c_list->refcount == 0))
+ {
+ if (ct->lastaccess < prune_threshold)
+ {
+ CatCacheRemoveCTup(cp, ct);
+ nremoved++;
+
+ /* don't let the removed entry update oldest_ts */
+ continue;
+ }
+ }
+
+ /* update the oldest timestamp if the entry remains alive */
+ if (ct->lastaccess < oldest_ts)
+ oldest_ts = ct->lastaccess;
+ }
+ }
+
+ cp->cc_oldest_ts = oldest_ts;
+
+ if (nremoved > 0)
+ elog(DEBUG1, "pruning catalog cache id=%d for %s: removed %d / %d",
+ cp->id, cp->cc_relname, nremoved, cp->cc_ntup + nremoved);
+
+ return nremoved > 0;
+}
+
/*
* ReleaseCatCache
*
@@ -1888,6 +1961,7 @@ CatalogCacheCreateEntry(CatCache *cache, HeapTuple ntp, Datum *arguments,
ct->dead = false;
ct->negative = negative;
ct->hash_value = hashValue;
+ ct->lastaccess = catcacheclock;
dlist_push_head(&cache->cc_bucket[hashIndex], &ct->cache_elem);
@@ -1899,7 +1973,11 @@ CatalogCacheCreateEntry(CatCache *cache, HeapTuple ntp, Datum *arguments,
* arbitrarily, we enlarge when fill factor > 2.
*/
if (cache->cc_ntup > cache->cc_nbuckets * 2)
- RehashCatCache(cache);
+ {
+ /* try removing old entries before expanding hash */
+ if (!CatCacheCleanupOldEntries(cache))
+ RehashCatCache(cache);
+ }
return ct;
}
diff --git a/src/backend/utils/misc/guc.c b/src/backend/utils/misc/guc.c
index 17579eeaca9..f92f0513a6f 100644
--- a/src/backend/utils/misc/guc.c
+++ b/src/backend/utils/misc/guc.c
@@ -88,6 +88,7 @@
#include "utils/acl.h"
#include "utils/builtins.h"
#include "utils/bytea.h"
+#include "utils/catcache.h"
#include "utils/float.h"
#include "utils/guc_tables.h"
#include "utils/memutils.h"
@@ -3445,6 +3446,17 @@ static struct config_int ConfigureNamesInt[] =
NULL, NULL, NULL
},
+ {
+ {"catalog_cache_prune_min_age", PGC_USERSET, RESOURCES_MEM,
+ gettext_noop("System catalog cache entries that are living unused more than this seconds are considered for removal."),
+ gettext_noop("The value of -1 turns off pruning."),
+ GUC_UNIT_S
+ },
+ &catalog_cache_prune_min_age,
+ -1, -1, INT_MAX,
+ NULL, NULL, NULL
+ },
+
/* End-of-list marker */
{
{NULL, 0, 0, NULL, NULL}, NULL, 0, 0, 0, NULL, NULL, NULL
diff --git a/src/include/utils/catcache.h b/src/include/utils/catcache.h
index ddc2762eb3f..57a15bfec22 100644
--- a/src/include/utils/catcache.h
+++ b/src/include/utils/catcache.h
@@ -22,6 +22,7 @@
#include "access/htup.h"
#include "access/skey.h"
+#include "datatype/timestamp.h"
#include "lib/ilist.h"
#include "utils/relcache.h"
@@ -61,6 +62,7 @@ typedef struct catcache
slist_node cc_next; /* list link */
ScanKeyData cc_skey[CATCACHE_MAXKEYS]; /* precomputed key info for heap
* scans */
+ TimestampTz cc_oldest_ts; /* timestamp (us) of the oldest tuple */
/*
* Keep these at the end, so that compiling catcache.c with CATCACHE_STATS
@@ -119,6 +121,7 @@ typedef struct catctup
bool dead; /* dead but not yet removed? */
bool negative; /* negative cache entry? */
HeapTupleData tuple; /* tuple management header */
+ TimestampTz lastaccess; /* timestamp in us of the last usage */
/*
* The tuple may also be a member of at most one CatCList. (If a single
@@ -189,6 +192,20 @@ typedef struct catcacheheader
/* this extern duplicates utils/memutils.h... */
extern PGDLLIMPORT MemoryContext CacheMemoryContext;
+
+/* for guc.c, not PGDLLPMPORT'ed */
+extern int catalog_cache_prune_min_age;
+
+/* source clock for access timestamp of catcache entries */
+extern TimestampTz catcacheclock;
+
+/* SetCatCacheClock - set catcache timestamp source clock */
+static inline void
+SetCatCacheClock(TimestampTz ts)
+{
+ catcacheclock = ts;
+}
+
extern void CreateCacheMemoryContext(void);
extern CatCache *InitCatCache(int id, Oid reloid, Oid indexoid,
--
2.29.2
--------------38A9F5DCD32945DE1430BC1D--
^ permalink raw reply [nested|flat] 92+ messages in thread
* Convert *GetDatum() and DatumGet*() macros to inline functions
@ 2022-08-28 15:55 Peter Eisentraut <[email protected]>
2022-08-30 13:36 ` Re: Convert *GetDatum() and DatumGet*() macros to inline functions Aleksander Alekseev <[email protected]>
0 siblings, 1 reply; 92+ messages in thread
From: Peter Eisentraut @ 2022-08-28 15:55 UTC (permalink / raw)
To: pgsql-hackers
I once wrote code like this:
char *oid = get_from_somewhere();
...
values[i++] = ObjectIdGetDatum(oid);
This compiles cleanly and even appears to work in practice, except of
course it doesn't.
The FooGetDatum() macros just cast whatever you give it to Datum,
without checking whether the input was really foo.
To address this, I converted these macros to inline functions, which
enables type checking of the input argument. For symmetry, I also
converted the corresponding DatumGetFoo() macros (but those are less
likely to cover mistakes, since the input argument is always Datum).
This is patch 0002.
(I left some of the DatumGet... of the varlena types in fmgr.h as
macros. These ultimately map to functions that do type checking, so
there would be little more to be learnt from that. But we could do
those for consistency as well.)
This whole thing threw up a bunch of compiler warnings and errors, which
revealed a number of existing misuses. These are fixed in patch 0001.
These include
- using FooGetDatum on things that are already Datum,
- using DatumGetPointer on things that are already pointers,
- using PG_RETURN_TYPE on things that are Datum,
- using PG_RETURN_TYPE of the wrong type,
and others, including my personal favorite:
- using PointerGetDatum where DatumGetPointer should be used.
(AFAICT, unlike my initial example, I don't think any of those would
cause wrong behavior.)
From 4371c74b2d01408653f3917bb838b051537afaa2 Mon Sep 17 00:00:00 2001
From: Peter Eisentraut <[email protected]>
Date: Sun, 28 Aug 2022 10:47:10 +0200
Subject: [PATCH 1/2] Fix incorrect uses of Datum conversion macros
---
contrib/btree_gist/btree_utils_num.c | 2 +-
contrib/dblink/dblink.c | 2 +-
contrib/hstore/hstore_op.c | 2 +-
contrib/pageinspect/heapfuncs.c | 4 ++--
src/backend/access/brin/brin_bloom.c | 2 +-
src/backend/access/brin/brin_minmax_multi.c | 12 ++++++------
src/backend/access/common/toast_compression.c | 2 +-
src/backend/access/table/toast_helper.c | 2 +-
src/backend/access/transam/xlogfuncs.c | 2 +-
src/backend/statistics/mcv.c | 4 ++--
src/backend/utils/adt/amutils.c | 2 +-
src/backend/utils/adt/multirangetypes.c | 2 +-
src/backend/utils/adt/pg_lsn.c | 4 ++--
src/backend/utils/adt/pgstatfuncs.c | 2 +-
src/backend/utils/adt/rangetypes_spgist.c | 4 ++--
src/backend/utils/adt/regexp.c | 2 +-
src/backend/utils/adt/tsgistidx.c | 2 +-
src/backend/utils/adt/tsquery_op.c | 2 +-
src/backend/utils/adt/varlena.c | 2 +-
src/backend/utils/adt/xml.c | 2 +-
src/backend/utils/resowner/resowner.c | 6 +++---
src/pl/plperl/plperl.c | 2 +-
22 files changed, 33 insertions(+), 33 deletions(-)
diff --git a/contrib/btree_gist/btree_utils_num.c b/contrib/btree_gist/btree_utils_num.c
index 5632ee0586..05c154afa3 100644
--- a/contrib/btree_gist/btree_utils_num.c
+++ b/contrib/btree_gist/btree_utils_num.c
@@ -153,7 +153,7 @@ gbt_num_fetch(GISTENTRY *entry, const gbtree_ninfo *tinfo)
datum = CashGetDatum(*(Cash *) entry->key);
break;
default:
- datum = PointerGetDatum(entry->key);
+ datum = entry->key;
}
retval = palloc(sizeof(GISTENTRY));
diff --git a/contrib/dblink/dblink.c b/contrib/dblink/dblink.c
index e323fdd0e6..7940387920 100644
--- a/contrib/dblink/dblink.c
+++ b/contrib/dblink/dblink.c
@@ -1336,7 +1336,7 @@ dblink_get_connections(PG_FUNCTION_ARGS)
}
if (astate)
- PG_RETURN_ARRAYTYPE_P(makeArrayResult(astate,
+ PG_RETURN_DATUM(makeArrayResult(astate,
CurrentMemoryContext));
else
PG_RETURN_NULL();
diff --git a/contrib/hstore/hstore_op.c b/contrib/hstore/hstore_op.c
index 2f540d7ed6..0d4ec16d1e 100644
--- a/contrib/hstore/hstore_op.c
+++ b/contrib/hstore/hstore_op.c
@@ -1064,7 +1064,7 @@ hstore_each(PG_FUNCTION_ARGS)
tuple = heap_form_tuple(funcctx->tuple_desc, dvalues, nulls);
res = HeapTupleGetDatum(tuple);
- SRF_RETURN_NEXT(funcctx, PointerGetDatum(res));
+ SRF_RETURN_NEXT(funcctx, res);
}
SRF_RETURN_DONE(funcctx);
diff --git a/contrib/pageinspect/heapfuncs.c b/contrib/pageinspect/heapfuncs.c
index 2ff70405cf..aed2753253 100644
--- a/contrib/pageinspect/heapfuncs.c
+++ b/contrib/pageinspect/heapfuncs.c
@@ -383,7 +383,7 @@ tuple_data_split_internal(Oid relid, char *tupdata,
errmsg("unexpected end of tuple data")));
if (attr->attlen == -1 && do_detoast)
- attr_data = DatumGetByteaPCopy(tupdata + off);
+ attr_data = pg_detoast_datum_copy((struct varlena *) (tupdata + off));
else
{
attr_data = (bytea *) palloc(len + VARHDRSZ);
@@ -492,7 +492,7 @@ tuple_data_split(PG_FUNCTION_ARGS)
if (t_bits)
pfree(t_bits);
- PG_RETURN_ARRAYTYPE_P(res);
+ PG_RETURN_DATUM(res);
}
/*
diff --git a/src/backend/access/brin/brin_bloom.c b/src/backend/access/brin/brin_bloom.c
index 6812ca9fd1..6b0af7267d 100644
--- a/src/backend/access/brin/brin_bloom.c
+++ b/src/backend/access/brin/brin_bloom.c
@@ -767,7 +767,7 @@ brin_bloom_summary_out(PG_FUNCTION_ARGS)
StringInfoData str;
/* detoast the data to get value with a full 4B header */
- filter = (BloomFilter *) PG_DETOAST_DATUM(PG_GETARG_BYTEA_PP(0));
+ filter = (BloomFilter *) PG_DETOAST_DATUM_PACKED(PG_GETARG_DATUM(0));
initStringInfo(&str);
appendStringInfoChar(&str, '{');
diff --git a/src/backend/access/brin/brin_minmax_multi.c b/src/backend/access/brin/brin_minmax_multi.c
index a581659fe2..c8f22d5d17 100644
--- a/src/backend/access/brin/brin_minmax_multi.c
+++ b/src/backend/access/brin/brin_minmax_multi.c
@@ -774,12 +774,12 @@ brin_range_deserialize(int maxvalues, SerializedRanges *serialized)
datalen += MAXALIGN(typlen);
else if (typlen == -1) /* varlena */
{
- datalen += MAXALIGN(VARSIZE_ANY(DatumGetPointer(ptr)));
- ptr += VARSIZE_ANY(DatumGetPointer(ptr));
+ datalen += MAXALIGN(VARSIZE_ANY(ptr));
+ ptr += VARSIZE_ANY(ptr);
}
else if (typlen == -2) /* cstring */
{
- Size slen = strlen(DatumGetCString(ptr)) + 1;
+ Size slen = strlen(ptr) + 1;
datalen += MAXALIGN(slen);
ptr += slen;
@@ -3033,7 +3033,7 @@ brin_minmax_multi_summary_out(PG_FUNCTION_ARGS)
* Detoast to get value with full 4B header (can't be stored in a toast
* table, but can use 1B header).
*/
- ranges = (SerializedRanges *) PG_DETOAST_DATUM(PG_GETARG_BYTEA_PP(0));
+ ranges = (SerializedRanges *) PG_DETOAST_DATUM_PACKED(PG_GETARG_DATUM(0));
/* lookup output func for the type */
getTypeOutputInfo(ranges->typid, &outfunc, &isvarlena);
@@ -3081,7 +3081,7 @@ brin_minmax_multi_summary_out(PG_FUNCTION_ARGS)
getTypeOutputInfo(ANYARRAYOID, &typoutput, &typIsVarlena);
- val = PointerGetDatum(makeArrayResult(astate_values, CurrentMemoryContext));
+ val = makeArrayResult(astate_values, CurrentMemoryContext);
extval = OidOutputFunctionCall(typoutput, val);
@@ -3121,7 +3121,7 @@ brin_minmax_multi_summary_out(PG_FUNCTION_ARGS)
getTypeOutputInfo(ANYARRAYOID, &typoutput, &typIsVarlena);
- val = PointerGetDatum(makeArrayResult(astate_values, CurrentMemoryContext));
+ val = makeArrayResult(astate_values, CurrentMemoryContext);
extval = OidOutputFunctionCall(typoutput, val);
diff --git a/src/backend/access/common/toast_compression.c b/src/backend/access/common/toast_compression.c
index f90f9f11e3..7156ae9c47 100644
--- a/src/backend/access/common/toast_compression.c
+++ b/src/backend/access/common/toast_compression.c
@@ -44,7 +44,7 @@ pglz_compress_datum(const struct varlena *value)
len;
struct varlena *tmp = NULL;
- valsize = VARSIZE_ANY_EXHDR(DatumGetPointer(value));
+ valsize = VARSIZE_ANY_EXHDR(value);
/*
* No point in wasting a palloc cycle if value size is outside the allowed
diff --git a/src/backend/access/table/toast_helper.c b/src/backend/access/table/toast_helper.c
index 0cc5a30f9b..74ba2189f0 100644
--- a/src/backend/access/table/toast_helper.c
+++ b/src/backend/access/table/toast_helper.c
@@ -330,7 +330,7 @@ toast_delete_external(Relation rel, Datum *values, bool *isnull,
if (isnull[i])
continue;
- else if (VARATT_IS_EXTERNAL_ONDISK(PointerGetDatum(value)))
+ else if (VARATT_IS_EXTERNAL_ONDISK(value))
toast_delete_datum(rel, value, is_speculative);
}
}
diff --git a/src/backend/access/transam/xlogfuncs.c b/src/backend/access/transam/xlogfuncs.c
index 61e0f4a29c..9cc757f1af 100644
--- a/src/backend/access/transam/xlogfuncs.c
+++ b/src/backend/access/transam/xlogfuncs.c
@@ -555,7 +555,7 @@ pg_wal_lsn_diff(PG_FUNCTION_ARGS)
PG_GETARG_DATUM(0),
PG_GETARG_DATUM(1));
- PG_RETURN_NUMERIC(result);
+ PG_RETURN_DATUM(result);
}
/*
diff --git a/src/backend/statistics/mcv.c b/src/backend/statistics/mcv.c
index 6eeacb0d47..f5a7c31272 100644
--- a/src/backend/statistics/mcv.c
+++ b/src/backend/statistics/mcv.c
@@ -1444,8 +1444,8 @@ pg_stats_ext_mcvlist_items(PG_FUNCTION_ARGS)
}
values[0] = Int32GetDatum(funcctx->call_cntr);
- values[1] = PointerGetDatum(makeArrayResult(astate_values, CurrentMemoryContext));
- values[2] = PointerGetDatum(makeArrayResult(astate_nulls, CurrentMemoryContext));
+ values[1] = makeArrayResult(astate_values, CurrentMemoryContext);
+ values[2] = makeArrayResult(astate_nulls, CurrentMemoryContext);
values[3] = Float8GetDatum(item->frequency);
values[4] = Float8GetDatum(item->base_frequency);
diff --git a/src/backend/utils/adt/amutils.c b/src/backend/utils/adt/amutils.c
index 9d78a90d69..60fd396f24 100644
--- a/src/backend/utils/adt/amutils.c
+++ b/src/backend/utils/adt/amutils.c
@@ -466,5 +466,5 @@ pg_indexam_progress_phasename(PG_FUNCTION_ARGS)
if (!name)
PG_RETURN_NULL();
- PG_RETURN_TEXT_P(CStringGetTextDatum(name));
+ PG_RETURN_DATUM(CStringGetTextDatum(name));
}
diff --git a/src/backend/utils/adt/multirangetypes.c b/src/backend/utils/adt/multirangetypes.c
index da5c7d0906..307d087c97 100644
--- a/src/backend/utils/adt/multirangetypes.c
+++ b/src/backend/utils/adt/multirangetypes.c
@@ -1491,7 +1491,7 @@ multirange_intersect_agg_transfn(PG_FUNCTION_ARGS)
ranges1,
range_count2,
ranges2);
- PG_RETURN_RANGE_P(result);
+ PG_RETURN_MULTIRANGE_P(result);
}
diff --git a/src/backend/utils/adt/pg_lsn.c b/src/backend/utils/adt/pg_lsn.c
index 45408787da..15266f36f5 100644
--- a/src/backend/utils/adt/pg_lsn.c
+++ b/src/backend/utils/adt/pg_lsn.c
@@ -271,7 +271,7 @@ pg_lsn_pli(PG_FUNCTION_ARGS)
/* Add two numerics */
res = DirectFunctionCall2(numeric_add,
- NumericGetDatum(num),
+ num,
NumericGetDatum(nbytes));
/* Convert to pg_lsn */
@@ -305,7 +305,7 @@ pg_lsn_mii(PG_FUNCTION_ARGS)
/* Subtract two numerics */
res = DirectFunctionCall2(numeric_sub,
- NumericGetDatum(num),
+ num,
NumericGetDatum(nbytes));
/* Convert to pg_lsn */
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index 4cca30aae7..be15b4b2e5 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -1119,7 +1119,7 @@ pg_stat_get_backend_client_addr(PG_FUNCTION_ARGS)
clean_ipv6_addr(beentry->st_clientaddr.addr.ss_family, remote_host);
- PG_RETURN_INET_P(DirectFunctionCall1(inet_in,
+ PG_RETURN_DATUM(DirectFunctionCall1(inet_in,
CStringGetDatum(remote_host)));
}
diff --git a/src/backend/utils/adt/rangetypes_spgist.c b/src/backend/utils/adt/rangetypes_spgist.c
index 71a6053b6a..a47f04d975 100644
--- a/src/backend/utils/adt/rangetypes_spgist.c
+++ b/src/backend/utils/adt/rangetypes_spgist.c
@@ -416,7 +416,7 @@ spg_range_quad_inner_consistent(PG_FUNCTION_ARGS)
/* This node has a centroid. Fetch it. */
centroid = DatumGetRangeTypeP(in->prefixDatum);
typcache = range_get_typcache(fcinfo,
- RangeTypeGetOid(DatumGetRangeTypeP(centroid)));
+ RangeTypeGetOid(centroid));
range_deserialize(typcache, centroid, ¢roidLower, ¢roidUpper,
¢roidEmpty);
@@ -557,7 +557,7 @@ spg_range_quad_inner_consistent(PG_FUNCTION_ARGS)
*/
if (in->traversalValue)
{
- prevCentroid = DatumGetRangeTypeP(in->traversalValue);
+ prevCentroid = in->traversalValue;
range_deserialize(typcache, prevCentroid,
&prevLower, &prevUpper, &prevEmpty);
}
diff --git a/src/backend/utils/adt/regexp.c b/src/backend/utils/adt/regexp.c
index 0e0c5d5ce8..dcf6681ef2 100644
--- a/src/backend/utils/adt/regexp.c
+++ b/src/backend/utils/adt/regexp.c
@@ -1757,7 +1757,7 @@ regexp_split_to_array(PG_FUNCTION_ARGS)
splitctx->next_match++;
}
- PG_RETURN_ARRAYTYPE_P(makeArrayResult(astate, CurrentMemoryContext));
+ PG_RETURN_DATUM(makeArrayResult(astate, CurrentMemoryContext));
}
/* This is separate to keep the opr_sanity regression test from complaining */
diff --git a/src/backend/utils/adt/tsgistidx.c b/src/backend/utils/adt/tsgistidx.c
index 47daf8199e..fe438f1c4d 100644
--- a/src/backend/utils/adt/tsgistidx.c
+++ b/src/backend/utils/adt/tsgistidx.c
@@ -102,7 +102,7 @@ static int outbuf_maxlen = 0;
Datum
gtsvectorout(PG_FUNCTION_ARGS)
{
- SignTSVector *key = (SignTSVector *) PG_DETOAST_DATUM(PG_GETARG_POINTER(0));
+ SignTSVector *key = (SignTSVector *) PG_DETOAST_DATUM(PG_GETARG_DATUM(0));
char *outbuf;
if (outbuf_maxlen == 0)
diff --git a/src/backend/utils/adt/tsquery_op.c b/src/backend/utils/adt/tsquery_op.c
index a784157ba5..8d815fa7a8 100644
--- a/src/backend/utils/adt/tsquery_op.c
+++ b/src/backend/utils/adt/tsquery_op.c
@@ -148,7 +148,7 @@ tsquery_phrase_distance(PG_FUNCTION_ARGS)
Datum
tsquery_phrase(PG_FUNCTION_ARGS)
{
- PG_RETURN_POINTER(DirectFunctionCall3(tsquery_phrase_distance,
+ PG_RETURN_DATUM(DirectFunctionCall3(tsquery_phrase_distance,
PG_GETARG_DATUM(0),
PG_GETARG_DATUM(1),
Int32GetDatum(1)));
diff --git a/src/backend/utils/adt/varlena.c b/src/backend/utils/adt/varlena.c
index 8539cef024..6ef317e1f9 100644
--- a/src/backend/utils/adt/varlena.c
+++ b/src/backend/utils/adt/varlena.c
@@ -4779,7 +4779,7 @@ text_to_array(PG_FUNCTION_ARGS)
if (tstate.astate == NULL)
PG_RETURN_ARRAYTYPE_P(construct_empty_array(TEXTOID));
- PG_RETURN_ARRAYTYPE_P(makeArrayResult(tstate.astate,
+ PG_RETURN_DATUM(makeArrayResult(tstate.astate,
CurrentMemoryContext));
}
diff --git a/src/backend/utils/adt/xml.c b/src/backend/utils/adt/xml.c
index 60a85c4697..606088cdfc 100644
--- a/src/backend/utils/adt/xml.c
+++ b/src/backend/utils/adt/xml.c
@@ -4174,7 +4174,7 @@ xpath(PG_FUNCTION_ARGS)
astate = initArrayResult(XMLOID, CurrentMemoryContext, true);
xpath_internal(xpath_expr_text, data, namespaces,
NULL, astate);
- PG_RETURN_ARRAYTYPE_P(makeArrayResult(astate, CurrentMemoryContext));
+ PG_RETURN_DATUM(makeArrayResult(astate, CurrentMemoryContext));
#else
NO_XML_SUPPORT();
return 0;
diff --git a/src/backend/utils/resowner/resowner.c b/src/backend/utils/resowner/resowner.c
index ceb4b0e3f7..ece5d98261 100644
--- a/src/backend/utils/resowner/resowner.c
+++ b/src/backend/utils/resowner/resowner.c
@@ -557,7 +557,7 @@ ResourceOwnerReleaseInternal(ResourceOwner owner,
/* Ditto for JIT contexts */
while (ResourceArrayGetAny(&(owner->jitarr), &foundres))
{
- JitContext *context = (JitContext *) PointerGetDatum(foundres);
+ JitContext *context = (JitContext *) DatumGetPointer(foundres);
jit_release_context(context);
}
@@ -566,7 +566,7 @@ ResourceOwnerReleaseInternal(ResourceOwner owner,
while (ResourceArrayGetAny(&(owner->cryptohasharr), &foundres))
{
pg_cryptohash_ctx *context =
- (pg_cryptohash_ctx *) PointerGetDatum(foundres);
+ (pg_cryptohash_ctx *) DatumGetPointer(foundres);
if (isCommit)
PrintCryptoHashLeakWarning(foundres);
@@ -576,7 +576,7 @@ ResourceOwnerReleaseInternal(ResourceOwner owner,
/* Ditto for HMAC contexts */
while (ResourceArrayGetAny(&(owner->hmacarr), &foundres))
{
- pg_hmac_ctx *context = (pg_hmac_ctx *) PointerGetDatum(foundres);
+ pg_hmac_ctx *context = (pg_hmac_ctx *) DatumGetPointer(foundres);
if (isCommit)
PrintHMACLeakWarning(foundres);
diff --git a/src/pl/plperl/plperl.c b/src/pl/plperl/plperl.c
index 5d192a0ce5..8f21e0d701 100644
--- a/src/pl/plperl/plperl.c
+++ b/src/pl/plperl/plperl.c
@@ -1848,7 +1848,7 @@ plperl_call_handler(PG_FUNCTION_ARGS)
{
current_call_data = &this_call_data;
if (CALLED_AS_TRIGGER(fcinfo))
- retval = PointerGetDatum(plperl_trigger_handler(fcinfo));
+ retval = plperl_trigger_handler(fcinfo);
else if (CALLED_AS_EVENT_TRIGGER(fcinfo))
{
plperl_event_trigger_handler(fcinfo);
--
2.37.1
From a54a726eadcab016714b431b23d273d9653fb2f6 Mon Sep 17 00:00:00 2001
From: Peter Eisentraut <[email protected]>
Date: Sun, 28 Aug 2022 10:47:38 +0200
Subject: [PATCH 2/2] Convert *GetDatum() and DatumGet*() macros to inline
functions
The previous macro implementations just cast the argument to a target
type but did not check whether the input type was appropriate. The
function implementation can do better type checking of the input type.
---
doc/src/sgml/xfunc.sgml | 2 +-
src/backend/utils/mb/mbutils.c | 12 +-
src/include/access/gin.h | 14 +-
src/include/funcapi.h | 14 +-
src/include/postgres.h | 275 +++++++++++++++++++---------
src/include/tsearch/ts_type.h | 46 ++++-
src/include/tsearch/ts_utils.h | 14 +-
src/include/utils/cash.h | 14 +-
src/include/utils/date.h | 42 ++++-
src/include/utils/expandeddatum.h | 13 +-
src/include/utils/expandedrecord.h | 16 +-
src/include/utils/geo_decls.h | 98 ++++++++--
src/include/utils/inet.h | 49 ++++-
src/include/utils/jsonb.h | 31 +++-
src/include/utils/jsonpath.h | 14 +-
src/include/utils/multirangetypes.h | 23 ++-
src/include/utils/numeric.h | 21 ++-
src/include/utils/pg_lsn.h | 13 +-
src/include/utils/rangetypes.h | 23 ++-
src/include/utils/timestamp.h | 44 ++++-
src/include/utils/uuid.h | 15 +-
src/include/utils/varbit.h | 21 ++-
src/include/utils/xid8.h | 14 +-
src/include/utils/xml.h | 13 +-
24 files changed, 655 insertions(+), 186 deletions(-)
diff --git a/doc/src/sgml/xfunc.sgml b/doc/src/sgml/xfunc.sgml
index b8cefb9c2c..cf5810b3c1 100644
--- a/doc/src/sgml/xfunc.sgml
+++ b/doc/src/sgml/xfunc.sgml
@@ -2763,7 +2763,7 @@ <title>Composite-Type Arguments</title>
is null. <function>GetAttributeByName</function> returns a <type>Datum</type>
value that you can convert to the proper data type by using the
appropriate <function>DatumGet<replaceable>XXX</replaceable>()</function>
- macro. Note that the return value is meaningless if the null flag is
+ function. Note that the return value is meaningless if the null flag is
set; always check the null flag before trying to do anything with the
result.
</para>
diff --git a/src/backend/utils/mb/mbutils.c b/src/backend/utils/mb/mbutils.c
index 0543c574c6..474ab476f5 100644
--- a/src/backend/utils/mb/mbutils.c
+++ b/src/backend/utils/mb/mbutils.c
@@ -409,8 +409,8 @@ pg_do_encoding_conversion(unsigned char *src, int len,
(void) OidFunctionCall6(proc,
Int32GetDatum(src_encoding),
Int32GetDatum(dest_encoding),
- CStringGetDatum(src),
- CStringGetDatum(result),
+ CStringGetDatum((char *) src),
+ CStringGetDatum((char *) result),
Int32GetDatum(len),
BoolGetDatum(false));
@@ -485,8 +485,8 @@ pg_do_encoding_conversion_buf(Oid proc,
result = OidFunctionCall6(proc,
Int32GetDatum(src_encoding),
Int32GetDatum(dest_encoding),
- CStringGetDatum(src),
- CStringGetDatum(dest),
+ CStringGetDatum((char *) src),
+ CStringGetDatum((char *) dest),
Int32GetDatum(srclen),
BoolGetDatum(noError));
return DatumGetInt32(result);
@@ -910,8 +910,8 @@ pg_unicode_to_server(pg_wchar c, unsigned char *s)
FunctionCall6(Utf8ToServerConvProc,
Int32GetDatum(PG_UTF8),
Int32GetDatum(server_encoding),
- CStringGetDatum(c_as_utf8),
- CStringGetDatum(s),
+ CStringGetDatum((char *) c_as_utf8),
+ CStringGetDatum((char *) s),
Int32GetDatum(c_as_utf8_len),
BoolGetDatum(false));
}
diff --git a/src/include/access/gin.h b/src/include/access/gin.h
index aacc665fdc..fff861e219 100644
--- a/src/include/access/gin.h
+++ b/src/include/access/gin.h
@@ -62,8 +62,18 @@ typedef char GinTernaryValue;
#define GIN_MAYBE 2 /* don't know if item is present / don't know
* if matches */
-#define DatumGetGinTernaryValue(X) ((GinTernaryValue)(X))
-#define GinTernaryValueGetDatum(X) ((Datum)(X))
+static inline GinTernaryValue
+DatumGetGinTernaryValue(Datum X)
+{
+ return (GinTernaryValue) X;
+}
+
+static inline Datum
+GinTernaryValueGetDatum(GinTernaryValue X)
+{
+ return (Datum) X;
+}
+
#define PG_RETURN_GIN_TERNARY_VALUE(x) return GinTernaryValueGetDatum(x)
/* GUC parameters */
diff --git a/src/include/funcapi.h b/src/include/funcapi.h
index dc3d819a1c..8c2521a591 100644
--- a/src/include/funcapi.h
+++ b/src/include/funcapi.h
@@ -204,7 +204,7 @@ extern TupleDesc build_function_result_tupdesc_t(HeapTuple procTuple);
* Datum HeapTupleHeaderGetDatum(HeapTupleHeader tuple) - convert a
* HeapTupleHeader to a Datum.
*
- * Macro declarations:
+ * Inline declarations:
* HeapTupleGetDatum(HeapTuple tuple) - convert a HeapTuple to a Datum.
*
* Obsolete routines and macros:
@@ -217,10 +217,6 @@ extern TupleDesc build_function_result_tupdesc_t(HeapTuple procTuple);
*----------
*/
-#define HeapTupleGetDatum(tuple) HeapTupleHeaderGetDatum((tuple)->t_data)
-/* obsolete version of above */
-#define TupleGetDatum(_slot, _tuple) HeapTupleGetDatum(_tuple)
-
extern TupleDesc RelationNameGetTupleDesc(const char *relname);
extern TupleDesc TypeGetTupleDesc(Oid typeoid, List *colaliases);
@@ -230,6 +226,14 @@ extern AttInMetadata *TupleDescGetAttInMetadata(TupleDesc tupdesc);
extern HeapTuple BuildTupleFromCStrings(AttInMetadata *attinmeta, char **values);
extern Datum HeapTupleHeaderGetDatum(HeapTupleHeader tuple);
+static inline Datum
+HeapTupleGetDatum(const HeapTupleData *tuple)
+{
+ return HeapTupleHeaderGetDatum(tuple->t_data);
+}
+/* obsolete version of above */
+#define TupleGetDatum(_slot, _tuple) HeapTupleGetDatum(_tuple)
+
/*----------
* Support for Set Returning Functions (SRFs)
diff --git a/src/include/postgres.h b/src/include/postgres.h
index 13903fa022..45c9f6563c 100644
--- a/src/include/postgres.h
+++ b/src/include/postgres.h
@@ -24,7 +24,7 @@
* section description
* ------- ------------------------------------------------
* 1) variable-length datatypes (TOAST support)
- * 2) Datum type + support macros
+ * 2) Datum type + support functions
* 3) miscellaneous
*
* NOTES
@@ -395,7 +395,7 @@ typedef struct
/* ----------------------------------------------------------------
- * Section 2: Datum type + support macros
+ * Section 2: Datum type + support functions
* ----------------------------------------------------------------
*/
@@ -405,7 +405,7 @@ typedef struct
*
* sizeof(Datum) == sizeof(void *) == 4 or 8
*
- * The macros below and the analogous macros for other types should be used to
+ * The functions below and the analogous functions for other types should be used to
* convert between a Datum and the appropriate C type.
*/
@@ -434,8 +434,11 @@ typedef struct NullableDatum
*
* Note: any nonzero value will be considered true.
*/
-
-#define DatumGetBool(X) ((bool) ((X) != 0))
+static inline bool
+DatumGetBool(Datum X)
+{
+ return (X != 0);
+}
/*
* BoolGetDatum
@@ -443,162 +446,231 @@ typedef struct NullableDatum
*
* Note: any nonzero value will be considered true.
*/
-
-#define BoolGetDatum(X) ((Datum) ((X) ? 1 : 0))
+static inline Datum
+BoolGetDatum(bool X)
+{
+ return (Datum) (X ? 1 : 0);
+}
/*
* DatumGetChar
* Returns character value of a datum.
*/
-
-#define DatumGetChar(X) ((char) (X))
+static inline char
+DatumGetChar(Datum X)
+{
+ return (char) X;
+}
/*
* CharGetDatum
* Returns datum representation for a character.
*/
-
-#define CharGetDatum(X) ((Datum) (X))
+static inline Datum
+CharGetDatum(char X)
+{
+ return (Datum) X;
+}
/*
* Int8GetDatum
* Returns datum representation for an 8-bit integer.
*/
-
-#define Int8GetDatum(X) ((Datum) (X))
+static inline Datum
+Int8GetDatum(int8 X)
+{
+ return (Datum) X;
+}
/*
* DatumGetUInt8
* Returns 8-bit unsigned integer value of a datum.
*/
-
-#define DatumGetUInt8(X) ((uint8) (X))
+static inline uint8
+DatumGetUInt8(Datum X)
+{
+ return (uint8) X;
+}
/*
* UInt8GetDatum
* Returns datum representation for an 8-bit unsigned integer.
*/
-
-#define UInt8GetDatum(X) ((Datum) (X))
+static inline Datum
+UInt8GetDatum(uint8 X)
+{
+ return (Datum) X;
+}
/*
* DatumGetInt16
* Returns 16-bit integer value of a datum.
*/
-
-#define DatumGetInt16(X) ((int16) (X))
+static inline int16
+DatumGetInt16(Datum X)
+{
+ return (int16) X;
+}
/*
* Int16GetDatum
* Returns datum representation for a 16-bit integer.
*/
-
-#define Int16GetDatum(X) ((Datum) (X))
+static inline Datum
+Int16GetDatum(int16 X)
+{
+ return (Datum) X;
+}
/*
* DatumGetUInt16
* Returns 16-bit unsigned integer value of a datum.
*/
-
-#define DatumGetUInt16(X) ((uint16) (X))
+static inline uint16
+DatumGetUInt16(Datum X)
+{
+ return (uint16) X;
+}
/*
* UInt16GetDatum
* Returns datum representation for a 16-bit unsigned integer.
*/
-
-#define UInt16GetDatum(X) ((Datum) (X))
+static inline Datum
+UInt16GetDatum(uint16 X)
+{
+ return (Datum) X;
+}
/*
* DatumGetInt32
* Returns 32-bit integer value of a datum.
*/
-
-#define DatumGetInt32(X) ((int32) (X))
+static inline int32
+DatumGetInt32(Datum X)
+{
+ return (int32) X;
+}
/*
* Int32GetDatum
* Returns datum representation for a 32-bit integer.
*/
-
-#define Int32GetDatum(X) ((Datum) (X))
+static inline Datum
+Int32GetDatum(int32 X)
+{
+ return (Datum) X;
+}
/*
* DatumGetUInt32
* Returns 32-bit unsigned integer value of a datum.
*/
-
-#define DatumGetUInt32(X) ((uint32) (X))
+static inline uint32
+DatumGetUInt32(Datum X)
+{
+ return (uint32) X;
+}
/*
* UInt32GetDatum
* Returns datum representation for a 32-bit unsigned integer.
*/
-
-#define UInt32GetDatum(X) ((Datum) (X))
+static inline Datum
+UInt32GetDatum(uint32 X)
+{
+ return (Datum) X;
+}
/*
* DatumGetObjectId
* Returns object identifier value of a datum.
*/
-
-#define DatumGetObjectId(X) ((Oid) (X))
+static inline Oid
+DatumGetObjectId(Datum X)
+{
+ return (Oid) X;
+}
/*
* ObjectIdGetDatum
* Returns datum representation for an object identifier.
*/
-
-#define ObjectIdGetDatum(X) ((Datum) (X))
+static inline Datum
+ObjectIdGetDatum(Oid X)
+{
+ return (Datum) X;
+}
/*
* DatumGetTransactionId
* Returns transaction identifier value of a datum.
*/
-
-#define DatumGetTransactionId(X) ((TransactionId) (X))
+static inline TransactionId
+DatumGetTransactionId(Datum X)
+{
+ return (TransactionId) X;
+}
/*
* TransactionIdGetDatum
* Returns datum representation for a transaction identifier.
*/
-
-#define TransactionIdGetDatum(X) ((Datum) (X))
+static inline Datum
+TransactionIdGetDatum(TransactionId X)
+{
+ return (Datum) X;
+}
/*
* MultiXactIdGetDatum
* Returns datum representation for a multixact identifier.
*/
-
-#define MultiXactIdGetDatum(X) ((Datum) (X))
+static inline Datum
+MultiXactIdGetDatum(MultiXactId X)
+{
+ return (Datum) X;
+}
/*
* DatumGetCommandId
* Returns command identifier value of a datum.
*/
-
-#define DatumGetCommandId(X) ((CommandId) (X))
+static inline CommandId
+DatumGetCommandId(Datum X)
+{
+ return (CommandId) X;
+}
/*
* CommandIdGetDatum
* Returns datum representation for a command identifier.
*/
-
-#define CommandIdGetDatum(X) ((Datum) (X))
+static inline Datum
+CommandIdGetDatum(CommandId X)
+{
+ return (Datum) X;
+}
/*
* DatumGetPointer
* Returns pointer value of a datum.
*/
-
-#define DatumGetPointer(X) ((Pointer) (X))
+static inline Pointer
+DatumGetPointer(Datum X)
+{
+ return (Pointer) X;
+}
/*
* PointerGetDatum
* Returns datum representation for a pointer.
*/
-
-#define PointerGetDatum(X) ((Datum) (X))
+static inline Datum
+PointerGetDatum(const void *X)
+{
+ return (Datum) X;
+}
/*
* DatumGetCString
@@ -607,8 +679,11 @@ typedef struct NullableDatum
* Note: C string is not a full-fledged Postgres type at present,
* but type input functions use this conversion for their inputs.
*/
-
-#define DatumGetCString(X) ((char *) DatumGetPointer(X))
+static inline char *
+DatumGetCString(Datum X)
+{
+ return (char *) DatumGetPointer(X);
+}
/*
* CStringGetDatum
@@ -619,15 +694,21 @@ typedef struct NullableDatum
* Note: CString is pass-by-reference; caller must ensure the pointed-to
* value has adequate lifetime.
*/
-
-#define CStringGetDatum(X) PointerGetDatum(X)
+static inline Datum
+CStringGetDatum(const char *X)
+{
+ return PointerGetDatum(X);
+}
/*
* DatumGetName
* Returns name value of a datum.
*/
-
-#define DatumGetName(X) ((Name) DatumGetPointer(X))
+static inline Name
+DatumGetName(Datum X)
+{
+ return (Name) DatumGetPointer(X);
+}
/*
* NameGetDatum
@@ -636,21 +717,27 @@ typedef struct NullableDatum
* Note: Name is pass-by-reference; caller must ensure the pointed-to
* value has adequate lifetime.
*/
-
-#define NameGetDatum(X) CStringGetDatum(NameStr(*(X)))
+static inline Datum
+NameGetDatum(const NameData *X)
+{
+ return CStringGetDatum(NameStr(*X));
+}
/*
* DatumGetInt64
* Returns 64-bit integer value of a datum.
*
- * Note: this macro hides whether int64 is pass by value or by reference.
+ * Note: this function hides whether int64 is pass by value or by reference.
*/
-
+static inline int64
+DatumGetInt64(Datum X)
+{
#ifdef USE_FLOAT8_BYVAL
-#define DatumGetInt64(X) ((int64) (X))
+ return (int64) X;
#else
-#define DatumGetInt64(X) (* ((int64 *) DatumGetPointer(X)))
+ return *((int64 *) DatumGetPointer(X));
#endif
+}
/*
* Int64GetDatum
@@ -659,25 +746,32 @@ typedef struct NullableDatum
* Note: if int64 is pass by reference, this function returns a reference
* to palloc'd space.
*/
-
#ifdef USE_FLOAT8_BYVAL
-#define Int64GetDatum(X) ((Datum) (X))
+static inline Datum
+Int64GetDatum(int64 X)
+{
+ return (Datum) X;
+}
#else
extern Datum Int64GetDatum(int64 X);
#endif
+
/*
* DatumGetUInt64
* Returns 64-bit unsigned integer value of a datum.
*
- * Note: this macro hides whether int64 is pass by value or by reference.
+ * Note: this function hides whether int64 is pass by value or by reference.
*/
-
+static inline uint64
+DatumGetUInt64(Datum X)
+{
#ifdef USE_FLOAT8_BYVAL
-#define DatumGetUInt64(X) ((uint64) (X))
+ return (uint64) X;
#else
-#define DatumGetUInt64(X) (* ((uint64 *) DatumGetPointer(X)))
+ return *((uint64 *) DatumGetPointer(X));
#endif
+}
/*
* UInt64GetDatum
@@ -686,12 +780,15 @@ extern Datum Int64GetDatum(int64 X);
* Note: if int64 is pass by reference, this function returns a reference
* to palloc'd space.
*/
-
+static inline Datum
+UInt64GetDatum(uint64 X)
+{
#ifdef USE_FLOAT8_BYVAL
-#define UInt64GetDatum(X) ((Datum) (X))
+ return (Datum) X;
#else
-#define UInt64GetDatum(X) Int64GetDatum((int64) (X))
+ return Int64GetDatum((int64) X);
#endif
+}
/*
* Float <-> Datum conversions
@@ -739,13 +836,12 @@ Float4GetDatum(float4 X)
* DatumGetFloat8
* Returns 8-byte floating point value of a datum.
*
- * Note: this macro hides whether float8 is pass by value or by reference.
+ * Note: this function hides whether float8 is pass by value or by reference.
*/
-
-#ifdef USE_FLOAT8_BYVAL
static inline float8
DatumGetFloat8(Datum X)
{
+#ifdef USE_FLOAT8_BYVAL
union
{
int64 value;
@@ -754,10 +850,10 @@ DatumGetFloat8(Datum X)
myunion.value = DatumGetInt64(X);
return myunion.retval;
-}
#else
-#define DatumGetFloat8(X) (* ((float8 *) DatumGetPointer(X)))
+ return *((float8 *) DatumGetPointer(X));
#endif
+}
/*
* Float8GetDatum
@@ -766,7 +862,6 @@ DatumGetFloat8(Datum X)
* Note: if float8 is pass by reference, this function returns a reference
* to palloc'd space.
*/
-
#ifdef USE_FLOAT8_BYVAL
static inline Datum
Float8GetDatum(float8 X)
@@ -789,22 +884,34 @@ extern Datum Float8GetDatum(float8 X);
* Int64GetDatumFast
* Float8GetDatumFast
*
- * These macros are intended to allow writing code that does not depend on
+ * These functions are intended to allow writing code that does not depend on
* whether int64 and float8 are pass-by-reference types, while not
* sacrificing performance when they are. The argument must be a variable
* that will exist and have the same value for as long as the Datum is needed.
* In the pass-by-ref case, the address of the variable is taken to use as
* the Datum. In the pass-by-val case, these will be the same as the non-Fast
- * macros.
+ * functions.
*/
+static inline Datum
+Int64GetDatumFast(int64 X)
+{
+#ifdef USE_FLOAT8_BYVAL
+ return Int64GetDatum(X);
+#else
+ return PointerGetDatum(&X);
+#endif
+}
+
+static inline Datum
+Float8GetDatumFast(float8 X)
+{
#ifdef USE_FLOAT8_BYVAL
-#define Int64GetDatumFast(X) Int64GetDatum(X)
-#define Float8GetDatumFast(X) Float8GetDatum(X)
+ return Float8GetDatum(X);
#else
-#define Int64GetDatumFast(X) PointerGetDatum(&(X))
-#define Float8GetDatumFast(X) PointerGetDatum(&(X))
+ return PointerGetDatum(&X);
#endif
+}
/* ----------------------------------------------------------------
diff --git a/src/include/tsearch/ts_type.h b/src/include/tsearch/ts_type.h
index 689b2d1cfb..f1ec84702d 100644
--- a/src/include/tsearch/ts_type.h
+++ b/src/include/tsearch/ts_type.h
@@ -111,12 +111,27 @@ typedef TSVectorData *TSVector;
#define POSDATAPTR(x,e) (_POSVECPTR(x,e)->pos)
/*
- * fmgr interface macros
+ * fmgr interface functions
*/
-#define DatumGetTSVector(X) ((TSVector) PG_DETOAST_DATUM(X))
-#define DatumGetTSVectorCopy(X) ((TSVector) PG_DETOAST_DATUM_COPY(X))
-#define TSVectorGetDatum(X) PointerGetDatum(X)
+static inline TSVector
+DatumGetTSVector(Datum X)
+{
+ return (TSVector) PG_DETOAST_DATUM(X);
+}
+
+static inline TSVector
+DatumGetTSVectorCopy(Datum X)
+{
+ return (TSVector) PG_DETOAST_DATUM_COPY(X);
+}
+
+static inline Datum
+TSVectorGetDatum(const TSVectorData *X)
+{
+ return PointerGetDatum(X);
+}
+
#define PG_GETARG_TSVECTOR(n) DatumGetTSVector(PG_GETARG_DATUM(n))
#define PG_GETARG_TSVECTOR_COPY(n) DatumGetTSVectorCopy(PG_GETARG_DATUM(n))
#define PG_RETURN_TSVECTOR(x) return TSVectorGetDatum(x)
@@ -227,14 +242,29 @@ typedef TSQueryData *TSQuery;
#define GETOPERAND(x) ( (char*)GETQUERY(x) + ((TSQuery)(x))->size * sizeof(QueryItem) )
/*
- * fmgr interface macros
+ * fmgr interface functions
* Note, TSQuery type marked as plain storage, so it can't be toasted
* but PG_DETOAST_DATUM_COPY is used for simplicity
*/
-#define DatumGetTSQuery(X) ((TSQuery) DatumGetPointer(X))
-#define DatumGetTSQueryCopy(X) ((TSQuery) PG_DETOAST_DATUM_COPY(X))
-#define TSQueryGetDatum(X) PointerGetDatum(X)
+static inline TSQuery
+DatumGetTSQuery(Datum X)
+{
+ return (TSQuery) DatumGetPointer(X);
+}
+
+static inline TSQuery
+DatumGetTSQueryCopy(Datum X)
+{
+ return (TSQuery) PG_DETOAST_DATUM_COPY(X);
+}
+
+static inline Datum
+TSQueryGetDatum(const TSQueryData *X)
+{
+ return PointerGetDatum(X);
+}
+
#define PG_GETARG_TSQUERY(n) DatumGetTSQuery(PG_GETARG_DATUM(n))
#define PG_GETARG_TSQUERY_COPY(n) DatumGetTSQueryCopy(PG_GETARG_DATUM(n))
#define PG_RETURN_TSQUERY(x) return TSQueryGetDatum(x)
diff --git a/src/include/tsearch/ts_utils.h b/src/include/tsearch/ts_utils.h
index c36c711dae..26cc415aa8 100644
--- a/src/include/tsearch/ts_utils.h
+++ b/src/include/tsearch/ts_utils.h
@@ -243,8 +243,18 @@ typedef uint64 TSQuerySign;
#define TSQS_SIGLEN (sizeof(TSQuerySign)*BITS_PER_BYTE)
-#define TSQuerySignGetDatum(X) Int64GetDatum((int64) (X))
-#define DatumGetTSQuerySign(X) ((TSQuerySign) DatumGetInt64(X))
+static inline Datum
+TSQuerySignGetDatum(TSQuerySign X)
+{
+ return Int64GetDatum((int64) X);
+}
+
+static inline TSQuerySign
+DatumGetTSQuerySign(Datum X)
+{
+ return (TSQuerySign) DatumGetInt64(X);
+}
+
#define PG_RETURN_TSQUERYSIGN(X) return TSQuerySignGetDatum(X)
#define PG_GETARG_TSQUERYSIGN(n) DatumGetTSQuerySign(PG_GETARG_DATUM(n))
diff --git a/src/include/utils/cash.h b/src/include/utils/cash.h
index 2e332d83b1..55d45fadd4 100644
--- a/src/include/utils/cash.h
+++ b/src/include/utils/cash.h
@@ -17,8 +17,18 @@
typedef int64 Cash;
/* Cash is pass-by-reference if and only if int64 is */
-#define DatumGetCash(X) ((Cash) DatumGetInt64(X))
-#define CashGetDatum(X) Int64GetDatum(X)
+static inline Cash
+DatumGetCash(Datum X)
+{
+ return DatumGetInt64(X);
+}
+
+static inline Datum
+CashGetDatum(Cash X)
+{
+ return Int64GetDatum(X);
+}
+
#define PG_GETARG_CASH(n) DatumGetCash(PG_GETARG_DATUM(n))
#define PG_RETURN_CASH(x) return CashGetDatum(x)
diff --git a/src/include/utils/date.h b/src/include/utils/date.h
index 3991da41f0..0bbe889128 100644
--- a/src/include/utils/date.h
+++ b/src/include/utils/date.h
@@ -45,18 +45,46 @@ typedef struct
#define MAX_TIME_PRECISION 6
/*
- * Macros for fmgr-callable functions.
+ * Functions for fmgr-callable functions.
*
* For TimeADT, we make use of the same support routines as for int64.
* Therefore TimeADT is pass-by-reference if and only if int64 is!
*/
-#define DatumGetDateADT(X) ((DateADT) DatumGetInt32(X))
-#define DatumGetTimeADT(X) ((TimeADT) DatumGetInt64(X))
-#define DatumGetTimeTzADTP(X) ((TimeTzADT *) DatumGetPointer(X))
+static inline DateADT
+DatumGetDateADT(Datum X)
+{
+ return (DateADT) DatumGetInt32(X);
+}
+
+static inline TimeADT
+DatumGetTimeADT(Datum X)
+{
+ return (TimeADT) DatumGetInt64(X);
+}
+
+static inline TimeTzADT *
+DatumGetTimeTzADTP(Datum X)
+{
+ return (TimeTzADT *) DatumGetPointer(X);
+}
-#define DateADTGetDatum(X) Int32GetDatum(X)
-#define TimeADTGetDatum(X) Int64GetDatum(X)
-#define TimeTzADTPGetDatum(X) PointerGetDatum(X)
+static inline Datum
+DateADTGetDatum(DateADT X)
+{
+ return Int32GetDatum(X);
+}
+
+static inline Datum
+TimeADTGetDatum(TimeADT X)
+{
+ return Int64GetDatum(X);
+}
+
+static inline Datum
+TimeTzADTPGetDatum(const TimeTzADT *X)
+{
+ return PointerGetDatum(X);
+}
#define PG_GETARG_DATEADT(n) DatumGetDateADT(PG_GETARG_DATUM(n))
#define PG_GETARG_TIMEADT(n) DatumGetTimeADT(PG_GETARG_DATUM(n))
diff --git a/src/include/utils/expandeddatum.h b/src/include/utils/expandeddatum.h
index ffdb0c45bd..c4818eb054 100644
--- a/src/include/utils/expandeddatum.h
+++ b/src/include/utils/expandeddatum.h
@@ -133,8 +133,17 @@ struct ExpandedObjectHeader
* (More of these might be worth inlining later.)
*/
-#define EOHPGetRWDatum(eohptr) PointerGetDatum((eohptr)->eoh_rw_ptr)
-#define EOHPGetRODatum(eohptr) PointerGetDatum((eohptr)->eoh_ro_ptr)
+static inline Datum
+EOHPGetRWDatum(const struct ExpandedObjectHeader *eohptr)
+{
+ return PointerGetDatum(eohptr->eoh_rw_ptr);
+}
+
+static inline Datum
+EOHPGetRODatum(const struct ExpandedObjectHeader *eohptr)
+{
+ return PointerGetDatum(eohptr->eoh_ro_ptr);
+}
/* Does the Datum represent a writable expanded object? */
#define DatumIsReadWriteExpandedObject(d, isnull, typlen) \
diff --git a/src/include/utils/expandedrecord.h b/src/include/utils/expandedrecord.h
index be60e2ce53..aaf5b6bfd1 100644
--- a/src/include/utils/expandedrecord.h
+++ b/src/include/utils/expandedrecord.h
@@ -138,10 +138,20 @@ typedef struct ExpandedRecordHeader
MemoryContextCallback er_mcb;
} ExpandedRecordHeader;
-/* fmgr macros for expanded record objects */
+/* fmgr functions and macros for expanded record objects */
+static inline Datum
+ExpandedRecordGetDatum(const ExpandedRecordHeader *erh)
+{
+ return EOHPGetRWDatum(&erh->hdr);
+}
+
+static inline Datum
+ExpandedRecordGetRODatum(const ExpandedRecordHeader *erh)
+{
+ return EOHPGetRODatum(&erh->hdr);
+}
+
#define PG_GETARG_EXPANDED_RECORD(n) DatumGetExpandedRecord(PG_GETARG_DATUM(n))
-#define ExpandedRecordGetDatum(erh) EOHPGetRWDatum(&(erh)->hdr)
-#define ExpandedRecordGetRODatum(erh) EOHPGetRODatum(&(erh)->hdr)
#define PG_RETURN_EXPANDED_RECORD(x) PG_RETURN_DATUM(ExpandedRecordGetDatum(x))
/* assorted other macros */
diff --git a/src/include/utils/geo_decls.h b/src/include/utils/geo_decls.h
index 719030777e..05726778ad 100644
--- a/src/include/utils/geo_decls.h
+++ b/src/include/utils/geo_decls.h
@@ -166,48 +166,112 @@ typedef struct
} CIRCLE;
/*
- * fmgr interface macros
+ * fmgr interface functions
*
* Path and Polygon are toastable varlena types, the others are just
* fixed-size pass-by-reference types.
*/
-#define DatumGetPointP(X) ((Point *) DatumGetPointer(X))
-#define PointPGetDatum(X) PointerGetDatum(X)
+static inline Point *
+DatumGetPointP(Datum X)
+{
+ return (Point *) DatumGetPointer(X);
+}
+static inline Datum
+PointPGetDatum(const Point *X)
+{
+ return PointerGetDatum(X);
+}
#define PG_GETARG_POINT_P(n) DatumGetPointP(PG_GETARG_DATUM(n))
#define PG_RETURN_POINT_P(x) return PointPGetDatum(x)
-#define DatumGetLsegP(X) ((LSEG *) DatumGetPointer(X))
-#define LsegPGetDatum(X) PointerGetDatum(X)
+static inline LSEG *
+DatumGetLsegP(Datum X)
+{
+ return (LSEG *) DatumGetPointer(X);
+}
+static inline Datum
+LsegPGetDatum(const LSEG *X)
+{
+ return PointerGetDatum(X);
+}
#define PG_GETARG_LSEG_P(n) DatumGetLsegP(PG_GETARG_DATUM(n))
#define PG_RETURN_LSEG_P(x) return LsegPGetDatum(x)
-#define DatumGetPathP(X) ((PATH *) PG_DETOAST_DATUM(X))
-#define DatumGetPathPCopy(X) ((PATH *) PG_DETOAST_DATUM_COPY(X))
-#define PathPGetDatum(X) PointerGetDatum(X)
+static inline PATH *
+DatumGetPathP(Datum X)
+{
+ return (PATH *) PG_DETOAST_DATUM(X);
+}
+static inline PATH *
+DatumGetPathPCopy(Datum X)
+{
+ return (PATH *) PG_DETOAST_DATUM_COPY(X);
+}
+static inline Datum
+PathPGetDatum(const PATH *X)
+{
+ return PointerGetDatum(X);
+}
#define PG_GETARG_PATH_P(n) DatumGetPathP(PG_GETARG_DATUM(n))
#define PG_GETARG_PATH_P_COPY(n) DatumGetPathPCopy(PG_GETARG_DATUM(n))
#define PG_RETURN_PATH_P(x) return PathPGetDatum(x)
-#define DatumGetLineP(X) ((LINE *) DatumGetPointer(X))
-#define LinePGetDatum(X) PointerGetDatum(X)
+static inline LINE *
+DatumGetLineP(Datum X)
+{
+ return (LINE *) DatumGetPointer(X);
+}
+static inline Datum
+LinePGetDatum(const LINE *X)
+{
+ return PointerGetDatum(X);
+}
#define PG_GETARG_LINE_P(n) DatumGetLineP(PG_GETARG_DATUM(n))
#define PG_RETURN_LINE_P(x) return LinePGetDatum(x)
-#define DatumGetBoxP(X) ((BOX *) DatumGetPointer(X))
-#define BoxPGetDatum(X) PointerGetDatum(X)
+static inline BOX *
+DatumGetBoxP(Datum X)
+{
+ return (BOX *) DatumGetPointer(X);
+}
+static inline Datum
+BoxPGetDatum(const BOX *X)
+{
+ return PointerGetDatum(X);
+}
#define PG_GETARG_BOX_P(n) DatumGetBoxP(PG_GETARG_DATUM(n))
#define PG_RETURN_BOX_P(x) return BoxPGetDatum(x)
-#define DatumGetPolygonP(X) ((POLYGON *) PG_DETOAST_DATUM(X))
-#define DatumGetPolygonPCopy(X) ((POLYGON *) PG_DETOAST_DATUM_COPY(X))
-#define PolygonPGetDatum(X) PointerGetDatum(X)
+static inline POLYGON *
+DatumGetPolygonP(Datum X)
+{
+ return (POLYGON *) PG_DETOAST_DATUM(X);
+}
+static inline POLYGON *
+DatumGetPolygonPCopy(Datum X)
+{
+ return (POLYGON *) PG_DETOAST_DATUM_COPY(X);
+}
+static inline Datum
+PolygonPGetDatum(const POLYGON *X)
+{
+ return PointerGetDatum(X);
+}
#define PG_GETARG_POLYGON_P(n) DatumGetPolygonP(PG_GETARG_DATUM(n))
#define PG_GETARG_POLYGON_P_COPY(n) DatumGetPolygonPCopy(PG_GETARG_DATUM(n))
#define PG_RETURN_POLYGON_P(x) return PolygonPGetDatum(x)
-#define DatumGetCircleP(X) ((CIRCLE *) DatumGetPointer(X))
-#define CirclePGetDatum(X) PointerGetDatum(X)
+static inline CIRCLE *
+DatumGetCircleP(Datum X)
+{
+ return (CIRCLE *) DatumGetPointer(X);
+}
+static inline Datum
+CirclePGetDatum(const CIRCLE *X)
+{
+ return PointerGetDatum(X);
+}
#define PG_GETARG_CIRCLE_P(n) DatumGetCircleP(PG_GETARG_DATUM(n))
#define PG_RETURN_CIRCLE_P(x) return CirclePGetDatum(x)
diff --git a/src/include/utils/inet.h b/src/include/utils/inet.h
index b1ec9723df..5438cfaba5 100644
--- a/src/include/utils/inet.h
+++ b/src/include/utils/inet.h
@@ -119,23 +119,58 @@ typedef struct macaddr8
/*
* fmgr interface macros
*/
-#define DatumGetInetPP(X) ((inet *) PG_DETOAST_DATUM_PACKED(X))
-#define InetPGetDatum(X) PointerGetDatum(X)
+static inline inet *
+DatumGetInetPP(Datum X)
+{
+ return (inet *) PG_DETOAST_DATUM_PACKED(X);
+}
+
+static inline Datum
+InetPGetDatum(const inet *X)
+{
+ return PointerGetDatum(X);
+}
+
#define PG_GETARG_INET_PP(n) DatumGetInetPP(PG_GETARG_DATUM(n))
#define PG_RETURN_INET_P(x) return InetPGetDatum(x)
+
/* obsolescent variants */
-#define DatumGetInetP(X) ((inet *) PG_DETOAST_DATUM(X))
+static inline inet *
+DatumGetInetP(Datum X)
+{
+ return (inet *) PG_DETOAST_DATUM(X);
+}
#define PG_GETARG_INET_P(n) DatumGetInetP(PG_GETARG_DATUM(n))
/* macaddr is a fixed-length pass-by-reference datatype */
-#define DatumGetMacaddrP(X) ((macaddr *) DatumGetPointer(X))
-#define MacaddrPGetDatum(X) PointerGetDatum(X)
+static inline macaddr *
+DatumGetMacaddrP(Datum X)
+{
+ return (macaddr *) DatumGetPointer(X);
+}
+
+static inline Datum
+MacaddrPGetDatum(const macaddr *X)
+{
+ return PointerGetDatum(X);
+}
+
#define PG_GETARG_MACADDR_P(n) DatumGetMacaddrP(PG_GETARG_DATUM(n))
#define PG_RETURN_MACADDR_P(x) return MacaddrPGetDatum(x)
/* macaddr8 is a fixed-length pass-by-reference datatype */
-#define DatumGetMacaddr8P(X) ((macaddr8 *) DatumGetPointer(X))
-#define Macaddr8PGetDatum(X) PointerGetDatum(X)
+static inline macaddr8 *
+DatumGetMacaddr8P(Datum X)
+{
+ return (macaddr8 *) DatumGetPointer(X);
+}
+
+static inline Datum
+Macaddr8PGetDatum(const macaddr8 *X)
+{
+ return PointerGetDatum(X);
+}
+
#define PG_GETARG_MACADDR8_P(n) DatumGetMacaddr8P(PG_GETARG_DATUM(n))
#define PG_RETURN_MACADDR8_P(x) return Macaddr8PGetDatum(x)
diff --git a/src/include/utils/jsonb.h b/src/include/utils/jsonb.h
index bae466b523..536d00b3b4 100644
--- a/src/include/utils/jsonb.h
+++ b/src/include/utils/jsonb.h
@@ -67,14 +67,6 @@ typedef enum
#define JGINFLAG_HASHED 0x10 /* OR'd into flag if value was hashed */
#define JGIN_MAXLENGTH 125 /* max length of text part before hashing */
-/* Convenience macros */
-#define DatumGetJsonbP(d) ((Jsonb *) PG_DETOAST_DATUM(d))
-#define DatumGetJsonbPCopy(d) ((Jsonb *) PG_DETOAST_DATUM_COPY(d))
-#define JsonbPGetDatum(p) PointerGetDatum(p)
-#define PG_GETARG_JSONB_P(x) DatumGetJsonbP(PG_GETARG_DATUM(x))
-#define PG_GETARG_JSONB_P_COPY(x) DatumGetJsonbPCopy(PG_GETARG_DATUM(x))
-#define PG_RETURN_JSONB_P(x) PG_RETURN_POINTER(x)
-
typedef struct JsonbPair JsonbPair;
typedef struct JsonbValue JsonbValue;
@@ -393,6 +385,29 @@ typedef enum /* type categories for datum_to_jsonb */
JSONBTYPE_OTHER /* all else */
} JsonbTypeCategory;
+/* Convenience macros */
+static inline Jsonb *
+DatumGetJsonbP(Datum d)
+{
+ return (Jsonb *) PG_DETOAST_DATUM(d);
+}
+
+static inline Jsonb *
+DatumGetJsonbPCopy(Datum d)
+{
+ return (Jsonb *) PG_DETOAST_DATUM_COPY(d);
+}
+
+static inline Datum
+JsonbPGetDatum(const Jsonb *p)
+{
+ return PointerGetDatum(p);
+}
+
+#define PG_GETARG_JSONB_P(x) DatumGetJsonbP(PG_GETARG_DATUM(x))
+#define PG_GETARG_JSONB_P_COPY(x) DatumGetJsonbPCopy(PG_GETARG_DATUM(x))
+#define PG_RETURN_JSONB_P(x) PG_RETURN_POINTER(x)
+
/* Support functions */
extern uint32 getJsonbOffset(const JsonbContainer *jc, int index);
extern uint32 getJsonbLength(const JsonbContainer *jc, int index);
diff --git a/src/include/utils/jsonpath.h b/src/include/utils/jsonpath.h
index 8e79b8dc9f..ea32a8f072 100644
--- a/src/include/utils/jsonpath.h
+++ b/src/include/utils/jsonpath.h
@@ -32,8 +32,18 @@ typedef struct
#define JSONPATH_LAX (0x80000000)
#define JSONPATH_HDRSZ (offsetof(JsonPath, data))
-#define DatumGetJsonPathP(d) ((JsonPath *) DatumGetPointer(PG_DETOAST_DATUM(d)))
-#define DatumGetJsonPathPCopy(d) ((JsonPath *) DatumGetPointer(PG_DETOAST_DATUM_COPY(d)))
+static inline JsonPath *
+DatumGetJsonPathP(Datum d)
+{
+ return (JsonPath *) PG_DETOAST_DATUM(d);
+}
+
+static inline JsonPath *
+DatumGetJsonPathPCopy(Datum d)
+{
+ return (JsonPath *) PG_DETOAST_DATUM_COPY(d);
+}
+
#define PG_GETARG_JSONPATH_P(x) DatumGetJsonPathP(PG_GETARG_DATUM(x))
#define PG_GETARG_JSONPATH_P_COPY(x) DatumGetJsonPathPCopy(PG_GETARG_DATUM(x))
#define PG_RETURN_JSONPATH_P(p) PG_RETURN_POINTER(p)
diff --git a/src/include/utils/multirangetypes.h b/src/include/utils/multirangetypes.h
index 915330f990..23bce0005f 100644
--- a/src/include/utils/multirangetypes.h
+++ b/src/include/utils/multirangetypes.h
@@ -42,11 +42,26 @@ typedef struct
#define MultirangeIsEmpty(mr) ((mr)->rangeCount == 0)
/*
- * fmgr macros for multirange type objects
+ * fmgr functions for multirange type objects
*/
-#define DatumGetMultirangeTypeP(X) ((MultirangeType *) PG_DETOAST_DATUM(X))
-#define DatumGetMultirangeTypePCopy(X) ((MultirangeType *) PG_DETOAST_DATUM_COPY(X))
-#define MultirangeTypePGetDatum(X) PointerGetDatum(X)
+static inline MultirangeType *
+DatumGetMultirangeTypeP(Datum X)
+{
+ return (MultirangeType *) PG_DETOAST_DATUM(X);
+}
+
+static inline MultirangeType *
+DatumGetMultirangeTypePCopy(Datum X)
+{
+ return (MultirangeType *) PG_DETOAST_DATUM_COPY(X);
+}
+
+static inline Datum
+MultirangeTypePGetDatum(const MultirangeType *X)
+{
+ return PointerGetDatum(X);
+}
+
#define PG_GETARG_MULTIRANGE_P(n) DatumGetMultirangeTypeP(PG_GETARG_DATUM(n))
#define PG_GETARG_MULTIRANGE_P_COPY(n) DatumGetMultirangeTypePCopy(PG_GETARG_DATUM(n))
#define PG_RETURN_MULTIRANGE_P(x) return MultirangeTypePGetDatum(x)
diff --git a/src/include/utils/numeric.h b/src/include/utils/numeric.h
index 3caa74dfe7..88c62c5348 100644
--- a/src/include/utils/numeric.h
+++ b/src/include/utils/numeric.h
@@ -56,9 +56,24 @@ typedef struct NumericData *Numeric;
* fmgr interface macros
*/
-#define DatumGetNumeric(X) ((Numeric) PG_DETOAST_DATUM(X))
-#define DatumGetNumericCopy(X) ((Numeric) PG_DETOAST_DATUM_COPY(X))
-#define NumericGetDatum(X) PointerGetDatum(X)
+static inline Numeric
+DatumGetNumeric(Datum X)
+{
+ return (Numeric) PG_DETOAST_DATUM(X);
+}
+
+static inline Numeric
+DatumGetNumericCopy(Datum X)
+{
+ return (Numeric) PG_DETOAST_DATUM_COPY(X);
+}
+
+static inline Datum
+NumericGetDatum(Numeric X)
+{
+ return PointerGetDatum(X);
+}
+
#define PG_GETARG_NUMERIC(n) DatumGetNumeric(PG_GETARG_DATUM(n))
#define PG_GETARG_NUMERIC_COPY(n) DatumGetNumericCopy(PG_GETARG_DATUM(n))
#define PG_RETURN_NUMERIC(x) return NumericGetDatum(x)
diff --git a/src/include/utils/pg_lsn.h b/src/include/utils/pg_lsn.h
index 7b708f1073..5313afbfe2 100644
--- a/src/include/utils/pg_lsn.h
+++ b/src/include/utils/pg_lsn.h
@@ -18,8 +18,17 @@
#include "access/xlogdefs.h"
#include "fmgr.h"
-#define DatumGetLSN(X) ((XLogRecPtr) DatumGetInt64(X))
-#define LSNGetDatum(X) (Int64GetDatum((int64) (X)))
+static inline XLogRecPtr
+DatumGetLSN(Datum X)
+{
+ return (XLogRecPtr) DatumGetInt64(X);
+}
+
+static inline Datum
+LSNGetDatum(XLogRecPtr X)
+{
+ return Int64GetDatum((int64) X);
+}
#define PG_GETARG_LSN(n) DatumGetLSN(PG_GETARG_DATUM(n))
#define PG_RETURN_LSN(x) return LSNGetDatum(x)
diff --git a/src/include/utils/rangetypes.h b/src/include/utils/rangetypes.h
index 993fad4fc2..661e4892ec 100644
--- a/src/include/utils/rangetypes.h
+++ b/src/include/utils/rangetypes.h
@@ -68,11 +68,26 @@ typedef struct
} RangeBound;
/*
- * fmgr macros for range type objects
+ * fmgr functions for range type objects
*/
-#define DatumGetRangeTypeP(X) ((RangeType *) PG_DETOAST_DATUM(X))
-#define DatumGetRangeTypePCopy(X) ((RangeType *) PG_DETOAST_DATUM_COPY(X))
-#define RangeTypePGetDatum(X) PointerGetDatum(X)
+static inline RangeType *
+DatumGetRangeTypeP(Datum X)
+{
+ return (RangeType *) PG_DETOAST_DATUM(X);
+}
+
+static inline RangeType *
+DatumGetRangeTypePCopy(Datum X)
+{
+ return (RangeType *) PG_DETOAST_DATUM_COPY(X);
+}
+
+static inline Datum
+RangeTypePGetDatum(const RangeType *X)
+{
+ return PointerGetDatum(X);
+}
+
#define PG_GETARG_RANGE_P(n) DatumGetRangeTypeP(PG_GETARG_DATUM(n))
#define PG_GETARG_RANGE_P_COPY(n) DatumGetRangeTypePCopy(PG_GETARG_DATUM(n))
#define PG_RETURN_RANGE_P(x) return RangeTypePGetDatum(x)
diff --git a/src/include/utils/timestamp.h b/src/include/utils/timestamp.h
index edf3a97318..3602638bdc 100644
--- a/src/include/utils/timestamp.h
+++ b/src/include/utils/timestamp.h
@@ -19,18 +19,46 @@
/*
- * Macros for fmgr-callable functions.
+ * Functions for fmgr-callable functions.
*
* For Timestamp, we make use of the same support routines as for int64.
* Therefore Timestamp is pass-by-reference if and only if int64 is!
*/
-#define DatumGetTimestamp(X) ((Timestamp) DatumGetInt64(X))
-#define DatumGetTimestampTz(X) ((TimestampTz) DatumGetInt64(X))
-#define DatumGetIntervalP(X) ((Interval *) DatumGetPointer(X))
-
-#define TimestampGetDatum(X) Int64GetDatum(X)
-#define TimestampTzGetDatum(X) Int64GetDatum(X)
-#define IntervalPGetDatum(X) PointerGetDatum(X)
+static inline Timestamp
+DatumGetTimestamp(Datum X)
+{
+ return (Timestamp) DatumGetInt64(X);
+}
+
+static inline TimestampTz
+DatumGetTimestampTz(Datum X)
+{
+ return (TimestampTz) DatumGetInt64(X);
+}
+
+static inline Interval *
+DatumGetIntervalP(Datum X)
+{
+ return (Interval *) DatumGetPointer(X);
+}
+
+static inline Datum
+TimestampGetDatum(Timestamp X)
+{
+ return Int64GetDatum(X);
+}
+
+static inline Datum
+TimestampTzGetDatum(TimestampTz X)
+{
+ return Int64GetDatum(X);
+}
+
+static inline Datum
+IntervalPGetDatum(const Interval *X)
+{
+ return PointerGetDatum(X);
+}
#define PG_GETARG_TIMESTAMP(n) DatumGetTimestamp(PG_GETARG_DATUM(n))
#define PG_GETARG_TIMESTAMPTZ(n) DatumGetTimestampTz(PG_GETARG_DATUM(n))
diff --git a/src/include/utils/uuid.h b/src/include/utils/uuid.h
index 0029da4e7f..69f4332d0f 100644
--- a/src/include/utils/uuid.h
+++ b/src/include/utils/uuid.h
@@ -23,9 +23,20 @@ typedef struct pg_uuid_t
} pg_uuid_t;
/* fmgr interface macros */
-#define UUIDPGetDatum(X) PointerGetDatum(X)
+static inline Datum
+UUIDPGetDatum(const pg_uuid_t *X)
+{
+ return PointerGetDatum(X);
+}
+
#define PG_RETURN_UUID_P(X) return UUIDPGetDatum(X)
-#define DatumGetUUIDP(X) ((pg_uuid_t *) DatumGetPointer(X))
+
+static inline pg_uuid_t *
+DatumGetUUIDP(Datum X)
+{
+ return (pg_uuid_t *) DatumGetPointer(X);
+}
+
#define PG_GETARG_UUID_P(X) DatumGetUUIDP(PG_GETARG_DATUM(X))
#endif /* UUID_H */
diff --git a/src/include/utils/varbit.h b/src/include/utils/varbit.h
index 039ba860cf..e65e7f65e7 100644
--- a/src/include/utils/varbit.h
+++ b/src/include/utils/varbit.h
@@ -41,9 +41,24 @@ typedef struct
* BIT and BIT VARYING are toastable varlena types. They are the same
* as far as representation goes, so we just have one set of macros.
*/
-#define DatumGetVarBitP(X) ((VarBit *) PG_DETOAST_DATUM(X))
-#define DatumGetVarBitPCopy(X) ((VarBit *) PG_DETOAST_DATUM_COPY(X))
-#define VarBitPGetDatum(X) PointerGetDatum(X)
+static inline VarBit *
+DatumGetVarBitP(Datum X)
+{
+ return (VarBit *) PG_DETOAST_DATUM(X);
+}
+
+static inline VarBit *
+DatumGetVarBitPCopy(Datum X)
+{
+ return (VarBit *) PG_DETOAST_DATUM_COPY(X);
+}
+
+static inline Datum
+VarBitPGetDatum(const VarBit *X)
+{
+ return PointerGetDatum(X);
+}
+
#define PG_GETARG_VARBIT_P(n) DatumGetVarBitP(PG_GETARG_DATUM(n))
#define PG_GETARG_VARBIT_P_COPY(n) DatumGetVarBitPCopy(PG_GETARG_DATUM(n))
#define PG_RETURN_VARBIT_P(x) return VarBitPGetDatum(x)
diff --git a/src/include/utils/xid8.h b/src/include/utils/xid8.h
index b702fc1a91..9c5ce241db 100644
--- a/src/include/utils/xid8.h
+++ b/src/include/utils/xid8.h
@@ -14,8 +14,18 @@
#include "access/transam.h"
-#define DatumGetFullTransactionId(X) (FullTransactionIdFromU64(DatumGetUInt64(X)))
-#define FullTransactionIdGetDatum(X) (UInt64GetDatum(U64FromFullTransactionId(X)))
+static inline FullTransactionId
+DatumGetFullTransactionId(Datum X)
+{
+ return FullTransactionIdFromU64(DatumGetUInt64(X));
+}
+
+static inline Datum
+FullTransactionIdGetDatum(FullTransactionId X)
+{
+ return UInt64GetDatum(U64FromFullTransactionId(X));
+}
+
#define PG_GETARG_FULLTRANSACTIONID(X) DatumGetFullTransactionId(PG_GETARG_DATUM(X))
#define PG_RETURN_FULLTRANSACTIONID(X) return FullTransactionIdGetDatum(X)
diff --git a/src/include/utils/xml.h b/src/include/utils/xml.h
index 6620a62619..c71174d204 100644
--- a/src/include/utils/xml.h
+++ b/src/include/utils/xml.h
@@ -47,8 +47,17 @@ typedef enum
/* struct PgXmlErrorContext is private to xml.c */
typedef struct PgXmlErrorContext PgXmlErrorContext;
-#define DatumGetXmlP(X) ((xmltype *) PG_DETOAST_DATUM(X))
-#define XmlPGetDatum(X) PointerGetDatum(X)
+static inline xmltype *
+DatumGetXmlP(Datum X)
+{
+ return (xmltype *) PG_DETOAST_DATUM(X);
+}
+
+static inline Datum
+XmlPGetDatum(const xmltype *X)
+{
+ return PointerGetDatum(X);
+}
#define PG_GETARG_XML_P(n) DatumGetXmlP(PG_GETARG_DATUM(n))
#define PG_RETURN_XML_P(x) PG_RETURN_POINTER(x)
--
2.37.1
Attachments:
[text/plain] 0001-Fix-incorrect-uses-of-Datum-conversion-macros.patch (15.0K, ../../[email protected]/2-0001-Fix-incorrect-uses-of-Datum-conversion-macros.patch)
download | inline diff:
From 4371c74b2d01408653f3917bb838b051537afaa2 Mon Sep 17 00:00:00 2001
From: Peter Eisentraut <[email protected]>
Date: Sun, 28 Aug 2022 10:47:10 +0200
Subject: [PATCH 1/2] Fix incorrect uses of Datum conversion macros
---
contrib/btree_gist/btree_utils_num.c | 2 +-
contrib/dblink/dblink.c | 2 +-
contrib/hstore/hstore_op.c | 2 +-
contrib/pageinspect/heapfuncs.c | 4 ++--
src/backend/access/brin/brin_bloom.c | 2 +-
src/backend/access/brin/brin_minmax_multi.c | 12 ++++++------
src/backend/access/common/toast_compression.c | 2 +-
src/backend/access/table/toast_helper.c | 2 +-
src/backend/access/transam/xlogfuncs.c | 2 +-
src/backend/statistics/mcv.c | 4 ++--
src/backend/utils/adt/amutils.c | 2 +-
src/backend/utils/adt/multirangetypes.c | 2 +-
src/backend/utils/adt/pg_lsn.c | 4 ++--
src/backend/utils/adt/pgstatfuncs.c | 2 +-
src/backend/utils/adt/rangetypes_spgist.c | 4 ++--
src/backend/utils/adt/regexp.c | 2 +-
src/backend/utils/adt/tsgistidx.c | 2 +-
src/backend/utils/adt/tsquery_op.c | 2 +-
src/backend/utils/adt/varlena.c | 2 +-
src/backend/utils/adt/xml.c | 2 +-
src/backend/utils/resowner/resowner.c | 6 +++---
src/pl/plperl/plperl.c | 2 +-
22 files changed, 33 insertions(+), 33 deletions(-)
diff --git a/contrib/btree_gist/btree_utils_num.c b/contrib/btree_gist/btree_utils_num.c
index 5632ee0586..05c154afa3 100644
--- a/contrib/btree_gist/btree_utils_num.c
+++ b/contrib/btree_gist/btree_utils_num.c
@@ -153,7 +153,7 @@ gbt_num_fetch(GISTENTRY *entry, const gbtree_ninfo *tinfo)
datum = CashGetDatum(*(Cash *) entry->key);
break;
default:
- datum = PointerGetDatum(entry->key);
+ datum = entry->key;
}
retval = palloc(sizeof(GISTENTRY));
diff --git a/contrib/dblink/dblink.c b/contrib/dblink/dblink.c
index e323fdd0e6..7940387920 100644
--- a/contrib/dblink/dblink.c
+++ b/contrib/dblink/dblink.c
@@ -1336,7 +1336,7 @@ dblink_get_connections(PG_FUNCTION_ARGS)
}
if (astate)
- PG_RETURN_ARRAYTYPE_P(makeArrayResult(astate,
+ PG_RETURN_DATUM(makeArrayResult(astate,
CurrentMemoryContext));
else
PG_RETURN_NULL();
diff --git a/contrib/hstore/hstore_op.c b/contrib/hstore/hstore_op.c
index 2f540d7ed6..0d4ec16d1e 100644
--- a/contrib/hstore/hstore_op.c
+++ b/contrib/hstore/hstore_op.c
@@ -1064,7 +1064,7 @@ hstore_each(PG_FUNCTION_ARGS)
tuple = heap_form_tuple(funcctx->tuple_desc, dvalues, nulls);
res = HeapTupleGetDatum(tuple);
- SRF_RETURN_NEXT(funcctx, PointerGetDatum(res));
+ SRF_RETURN_NEXT(funcctx, res);
}
SRF_RETURN_DONE(funcctx);
diff --git a/contrib/pageinspect/heapfuncs.c b/contrib/pageinspect/heapfuncs.c
index 2ff70405cf..aed2753253 100644
--- a/contrib/pageinspect/heapfuncs.c
+++ b/contrib/pageinspect/heapfuncs.c
@@ -383,7 +383,7 @@ tuple_data_split_internal(Oid relid, char *tupdata,
errmsg("unexpected end of tuple data")));
if (attr->attlen == -1 && do_detoast)
- attr_data = DatumGetByteaPCopy(tupdata + off);
+ attr_data = pg_detoast_datum_copy((struct varlena *) (tupdata + off));
else
{
attr_data = (bytea *) palloc(len + VARHDRSZ);
@@ -492,7 +492,7 @@ tuple_data_split(PG_FUNCTION_ARGS)
if (t_bits)
pfree(t_bits);
- PG_RETURN_ARRAYTYPE_P(res);
+ PG_RETURN_DATUM(res);
}
/*
diff --git a/src/backend/access/brin/brin_bloom.c b/src/backend/access/brin/brin_bloom.c
index 6812ca9fd1..6b0af7267d 100644
--- a/src/backend/access/brin/brin_bloom.c
+++ b/src/backend/access/brin/brin_bloom.c
@@ -767,7 +767,7 @@ brin_bloom_summary_out(PG_FUNCTION_ARGS)
StringInfoData str;
/* detoast the data to get value with a full 4B header */
- filter = (BloomFilter *) PG_DETOAST_DATUM(PG_GETARG_BYTEA_PP(0));
+ filter = (BloomFilter *) PG_DETOAST_DATUM_PACKED(PG_GETARG_DATUM(0));
initStringInfo(&str);
appendStringInfoChar(&str, '{');
diff --git a/src/backend/access/brin/brin_minmax_multi.c b/src/backend/access/brin/brin_minmax_multi.c
index a581659fe2..c8f22d5d17 100644
--- a/src/backend/access/brin/brin_minmax_multi.c
+++ b/src/backend/access/brin/brin_minmax_multi.c
@@ -774,12 +774,12 @@ brin_range_deserialize(int maxvalues, SerializedRanges *serialized)
datalen += MAXALIGN(typlen);
else if (typlen == -1) /* varlena */
{
- datalen += MAXALIGN(VARSIZE_ANY(DatumGetPointer(ptr)));
- ptr += VARSIZE_ANY(DatumGetPointer(ptr));
+ datalen += MAXALIGN(VARSIZE_ANY(ptr));
+ ptr += VARSIZE_ANY(ptr);
}
else if (typlen == -2) /* cstring */
{
- Size slen = strlen(DatumGetCString(ptr)) + 1;
+ Size slen = strlen(ptr) + 1;
datalen += MAXALIGN(slen);
ptr += slen;
@@ -3033,7 +3033,7 @@ brin_minmax_multi_summary_out(PG_FUNCTION_ARGS)
* Detoast to get value with full 4B header (can't be stored in a toast
* table, but can use 1B header).
*/
- ranges = (SerializedRanges *) PG_DETOAST_DATUM(PG_GETARG_BYTEA_PP(0));
+ ranges = (SerializedRanges *) PG_DETOAST_DATUM_PACKED(PG_GETARG_DATUM(0));
/* lookup output func for the type */
getTypeOutputInfo(ranges->typid, &outfunc, &isvarlena);
@@ -3081,7 +3081,7 @@ brin_minmax_multi_summary_out(PG_FUNCTION_ARGS)
getTypeOutputInfo(ANYARRAYOID, &typoutput, &typIsVarlena);
- val = PointerGetDatum(makeArrayResult(astate_values, CurrentMemoryContext));
+ val = makeArrayResult(astate_values, CurrentMemoryContext);
extval = OidOutputFunctionCall(typoutput, val);
@@ -3121,7 +3121,7 @@ brin_minmax_multi_summary_out(PG_FUNCTION_ARGS)
getTypeOutputInfo(ANYARRAYOID, &typoutput, &typIsVarlena);
- val = PointerGetDatum(makeArrayResult(astate_values, CurrentMemoryContext));
+ val = makeArrayResult(astate_values, CurrentMemoryContext);
extval = OidOutputFunctionCall(typoutput, val);
diff --git a/src/backend/access/common/toast_compression.c b/src/backend/access/common/toast_compression.c
index f90f9f11e3..7156ae9c47 100644
--- a/src/backend/access/common/toast_compression.c
+++ b/src/backend/access/common/toast_compression.c
@@ -44,7 +44,7 @@ pglz_compress_datum(const struct varlena *value)
len;
struct varlena *tmp = NULL;
- valsize = VARSIZE_ANY_EXHDR(DatumGetPointer(value));
+ valsize = VARSIZE_ANY_EXHDR(value);
/*
* No point in wasting a palloc cycle if value size is outside the allowed
diff --git a/src/backend/access/table/toast_helper.c b/src/backend/access/table/toast_helper.c
index 0cc5a30f9b..74ba2189f0 100644
--- a/src/backend/access/table/toast_helper.c
+++ b/src/backend/access/table/toast_helper.c
@@ -330,7 +330,7 @@ toast_delete_external(Relation rel, Datum *values, bool *isnull,
if (isnull[i])
continue;
- else if (VARATT_IS_EXTERNAL_ONDISK(PointerGetDatum(value)))
+ else if (VARATT_IS_EXTERNAL_ONDISK(value))
toast_delete_datum(rel, value, is_speculative);
}
}
diff --git a/src/backend/access/transam/xlogfuncs.c b/src/backend/access/transam/xlogfuncs.c
index 61e0f4a29c..9cc757f1af 100644
--- a/src/backend/access/transam/xlogfuncs.c
+++ b/src/backend/access/transam/xlogfuncs.c
@@ -555,7 +555,7 @@ pg_wal_lsn_diff(PG_FUNCTION_ARGS)
PG_GETARG_DATUM(0),
PG_GETARG_DATUM(1));
- PG_RETURN_NUMERIC(result);
+ PG_RETURN_DATUM(result);
}
/*
diff --git a/src/backend/statistics/mcv.c b/src/backend/statistics/mcv.c
index 6eeacb0d47..f5a7c31272 100644
--- a/src/backend/statistics/mcv.c
+++ b/src/backend/statistics/mcv.c
@@ -1444,8 +1444,8 @@ pg_stats_ext_mcvlist_items(PG_FUNCTION_ARGS)
}
values[0] = Int32GetDatum(funcctx->call_cntr);
- values[1] = PointerGetDatum(makeArrayResult(astate_values, CurrentMemoryContext));
- values[2] = PointerGetDatum(makeArrayResult(astate_nulls, CurrentMemoryContext));
+ values[1] = makeArrayResult(astate_values, CurrentMemoryContext);
+ values[2] = makeArrayResult(astate_nulls, CurrentMemoryContext);
values[3] = Float8GetDatum(item->frequency);
values[4] = Float8GetDatum(item->base_frequency);
diff --git a/src/backend/utils/adt/amutils.c b/src/backend/utils/adt/amutils.c
index 9d78a90d69..60fd396f24 100644
--- a/src/backend/utils/adt/amutils.c
+++ b/src/backend/utils/adt/amutils.c
@@ -466,5 +466,5 @@ pg_indexam_progress_phasename(PG_FUNCTION_ARGS)
if (!name)
PG_RETURN_NULL();
- PG_RETURN_TEXT_P(CStringGetTextDatum(name));
+ PG_RETURN_DATUM(CStringGetTextDatum(name));
}
diff --git a/src/backend/utils/adt/multirangetypes.c b/src/backend/utils/adt/multirangetypes.c
index da5c7d0906..307d087c97 100644
--- a/src/backend/utils/adt/multirangetypes.c
+++ b/src/backend/utils/adt/multirangetypes.c
@@ -1491,7 +1491,7 @@ multirange_intersect_agg_transfn(PG_FUNCTION_ARGS)
ranges1,
range_count2,
ranges2);
- PG_RETURN_RANGE_P(result);
+ PG_RETURN_MULTIRANGE_P(result);
}
diff --git a/src/backend/utils/adt/pg_lsn.c b/src/backend/utils/adt/pg_lsn.c
index 45408787da..15266f36f5 100644
--- a/src/backend/utils/adt/pg_lsn.c
+++ b/src/backend/utils/adt/pg_lsn.c
@@ -271,7 +271,7 @@ pg_lsn_pli(PG_FUNCTION_ARGS)
/* Add two numerics */
res = DirectFunctionCall2(numeric_add,
- NumericGetDatum(num),
+ num,
NumericGetDatum(nbytes));
/* Convert to pg_lsn */
@@ -305,7 +305,7 @@ pg_lsn_mii(PG_FUNCTION_ARGS)
/* Subtract two numerics */
res = DirectFunctionCall2(numeric_sub,
- NumericGetDatum(num),
+ num,
NumericGetDatum(nbytes));
/* Convert to pg_lsn */
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index 4cca30aae7..be15b4b2e5 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -1119,7 +1119,7 @@ pg_stat_get_backend_client_addr(PG_FUNCTION_ARGS)
clean_ipv6_addr(beentry->st_clientaddr.addr.ss_family, remote_host);
- PG_RETURN_INET_P(DirectFunctionCall1(inet_in,
+ PG_RETURN_DATUM(DirectFunctionCall1(inet_in,
CStringGetDatum(remote_host)));
}
diff --git a/src/backend/utils/adt/rangetypes_spgist.c b/src/backend/utils/adt/rangetypes_spgist.c
index 71a6053b6a..a47f04d975 100644
--- a/src/backend/utils/adt/rangetypes_spgist.c
+++ b/src/backend/utils/adt/rangetypes_spgist.c
@@ -416,7 +416,7 @@ spg_range_quad_inner_consistent(PG_FUNCTION_ARGS)
/* This node has a centroid. Fetch it. */
centroid = DatumGetRangeTypeP(in->prefixDatum);
typcache = range_get_typcache(fcinfo,
- RangeTypeGetOid(DatumGetRangeTypeP(centroid)));
+ RangeTypeGetOid(centroid));
range_deserialize(typcache, centroid, ¢roidLower, ¢roidUpper,
¢roidEmpty);
@@ -557,7 +557,7 @@ spg_range_quad_inner_consistent(PG_FUNCTION_ARGS)
*/
if (in->traversalValue)
{
- prevCentroid = DatumGetRangeTypeP(in->traversalValue);
+ prevCentroid = in->traversalValue;
range_deserialize(typcache, prevCentroid,
&prevLower, &prevUpper, &prevEmpty);
}
diff --git a/src/backend/utils/adt/regexp.c b/src/backend/utils/adt/regexp.c
index 0e0c5d5ce8..dcf6681ef2 100644
--- a/src/backend/utils/adt/regexp.c
+++ b/src/backend/utils/adt/regexp.c
@@ -1757,7 +1757,7 @@ regexp_split_to_array(PG_FUNCTION_ARGS)
splitctx->next_match++;
}
- PG_RETURN_ARRAYTYPE_P(makeArrayResult(astate, CurrentMemoryContext));
+ PG_RETURN_DATUM(makeArrayResult(astate, CurrentMemoryContext));
}
/* This is separate to keep the opr_sanity regression test from complaining */
diff --git a/src/backend/utils/adt/tsgistidx.c b/src/backend/utils/adt/tsgistidx.c
index 47daf8199e..fe438f1c4d 100644
--- a/src/backend/utils/adt/tsgistidx.c
+++ b/src/backend/utils/adt/tsgistidx.c
@@ -102,7 +102,7 @@ static int outbuf_maxlen = 0;
Datum
gtsvectorout(PG_FUNCTION_ARGS)
{
- SignTSVector *key = (SignTSVector *) PG_DETOAST_DATUM(PG_GETARG_POINTER(0));
+ SignTSVector *key = (SignTSVector *) PG_DETOAST_DATUM(PG_GETARG_DATUM(0));
char *outbuf;
if (outbuf_maxlen == 0)
diff --git a/src/backend/utils/adt/tsquery_op.c b/src/backend/utils/adt/tsquery_op.c
index a784157ba5..8d815fa7a8 100644
--- a/src/backend/utils/adt/tsquery_op.c
+++ b/src/backend/utils/adt/tsquery_op.c
@@ -148,7 +148,7 @@ tsquery_phrase_distance(PG_FUNCTION_ARGS)
Datum
tsquery_phrase(PG_FUNCTION_ARGS)
{
- PG_RETURN_POINTER(DirectFunctionCall3(tsquery_phrase_distance,
+ PG_RETURN_DATUM(DirectFunctionCall3(tsquery_phrase_distance,
PG_GETARG_DATUM(0),
PG_GETARG_DATUM(1),
Int32GetDatum(1)));
diff --git a/src/backend/utils/adt/varlena.c b/src/backend/utils/adt/varlena.c
index 8539cef024..6ef317e1f9 100644
--- a/src/backend/utils/adt/varlena.c
+++ b/src/backend/utils/adt/varlena.c
@@ -4779,7 +4779,7 @@ text_to_array(PG_FUNCTION_ARGS)
if (tstate.astate == NULL)
PG_RETURN_ARRAYTYPE_P(construct_empty_array(TEXTOID));
- PG_RETURN_ARRAYTYPE_P(makeArrayResult(tstate.astate,
+ PG_RETURN_DATUM(makeArrayResult(tstate.astate,
CurrentMemoryContext));
}
diff --git a/src/backend/utils/adt/xml.c b/src/backend/utils/adt/xml.c
index 60a85c4697..606088cdfc 100644
--- a/src/backend/utils/adt/xml.c
+++ b/src/backend/utils/adt/xml.c
@@ -4174,7 +4174,7 @@ xpath(PG_FUNCTION_ARGS)
astate = initArrayResult(XMLOID, CurrentMemoryContext, true);
xpath_internal(xpath_expr_text, data, namespaces,
NULL, astate);
- PG_RETURN_ARRAYTYPE_P(makeArrayResult(astate, CurrentMemoryContext));
+ PG_RETURN_DATUM(makeArrayResult(astate, CurrentMemoryContext));
#else
NO_XML_SUPPORT();
return 0;
diff --git a/src/backend/utils/resowner/resowner.c b/src/backend/utils/resowner/resowner.c
index ceb4b0e3f7..ece5d98261 100644
--- a/src/backend/utils/resowner/resowner.c
+++ b/src/backend/utils/resowner/resowner.c
@@ -557,7 +557,7 @@ ResourceOwnerReleaseInternal(ResourceOwner owner,
/* Ditto for JIT contexts */
while (ResourceArrayGetAny(&(owner->jitarr), &foundres))
{
- JitContext *context = (JitContext *) PointerGetDatum(foundres);
+ JitContext *context = (JitContext *) DatumGetPointer(foundres);
jit_release_context(context);
}
@@ -566,7 +566,7 @@ ResourceOwnerReleaseInternal(ResourceOwner owner,
while (ResourceArrayGetAny(&(owner->cryptohasharr), &foundres))
{
pg_cryptohash_ctx *context =
- (pg_cryptohash_ctx *) PointerGetDatum(foundres);
+ (pg_cryptohash_ctx *) DatumGetPointer(foundres);
if (isCommit)
PrintCryptoHashLeakWarning(foundres);
@@ -576,7 +576,7 @@ ResourceOwnerReleaseInternal(ResourceOwner owner,
/* Ditto for HMAC contexts */
while (ResourceArrayGetAny(&(owner->hmacarr), &foundres))
{
- pg_hmac_ctx *context = (pg_hmac_ctx *) PointerGetDatum(foundres);
+ pg_hmac_ctx *context = (pg_hmac_ctx *) DatumGetPointer(foundres);
if (isCommit)
PrintHMACLeakWarning(foundres);
diff --git a/src/pl/plperl/plperl.c b/src/pl/plperl/plperl.c
index 5d192a0ce5..8f21e0d701 100644
--- a/src/pl/plperl/plperl.c
+++ b/src/pl/plperl/plperl.c
@@ -1848,7 +1848,7 @@ plperl_call_handler(PG_FUNCTION_ARGS)
{
current_call_data = &this_call_data;
if (CALLED_AS_TRIGGER(fcinfo))
- retval = PointerGetDatum(plperl_trigger_handler(fcinfo));
+ retval = plperl_trigger_handler(fcinfo);
else if (CALLED_AS_EVENT_TRIGGER(fcinfo))
{
plperl_event_trigger_handler(fcinfo);
--
2.37.1
[text/plain] 0002-Convert-GetDatum-and-DatumGet-macros-to-inline-funct.patch (39.9K, ../../[email protected]/3-0002-Convert-GetDatum-and-DatumGet-macros-to-inline-funct.patch)
download | inline diff:
From a54a726eadcab016714b431b23d273d9653fb2f6 Mon Sep 17 00:00:00 2001
From: Peter Eisentraut <[email protected]>
Date: Sun, 28 Aug 2022 10:47:38 +0200
Subject: [PATCH 2/2] Convert *GetDatum() and DatumGet*() macros to inline
functions
The previous macro implementations just cast the argument to a target
type but did not check whether the input type was appropriate. The
function implementation can do better type checking of the input type.
---
doc/src/sgml/xfunc.sgml | 2 +-
src/backend/utils/mb/mbutils.c | 12 +-
src/include/access/gin.h | 14 +-
src/include/funcapi.h | 14 +-
src/include/postgres.h | 275 +++++++++++++++++++---------
src/include/tsearch/ts_type.h | 46 ++++-
src/include/tsearch/ts_utils.h | 14 +-
src/include/utils/cash.h | 14 +-
src/include/utils/date.h | 42 ++++-
src/include/utils/expandeddatum.h | 13 +-
src/include/utils/expandedrecord.h | 16 +-
src/include/utils/geo_decls.h | 98 ++++++++--
src/include/utils/inet.h | 49 ++++-
src/include/utils/jsonb.h | 31 +++-
src/include/utils/jsonpath.h | 14 +-
src/include/utils/multirangetypes.h | 23 ++-
src/include/utils/numeric.h | 21 ++-
src/include/utils/pg_lsn.h | 13 +-
src/include/utils/rangetypes.h | 23 ++-
src/include/utils/timestamp.h | 44 ++++-
src/include/utils/uuid.h | 15 +-
src/include/utils/varbit.h | 21 ++-
src/include/utils/xid8.h | 14 +-
src/include/utils/xml.h | 13 +-
24 files changed, 655 insertions(+), 186 deletions(-)
diff --git a/doc/src/sgml/xfunc.sgml b/doc/src/sgml/xfunc.sgml
index b8cefb9c2c..cf5810b3c1 100644
--- a/doc/src/sgml/xfunc.sgml
+++ b/doc/src/sgml/xfunc.sgml
@@ -2763,7 +2763,7 @@ <title>Composite-Type Arguments</title>
is null. <function>GetAttributeByName</function> returns a <type>Datum</type>
value that you can convert to the proper data type by using the
appropriate <function>DatumGet<replaceable>XXX</replaceable>()</function>
- macro. Note that the return value is meaningless if the null flag is
+ function. Note that the return value is meaningless if the null flag is
set; always check the null flag before trying to do anything with the
result.
</para>
diff --git a/src/backend/utils/mb/mbutils.c b/src/backend/utils/mb/mbutils.c
index 0543c574c6..474ab476f5 100644
--- a/src/backend/utils/mb/mbutils.c
+++ b/src/backend/utils/mb/mbutils.c
@@ -409,8 +409,8 @@ pg_do_encoding_conversion(unsigned char *src, int len,
(void) OidFunctionCall6(proc,
Int32GetDatum(src_encoding),
Int32GetDatum(dest_encoding),
- CStringGetDatum(src),
- CStringGetDatum(result),
+ CStringGetDatum((char *) src),
+ CStringGetDatum((char *) result),
Int32GetDatum(len),
BoolGetDatum(false));
@@ -485,8 +485,8 @@ pg_do_encoding_conversion_buf(Oid proc,
result = OidFunctionCall6(proc,
Int32GetDatum(src_encoding),
Int32GetDatum(dest_encoding),
- CStringGetDatum(src),
- CStringGetDatum(dest),
+ CStringGetDatum((char *) src),
+ CStringGetDatum((char *) dest),
Int32GetDatum(srclen),
BoolGetDatum(noError));
return DatumGetInt32(result);
@@ -910,8 +910,8 @@ pg_unicode_to_server(pg_wchar c, unsigned char *s)
FunctionCall6(Utf8ToServerConvProc,
Int32GetDatum(PG_UTF8),
Int32GetDatum(server_encoding),
- CStringGetDatum(c_as_utf8),
- CStringGetDatum(s),
+ CStringGetDatum((char *) c_as_utf8),
+ CStringGetDatum((char *) s),
Int32GetDatum(c_as_utf8_len),
BoolGetDatum(false));
}
diff --git a/src/include/access/gin.h b/src/include/access/gin.h
index aacc665fdc..fff861e219 100644
--- a/src/include/access/gin.h
+++ b/src/include/access/gin.h
@@ -62,8 +62,18 @@ typedef char GinTernaryValue;
#define GIN_MAYBE 2 /* don't know if item is present / don't know
* if matches */
-#define DatumGetGinTernaryValue(X) ((GinTernaryValue)(X))
-#define GinTernaryValueGetDatum(X) ((Datum)(X))
+static inline GinTernaryValue
+DatumGetGinTernaryValue(Datum X)
+{
+ return (GinTernaryValue) X;
+}
+
+static inline Datum
+GinTernaryValueGetDatum(GinTernaryValue X)
+{
+ return (Datum) X;
+}
+
#define PG_RETURN_GIN_TERNARY_VALUE(x) return GinTernaryValueGetDatum(x)
/* GUC parameters */
diff --git a/src/include/funcapi.h b/src/include/funcapi.h
index dc3d819a1c..8c2521a591 100644
--- a/src/include/funcapi.h
+++ b/src/include/funcapi.h
@@ -204,7 +204,7 @@ extern TupleDesc build_function_result_tupdesc_t(HeapTuple procTuple);
* Datum HeapTupleHeaderGetDatum(HeapTupleHeader tuple) - convert a
* HeapTupleHeader to a Datum.
*
- * Macro declarations:
+ * Inline declarations:
* HeapTupleGetDatum(HeapTuple tuple) - convert a HeapTuple to a Datum.
*
* Obsolete routines and macros:
@@ -217,10 +217,6 @@ extern TupleDesc build_function_result_tupdesc_t(HeapTuple procTuple);
*----------
*/
-#define HeapTupleGetDatum(tuple) HeapTupleHeaderGetDatum((tuple)->t_data)
-/* obsolete version of above */
-#define TupleGetDatum(_slot, _tuple) HeapTupleGetDatum(_tuple)
-
extern TupleDesc RelationNameGetTupleDesc(const char *relname);
extern TupleDesc TypeGetTupleDesc(Oid typeoid, List *colaliases);
@@ -230,6 +226,14 @@ extern AttInMetadata *TupleDescGetAttInMetadata(TupleDesc tupdesc);
extern HeapTuple BuildTupleFromCStrings(AttInMetadata *attinmeta, char **values);
extern Datum HeapTupleHeaderGetDatum(HeapTupleHeader tuple);
+static inline Datum
+HeapTupleGetDatum(const HeapTupleData *tuple)
+{
+ return HeapTupleHeaderGetDatum(tuple->t_data);
+}
+/* obsolete version of above */
+#define TupleGetDatum(_slot, _tuple) HeapTupleGetDatum(_tuple)
+
/*----------
* Support for Set Returning Functions (SRFs)
diff --git a/src/include/postgres.h b/src/include/postgres.h
index 13903fa022..45c9f6563c 100644
--- a/src/include/postgres.h
+++ b/src/include/postgres.h
@@ -24,7 +24,7 @@
* section description
* ------- ------------------------------------------------
* 1) variable-length datatypes (TOAST support)
- * 2) Datum type + support macros
+ * 2) Datum type + support functions
* 3) miscellaneous
*
* NOTES
@@ -395,7 +395,7 @@ typedef struct
/* ----------------------------------------------------------------
- * Section 2: Datum type + support macros
+ * Section 2: Datum type + support functions
* ----------------------------------------------------------------
*/
@@ -405,7 +405,7 @@ typedef struct
*
* sizeof(Datum) == sizeof(void *) == 4 or 8
*
- * The macros below and the analogous macros for other types should be used to
+ * The functions below and the analogous functions for other types should be used to
* convert between a Datum and the appropriate C type.
*/
@@ -434,8 +434,11 @@ typedef struct NullableDatum
*
* Note: any nonzero value will be considered true.
*/
-
-#define DatumGetBool(X) ((bool) ((X) != 0))
+static inline bool
+DatumGetBool(Datum X)
+{
+ return (X != 0);
+}
/*
* BoolGetDatum
@@ -443,162 +446,231 @@ typedef struct NullableDatum
*
* Note: any nonzero value will be considered true.
*/
-
-#define BoolGetDatum(X) ((Datum) ((X) ? 1 : 0))
+static inline Datum
+BoolGetDatum(bool X)
+{
+ return (Datum) (X ? 1 : 0);
+}
/*
* DatumGetChar
* Returns character value of a datum.
*/
-
-#define DatumGetChar(X) ((char) (X))
+static inline char
+DatumGetChar(Datum X)
+{
+ return (char) X;
+}
/*
* CharGetDatum
* Returns datum representation for a character.
*/
-
-#define CharGetDatum(X) ((Datum) (X))
+static inline Datum
+CharGetDatum(char X)
+{
+ return (Datum) X;
+}
/*
* Int8GetDatum
* Returns datum representation for an 8-bit integer.
*/
-
-#define Int8GetDatum(X) ((Datum) (X))
+static inline Datum
+Int8GetDatum(int8 X)
+{
+ return (Datum) X;
+}
/*
* DatumGetUInt8
* Returns 8-bit unsigned integer value of a datum.
*/
-
-#define DatumGetUInt8(X) ((uint8) (X))
+static inline uint8
+DatumGetUInt8(Datum X)
+{
+ return (uint8) X;
+}
/*
* UInt8GetDatum
* Returns datum representation for an 8-bit unsigned integer.
*/
-
-#define UInt8GetDatum(X) ((Datum) (X))
+static inline Datum
+UInt8GetDatum(uint8 X)
+{
+ return (Datum) X;
+}
/*
* DatumGetInt16
* Returns 16-bit integer value of a datum.
*/
-
-#define DatumGetInt16(X) ((int16) (X))
+static inline int16
+DatumGetInt16(Datum X)
+{
+ return (int16) X;
+}
/*
* Int16GetDatum
* Returns datum representation for a 16-bit integer.
*/
-
-#define Int16GetDatum(X) ((Datum) (X))
+static inline Datum
+Int16GetDatum(int16 X)
+{
+ return (Datum) X;
+}
/*
* DatumGetUInt16
* Returns 16-bit unsigned integer value of a datum.
*/
-
-#define DatumGetUInt16(X) ((uint16) (X))
+static inline uint16
+DatumGetUInt16(Datum X)
+{
+ return (uint16) X;
+}
/*
* UInt16GetDatum
* Returns datum representation for a 16-bit unsigned integer.
*/
-
-#define UInt16GetDatum(X) ((Datum) (X))
+static inline Datum
+UInt16GetDatum(uint16 X)
+{
+ return (Datum) X;
+}
/*
* DatumGetInt32
* Returns 32-bit integer value of a datum.
*/
-
-#define DatumGetInt32(X) ((int32) (X))
+static inline int32
+DatumGetInt32(Datum X)
+{
+ return (int32) X;
+}
/*
* Int32GetDatum
* Returns datum representation for a 32-bit integer.
*/
-
-#define Int32GetDatum(X) ((Datum) (X))
+static inline Datum
+Int32GetDatum(int32 X)
+{
+ return (Datum) X;
+}
/*
* DatumGetUInt32
* Returns 32-bit unsigned integer value of a datum.
*/
-
-#define DatumGetUInt32(X) ((uint32) (X))
+static inline uint32
+DatumGetUInt32(Datum X)
+{
+ return (uint32) X;
+}
/*
* UInt32GetDatum
* Returns datum representation for a 32-bit unsigned integer.
*/
-
-#define UInt32GetDatum(X) ((Datum) (X))
+static inline Datum
+UInt32GetDatum(uint32 X)
+{
+ return (Datum) X;
+}
/*
* DatumGetObjectId
* Returns object identifier value of a datum.
*/
-
-#define DatumGetObjectId(X) ((Oid) (X))
+static inline Oid
+DatumGetObjectId(Datum X)
+{
+ return (Oid) X;
+}
/*
* ObjectIdGetDatum
* Returns datum representation for an object identifier.
*/
-
-#define ObjectIdGetDatum(X) ((Datum) (X))
+static inline Datum
+ObjectIdGetDatum(Oid X)
+{
+ return (Datum) X;
+}
/*
* DatumGetTransactionId
* Returns transaction identifier value of a datum.
*/
-
-#define DatumGetTransactionId(X) ((TransactionId) (X))
+static inline TransactionId
+DatumGetTransactionId(Datum X)
+{
+ return (TransactionId) X;
+}
/*
* TransactionIdGetDatum
* Returns datum representation for a transaction identifier.
*/
-
-#define TransactionIdGetDatum(X) ((Datum) (X))
+static inline Datum
+TransactionIdGetDatum(TransactionId X)
+{
+ return (Datum) X;
+}
/*
* MultiXactIdGetDatum
* Returns datum representation for a multixact identifier.
*/
-
-#define MultiXactIdGetDatum(X) ((Datum) (X))
+static inline Datum
+MultiXactIdGetDatum(MultiXactId X)
+{
+ return (Datum) X;
+}
/*
* DatumGetCommandId
* Returns command identifier value of a datum.
*/
-
-#define DatumGetCommandId(X) ((CommandId) (X))
+static inline CommandId
+DatumGetCommandId(Datum X)
+{
+ return (CommandId) X;
+}
/*
* CommandIdGetDatum
* Returns datum representation for a command identifier.
*/
-
-#define CommandIdGetDatum(X) ((Datum) (X))
+static inline Datum
+CommandIdGetDatum(CommandId X)
+{
+ return (Datum) X;
+}
/*
* DatumGetPointer
* Returns pointer value of a datum.
*/
-
-#define DatumGetPointer(X) ((Pointer) (X))
+static inline Pointer
+DatumGetPointer(Datum X)
+{
+ return (Pointer) X;
+}
/*
* PointerGetDatum
* Returns datum representation for a pointer.
*/
-
-#define PointerGetDatum(X) ((Datum) (X))
+static inline Datum
+PointerGetDatum(const void *X)
+{
+ return (Datum) X;
+}
/*
* DatumGetCString
@@ -607,8 +679,11 @@ typedef struct NullableDatum
* Note: C string is not a full-fledged Postgres type at present,
* but type input functions use this conversion for their inputs.
*/
-
-#define DatumGetCString(X) ((char *) DatumGetPointer(X))
+static inline char *
+DatumGetCString(Datum X)
+{
+ return (char *) DatumGetPointer(X);
+}
/*
* CStringGetDatum
@@ -619,15 +694,21 @@ typedef struct NullableDatum
* Note: CString is pass-by-reference; caller must ensure the pointed-to
* value has adequate lifetime.
*/
-
-#define CStringGetDatum(X) PointerGetDatum(X)
+static inline Datum
+CStringGetDatum(const char *X)
+{
+ return PointerGetDatum(X);
+}
/*
* DatumGetName
* Returns name value of a datum.
*/
-
-#define DatumGetName(X) ((Name) DatumGetPointer(X))
+static inline Name
+DatumGetName(Datum X)
+{
+ return (Name) DatumGetPointer(X);
+}
/*
* NameGetDatum
@@ -636,21 +717,27 @@ typedef struct NullableDatum
* Note: Name is pass-by-reference; caller must ensure the pointed-to
* value has adequate lifetime.
*/
-
-#define NameGetDatum(X) CStringGetDatum(NameStr(*(X)))
+static inline Datum
+NameGetDatum(const NameData *X)
+{
+ return CStringGetDatum(NameStr(*X));
+}
/*
* DatumGetInt64
* Returns 64-bit integer value of a datum.
*
- * Note: this macro hides whether int64 is pass by value or by reference.
+ * Note: this function hides whether int64 is pass by value or by reference.
*/
-
+static inline int64
+DatumGetInt64(Datum X)
+{
#ifdef USE_FLOAT8_BYVAL
-#define DatumGetInt64(X) ((int64) (X))
+ return (int64) X;
#else
-#define DatumGetInt64(X) (* ((int64 *) DatumGetPointer(X)))
+ return *((int64 *) DatumGetPointer(X));
#endif
+}
/*
* Int64GetDatum
@@ -659,25 +746,32 @@ typedef struct NullableDatum
* Note: if int64 is pass by reference, this function returns a reference
* to palloc'd space.
*/
-
#ifdef USE_FLOAT8_BYVAL
-#define Int64GetDatum(X) ((Datum) (X))
+static inline Datum
+Int64GetDatum(int64 X)
+{
+ return (Datum) X;
+}
#else
extern Datum Int64GetDatum(int64 X);
#endif
+
/*
* DatumGetUInt64
* Returns 64-bit unsigned integer value of a datum.
*
- * Note: this macro hides whether int64 is pass by value or by reference.
+ * Note: this function hides whether int64 is pass by value or by reference.
*/
-
+static inline uint64
+DatumGetUInt64(Datum X)
+{
#ifdef USE_FLOAT8_BYVAL
-#define DatumGetUInt64(X) ((uint64) (X))
+ return (uint64) X;
#else
-#define DatumGetUInt64(X) (* ((uint64 *) DatumGetPointer(X)))
+ return *((uint64 *) DatumGetPointer(X));
#endif
+}
/*
* UInt64GetDatum
@@ -686,12 +780,15 @@ extern Datum Int64GetDatum(int64 X);
* Note: if int64 is pass by reference, this function returns a reference
* to palloc'd space.
*/
-
+static inline Datum
+UInt64GetDatum(uint64 X)
+{
#ifdef USE_FLOAT8_BYVAL
-#define UInt64GetDatum(X) ((Datum) (X))
+ return (Datum) X;
#else
-#define UInt64GetDatum(X) Int64GetDatum((int64) (X))
+ return Int64GetDatum((int64) X);
#endif
+}
/*
* Float <-> Datum conversions
@@ -739,13 +836,12 @@ Float4GetDatum(float4 X)
* DatumGetFloat8
* Returns 8-byte floating point value of a datum.
*
- * Note: this macro hides whether float8 is pass by value or by reference.
+ * Note: this function hides whether float8 is pass by value or by reference.
*/
-
-#ifdef USE_FLOAT8_BYVAL
static inline float8
DatumGetFloat8(Datum X)
{
+#ifdef USE_FLOAT8_BYVAL
union
{
int64 value;
@@ -754,10 +850,10 @@ DatumGetFloat8(Datum X)
myunion.value = DatumGetInt64(X);
return myunion.retval;
-}
#else
-#define DatumGetFloat8(X) (* ((float8 *) DatumGetPointer(X)))
+ return *((float8 *) DatumGetPointer(X));
#endif
+}
/*
* Float8GetDatum
@@ -766,7 +862,6 @@ DatumGetFloat8(Datum X)
* Note: if float8 is pass by reference, this function returns a reference
* to palloc'd space.
*/
-
#ifdef USE_FLOAT8_BYVAL
static inline Datum
Float8GetDatum(float8 X)
@@ -789,22 +884,34 @@ extern Datum Float8GetDatum(float8 X);
* Int64GetDatumFast
* Float8GetDatumFast
*
- * These macros are intended to allow writing code that does not depend on
+ * These functions are intended to allow writing code that does not depend on
* whether int64 and float8 are pass-by-reference types, while not
* sacrificing performance when they are. The argument must be a variable
* that will exist and have the same value for as long as the Datum is needed.
* In the pass-by-ref case, the address of the variable is taken to use as
* the Datum. In the pass-by-val case, these will be the same as the non-Fast
- * macros.
+ * functions.
*/
+static inline Datum
+Int64GetDatumFast(int64 X)
+{
+#ifdef USE_FLOAT8_BYVAL
+ return Int64GetDatum(X);
+#else
+ return PointerGetDatum(&X);
+#endif
+}
+
+static inline Datum
+Float8GetDatumFast(float8 X)
+{
#ifdef USE_FLOAT8_BYVAL
-#define Int64GetDatumFast(X) Int64GetDatum(X)
-#define Float8GetDatumFast(X) Float8GetDatum(X)
+ return Float8GetDatum(X);
#else
-#define Int64GetDatumFast(X) PointerGetDatum(&(X))
-#define Float8GetDatumFast(X) PointerGetDatum(&(X))
+ return PointerGetDatum(&X);
#endif
+}
/* ----------------------------------------------------------------
diff --git a/src/include/tsearch/ts_type.h b/src/include/tsearch/ts_type.h
index 689b2d1cfb..f1ec84702d 100644
--- a/src/include/tsearch/ts_type.h
+++ b/src/include/tsearch/ts_type.h
@@ -111,12 +111,27 @@ typedef TSVectorData *TSVector;
#define POSDATAPTR(x,e) (_POSVECPTR(x,e)->pos)
/*
- * fmgr interface macros
+ * fmgr interface functions
*/
-#define DatumGetTSVector(X) ((TSVector) PG_DETOAST_DATUM(X))
-#define DatumGetTSVectorCopy(X) ((TSVector) PG_DETOAST_DATUM_COPY(X))
-#define TSVectorGetDatum(X) PointerGetDatum(X)
+static inline TSVector
+DatumGetTSVector(Datum X)
+{
+ return (TSVector) PG_DETOAST_DATUM(X);
+}
+
+static inline TSVector
+DatumGetTSVectorCopy(Datum X)
+{
+ return (TSVector) PG_DETOAST_DATUM_COPY(X);
+}
+
+static inline Datum
+TSVectorGetDatum(const TSVectorData *X)
+{
+ return PointerGetDatum(X);
+}
+
#define PG_GETARG_TSVECTOR(n) DatumGetTSVector(PG_GETARG_DATUM(n))
#define PG_GETARG_TSVECTOR_COPY(n) DatumGetTSVectorCopy(PG_GETARG_DATUM(n))
#define PG_RETURN_TSVECTOR(x) return TSVectorGetDatum(x)
@@ -227,14 +242,29 @@ typedef TSQueryData *TSQuery;
#define GETOPERAND(x) ( (char*)GETQUERY(x) + ((TSQuery)(x))->size * sizeof(QueryItem) )
/*
- * fmgr interface macros
+ * fmgr interface functions
* Note, TSQuery type marked as plain storage, so it can't be toasted
* but PG_DETOAST_DATUM_COPY is used for simplicity
*/
-#define DatumGetTSQuery(X) ((TSQuery) DatumGetPointer(X))
-#define DatumGetTSQueryCopy(X) ((TSQuery) PG_DETOAST_DATUM_COPY(X))
-#define TSQueryGetDatum(X) PointerGetDatum(X)
+static inline TSQuery
+DatumGetTSQuery(Datum X)
+{
+ return (TSQuery) DatumGetPointer(X);
+}
+
+static inline TSQuery
+DatumGetTSQueryCopy(Datum X)
+{
+ return (TSQuery) PG_DETOAST_DATUM_COPY(X);
+}
+
+static inline Datum
+TSQueryGetDatum(const TSQueryData *X)
+{
+ return PointerGetDatum(X);
+}
+
#define PG_GETARG_TSQUERY(n) DatumGetTSQuery(PG_GETARG_DATUM(n))
#define PG_GETARG_TSQUERY_COPY(n) DatumGetTSQueryCopy(PG_GETARG_DATUM(n))
#define PG_RETURN_TSQUERY(x) return TSQueryGetDatum(x)
diff --git a/src/include/tsearch/ts_utils.h b/src/include/tsearch/ts_utils.h
index c36c711dae..26cc415aa8 100644
--- a/src/include/tsearch/ts_utils.h
+++ b/src/include/tsearch/ts_utils.h
@@ -243,8 +243,18 @@ typedef uint64 TSQuerySign;
#define TSQS_SIGLEN (sizeof(TSQuerySign)*BITS_PER_BYTE)
-#define TSQuerySignGetDatum(X) Int64GetDatum((int64) (X))
-#define DatumGetTSQuerySign(X) ((TSQuerySign) DatumGetInt64(X))
+static inline Datum
+TSQuerySignGetDatum(TSQuerySign X)
+{
+ return Int64GetDatum((int64) X);
+}
+
+static inline TSQuerySign
+DatumGetTSQuerySign(Datum X)
+{
+ return (TSQuerySign) DatumGetInt64(X);
+}
+
#define PG_RETURN_TSQUERYSIGN(X) return TSQuerySignGetDatum(X)
#define PG_GETARG_TSQUERYSIGN(n) DatumGetTSQuerySign(PG_GETARG_DATUM(n))
diff --git a/src/include/utils/cash.h b/src/include/utils/cash.h
index 2e332d83b1..55d45fadd4 100644
--- a/src/include/utils/cash.h
+++ b/src/include/utils/cash.h
@@ -17,8 +17,18 @@
typedef int64 Cash;
/* Cash is pass-by-reference if and only if int64 is */
-#define DatumGetCash(X) ((Cash) DatumGetInt64(X))
-#define CashGetDatum(X) Int64GetDatum(X)
+static inline Cash
+DatumGetCash(Datum X)
+{
+ return DatumGetInt64(X);
+}
+
+static inline Datum
+CashGetDatum(Cash X)
+{
+ return Int64GetDatum(X);
+}
+
#define PG_GETARG_CASH(n) DatumGetCash(PG_GETARG_DATUM(n))
#define PG_RETURN_CASH(x) return CashGetDatum(x)
diff --git a/src/include/utils/date.h b/src/include/utils/date.h
index 3991da41f0..0bbe889128 100644
--- a/src/include/utils/date.h
+++ b/src/include/utils/date.h
@@ -45,18 +45,46 @@ typedef struct
#define MAX_TIME_PRECISION 6
/*
- * Macros for fmgr-callable functions.
+ * Functions for fmgr-callable functions.
*
* For TimeADT, we make use of the same support routines as for int64.
* Therefore TimeADT is pass-by-reference if and only if int64 is!
*/
-#define DatumGetDateADT(X) ((DateADT) DatumGetInt32(X))
-#define DatumGetTimeADT(X) ((TimeADT) DatumGetInt64(X))
-#define DatumGetTimeTzADTP(X) ((TimeTzADT *) DatumGetPointer(X))
+static inline DateADT
+DatumGetDateADT(Datum X)
+{
+ return (DateADT) DatumGetInt32(X);
+}
+
+static inline TimeADT
+DatumGetTimeADT(Datum X)
+{
+ return (TimeADT) DatumGetInt64(X);
+}
+
+static inline TimeTzADT *
+DatumGetTimeTzADTP(Datum X)
+{
+ return (TimeTzADT *) DatumGetPointer(X);
+}
-#define DateADTGetDatum(X) Int32GetDatum(X)
-#define TimeADTGetDatum(X) Int64GetDatum(X)
-#define TimeTzADTPGetDatum(X) PointerGetDatum(X)
+static inline Datum
+DateADTGetDatum(DateADT X)
+{
+ return Int32GetDatum(X);
+}
+
+static inline Datum
+TimeADTGetDatum(TimeADT X)
+{
+ return Int64GetDatum(X);
+}
+
+static inline Datum
+TimeTzADTPGetDatum(const TimeTzADT *X)
+{
+ return PointerGetDatum(X);
+}
#define PG_GETARG_DATEADT(n) DatumGetDateADT(PG_GETARG_DATUM(n))
#define PG_GETARG_TIMEADT(n) DatumGetTimeADT(PG_GETARG_DATUM(n))
diff --git a/src/include/utils/expandeddatum.h b/src/include/utils/expandeddatum.h
index ffdb0c45bd..c4818eb054 100644
--- a/src/include/utils/expandeddatum.h
+++ b/src/include/utils/expandeddatum.h
@@ -133,8 +133,17 @@ struct ExpandedObjectHeader
* (More of these might be worth inlining later.)
*/
-#define EOHPGetRWDatum(eohptr) PointerGetDatum((eohptr)->eoh_rw_ptr)
-#define EOHPGetRODatum(eohptr) PointerGetDatum((eohptr)->eoh_ro_ptr)
+static inline Datum
+EOHPGetRWDatum(const struct ExpandedObjectHeader *eohptr)
+{
+ return PointerGetDatum(eohptr->eoh_rw_ptr);
+}
+
+static inline Datum
+EOHPGetRODatum(const struct ExpandedObjectHeader *eohptr)
+{
+ return PointerGetDatum(eohptr->eoh_ro_ptr);
+}
/* Does the Datum represent a writable expanded object? */
#define DatumIsReadWriteExpandedObject(d, isnull, typlen) \
diff --git a/src/include/utils/expandedrecord.h b/src/include/utils/expandedrecord.h
index be60e2ce53..aaf5b6bfd1 100644
--- a/src/include/utils/expandedrecord.h
+++ b/src/include/utils/expandedrecord.h
@@ -138,10 +138,20 @@ typedef struct ExpandedRecordHeader
MemoryContextCallback er_mcb;
} ExpandedRecordHeader;
-/* fmgr macros for expanded record objects */
+/* fmgr functions and macros for expanded record objects */
+static inline Datum
+ExpandedRecordGetDatum(const ExpandedRecordHeader *erh)
+{
+ return EOHPGetRWDatum(&erh->hdr);
+}
+
+static inline Datum
+ExpandedRecordGetRODatum(const ExpandedRecordHeader *erh)
+{
+ return EOHPGetRODatum(&erh->hdr);
+}
+
#define PG_GETARG_EXPANDED_RECORD(n) DatumGetExpandedRecord(PG_GETARG_DATUM(n))
-#define ExpandedRecordGetDatum(erh) EOHPGetRWDatum(&(erh)->hdr)
-#define ExpandedRecordGetRODatum(erh) EOHPGetRODatum(&(erh)->hdr)
#define PG_RETURN_EXPANDED_RECORD(x) PG_RETURN_DATUM(ExpandedRecordGetDatum(x))
/* assorted other macros */
diff --git a/src/include/utils/geo_decls.h b/src/include/utils/geo_decls.h
index 719030777e..05726778ad 100644
--- a/src/include/utils/geo_decls.h
+++ b/src/include/utils/geo_decls.h
@@ -166,48 +166,112 @@ typedef struct
} CIRCLE;
/*
- * fmgr interface macros
+ * fmgr interface functions
*
* Path and Polygon are toastable varlena types, the others are just
* fixed-size pass-by-reference types.
*/
-#define DatumGetPointP(X) ((Point *) DatumGetPointer(X))
-#define PointPGetDatum(X) PointerGetDatum(X)
+static inline Point *
+DatumGetPointP(Datum X)
+{
+ return (Point *) DatumGetPointer(X);
+}
+static inline Datum
+PointPGetDatum(const Point *X)
+{
+ return PointerGetDatum(X);
+}
#define PG_GETARG_POINT_P(n) DatumGetPointP(PG_GETARG_DATUM(n))
#define PG_RETURN_POINT_P(x) return PointPGetDatum(x)
-#define DatumGetLsegP(X) ((LSEG *) DatumGetPointer(X))
-#define LsegPGetDatum(X) PointerGetDatum(X)
+static inline LSEG *
+DatumGetLsegP(Datum X)
+{
+ return (LSEG *) DatumGetPointer(X);
+}
+static inline Datum
+LsegPGetDatum(const LSEG *X)
+{
+ return PointerGetDatum(X);
+}
#define PG_GETARG_LSEG_P(n) DatumGetLsegP(PG_GETARG_DATUM(n))
#define PG_RETURN_LSEG_P(x) return LsegPGetDatum(x)
-#define DatumGetPathP(X) ((PATH *) PG_DETOAST_DATUM(X))
-#define DatumGetPathPCopy(X) ((PATH *) PG_DETOAST_DATUM_COPY(X))
-#define PathPGetDatum(X) PointerGetDatum(X)
+static inline PATH *
+DatumGetPathP(Datum X)
+{
+ return (PATH *) PG_DETOAST_DATUM(X);
+}
+static inline PATH *
+DatumGetPathPCopy(Datum X)
+{
+ return (PATH *) PG_DETOAST_DATUM_COPY(X);
+}
+static inline Datum
+PathPGetDatum(const PATH *X)
+{
+ return PointerGetDatum(X);
+}
#define PG_GETARG_PATH_P(n) DatumGetPathP(PG_GETARG_DATUM(n))
#define PG_GETARG_PATH_P_COPY(n) DatumGetPathPCopy(PG_GETARG_DATUM(n))
#define PG_RETURN_PATH_P(x) return PathPGetDatum(x)
-#define DatumGetLineP(X) ((LINE *) DatumGetPointer(X))
-#define LinePGetDatum(X) PointerGetDatum(X)
+static inline LINE *
+DatumGetLineP(Datum X)
+{
+ return (LINE *) DatumGetPointer(X);
+}
+static inline Datum
+LinePGetDatum(const LINE *X)
+{
+ return PointerGetDatum(X);
+}
#define PG_GETARG_LINE_P(n) DatumGetLineP(PG_GETARG_DATUM(n))
#define PG_RETURN_LINE_P(x) return LinePGetDatum(x)
-#define DatumGetBoxP(X) ((BOX *) DatumGetPointer(X))
-#define BoxPGetDatum(X) PointerGetDatum(X)
+static inline BOX *
+DatumGetBoxP(Datum X)
+{
+ return (BOX *) DatumGetPointer(X);
+}
+static inline Datum
+BoxPGetDatum(const BOX *X)
+{
+ return PointerGetDatum(X);
+}
#define PG_GETARG_BOX_P(n) DatumGetBoxP(PG_GETARG_DATUM(n))
#define PG_RETURN_BOX_P(x) return BoxPGetDatum(x)
-#define DatumGetPolygonP(X) ((POLYGON *) PG_DETOAST_DATUM(X))
-#define DatumGetPolygonPCopy(X) ((POLYGON *) PG_DETOAST_DATUM_COPY(X))
-#define PolygonPGetDatum(X) PointerGetDatum(X)
+static inline POLYGON *
+DatumGetPolygonP(Datum X)
+{
+ return (POLYGON *) PG_DETOAST_DATUM(X);
+}
+static inline POLYGON *
+DatumGetPolygonPCopy(Datum X)
+{
+ return (POLYGON *) PG_DETOAST_DATUM_COPY(X);
+}
+static inline Datum
+PolygonPGetDatum(const POLYGON *X)
+{
+ return PointerGetDatum(X);
+}
#define PG_GETARG_POLYGON_P(n) DatumGetPolygonP(PG_GETARG_DATUM(n))
#define PG_GETARG_POLYGON_P_COPY(n) DatumGetPolygonPCopy(PG_GETARG_DATUM(n))
#define PG_RETURN_POLYGON_P(x) return PolygonPGetDatum(x)
-#define DatumGetCircleP(X) ((CIRCLE *) DatumGetPointer(X))
-#define CirclePGetDatum(X) PointerGetDatum(X)
+static inline CIRCLE *
+DatumGetCircleP(Datum X)
+{
+ return (CIRCLE *) DatumGetPointer(X);
+}
+static inline Datum
+CirclePGetDatum(const CIRCLE *X)
+{
+ return PointerGetDatum(X);
+}
#define PG_GETARG_CIRCLE_P(n) DatumGetCircleP(PG_GETARG_DATUM(n))
#define PG_RETURN_CIRCLE_P(x) return CirclePGetDatum(x)
diff --git a/src/include/utils/inet.h b/src/include/utils/inet.h
index b1ec9723df..5438cfaba5 100644
--- a/src/include/utils/inet.h
+++ b/src/include/utils/inet.h
@@ -119,23 +119,58 @@ typedef struct macaddr8
/*
* fmgr interface macros
*/
-#define DatumGetInetPP(X) ((inet *) PG_DETOAST_DATUM_PACKED(X))
-#define InetPGetDatum(X) PointerGetDatum(X)
+static inline inet *
+DatumGetInetPP(Datum X)
+{
+ return (inet *) PG_DETOAST_DATUM_PACKED(X);
+}
+
+static inline Datum
+InetPGetDatum(const inet *X)
+{
+ return PointerGetDatum(X);
+}
+
#define PG_GETARG_INET_PP(n) DatumGetInetPP(PG_GETARG_DATUM(n))
#define PG_RETURN_INET_P(x) return InetPGetDatum(x)
+
/* obsolescent variants */
-#define DatumGetInetP(X) ((inet *) PG_DETOAST_DATUM(X))
+static inline inet *
+DatumGetInetP(Datum X)
+{
+ return (inet *) PG_DETOAST_DATUM(X);
+}
#define PG_GETARG_INET_P(n) DatumGetInetP(PG_GETARG_DATUM(n))
/* macaddr is a fixed-length pass-by-reference datatype */
-#define DatumGetMacaddrP(X) ((macaddr *) DatumGetPointer(X))
-#define MacaddrPGetDatum(X) PointerGetDatum(X)
+static inline macaddr *
+DatumGetMacaddrP(Datum X)
+{
+ return (macaddr *) DatumGetPointer(X);
+}
+
+static inline Datum
+MacaddrPGetDatum(const macaddr *X)
+{
+ return PointerGetDatum(X);
+}
+
#define PG_GETARG_MACADDR_P(n) DatumGetMacaddrP(PG_GETARG_DATUM(n))
#define PG_RETURN_MACADDR_P(x) return MacaddrPGetDatum(x)
/* macaddr8 is a fixed-length pass-by-reference datatype */
-#define DatumGetMacaddr8P(X) ((macaddr8 *) DatumGetPointer(X))
-#define Macaddr8PGetDatum(X) PointerGetDatum(X)
+static inline macaddr8 *
+DatumGetMacaddr8P(Datum X)
+{
+ return (macaddr8 *) DatumGetPointer(X);
+}
+
+static inline Datum
+Macaddr8PGetDatum(const macaddr8 *X)
+{
+ return PointerGetDatum(X);
+}
+
#define PG_GETARG_MACADDR8_P(n) DatumGetMacaddr8P(PG_GETARG_DATUM(n))
#define PG_RETURN_MACADDR8_P(x) return Macaddr8PGetDatum(x)
diff --git a/src/include/utils/jsonb.h b/src/include/utils/jsonb.h
index bae466b523..536d00b3b4 100644
--- a/src/include/utils/jsonb.h
+++ b/src/include/utils/jsonb.h
@@ -67,14 +67,6 @@ typedef enum
#define JGINFLAG_HASHED 0x10 /* OR'd into flag if value was hashed */
#define JGIN_MAXLENGTH 125 /* max length of text part before hashing */
-/* Convenience macros */
-#define DatumGetJsonbP(d) ((Jsonb *) PG_DETOAST_DATUM(d))
-#define DatumGetJsonbPCopy(d) ((Jsonb *) PG_DETOAST_DATUM_COPY(d))
-#define JsonbPGetDatum(p) PointerGetDatum(p)
-#define PG_GETARG_JSONB_P(x) DatumGetJsonbP(PG_GETARG_DATUM(x))
-#define PG_GETARG_JSONB_P_COPY(x) DatumGetJsonbPCopy(PG_GETARG_DATUM(x))
-#define PG_RETURN_JSONB_P(x) PG_RETURN_POINTER(x)
-
typedef struct JsonbPair JsonbPair;
typedef struct JsonbValue JsonbValue;
@@ -393,6 +385,29 @@ typedef enum /* type categories for datum_to_jsonb */
JSONBTYPE_OTHER /* all else */
} JsonbTypeCategory;
+/* Convenience macros */
+static inline Jsonb *
+DatumGetJsonbP(Datum d)
+{
+ return (Jsonb *) PG_DETOAST_DATUM(d);
+}
+
+static inline Jsonb *
+DatumGetJsonbPCopy(Datum d)
+{
+ return (Jsonb *) PG_DETOAST_DATUM_COPY(d);
+}
+
+static inline Datum
+JsonbPGetDatum(const Jsonb *p)
+{
+ return PointerGetDatum(p);
+}
+
+#define PG_GETARG_JSONB_P(x) DatumGetJsonbP(PG_GETARG_DATUM(x))
+#define PG_GETARG_JSONB_P_COPY(x) DatumGetJsonbPCopy(PG_GETARG_DATUM(x))
+#define PG_RETURN_JSONB_P(x) PG_RETURN_POINTER(x)
+
/* Support functions */
extern uint32 getJsonbOffset(const JsonbContainer *jc, int index);
extern uint32 getJsonbLength(const JsonbContainer *jc, int index);
diff --git a/src/include/utils/jsonpath.h b/src/include/utils/jsonpath.h
index 8e79b8dc9f..ea32a8f072 100644
--- a/src/include/utils/jsonpath.h
+++ b/src/include/utils/jsonpath.h
@@ -32,8 +32,18 @@ typedef struct
#define JSONPATH_LAX (0x80000000)
#define JSONPATH_HDRSZ (offsetof(JsonPath, data))
-#define DatumGetJsonPathP(d) ((JsonPath *) DatumGetPointer(PG_DETOAST_DATUM(d)))
-#define DatumGetJsonPathPCopy(d) ((JsonPath *) DatumGetPointer(PG_DETOAST_DATUM_COPY(d)))
+static inline JsonPath *
+DatumGetJsonPathP(Datum d)
+{
+ return (JsonPath *) PG_DETOAST_DATUM(d);
+}
+
+static inline JsonPath *
+DatumGetJsonPathPCopy(Datum d)
+{
+ return (JsonPath *) PG_DETOAST_DATUM_COPY(d);
+}
+
#define PG_GETARG_JSONPATH_P(x) DatumGetJsonPathP(PG_GETARG_DATUM(x))
#define PG_GETARG_JSONPATH_P_COPY(x) DatumGetJsonPathPCopy(PG_GETARG_DATUM(x))
#define PG_RETURN_JSONPATH_P(p) PG_RETURN_POINTER(p)
diff --git a/src/include/utils/multirangetypes.h b/src/include/utils/multirangetypes.h
index 915330f990..23bce0005f 100644
--- a/src/include/utils/multirangetypes.h
+++ b/src/include/utils/multirangetypes.h
@@ -42,11 +42,26 @@ typedef struct
#define MultirangeIsEmpty(mr) ((mr)->rangeCount == 0)
/*
- * fmgr macros for multirange type objects
+ * fmgr functions for multirange type objects
*/
-#define DatumGetMultirangeTypeP(X) ((MultirangeType *) PG_DETOAST_DATUM(X))
-#define DatumGetMultirangeTypePCopy(X) ((MultirangeType *) PG_DETOAST_DATUM_COPY(X))
-#define MultirangeTypePGetDatum(X) PointerGetDatum(X)
+static inline MultirangeType *
+DatumGetMultirangeTypeP(Datum X)
+{
+ return (MultirangeType *) PG_DETOAST_DATUM(X);
+}
+
+static inline MultirangeType *
+DatumGetMultirangeTypePCopy(Datum X)
+{
+ return (MultirangeType *) PG_DETOAST_DATUM_COPY(X);
+}
+
+static inline Datum
+MultirangeTypePGetDatum(const MultirangeType *X)
+{
+ return PointerGetDatum(X);
+}
+
#define PG_GETARG_MULTIRANGE_P(n) DatumGetMultirangeTypeP(PG_GETARG_DATUM(n))
#define PG_GETARG_MULTIRANGE_P_COPY(n) DatumGetMultirangeTypePCopy(PG_GETARG_DATUM(n))
#define PG_RETURN_MULTIRANGE_P(x) return MultirangeTypePGetDatum(x)
diff --git a/src/include/utils/numeric.h b/src/include/utils/numeric.h
index 3caa74dfe7..88c62c5348 100644
--- a/src/include/utils/numeric.h
+++ b/src/include/utils/numeric.h
@@ -56,9 +56,24 @@ typedef struct NumericData *Numeric;
* fmgr interface macros
*/
-#define DatumGetNumeric(X) ((Numeric) PG_DETOAST_DATUM(X))
-#define DatumGetNumericCopy(X) ((Numeric) PG_DETOAST_DATUM_COPY(X))
-#define NumericGetDatum(X) PointerGetDatum(X)
+static inline Numeric
+DatumGetNumeric(Datum X)
+{
+ return (Numeric) PG_DETOAST_DATUM(X);
+}
+
+static inline Numeric
+DatumGetNumericCopy(Datum X)
+{
+ return (Numeric) PG_DETOAST_DATUM_COPY(X);
+}
+
+static inline Datum
+NumericGetDatum(Numeric X)
+{
+ return PointerGetDatum(X);
+}
+
#define PG_GETARG_NUMERIC(n) DatumGetNumeric(PG_GETARG_DATUM(n))
#define PG_GETARG_NUMERIC_COPY(n) DatumGetNumericCopy(PG_GETARG_DATUM(n))
#define PG_RETURN_NUMERIC(x) return NumericGetDatum(x)
diff --git a/src/include/utils/pg_lsn.h b/src/include/utils/pg_lsn.h
index 7b708f1073..5313afbfe2 100644
--- a/src/include/utils/pg_lsn.h
+++ b/src/include/utils/pg_lsn.h
@@ -18,8 +18,17 @@
#include "access/xlogdefs.h"
#include "fmgr.h"
-#define DatumGetLSN(X) ((XLogRecPtr) DatumGetInt64(X))
-#define LSNGetDatum(X) (Int64GetDatum((int64) (X)))
+static inline XLogRecPtr
+DatumGetLSN(Datum X)
+{
+ return (XLogRecPtr) DatumGetInt64(X);
+}
+
+static inline Datum
+LSNGetDatum(XLogRecPtr X)
+{
+ return Int64GetDatum((int64) X);
+}
#define PG_GETARG_LSN(n) DatumGetLSN(PG_GETARG_DATUM(n))
#define PG_RETURN_LSN(x) return LSNGetDatum(x)
diff --git a/src/include/utils/rangetypes.h b/src/include/utils/rangetypes.h
index 993fad4fc2..661e4892ec 100644
--- a/src/include/utils/rangetypes.h
+++ b/src/include/utils/rangetypes.h
@@ -68,11 +68,26 @@ typedef struct
} RangeBound;
/*
- * fmgr macros for range type objects
+ * fmgr functions for range type objects
*/
-#define DatumGetRangeTypeP(X) ((RangeType *) PG_DETOAST_DATUM(X))
-#define DatumGetRangeTypePCopy(X) ((RangeType *) PG_DETOAST_DATUM_COPY(X))
-#define RangeTypePGetDatum(X) PointerGetDatum(X)
+static inline RangeType *
+DatumGetRangeTypeP(Datum X)
+{
+ return (RangeType *) PG_DETOAST_DATUM(X);
+}
+
+static inline RangeType *
+DatumGetRangeTypePCopy(Datum X)
+{
+ return (RangeType *) PG_DETOAST_DATUM_COPY(X);
+}
+
+static inline Datum
+RangeTypePGetDatum(const RangeType *X)
+{
+ return PointerGetDatum(X);
+}
+
#define PG_GETARG_RANGE_P(n) DatumGetRangeTypeP(PG_GETARG_DATUM(n))
#define PG_GETARG_RANGE_P_COPY(n) DatumGetRangeTypePCopy(PG_GETARG_DATUM(n))
#define PG_RETURN_RANGE_P(x) return RangeTypePGetDatum(x)
diff --git a/src/include/utils/timestamp.h b/src/include/utils/timestamp.h
index edf3a97318..3602638bdc 100644
--- a/src/include/utils/timestamp.h
+++ b/src/include/utils/timestamp.h
@@ -19,18 +19,46 @@
/*
- * Macros for fmgr-callable functions.
+ * Functions for fmgr-callable functions.
*
* For Timestamp, we make use of the same support routines as for int64.
* Therefore Timestamp is pass-by-reference if and only if int64 is!
*/
-#define DatumGetTimestamp(X) ((Timestamp) DatumGetInt64(X))
-#define DatumGetTimestampTz(X) ((TimestampTz) DatumGetInt64(X))
-#define DatumGetIntervalP(X) ((Interval *) DatumGetPointer(X))
-
-#define TimestampGetDatum(X) Int64GetDatum(X)
-#define TimestampTzGetDatum(X) Int64GetDatum(X)
-#define IntervalPGetDatum(X) PointerGetDatum(X)
+static inline Timestamp
+DatumGetTimestamp(Datum X)
+{
+ return (Timestamp) DatumGetInt64(X);
+}
+
+static inline TimestampTz
+DatumGetTimestampTz(Datum X)
+{
+ return (TimestampTz) DatumGetInt64(X);
+}
+
+static inline Interval *
+DatumGetIntervalP(Datum X)
+{
+ return (Interval *) DatumGetPointer(X);
+}
+
+static inline Datum
+TimestampGetDatum(Timestamp X)
+{
+ return Int64GetDatum(X);
+}
+
+static inline Datum
+TimestampTzGetDatum(TimestampTz X)
+{
+ return Int64GetDatum(X);
+}
+
+static inline Datum
+IntervalPGetDatum(const Interval *X)
+{
+ return PointerGetDatum(X);
+}
#define PG_GETARG_TIMESTAMP(n) DatumGetTimestamp(PG_GETARG_DATUM(n))
#define PG_GETARG_TIMESTAMPTZ(n) DatumGetTimestampTz(PG_GETARG_DATUM(n))
diff --git a/src/include/utils/uuid.h b/src/include/utils/uuid.h
index 0029da4e7f..69f4332d0f 100644
--- a/src/include/utils/uuid.h
+++ b/src/include/utils/uuid.h
@@ -23,9 +23,20 @@ typedef struct pg_uuid_t
} pg_uuid_t;
/* fmgr interface macros */
-#define UUIDPGetDatum(X) PointerGetDatum(X)
+static inline Datum
+UUIDPGetDatum(const pg_uuid_t *X)
+{
+ return PointerGetDatum(X);
+}
+
#define PG_RETURN_UUID_P(X) return UUIDPGetDatum(X)
-#define DatumGetUUIDP(X) ((pg_uuid_t *) DatumGetPointer(X))
+
+static inline pg_uuid_t *
+DatumGetUUIDP(Datum X)
+{
+ return (pg_uuid_t *) DatumGetPointer(X);
+}
+
#define PG_GETARG_UUID_P(X) DatumGetUUIDP(PG_GETARG_DATUM(X))
#endif /* UUID_H */
diff --git a/src/include/utils/varbit.h b/src/include/utils/varbit.h
index 039ba860cf..e65e7f65e7 100644
--- a/src/include/utils/varbit.h
+++ b/src/include/utils/varbit.h
@@ -41,9 +41,24 @@ typedef struct
* BIT and BIT VARYING are toastable varlena types. They are the same
* as far as representation goes, so we just have one set of macros.
*/
-#define DatumGetVarBitP(X) ((VarBit *) PG_DETOAST_DATUM(X))
-#define DatumGetVarBitPCopy(X) ((VarBit *) PG_DETOAST_DATUM_COPY(X))
-#define VarBitPGetDatum(X) PointerGetDatum(X)
+static inline VarBit *
+DatumGetVarBitP(Datum X)
+{
+ return (VarBit *) PG_DETOAST_DATUM(X);
+}
+
+static inline VarBit *
+DatumGetVarBitPCopy(Datum X)
+{
+ return (VarBit *) PG_DETOAST_DATUM_COPY(X);
+}
+
+static inline Datum
+VarBitPGetDatum(const VarBit *X)
+{
+ return PointerGetDatum(X);
+}
+
#define PG_GETARG_VARBIT_P(n) DatumGetVarBitP(PG_GETARG_DATUM(n))
#define PG_GETARG_VARBIT_P_COPY(n) DatumGetVarBitPCopy(PG_GETARG_DATUM(n))
#define PG_RETURN_VARBIT_P(x) return VarBitPGetDatum(x)
diff --git a/src/include/utils/xid8.h b/src/include/utils/xid8.h
index b702fc1a91..9c5ce241db 100644
--- a/src/include/utils/xid8.h
+++ b/src/include/utils/xid8.h
@@ -14,8 +14,18 @@
#include "access/transam.h"
-#define DatumGetFullTransactionId(X) (FullTransactionIdFromU64(DatumGetUInt64(X)))
-#define FullTransactionIdGetDatum(X) (UInt64GetDatum(U64FromFullTransactionId(X)))
+static inline FullTransactionId
+DatumGetFullTransactionId(Datum X)
+{
+ return FullTransactionIdFromU64(DatumGetUInt64(X));
+}
+
+static inline Datum
+FullTransactionIdGetDatum(FullTransactionId X)
+{
+ return UInt64GetDatum(U64FromFullTransactionId(X));
+}
+
#define PG_GETARG_FULLTRANSACTIONID(X) DatumGetFullTransactionId(PG_GETARG_DATUM(X))
#define PG_RETURN_FULLTRANSACTIONID(X) return FullTransactionIdGetDatum(X)
diff --git a/src/include/utils/xml.h b/src/include/utils/xml.h
index 6620a62619..c71174d204 100644
--- a/src/include/utils/xml.h
+++ b/src/include/utils/xml.h
@@ -47,8 +47,17 @@ typedef enum
/* struct PgXmlErrorContext is private to xml.c */
typedef struct PgXmlErrorContext PgXmlErrorContext;
-#define DatumGetXmlP(X) ((xmltype *) PG_DETOAST_DATUM(X))
-#define XmlPGetDatum(X) PointerGetDatum(X)
+static inline xmltype *
+DatumGetXmlP(Datum X)
+{
+ return (xmltype *) PG_DETOAST_DATUM(X);
+}
+
+static inline Datum
+XmlPGetDatum(const xmltype *X)
+{
+ return PointerGetDatum(X);
+}
#define PG_GETARG_XML_P(n) DatumGetXmlP(PG_GETARG_DATUM(n))
#define PG_RETURN_XML_P(x) PG_RETURN_POINTER(x)
--
2.37.1
^ permalink raw reply [nested|flat] 92+ messages in thread
* Re: Convert *GetDatum() and DatumGet*() macros to inline functions
2022-08-28 15:55 Convert *GetDatum() and DatumGet*() macros to inline functions Peter Eisentraut <[email protected]>
@ 2022-08-30 13:36 ` Aleksander Alekseev <[email protected]>
2022-08-30 13:46 ` Re: Convert *GetDatum() and DatumGet*() macros to inline functions Tom Lane <[email protected]>
0 siblings, 1 reply; 92+ messages in thread
From: Aleksander Alekseev @ 2022-08-30 13:36 UTC (permalink / raw)
To: pgsql-hackers; +Cc: Peter Eisentraut <[email protected]>
Hi Peter,
> To address this, I converted these macros to inline functions
This is a great change!
I encountered passing the wrong arguments to these macros many times,
and this is indeed pretty annoying. I wish we could forbid doing other
stupid things as well, e.g. comparing two Datum's directly, which for
Timestamps works just fine but only on 64-bit platforms. Although this
is certainly out of scope of this thread.
The patch looks good to me, I merely added a link to the discussion. I
added it to the CF application. Cfbot is making its mind at the moment
of writing.
Do you think this should be backported?
--
Best regards,
Aleksander Alekseev
Attachments:
[application/octet-stream] v2-0001-Fix-incorrect-uses-of-Datum-conversion-macros.patch (15.2K, ../../CAJ7c6TPGFrzcV4Q-LBWPrpbYLo0ocj9i_O=EJHAP9U5BCwPu6w@mail.gmail.com/2-v2-0001-Fix-incorrect-uses-of-Datum-conversion-macros.patch)
download | inline diff:
From c26047191fe7547ed44360743ade30f24a3ad641 Mon Sep 17 00:00:00 2001
From: Peter Eisentraut <[email protected]>
Date: Sun, 28 Aug 2022 10:47:10 +0200
Subject: [PATCH v2 1/2] Fix incorrect uses of Datum conversion macros
Peter Eisentraut, reviewed by Aleksander Alekseev
Discussion: http://postgr.es/m/8528fb7e-0aa2-6b54-85fb-0c0886dbd6ed%40enterprisedb.com
---
contrib/btree_gist/btree_utils_num.c | 2 +-
contrib/dblink/dblink.c | 2 +-
contrib/hstore/hstore_op.c | 2 +-
contrib/pageinspect/heapfuncs.c | 4 ++--
src/backend/access/brin/brin_bloom.c | 2 +-
src/backend/access/brin/brin_minmax_multi.c | 12 ++++++------
src/backend/access/common/toast_compression.c | 2 +-
src/backend/access/table/toast_helper.c | 2 +-
src/backend/access/transam/xlogfuncs.c | 2 +-
src/backend/statistics/mcv.c | 4 ++--
src/backend/utils/adt/amutils.c | 2 +-
src/backend/utils/adt/multirangetypes.c | 2 +-
src/backend/utils/adt/pg_lsn.c | 4 ++--
src/backend/utils/adt/pgstatfuncs.c | 2 +-
src/backend/utils/adt/rangetypes_spgist.c | 4 ++--
src/backend/utils/adt/regexp.c | 2 +-
src/backend/utils/adt/tsgistidx.c | 2 +-
src/backend/utils/adt/tsquery_op.c | 2 +-
src/backend/utils/adt/varlena.c | 2 +-
src/backend/utils/adt/xml.c | 2 +-
src/backend/utils/resowner/resowner.c | 6 +++---
src/pl/plperl/plperl.c | 2 +-
22 files changed, 33 insertions(+), 33 deletions(-)
diff --git a/contrib/btree_gist/btree_utils_num.c b/contrib/btree_gist/btree_utils_num.c
index 5632ee0586..05c154afa3 100644
--- a/contrib/btree_gist/btree_utils_num.c
+++ b/contrib/btree_gist/btree_utils_num.c
@@ -153,7 +153,7 @@ gbt_num_fetch(GISTENTRY *entry, const gbtree_ninfo *tinfo)
datum = CashGetDatum(*(Cash *) entry->key);
break;
default:
- datum = PointerGetDatum(entry->key);
+ datum = entry->key;
}
retval = palloc(sizeof(GISTENTRY));
diff --git a/contrib/dblink/dblink.c b/contrib/dblink/dblink.c
index e323fdd0e6..7940387920 100644
--- a/contrib/dblink/dblink.c
+++ b/contrib/dblink/dblink.c
@@ -1336,7 +1336,7 @@ dblink_get_connections(PG_FUNCTION_ARGS)
}
if (astate)
- PG_RETURN_ARRAYTYPE_P(makeArrayResult(astate,
+ PG_RETURN_DATUM(makeArrayResult(astate,
CurrentMemoryContext));
else
PG_RETURN_NULL();
diff --git a/contrib/hstore/hstore_op.c b/contrib/hstore/hstore_op.c
index 2f540d7ed6..0d4ec16d1e 100644
--- a/contrib/hstore/hstore_op.c
+++ b/contrib/hstore/hstore_op.c
@@ -1064,7 +1064,7 @@ hstore_each(PG_FUNCTION_ARGS)
tuple = heap_form_tuple(funcctx->tuple_desc, dvalues, nulls);
res = HeapTupleGetDatum(tuple);
- SRF_RETURN_NEXT(funcctx, PointerGetDatum(res));
+ SRF_RETURN_NEXT(funcctx, res);
}
SRF_RETURN_DONE(funcctx);
diff --git a/contrib/pageinspect/heapfuncs.c b/contrib/pageinspect/heapfuncs.c
index 2ff70405cf..aed2753253 100644
--- a/contrib/pageinspect/heapfuncs.c
+++ b/contrib/pageinspect/heapfuncs.c
@@ -383,7 +383,7 @@ tuple_data_split_internal(Oid relid, char *tupdata,
errmsg("unexpected end of tuple data")));
if (attr->attlen == -1 && do_detoast)
- attr_data = DatumGetByteaPCopy(tupdata + off);
+ attr_data = pg_detoast_datum_copy((struct varlena *) (tupdata + off));
else
{
attr_data = (bytea *) palloc(len + VARHDRSZ);
@@ -492,7 +492,7 @@ tuple_data_split(PG_FUNCTION_ARGS)
if (t_bits)
pfree(t_bits);
- PG_RETURN_ARRAYTYPE_P(res);
+ PG_RETURN_DATUM(res);
}
/*
diff --git a/src/backend/access/brin/brin_bloom.c b/src/backend/access/brin/brin_bloom.c
index 6812ca9fd1..6b0af7267d 100644
--- a/src/backend/access/brin/brin_bloom.c
+++ b/src/backend/access/brin/brin_bloom.c
@@ -767,7 +767,7 @@ brin_bloom_summary_out(PG_FUNCTION_ARGS)
StringInfoData str;
/* detoast the data to get value with a full 4B header */
- filter = (BloomFilter *) PG_DETOAST_DATUM(PG_GETARG_BYTEA_PP(0));
+ filter = (BloomFilter *) PG_DETOAST_DATUM_PACKED(PG_GETARG_DATUM(0));
initStringInfo(&str);
appendStringInfoChar(&str, '{');
diff --git a/src/backend/access/brin/brin_minmax_multi.c b/src/backend/access/brin/brin_minmax_multi.c
index a581659fe2..c8f22d5d17 100644
--- a/src/backend/access/brin/brin_minmax_multi.c
+++ b/src/backend/access/brin/brin_minmax_multi.c
@@ -774,12 +774,12 @@ brin_range_deserialize(int maxvalues, SerializedRanges *serialized)
datalen += MAXALIGN(typlen);
else if (typlen == -1) /* varlena */
{
- datalen += MAXALIGN(VARSIZE_ANY(DatumGetPointer(ptr)));
- ptr += VARSIZE_ANY(DatumGetPointer(ptr));
+ datalen += MAXALIGN(VARSIZE_ANY(ptr));
+ ptr += VARSIZE_ANY(ptr);
}
else if (typlen == -2) /* cstring */
{
- Size slen = strlen(DatumGetCString(ptr)) + 1;
+ Size slen = strlen(ptr) + 1;
datalen += MAXALIGN(slen);
ptr += slen;
@@ -3033,7 +3033,7 @@ brin_minmax_multi_summary_out(PG_FUNCTION_ARGS)
* Detoast to get value with full 4B header (can't be stored in a toast
* table, but can use 1B header).
*/
- ranges = (SerializedRanges *) PG_DETOAST_DATUM(PG_GETARG_BYTEA_PP(0));
+ ranges = (SerializedRanges *) PG_DETOAST_DATUM_PACKED(PG_GETARG_DATUM(0));
/* lookup output func for the type */
getTypeOutputInfo(ranges->typid, &outfunc, &isvarlena);
@@ -3081,7 +3081,7 @@ brin_minmax_multi_summary_out(PG_FUNCTION_ARGS)
getTypeOutputInfo(ANYARRAYOID, &typoutput, &typIsVarlena);
- val = PointerGetDatum(makeArrayResult(astate_values, CurrentMemoryContext));
+ val = makeArrayResult(astate_values, CurrentMemoryContext);
extval = OidOutputFunctionCall(typoutput, val);
@@ -3121,7 +3121,7 @@ brin_minmax_multi_summary_out(PG_FUNCTION_ARGS)
getTypeOutputInfo(ANYARRAYOID, &typoutput, &typIsVarlena);
- val = PointerGetDatum(makeArrayResult(astate_values, CurrentMemoryContext));
+ val = makeArrayResult(astate_values, CurrentMemoryContext);
extval = OidOutputFunctionCall(typoutput, val);
diff --git a/src/backend/access/common/toast_compression.c b/src/backend/access/common/toast_compression.c
index f90f9f11e3..7156ae9c47 100644
--- a/src/backend/access/common/toast_compression.c
+++ b/src/backend/access/common/toast_compression.c
@@ -44,7 +44,7 @@ pglz_compress_datum(const struct varlena *value)
len;
struct varlena *tmp = NULL;
- valsize = VARSIZE_ANY_EXHDR(DatumGetPointer(value));
+ valsize = VARSIZE_ANY_EXHDR(value);
/*
* No point in wasting a palloc cycle if value size is outside the allowed
diff --git a/src/backend/access/table/toast_helper.c b/src/backend/access/table/toast_helper.c
index 0cc5a30f9b..74ba2189f0 100644
--- a/src/backend/access/table/toast_helper.c
+++ b/src/backend/access/table/toast_helper.c
@@ -330,7 +330,7 @@ toast_delete_external(Relation rel, Datum *values, bool *isnull,
if (isnull[i])
continue;
- else if (VARATT_IS_EXTERNAL_ONDISK(PointerGetDatum(value)))
+ else if (VARATT_IS_EXTERNAL_ONDISK(value))
toast_delete_datum(rel, value, is_speculative);
}
}
diff --git a/src/backend/access/transam/xlogfuncs.c b/src/backend/access/transam/xlogfuncs.c
index 61e0f4a29c..9cc757f1af 100644
--- a/src/backend/access/transam/xlogfuncs.c
+++ b/src/backend/access/transam/xlogfuncs.c
@@ -555,7 +555,7 @@ pg_wal_lsn_diff(PG_FUNCTION_ARGS)
PG_GETARG_DATUM(0),
PG_GETARG_DATUM(1));
- PG_RETURN_NUMERIC(result);
+ PG_RETURN_DATUM(result);
}
/*
diff --git a/src/backend/statistics/mcv.c b/src/backend/statistics/mcv.c
index 6eeacb0d47..f5a7c31272 100644
--- a/src/backend/statistics/mcv.c
+++ b/src/backend/statistics/mcv.c
@@ -1444,8 +1444,8 @@ pg_stats_ext_mcvlist_items(PG_FUNCTION_ARGS)
}
values[0] = Int32GetDatum(funcctx->call_cntr);
- values[1] = PointerGetDatum(makeArrayResult(astate_values, CurrentMemoryContext));
- values[2] = PointerGetDatum(makeArrayResult(astate_nulls, CurrentMemoryContext));
+ values[1] = makeArrayResult(astate_values, CurrentMemoryContext);
+ values[2] = makeArrayResult(astate_nulls, CurrentMemoryContext);
values[3] = Float8GetDatum(item->frequency);
values[4] = Float8GetDatum(item->base_frequency);
diff --git a/src/backend/utils/adt/amutils.c b/src/backend/utils/adt/amutils.c
index 9d78a90d69..60fd396f24 100644
--- a/src/backend/utils/adt/amutils.c
+++ b/src/backend/utils/adt/amutils.c
@@ -466,5 +466,5 @@ pg_indexam_progress_phasename(PG_FUNCTION_ARGS)
if (!name)
PG_RETURN_NULL();
- PG_RETURN_TEXT_P(CStringGetTextDatum(name));
+ PG_RETURN_DATUM(CStringGetTextDatum(name));
}
diff --git a/src/backend/utils/adt/multirangetypes.c b/src/backend/utils/adt/multirangetypes.c
index da5c7d0906..307d087c97 100644
--- a/src/backend/utils/adt/multirangetypes.c
+++ b/src/backend/utils/adt/multirangetypes.c
@@ -1491,7 +1491,7 @@ multirange_intersect_agg_transfn(PG_FUNCTION_ARGS)
ranges1,
range_count2,
ranges2);
- PG_RETURN_RANGE_P(result);
+ PG_RETURN_MULTIRANGE_P(result);
}
diff --git a/src/backend/utils/adt/pg_lsn.c b/src/backend/utils/adt/pg_lsn.c
index 45408787da..15266f36f5 100644
--- a/src/backend/utils/adt/pg_lsn.c
+++ b/src/backend/utils/adt/pg_lsn.c
@@ -271,7 +271,7 @@ pg_lsn_pli(PG_FUNCTION_ARGS)
/* Add two numerics */
res = DirectFunctionCall2(numeric_add,
- NumericGetDatum(num),
+ num,
NumericGetDatum(nbytes));
/* Convert to pg_lsn */
@@ -305,7 +305,7 @@ pg_lsn_mii(PG_FUNCTION_ARGS)
/* Subtract two numerics */
res = DirectFunctionCall2(numeric_sub,
- NumericGetDatum(num),
+ num,
NumericGetDatum(nbytes));
/* Convert to pg_lsn */
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index 4cca30aae7..be15b4b2e5 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -1119,7 +1119,7 @@ pg_stat_get_backend_client_addr(PG_FUNCTION_ARGS)
clean_ipv6_addr(beentry->st_clientaddr.addr.ss_family, remote_host);
- PG_RETURN_INET_P(DirectFunctionCall1(inet_in,
+ PG_RETURN_DATUM(DirectFunctionCall1(inet_in,
CStringGetDatum(remote_host)));
}
diff --git a/src/backend/utils/adt/rangetypes_spgist.c b/src/backend/utils/adt/rangetypes_spgist.c
index 71a6053b6a..a47f04d975 100644
--- a/src/backend/utils/adt/rangetypes_spgist.c
+++ b/src/backend/utils/adt/rangetypes_spgist.c
@@ -416,7 +416,7 @@ spg_range_quad_inner_consistent(PG_FUNCTION_ARGS)
/* This node has a centroid. Fetch it. */
centroid = DatumGetRangeTypeP(in->prefixDatum);
typcache = range_get_typcache(fcinfo,
- RangeTypeGetOid(DatumGetRangeTypeP(centroid)));
+ RangeTypeGetOid(centroid));
range_deserialize(typcache, centroid, ¢roidLower, ¢roidUpper,
¢roidEmpty);
@@ -557,7 +557,7 @@ spg_range_quad_inner_consistent(PG_FUNCTION_ARGS)
*/
if (in->traversalValue)
{
- prevCentroid = DatumGetRangeTypeP(in->traversalValue);
+ prevCentroid = in->traversalValue;
range_deserialize(typcache, prevCentroid,
&prevLower, &prevUpper, &prevEmpty);
}
diff --git a/src/backend/utils/adt/regexp.c b/src/backend/utils/adt/regexp.c
index 0e0c5d5ce8..dcf6681ef2 100644
--- a/src/backend/utils/adt/regexp.c
+++ b/src/backend/utils/adt/regexp.c
@@ -1757,7 +1757,7 @@ regexp_split_to_array(PG_FUNCTION_ARGS)
splitctx->next_match++;
}
- PG_RETURN_ARRAYTYPE_P(makeArrayResult(astate, CurrentMemoryContext));
+ PG_RETURN_DATUM(makeArrayResult(astate, CurrentMemoryContext));
}
/* This is separate to keep the opr_sanity regression test from complaining */
diff --git a/src/backend/utils/adt/tsgistidx.c b/src/backend/utils/adt/tsgistidx.c
index 47daf8199e..fe438f1c4d 100644
--- a/src/backend/utils/adt/tsgistidx.c
+++ b/src/backend/utils/adt/tsgistidx.c
@@ -102,7 +102,7 @@ static int outbuf_maxlen = 0;
Datum
gtsvectorout(PG_FUNCTION_ARGS)
{
- SignTSVector *key = (SignTSVector *) PG_DETOAST_DATUM(PG_GETARG_POINTER(0));
+ SignTSVector *key = (SignTSVector *) PG_DETOAST_DATUM(PG_GETARG_DATUM(0));
char *outbuf;
if (outbuf_maxlen == 0)
diff --git a/src/backend/utils/adt/tsquery_op.c b/src/backend/utils/adt/tsquery_op.c
index a784157ba5..8d815fa7a8 100644
--- a/src/backend/utils/adt/tsquery_op.c
+++ b/src/backend/utils/adt/tsquery_op.c
@@ -148,7 +148,7 @@ tsquery_phrase_distance(PG_FUNCTION_ARGS)
Datum
tsquery_phrase(PG_FUNCTION_ARGS)
{
- PG_RETURN_POINTER(DirectFunctionCall3(tsquery_phrase_distance,
+ PG_RETURN_DATUM(DirectFunctionCall3(tsquery_phrase_distance,
PG_GETARG_DATUM(0),
PG_GETARG_DATUM(1),
Int32GetDatum(1)));
diff --git a/src/backend/utils/adt/varlena.c b/src/backend/utils/adt/varlena.c
index 8539cef024..6ef317e1f9 100644
--- a/src/backend/utils/adt/varlena.c
+++ b/src/backend/utils/adt/varlena.c
@@ -4779,7 +4779,7 @@ text_to_array(PG_FUNCTION_ARGS)
if (tstate.astate == NULL)
PG_RETURN_ARRAYTYPE_P(construct_empty_array(TEXTOID));
- PG_RETURN_ARRAYTYPE_P(makeArrayResult(tstate.astate,
+ PG_RETURN_DATUM(makeArrayResult(tstate.astate,
CurrentMemoryContext));
}
diff --git a/src/backend/utils/adt/xml.c b/src/backend/utils/adt/xml.c
index 60a85c4697..606088cdfc 100644
--- a/src/backend/utils/adt/xml.c
+++ b/src/backend/utils/adt/xml.c
@@ -4174,7 +4174,7 @@ xpath(PG_FUNCTION_ARGS)
astate = initArrayResult(XMLOID, CurrentMemoryContext, true);
xpath_internal(xpath_expr_text, data, namespaces,
NULL, astate);
- PG_RETURN_ARRAYTYPE_P(makeArrayResult(astate, CurrentMemoryContext));
+ PG_RETURN_DATUM(makeArrayResult(astate, CurrentMemoryContext));
#else
NO_XML_SUPPORT();
return 0;
diff --git a/src/backend/utils/resowner/resowner.c b/src/backend/utils/resowner/resowner.c
index ceb4b0e3f7..ece5d98261 100644
--- a/src/backend/utils/resowner/resowner.c
+++ b/src/backend/utils/resowner/resowner.c
@@ -557,7 +557,7 @@ ResourceOwnerReleaseInternal(ResourceOwner owner,
/* Ditto for JIT contexts */
while (ResourceArrayGetAny(&(owner->jitarr), &foundres))
{
- JitContext *context = (JitContext *) PointerGetDatum(foundres);
+ JitContext *context = (JitContext *) DatumGetPointer(foundres);
jit_release_context(context);
}
@@ -566,7 +566,7 @@ ResourceOwnerReleaseInternal(ResourceOwner owner,
while (ResourceArrayGetAny(&(owner->cryptohasharr), &foundres))
{
pg_cryptohash_ctx *context =
- (pg_cryptohash_ctx *) PointerGetDatum(foundres);
+ (pg_cryptohash_ctx *) DatumGetPointer(foundres);
if (isCommit)
PrintCryptoHashLeakWarning(foundres);
@@ -576,7 +576,7 @@ ResourceOwnerReleaseInternal(ResourceOwner owner,
/* Ditto for HMAC contexts */
while (ResourceArrayGetAny(&(owner->hmacarr), &foundres))
{
- pg_hmac_ctx *context = (pg_hmac_ctx *) PointerGetDatum(foundres);
+ pg_hmac_ctx *context = (pg_hmac_ctx *) DatumGetPointer(foundres);
if (isCommit)
PrintHMACLeakWarning(foundres);
diff --git a/src/pl/plperl/plperl.c b/src/pl/plperl/plperl.c
index 5d192a0ce5..8f21e0d701 100644
--- a/src/pl/plperl/plperl.c
+++ b/src/pl/plperl/plperl.c
@@ -1848,7 +1848,7 @@ plperl_call_handler(PG_FUNCTION_ARGS)
{
current_call_data = &this_call_data;
if (CALLED_AS_TRIGGER(fcinfo))
- retval = PointerGetDatum(plperl_trigger_handler(fcinfo));
+ retval = plperl_trigger_handler(fcinfo);
else if (CALLED_AS_EVENT_TRIGGER(fcinfo))
{
plperl_event_trigger_handler(fcinfo);
--
2.37.2
[application/octet-stream] v2-0002-Convert-GetDatum-and-DatumGet-macros-to-inline-fu.patch (40.0K, ../../CAJ7c6TPGFrzcV4Q-LBWPrpbYLo0ocj9i_O=EJHAP9U5BCwPu6w@mail.gmail.com/3-v2-0002-Convert-GetDatum-and-DatumGet-macros-to-inline-fu.patch)
download | inline diff:
From 14cd3e0ad4a7f05937b5c6df79d0d8c4963fcb07 Mon Sep 17 00:00:00 2001
From: Peter Eisentraut <[email protected]>
Date: Sun, 28 Aug 2022 10:47:38 +0200
Subject: [PATCH v2 2/2] Convert *GetDatum() and DatumGet*() macros to inline
functions
The previous macro implementations just cast the argument to a target
type but did not check whether the input type was appropriate. The
function implementation can do better type checking of the input type.
Peter Eisentraut, reviewed by Aleksander Alekseev
Discussion: http://postgr.es/m/8528fb7e-0aa2-6b54-85fb-0c0886dbd6ed%40enterprisedb.com
---
doc/src/sgml/xfunc.sgml | 2 +-
src/backend/utils/mb/mbutils.c | 12 +-
src/include/access/gin.h | 14 +-
src/include/funcapi.h | 14 +-
src/include/postgres.h | 275 +++++++++++++++++++---------
src/include/tsearch/ts_type.h | 46 ++++-
src/include/tsearch/ts_utils.h | 14 +-
src/include/utils/cash.h | 14 +-
src/include/utils/date.h | 42 ++++-
src/include/utils/expandeddatum.h | 13 +-
src/include/utils/expandedrecord.h | 16 +-
src/include/utils/geo_decls.h | 98 ++++++++--
src/include/utils/inet.h | 49 ++++-
src/include/utils/jsonb.h | 31 +++-
src/include/utils/jsonpath.h | 14 +-
src/include/utils/multirangetypes.h | 23 ++-
src/include/utils/numeric.h | 21 ++-
src/include/utils/pg_lsn.h | 13 +-
src/include/utils/rangetypes.h | 23 ++-
src/include/utils/timestamp.h | 44 ++++-
src/include/utils/uuid.h | 15 +-
src/include/utils/varbit.h | 21 ++-
src/include/utils/xid8.h | 14 +-
src/include/utils/xml.h | 13 +-
24 files changed, 655 insertions(+), 186 deletions(-)
diff --git a/doc/src/sgml/xfunc.sgml b/doc/src/sgml/xfunc.sgml
index b8cefb9c2c..cf5810b3c1 100644
--- a/doc/src/sgml/xfunc.sgml
+++ b/doc/src/sgml/xfunc.sgml
@@ -2763,7 +2763,7 @@ c_overpaid(PG_FUNCTION_ARGS)
is null. <function>GetAttributeByName</function> returns a <type>Datum</type>
value that you can convert to the proper data type by using the
appropriate <function>DatumGet<replaceable>XXX</replaceable>()</function>
- macro. Note that the return value is meaningless if the null flag is
+ function. Note that the return value is meaningless if the null flag is
set; always check the null flag before trying to do anything with the
result.
</para>
diff --git a/src/backend/utils/mb/mbutils.c b/src/backend/utils/mb/mbutils.c
index 0543c574c6..474ab476f5 100644
--- a/src/backend/utils/mb/mbutils.c
+++ b/src/backend/utils/mb/mbutils.c
@@ -409,8 +409,8 @@ pg_do_encoding_conversion(unsigned char *src, int len,
(void) OidFunctionCall6(proc,
Int32GetDatum(src_encoding),
Int32GetDatum(dest_encoding),
- CStringGetDatum(src),
- CStringGetDatum(result),
+ CStringGetDatum((char *) src),
+ CStringGetDatum((char *) result),
Int32GetDatum(len),
BoolGetDatum(false));
@@ -485,8 +485,8 @@ pg_do_encoding_conversion_buf(Oid proc,
result = OidFunctionCall6(proc,
Int32GetDatum(src_encoding),
Int32GetDatum(dest_encoding),
- CStringGetDatum(src),
- CStringGetDatum(dest),
+ CStringGetDatum((char *) src),
+ CStringGetDatum((char *) dest),
Int32GetDatum(srclen),
BoolGetDatum(noError));
return DatumGetInt32(result);
@@ -910,8 +910,8 @@ pg_unicode_to_server(pg_wchar c, unsigned char *s)
FunctionCall6(Utf8ToServerConvProc,
Int32GetDatum(PG_UTF8),
Int32GetDatum(server_encoding),
- CStringGetDatum(c_as_utf8),
- CStringGetDatum(s),
+ CStringGetDatum((char *) c_as_utf8),
+ CStringGetDatum((char *) s),
Int32GetDatum(c_as_utf8_len),
BoolGetDatum(false));
}
diff --git a/src/include/access/gin.h b/src/include/access/gin.h
index aacc665fdc..fff861e219 100644
--- a/src/include/access/gin.h
+++ b/src/include/access/gin.h
@@ -62,8 +62,18 @@ typedef char GinTernaryValue;
#define GIN_MAYBE 2 /* don't know if item is present / don't know
* if matches */
-#define DatumGetGinTernaryValue(X) ((GinTernaryValue)(X))
-#define GinTernaryValueGetDatum(X) ((Datum)(X))
+static inline GinTernaryValue
+DatumGetGinTernaryValue(Datum X)
+{
+ return (GinTernaryValue) X;
+}
+
+static inline Datum
+GinTernaryValueGetDatum(GinTernaryValue X)
+{
+ return (Datum) X;
+}
+
#define PG_RETURN_GIN_TERNARY_VALUE(x) return GinTernaryValueGetDatum(x)
/* GUC parameters */
diff --git a/src/include/funcapi.h b/src/include/funcapi.h
index dc3d819a1c..8c2521a591 100644
--- a/src/include/funcapi.h
+++ b/src/include/funcapi.h
@@ -204,7 +204,7 @@ extern TupleDesc build_function_result_tupdesc_t(HeapTuple procTuple);
* Datum HeapTupleHeaderGetDatum(HeapTupleHeader tuple) - convert a
* HeapTupleHeader to a Datum.
*
- * Macro declarations:
+ * Inline declarations:
* HeapTupleGetDatum(HeapTuple tuple) - convert a HeapTuple to a Datum.
*
* Obsolete routines and macros:
@@ -217,10 +217,6 @@ extern TupleDesc build_function_result_tupdesc_t(HeapTuple procTuple);
*----------
*/
-#define HeapTupleGetDatum(tuple) HeapTupleHeaderGetDatum((tuple)->t_data)
-/* obsolete version of above */
-#define TupleGetDatum(_slot, _tuple) HeapTupleGetDatum(_tuple)
-
extern TupleDesc RelationNameGetTupleDesc(const char *relname);
extern TupleDesc TypeGetTupleDesc(Oid typeoid, List *colaliases);
@@ -230,6 +226,14 @@ extern AttInMetadata *TupleDescGetAttInMetadata(TupleDesc tupdesc);
extern HeapTuple BuildTupleFromCStrings(AttInMetadata *attinmeta, char **values);
extern Datum HeapTupleHeaderGetDatum(HeapTupleHeader tuple);
+static inline Datum
+HeapTupleGetDatum(const HeapTupleData *tuple)
+{
+ return HeapTupleHeaderGetDatum(tuple->t_data);
+}
+/* obsolete version of above */
+#define TupleGetDatum(_slot, _tuple) HeapTupleGetDatum(_tuple)
+
/*----------
* Support for Set Returning Functions (SRFs)
diff --git a/src/include/postgres.h b/src/include/postgres.h
index 13903fa022..45c9f6563c 100644
--- a/src/include/postgres.h
+++ b/src/include/postgres.h
@@ -24,7 +24,7 @@
* section description
* ------- ------------------------------------------------
* 1) variable-length datatypes (TOAST support)
- * 2) Datum type + support macros
+ * 2) Datum type + support functions
* 3) miscellaneous
*
* NOTES
@@ -395,7 +395,7 @@ typedef struct
/* ----------------------------------------------------------------
- * Section 2: Datum type + support macros
+ * Section 2: Datum type + support functions
* ----------------------------------------------------------------
*/
@@ -405,7 +405,7 @@ typedef struct
*
* sizeof(Datum) == sizeof(void *) == 4 or 8
*
- * The macros below and the analogous macros for other types should be used to
+ * The functions below and the analogous functions for other types should be used to
* convert between a Datum and the appropriate C type.
*/
@@ -434,8 +434,11 @@ typedef struct NullableDatum
*
* Note: any nonzero value will be considered true.
*/
-
-#define DatumGetBool(X) ((bool) ((X) != 0))
+static inline bool
+DatumGetBool(Datum X)
+{
+ return (X != 0);
+}
/*
* BoolGetDatum
@@ -443,162 +446,231 @@ typedef struct NullableDatum
*
* Note: any nonzero value will be considered true.
*/
-
-#define BoolGetDatum(X) ((Datum) ((X) ? 1 : 0))
+static inline Datum
+BoolGetDatum(bool X)
+{
+ return (Datum) (X ? 1 : 0);
+}
/*
* DatumGetChar
* Returns character value of a datum.
*/
-
-#define DatumGetChar(X) ((char) (X))
+static inline char
+DatumGetChar(Datum X)
+{
+ return (char) X;
+}
/*
* CharGetDatum
* Returns datum representation for a character.
*/
-
-#define CharGetDatum(X) ((Datum) (X))
+static inline Datum
+CharGetDatum(char X)
+{
+ return (Datum) X;
+}
/*
* Int8GetDatum
* Returns datum representation for an 8-bit integer.
*/
-
-#define Int8GetDatum(X) ((Datum) (X))
+static inline Datum
+Int8GetDatum(int8 X)
+{
+ return (Datum) X;
+}
/*
* DatumGetUInt8
* Returns 8-bit unsigned integer value of a datum.
*/
-
-#define DatumGetUInt8(X) ((uint8) (X))
+static inline uint8
+DatumGetUInt8(Datum X)
+{
+ return (uint8) X;
+}
/*
* UInt8GetDatum
* Returns datum representation for an 8-bit unsigned integer.
*/
-
-#define UInt8GetDatum(X) ((Datum) (X))
+static inline Datum
+UInt8GetDatum(uint8 X)
+{
+ return (Datum) X;
+}
/*
* DatumGetInt16
* Returns 16-bit integer value of a datum.
*/
-
-#define DatumGetInt16(X) ((int16) (X))
+static inline int16
+DatumGetInt16(Datum X)
+{
+ return (int16) X;
+}
/*
* Int16GetDatum
* Returns datum representation for a 16-bit integer.
*/
-
-#define Int16GetDatum(X) ((Datum) (X))
+static inline Datum
+Int16GetDatum(int16 X)
+{
+ return (Datum) X;
+}
/*
* DatumGetUInt16
* Returns 16-bit unsigned integer value of a datum.
*/
-
-#define DatumGetUInt16(X) ((uint16) (X))
+static inline uint16
+DatumGetUInt16(Datum X)
+{
+ return (uint16) X;
+}
/*
* UInt16GetDatum
* Returns datum representation for a 16-bit unsigned integer.
*/
-
-#define UInt16GetDatum(X) ((Datum) (X))
+static inline Datum
+UInt16GetDatum(uint16 X)
+{
+ return (Datum) X;
+}
/*
* DatumGetInt32
* Returns 32-bit integer value of a datum.
*/
-
-#define DatumGetInt32(X) ((int32) (X))
+static inline int32
+DatumGetInt32(Datum X)
+{
+ return (int32) X;
+}
/*
* Int32GetDatum
* Returns datum representation for a 32-bit integer.
*/
-
-#define Int32GetDatum(X) ((Datum) (X))
+static inline Datum
+Int32GetDatum(int32 X)
+{
+ return (Datum) X;
+}
/*
* DatumGetUInt32
* Returns 32-bit unsigned integer value of a datum.
*/
-
-#define DatumGetUInt32(X) ((uint32) (X))
+static inline uint32
+DatumGetUInt32(Datum X)
+{
+ return (uint32) X;
+}
/*
* UInt32GetDatum
* Returns datum representation for a 32-bit unsigned integer.
*/
-
-#define UInt32GetDatum(X) ((Datum) (X))
+static inline Datum
+UInt32GetDatum(uint32 X)
+{
+ return (Datum) X;
+}
/*
* DatumGetObjectId
* Returns object identifier value of a datum.
*/
-
-#define DatumGetObjectId(X) ((Oid) (X))
+static inline Oid
+DatumGetObjectId(Datum X)
+{
+ return (Oid) X;
+}
/*
* ObjectIdGetDatum
* Returns datum representation for an object identifier.
*/
-
-#define ObjectIdGetDatum(X) ((Datum) (X))
+static inline Datum
+ObjectIdGetDatum(Oid X)
+{
+ return (Datum) X;
+}
/*
* DatumGetTransactionId
* Returns transaction identifier value of a datum.
*/
-
-#define DatumGetTransactionId(X) ((TransactionId) (X))
+static inline TransactionId
+DatumGetTransactionId(Datum X)
+{
+ return (TransactionId) X;
+}
/*
* TransactionIdGetDatum
* Returns datum representation for a transaction identifier.
*/
-
-#define TransactionIdGetDatum(X) ((Datum) (X))
+static inline Datum
+TransactionIdGetDatum(TransactionId X)
+{
+ return (Datum) X;
+}
/*
* MultiXactIdGetDatum
* Returns datum representation for a multixact identifier.
*/
-
-#define MultiXactIdGetDatum(X) ((Datum) (X))
+static inline Datum
+MultiXactIdGetDatum(MultiXactId X)
+{
+ return (Datum) X;
+}
/*
* DatumGetCommandId
* Returns command identifier value of a datum.
*/
-
-#define DatumGetCommandId(X) ((CommandId) (X))
+static inline CommandId
+DatumGetCommandId(Datum X)
+{
+ return (CommandId) X;
+}
/*
* CommandIdGetDatum
* Returns datum representation for a command identifier.
*/
-
-#define CommandIdGetDatum(X) ((Datum) (X))
+static inline Datum
+CommandIdGetDatum(CommandId X)
+{
+ return (Datum) X;
+}
/*
* DatumGetPointer
* Returns pointer value of a datum.
*/
-
-#define DatumGetPointer(X) ((Pointer) (X))
+static inline Pointer
+DatumGetPointer(Datum X)
+{
+ return (Pointer) X;
+}
/*
* PointerGetDatum
* Returns datum representation for a pointer.
*/
-
-#define PointerGetDatum(X) ((Datum) (X))
+static inline Datum
+PointerGetDatum(const void *X)
+{
+ return (Datum) X;
+}
/*
* DatumGetCString
@@ -607,8 +679,11 @@ typedef struct NullableDatum
* Note: C string is not a full-fledged Postgres type at present,
* but type input functions use this conversion for their inputs.
*/
-
-#define DatumGetCString(X) ((char *) DatumGetPointer(X))
+static inline char *
+DatumGetCString(Datum X)
+{
+ return (char *) DatumGetPointer(X);
+}
/*
* CStringGetDatum
@@ -619,15 +694,21 @@ typedef struct NullableDatum
* Note: CString is pass-by-reference; caller must ensure the pointed-to
* value has adequate lifetime.
*/
-
-#define CStringGetDatum(X) PointerGetDatum(X)
+static inline Datum
+CStringGetDatum(const char *X)
+{
+ return PointerGetDatum(X);
+}
/*
* DatumGetName
* Returns name value of a datum.
*/
-
-#define DatumGetName(X) ((Name) DatumGetPointer(X))
+static inline Name
+DatumGetName(Datum X)
+{
+ return (Name) DatumGetPointer(X);
+}
/*
* NameGetDatum
@@ -636,21 +717,27 @@ typedef struct NullableDatum
* Note: Name is pass-by-reference; caller must ensure the pointed-to
* value has adequate lifetime.
*/
-
-#define NameGetDatum(X) CStringGetDatum(NameStr(*(X)))
+static inline Datum
+NameGetDatum(const NameData *X)
+{
+ return CStringGetDatum(NameStr(*X));
+}
/*
* DatumGetInt64
* Returns 64-bit integer value of a datum.
*
- * Note: this macro hides whether int64 is pass by value or by reference.
+ * Note: this function hides whether int64 is pass by value or by reference.
*/
-
+static inline int64
+DatumGetInt64(Datum X)
+{
#ifdef USE_FLOAT8_BYVAL
-#define DatumGetInt64(X) ((int64) (X))
+ return (int64) X;
#else
-#define DatumGetInt64(X) (* ((int64 *) DatumGetPointer(X)))
+ return *((int64 *) DatumGetPointer(X));
#endif
+}
/*
* Int64GetDatum
@@ -659,25 +746,32 @@ typedef struct NullableDatum
* Note: if int64 is pass by reference, this function returns a reference
* to palloc'd space.
*/
-
#ifdef USE_FLOAT8_BYVAL
-#define Int64GetDatum(X) ((Datum) (X))
+static inline Datum
+Int64GetDatum(int64 X)
+{
+ return (Datum) X;
+}
#else
extern Datum Int64GetDatum(int64 X);
#endif
+
/*
* DatumGetUInt64
* Returns 64-bit unsigned integer value of a datum.
*
- * Note: this macro hides whether int64 is pass by value or by reference.
+ * Note: this function hides whether int64 is pass by value or by reference.
*/
-
+static inline uint64
+DatumGetUInt64(Datum X)
+{
#ifdef USE_FLOAT8_BYVAL
-#define DatumGetUInt64(X) ((uint64) (X))
+ return (uint64) X;
#else
-#define DatumGetUInt64(X) (* ((uint64 *) DatumGetPointer(X)))
+ return *((uint64 *) DatumGetPointer(X));
#endif
+}
/*
* UInt64GetDatum
@@ -686,12 +780,15 @@ extern Datum Int64GetDatum(int64 X);
* Note: if int64 is pass by reference, this function returns a reference
* to palloc'd space.
*/
-
+static inline Datum
+UInt64GetDatum(uint64 X)
+{
#ifdef USE_FLOAT8_BYVAL
-#define UInt64GetDatum(X) ((Datum) (X))
+ return (Datum) X;
#else
-#define UInt64GetDatum(X) Int64GetDatum((int64) (X))
+ return Int64GetDatum((int64) X);
#endif
+}
/*
* Float <-> Datum conversions
@@ -739,13 +836,12 @@ Float4GetDatum(float4 X)
* DatumGetFloat8
* Returns 8-byte floating point value of a datum.
*
- * Note: this macro hides whether float8 is pass by value or by reference.
+ * Note: this function hides whether float8 is pass by value or by reference.
*/
-
-#ifdef USE_FLOAT8_BYVAL
static inline float8
DatumGetFloat8(Datum X)
{
+#ifdef USE_FLOAT8_BYVAL
union
{
int64 value;
@@ -754,10 +850,10 @@ DatumGetFloat8(Datum X)
myunion.value = DatumGetInt64(X);
return myunion.retval;
-}
#else
-#define DatumGetFloat8(X) (* ((float8 *) DatumGetPointer(X)))
+ return *((float8 *) DatumGetPointer(X));
#endif
+}
/*
* Float8GetDatum
@@ -766,7 +862,6 @@ DatumGetFloat8(Datum X)
* Note: if float8 is pass by reference, this function returns a reference
* to palloc'd space.
*/
-
#ifdef USE_FLOAT8_BYVAL
static inline Datum
Float8GetDatum(float8 X)
@@ -789,22 +884,34 @@ extern Datum Float8GetDatum(float8 X);
* Int64GetDatumFast
* Float8GetDatumFast
*
- * These macros are intended to allow writing code that does not depend on
+ * These functions are intended to allow writing code that does not depend on
* whether int64 and float8 are pass-by-reference types, while not
* sacrificing performance when they are. The argument must be a variable
* that will exist and have the same value for as long as the Datum is needed.
* In the pass-by-ref case, the address of the variable is taken to use as
* the Datum. In the pass-by-val case, these will be the same as the non-Fast
- * macros.
+ * functions.
*/
+static inline Datum
+Int64GetDatumFast(int64 X)
+{
+#ifdef USE_FLOAT8_BYVAL
+ return Int64GetDatum(X);
+#else
+ return PointerGetDatum(&X);
+#endif
+}
+
+static inline Datum
+Float8GetDatumFast(float8 X)
+{
#ifdef USE_FLOAT8_BYVAL
-#define Int64GetDatumFast(X) Int64GetDatum(X)
-#define Float8GetDatumFast(X) Float8GetDatum(X)
+ return Float8GetDatum(X);
#else
-#define Int64GetDatumFast(X) PointerGetDatum(&(X))
-#define Float8GetDatumFast(X) PointerGetDatum(&(X))
+ return PointerGetDatum(&X);
#endif
+}
/* ----------------------------------------------------------------
diff --git a/src/include/tsearch/ts_type.h b/src/include/tsearch/ts_type.h
index 689b2d1cfb..f1ec84702d 100644
--- a/src/include/tsearch/ts_type.h
+++ b/src/include/tsearch/ts_type.h
@@ -111,12 +111,27 @@ typedef TSVectorData *TSVector;
#define POSDATAPTR(x,e) (_POSVECPTR(x,e)->pos)
/*
- * fmgr interface macros
+ * fmgr interface functions
*/
-#define DatumGetTSVector(X) ((TSVector) PG_DETOAST_DATUM(X))
-#define DatumGetTSVectorCopy(X) ((TSVector) PG_DETOAST_DATUM_COPY(X))
-#define TSVectorGetDatum(X) PointerGetDatum(X)
+static inline TSVector
+DatumGetTSVector(Datum X)
+{
+ return (TSVector) PG_DETOAST_DATUM(X);
+}
+
+static inline TSVector
+DatumGetTSVectorCopy(Datum X)
+{
+ return (TSVector) PG_DETOAST_DATUM_COPY(X);
+}
+
+static inline Datum
+TSVectorGetDatum(const TSVectorData *X)
+{
+ return PointerGetDatum(X);
+}
+
#define PG_GETARG_TSVECTOR(n) DatumGetTSVector(PG_GETARG_DATUM(n))
#define PG_GETARG_TSVECTOR_COPY(n) DatumGetTSVectorCopy(PG_GETARG_DATUM(n))
#define PG_RETURN_TSVECTOR(x) return TSVectorGetDatum(x)
@@ -227,14 +242,29 @@ typedef TSQueryData *TSQuery;
#define GETOPERAND(x) ( (char*)GETQUERY(x) + ((TSQuery)(x))->size * sizeof(QueryItem) )
/*
- * fmgr interface macros
+ * fmgr interface functions
* Note, TSQuery type marked as plain storage, so it can't be toasted
* but PG_DETOAST_DATUM_COPY is used for simplicity
*/
-#define DatumGetTSQuery(X) ((TSQuery) DatumGetPointer(X))
-#define DatumGetTSQueryCopy(X) ((TSQuery) PG_DETOAST_DATUM_COPY(X))
-#define TSQueryGetDatum(X) PointerGetDatum(X)
+static inline TSQuery
+DatumGetTSQuery(Datum X)
+{
+ return (TSQuery) DatumGetPointer(X);
+}
+
+static inline TSQuery
+DatumGetTSQueryCopy(Datum X)
+{
+ return (TSQuery) PG_DETOAST_DATUM_COPY(X);
+}
+
+static inline Datum
+TSQueryGetDatum(const TSQueryData *X)
+{
+ return PointerGetDatum(X);
+}
+
#define PG_GETARG_TSQUERY(n) DatumGetTSQuery(PG_GETARG_DATUM(n))
#define PG_GETARG_TSQUERY_COPY(n) DatumGetTSQueryCopy(PG_GETARG_DATUM(n))
#define PG_RETURN_TSQUERY(x) return TSQueryGetDatum(x)
diff --git a/src/include/tsearch/ts_utils.h b/src/include/tsearch/ts_utils.h
index c36c711dae..26cc415aa8 100644
--- a/src/include/tsearch/ts_utils.h
+++ b/src/include/tsearch/ts_utils.h
@@ -243,8 +243,18 @@ typedef uint64 TSQuerySign;
#define TSQS_SIGLEN (sizeof(TSQuerySign)*BITS_PER_BYTE)
-#define TSQuerySignGetDatum(X) Int64GetDatum((int64) (X))
-#define DatumGetTSQuerySign(X) ((TSQuerySign) DatumGetInt64(X))
+static inline Datum
+TSQuerySignGetDatum(TSQuerySign X)
+{
+ return Int64GetDatum((int64) X);
+}
+
+static inline TSQuerySign
+DatumGetTSQuerySign(Datum X)
+{
+ return (TSQuerySign) DatumGetInt64(X);
+}
+
#define PG_RETURN_TSQUERYSIGN(X) return TSQuerySignGetDatum(X)
#define PG_GETARG_TSQUERYSIGN(n) DatumGetTSQuerySign(PG_GETARG_DATUM(n))
diff --git a/src/include/utils/cash.h b/src/include/utils/cash.h
index 2e332d83b1..55d45fadd4 100644
--- a/src/include/utils/cash.h
+++ b/src/include/utils/cash.h
@@ -17,8 +17,18 @@
typedef int64 Cash;
/* Cash is pass-by-reference if and only if int64 is */
-#define DatumGetCash(X) ((Cash) DatumGetInt64(X))
-#define CashGetDatum(X) Int64GetDatum(X)
+static inline Cash
+DatumGetCash(Datum X)
+{
+ return DatumGetInt64(X);
+}
+
+static inline Datum
+CashGetDatum(Cash X)
+{
+ return Int64GetDatum(X);
+}
+
#define PG_GETARG_CASH(n) DatumGetCash(PG_GETARG_DATUM(n))
#define PG_RETURN_CASH(x) return CashGetDatum(x)
diff --git a/src/include/utils/date.h b/src/include/utils/date.h
index 3991da41f0..0bbe889128 100644
--- a/src/include/utils/date.h
+++ b/src/include/utils/date.h
@@ -45,18 +45,46 @@ typedef struct
#define MAX_TIME_PRECISION 6
/*
- * Macros for fmgr-callable functions.
+ * Functions for fmgr-callable functions.
*
* For TimeADT, we make use of the same support routines as for int64.
* Therefore TimeADT is pass-by-reference if and only if int64 is!
*/
-#define DatumGetDateADT(X) ((DateADT) DatumGetInt32(X))
-#define DatumGetTimeADT(X) ((TimeADT) DatumGetInt64(X))
-#define DatumGetTimeTzADTP(X) ((TimeTzADT *) DatumGetPointer(X))
+static inline DateADT
+DatumGetDateADT(Datum X)
+{
+ return (DateADT) DatumGetInt32(X);
+}
+
+static inline TimeADT
+DatumGetTimeADT(Datum X)
+{
+ return (TimeADT) DatumGetInt64(X);
+}
+
+static inline TimeTzADT *
+DatumGetTimeTzADTP(Datum X)
+{
+ return (TimeTzADT *) DatumGetPointer(X);
+}
-#define DateADTGetDatum(X) Int32GetDatum(X)
-#define TimeADTGetDatum(X) Int64GetDatum(X)
-#define TimeTzADTPGetDatum(X) PointerGetDatum(X)
+static inline Datum
+DateADTGetDatum(DateADT X)
+{
+ return Int32GetDatum(X);
+}
+
+static inline Datum
+TimeADTGetDatum(TimeADT X)
+{
+ return Int64GetDatum(X);
+}
+
+static inline Datum
+TimeTzADTPGetDatum(const TimeTzADT *X)
+{
+ return PointerGetDatum(X);
+}
#define PG_GETARG_DATEADT(n) DatumGetDateADT(PG_GETARG_DATUM(n))
#define PG_GETARG_TIMEADT(n) DatumGetTimeADT(PG_GETARG_DATUM(n))
diff --git a/src/include/utils/expandeddatum.h b/src/include/utils/expandeddatum.h
index ffdb0c45bd..c4818eb054 100644
--- a/src/include/utils/expandeddatum.h
+++ b/src/include/utils/expandeddatum.h
@@ -133,8 +133,17 @@ struct ExpandedObjectHeader
* (More of these might be worth inlining later.)
*/
-#define EOHPGetRWDatum(eohptr) PointerGetDatum((eohptr)->eoh_rw_ptr)
-#define EOHPGetRODatum(eohptr) PointerGetDatum((eohptr)->eoh_ro_ptr)
+static inline Datum
+EOHPGetRWDatum(const struct ExpandedObjectHeader *eohptr)
+{
+ return PointerGetDatum(eohptr->eoh_rw_ptr);
+}
+
+static inline Datum
+EOHPGetRODatum(const struct ExpandedObjectHeader *eohptr)
+{
+ return PointerGetDatum(eohptr->eoh_ro_ptr);
+}
/* Does the Datum represent a writable expanded object? */
#define DatumIsReadWriteExpandedObject(d, isnull, typlen) \
diff --git a/src/include/utils/expandedrecord.h b/src/include/utils/expandedrecord.h
index be60e2ce53..aaf5b6bfd1 100644
--- a/src/include/utils/expandedrecord.h
+++ b/src/include/utils/expandedrecord.h
@@ -138,10 +138,20 @@ typedef struct ExpandedRecordHeader
MemoryContextCallback er_mcb;
} ExpandedRecordHeader;
-/* fmgr macros for expanded record objects */
+/* fmgr functions and macros for expanded record objects */
+static inline Datum
+ExpandedRecordGetDatum(const ExpandedRecordHeader *erh)
+{
+ return EOHPGetRWDatum(&erh->hdr);
+}
+
+static inline Datum
+ExpandedRecordGetRODatum(const ExpandedRecordHeader *erh)
+{
+ return EOHPGetRODatum(&erh->hdr);
+}
+
#define PG_GETARG_EXPANDED_RECORD(n) DatumGetExpandedRecord(PG_GETARG_DATUM(n))
-#define ExpandedRecordGetDatum(erh) EOHPGetRWDatum(&(erh)->hdr)
-#define ExpandedRecordGetRODatum(erh) EOHPGetRODatum(&(erh)->hdr)
#define PG_RETURN_EXPANDED_RECORD(x) PG_RETURN_DATUM(ExpandedRecordGetDatum(x))
/* assorted other macros */
diff --git a/src/include/utils/geo_decls.h b/src/include/utils/geo_decls.h
index 719030777e..05726778ad 100644
--- a/src/include/utils/geo_decls.h
+++ b/src/include/utils/geo_decls.h
@@ -166,48 +166,112 @@ typedef struct
} CIRCLE;
/*
- * fmgr interface macros
+ * fmgr interface functions
*
* Path and Polygon are toastable varlena types, the others are just
* fixed-size pass-by-reference types.
*/
-#define DatumGetPointP(X) ((Point *) DatumGetPointer(X))
-#define PointPGetDatum(X) PointerGetDatum(X)
+static inline Point *
+DatumGetPointP(Datum X)
+{
+ return (Point *) DatumGetPointer(X);
+}
+static inline Datum
+PointPGetDatum(const Point *X)
+{
+ return PointerGetDatum(X);
+}
#define PG_GETARG_POINT_P(n) DatumGetPointP(PG_GETARG_DATUM(n))
#define PG_RETURN_POINT_P(x) return PointPGetDatum(x)
-#define DatumGetLsegP(X) ((LSEG *) DatumGetPointer(X))
-#define LsegPGetDatum(X) PointerGetDatum(X)
+static inline LSEG *
+DatumGetLsegP(Datum X)
+{
+ return (LSEG *) DatumGetPointer(X);
+}
+static inline Datum
+LsegPGetDatum(const LSEG *X)
+{
+ return PointerGetDatum(X);
+}
#define PG_GETARG_LSEG_P(n) DatumGetLsegP(PG_GETARG_DATUM(n))
#define PG_RETURN_LSEG_P(x) return LsegPGetDatum(x)
-#define DatumGetPathP(X) ((PATH *) PG_DETOAST_DATUM(X))
-#define DatumGetPathPCopy(X) ((PATH *) PG_DETOAST_DATUM_COPY(X))
-#define PathPGetDatum(X) PointerGetDatum(X)
+static inline PATH *
+DatumGetPathP(Datum X)
+{
+ return (PATH *) PG_DETOAST_DATUM(X);
+}
+static inline PATH *
+DatumGetPathPCopy(Datum X)
+{
+ return (PATH *) PG_DETOAST_DATUM_COPY(X);
+}
+static inline Datum
+PathPGetDatum(const PATH *X)
+{
+ return PointerGetDatum(X);
+}
#define PG_GETARG_PATH_P(n) DatumGetPathP(PG_GETARG_DATUM(n))
#define PG_GETARG_PATH_P_COPY(n) DatumGetPathPCopy(PG_GETARG_DATUM(n))
#define PG_RETURN_PATH_P(x) return PathPGetDatum(x)
-#define DatumGetLineP(X) ((LINE *) DatumGetPointer(X))
-#define LinePGetDatum(X) PointerGetDatum(X)
+static inline LINE *
+DatumGetLineP(Datum X)
+{
+ return (LINE *) DatumGetPointer(X);
+}
+static inline Datum
+LinePGetDatum(const LINE *X)
+{
+ return PointerGetDatum(X);
+}
#define PG_GETARG_LINE_P(n) DatumGetLineP(PG_GETARG_DATUM(n))
#define PG_RETURN_LINE_P(x) return LinePGetDatum(x)
-#define DatumGetBoxP(X) ((BOX *) DatumGetPointer(X))
-#define BoxPGetDatum(X) PointerGetDatum(X)
+static inline BOX *
+DatumGetBoxP(Datum X)
+{
+ return (BOX *) DatumGetPointer(X);
+}
+static inline Datum
+BoxPGetDatum(const BOX *X)
+{
+ return PointerGetDatum(X);
+}
#define PG_GETARG_BOX_P(n) DatumGetBoxP(PG_GETARG_DATUM(n))
#define PG_RETURN_BOX_P(x) return BoxPGetDatum(x)
-#define DatumGetPolygonP(X) ((POLYGON *) PG_DETOAST_DATUM(X))
-#define DatumGetPolygonPCopy(X) ((POLYGON *) PG_DETOAST_DATUM_COPY(X))
-#define PolygonPGetDatum(X) PointerGetDatum(X)
+static inline POLYGON *
+DatumGetPolygonP(Datum X)
+{
+ return (POLYGON *) PG_DETOAST_DATUM(X);
+}
+static inline POLYGON *
+DatumGetPolygonPCopy(Datum X)
+{
+ return (POLYGON *) PG_DETOAST_DATUM_COPY(X);
+}
+static inline Datum
+PolygonPGetDatum(const POLYGON *X)
+{
+ return PointerGetDatum(X);
+}
#define PG_GETARG_POLYGON_P(n) DatumGetPolygonP(PG_GETARG_DATUM(n))
#define PG_GETARG_POLYGON_P_COPY(n) DatumGetPolygonPCopy(PG_GETARG_DATUM(n))
#define PG_RETURN_POLYGON_P(x) return PolygonPGetDatum(x)
-#define DatumGetCircleP(X) ((CIRCLE *) DatumGetPointer(X))
-#define CirclePGetDatum(X) PointerGetDatum(X)
+static inline CIRCLE *
+DatumGetCircleP(Datum X)
+{
+ return (CIRCLE *) DatumGetPointer(X);
+}
+static inline Datum
+CirclePGetDatum(const CIRCLE *X)
+{
+ return PointerGetDatum(X);
+}
#define PG_GETARG_CIRCLE_P(n) DatumGetCircleP(PG_GETARG_DATUM(n))
#define PG_RETURN_CIRCLE_P(x) return CirclePGetDatum(x)
diff --git a/src/include/utils/inet.h b/src/include/utils/inet.h
index b1ec9723df..5438cfaba5 100644
--- a/src/include/utils/inet.h
+++ b/src/include/utils/inet.h
@@ -119,23 +119,58 @@ typedef struct macaddr8
/*
* fmgr interface macros
*/
-#define DatumGetInetPP(X) ((inet *) PG_DETOAST_DATUM_PACKED(X))
-#define InetPGetDatum(X) PointerGetDatum(X)
+static inline inet *
+DatumGetInetPP(Datum X)
+{
+ return (inet *) PG_DETOAST_DATUM_PACKED(X);
+}
+
+static inline Datum
+InetPGetDatum(const inet *X)
+{
+ return PointerGetDatum(X);
+}
+
#define PG_GETARG_INET_PP(n) DatumGetInetPP(PG_GETARG_DATUM(n))
#define PG_RETURN_INET_P(x) return InetPGetDatum(x)
+
/* obsolescent variants */
-#define DatumGetInetP(X) ((inet *) PG_DETOAST_DATUM(X))
+static inline inet *
+DatumGetInetP(Datum X)
+{
+ return (inet *) PG_DETOAST_DATUM(X);
+}
#define PG_GETARG_INET_P(n) DatumGetInetP(PG_GETARG_DATUM(n))
/* macaddr is a fixed-length pass-by-reference datatype */
-#define DatumGetMacaddrP(X) ((macaddr *) DatumGetPointer(X))
-#define MacaddrPGetDatum(X) PointerGetDatum(X)
+static inline macaddr *
+DatumGetMacaddrP(Datum X)
+{
+ return (macaddr *) DatumGetPointer(X);
+}
+
+static inline Datum
+MacaddrPGetDatum(const macaddr *X)
+{
+ return PointerGetDatum(X);
+}
+
#define PG_GETARG_MACADDR_P(n) DatumGetMacaddrP(PG_GETARG_DATUM(n))
#define PG_RETURN_MACADDR_P(x) return MacaddrPGetDatum(x)
/* macaddr8 is a fixed-length pass-by-reference datatype */
-#define DatumGetMacaddr8P(X) ((macaddr8 *) DatumGetPointer(X))
-#define Macaddr8PGetDatum(X) PointerGetDatum(X)
+static inline macaddr8 *
+DatumGetMacaddr8P(Datum X)
+{
+ return (macaddr8 *) DatumGetPointer(X);
+}
+
+static inline Datum
+Macaddr8PGetDatum(const macaddr8 *X)
+{
+ return PointerGetDatum(X);
+}
+
#define PG_GETARG_MACADDR8_P(n) DatumGetMacaddr8P(PG_GETARG_DATUM(n))
#define PG_RETURN_MACADDR8_P(x) return Macaddr8PGetDatum(x)
diff --git a/src/include/utils/jsonb.h b/src/include/utils/jsonb.h
index bae466b523..536d00b3b4 100644
--- a/src/include/utils/jsonb.h
+++ b/src/include/utils/jsonb.h
@@ -67,14 +67,6 @@ typedef enum
#define JGINFLAG_HASHED 0x10 /* OR'd into flag if value was hashed */
#define JGIN_MAXLENGTH 125 /* max length of text part before hashing */
-/* Convenience macros */
-#define DatumGetJsonbP(d) ((Jsonb *) PG_DETOAST_DATUM(d))
-#define DatumGetJsonbPCopy(d) ((Jsonb *) PG_DETOAST_DATUM_COPY(d))
-#define JsonbPGetDatum(p) PointerGetDatum(p)
-#define PG_GETARG_JSONB_P(x) DatumGetJsonbP(PG_GETARG_DATUM(x))
-#define PG_GETARG_JSONB_P_COPY(x) DatumGetJsonbPCopy(PG_GETARG_DATUM(x))
-#define PG_RETURN_JSONB_P(x) PG_RETURN_POINTER(x)
-
typedef struct JsonbPair JsonbPair;
typedef struct JsonbValue JsonbValue;
@@ -393,6 +385,29 @@ typedef enum /* type categories for datum_to_jsonb */
JSONBTYPE_OTHER /* all else */
} JsonbTypeCategory;
+/* Convenience macros */
+static inline Jsonb *
+DatumGetJsonbP(Datum d)
+{
+ return (Jsonb *) PG_DETOAST_DATUM(d);
+}
+
+static inline Jsonb *
+DatumGetJsonbPCopy(Datum d)
+{
+ return (Jsonb *) PG_DETOAST_DATUM_COPY(d);
+}
+
+static inline Datum
+JsonbPGetDatum(const Jsonb *p)
+{
+ return PointerGetDatum(p);
+}
+
+#define PG_GETARG_JSONB_P(x) DatumGetJsonbP(PG_GETARG_DATUM(x))
+#define PG_GETARG_JSONB_P_COPY(x) DatumGetJsonbPCopy(PG_GETARG_DATUM(x))
+#define PG_RETURN_JSONB_P(x) PG_RETURN_POINTER(x)
+
/* Support functions */
extern uint32 getJsonbOffset(const JsonbContainer *jc, int index);
extern uint32 getJsonbLength(const JsonbContainer *jc, int index);
diff --git a/src/include/utils/jsonpath.h b/src/include/utils/jsonpath.h
index 8e79b8dc9f..ea32a8f072 100644
--- a/src/include/utils/jsonpath.h
+++ b/src/include/utils/jsonpath.h
@@ -32,8 +32,18 @@ typedef struct
#define JSONPATH_LAX (0x80000000)
#define JSONPATH_HDRSZ (offsetof(JsonPath, data))
-#define DatumGetJsonPathP(d) ((JsonPath *) DatumGetPointer(PG_DETOAST_DATUM(d)))
-#define DatumGetJsonPathPCopy(d) ((JsonPath *) DatumGetPointer(PG_DETOAST_DATUM_COPY(d)))
+static inline JsonPath *
+DatumGetJsonPathP(Datum d)
+{
+ return (JsonPath *) PG_DETOAST_DATUM(d);
+}
+
+static inline JsonPath *
+DatumGetJsonPathPCopy(Datum d)
+{
+ return (JsonPath *) PG_DETOAST_DATUM_COPY(d);
+}
+
#define PG_GETARG_JSONPATH_P(x) DatumGetJsonPathP(PG_GETARG_DATUM(x))
#define PG_GETARG_JSONPATH_P_COPY(x) DatumGetJsonPathPCopy(PG_GETARG_DATUM(x))
#define PG_RETURN_JSONPATH_P(p) PG_RETURN_POINTER(p)
diff --git a/src/include/utils/multirangetypes.h b/src/include/utils/multirangetypes.h
index 915330f990..23bce0005f 100644
--- a/src/include/utils/multirangetypes.h
+++ b/src/include/utils/multirangetypes.h
@@ -42,11 +42,26 @@ typedef struct
#define MultirangeIsEmpty(mr) ((mr)->rangeCount == 0)
/*
- * fmgr macros for multirange type objects
+ * fmgr functions for multirange type objects
*/
-#define DatumGetMultirangeTypeP(X) ((MultirangeType *) PG_DETOAST_DATUM(X))
-#define DatumGetMultirangeTypePCopy(X) ((MultirangeType *) PG_DETOAST_DATUM_COPY(X))
-#define MultirangeTypePGetDatum(X) PointerGetDatum(X)
+static inline MultirangeType *
+DatumGetMultirangeTypeP(Datum X)
+{
+ return (MultirangeType *) PG_DETOAST_DATUM(X);
+}
+
+static inline MultirangeType *
+DatumGetMultirangeTypePCopy(Datum X)
+{
+ return (MultirangeType *) PG_DETOAST_DATUM_COPY(X);
+}
+
+static inline Datum
+MultirangeTypePGetDatum(const MultirangeType *X)
+{
+ return PointerGetDatum(X);
+}
+
#define PG_GETARG_MULTIRANGE_P(n) DatumGetMultirangeTypeP(PG_GETARG_DATUM(n))
#define PG_GETARG_MULTIRANGE_P_COPY(n) DatumGetMultirangeTypePCopy(PG_GETARG_DATUM(n))
#define PG_RETURN_MULTIRANGE_P(x) return MultirangeTypePGetDatum(x)
diff --git a/src/include/utils/numeric.h b/src/include/utils/numeric.h
index 3caa74dfe7..88c62c5348 100644
--- a/src/include/utils/numeric.h
+++ b/src/include/utils/numeric.h
@@ -56,9 +56,24 @@ typedef struct NumericData *Numeric;
* fmgr interface macros
*/
-#define DatumGetNumeric(X) ((Numeric) PG_DETOAST_DATUM(X))
-#define DatumGetNumericCopy(X) ((Numeric) PG_DETOAST_DATUM_COPY(X))
-#define NumericGetDatum(X) PointerGetDatum(X)
+static inline Numeric
+DatumGetNumeric(Datum X)
+{
+ return (Numeric) PG_DETOAST_DATUM(X);
+}
+
+static inline Numeric
+DatumGetNumericCopy(Datum X)
+{
+ return (Numeric) PG_DETOAST_DATUM_COPY(X);
+}
+
+static inline Datum
+NumericGetDatum(Numeric X)
+{
+ return PointerGetDatum(X);
+}
+
#define PG_GETARG_NUMERIC(n) DatumGetNumeric(PG_GETARG_DATUM(n))
#define PG_GETARG_NUMERIC_COPY(n) DatumGetNumericCopy(PG_GETARG_DATUM(n))
#define PG_RETURN_NUMERIC(x) return NumericGetDatum(x)
diff --git a/src/include/utils/pg_lsn.h b/src/include/utils/pg_lsn.h
index 7b708f1073..5313afbfe2 100644
--- a/src/include/utils/pg_lsn.h
+++ b/src/include/utils/pg_lsn.h
@@ -18,8 +18,17 @@
#include "access/xlogdefs.h"
#include "fmgr.h"
-#define DatumGetLSN(X) ((XLogRecPtr) DatumGetInt64(X))
-#define LSNGetDatum(X) (Int64GetDatum((int64) (X)))
+static inline XLogRecPtr
+DatumGetLSN(Datum X)
+{
+ return (XLogRecPtr) DatumGetInt64(X);
+}
+
+static inline Datum
+LSNGetDatum(XLogRecPtr X)
+{
+ return Int64GetDatum((int64) X);
+}
#define PG_GETARG_LSN(n) DatumGetLSN(PG_GETARG_DATUM(n))
#define PG_RETURN_LSN(x) return LSNGetDatum(x)
diff --git a/src/include/utils/rangetypes.h b/src/include/utils/rangetypes.h
index 993fad4fc2..661e4892ec 100644
--- a/src/include/utils/rangetypes.h
+++ b/src/include/utils/rangetypes.h
@@ -68,11 +68,26 @@ typedef struct
} RangeBound;
/*
- * fmgr macros for range type objects
+ * fmgr functions for range type objects
*/
-#define DatumGetRangeTypeP(X) ((RangeType *) PG_DETOAST_DATUM(X))
-#define DatumGetRangeTypePCopy(X) ((RangeType *) PG_DETOAST_DATUM_COPY(X))
-#define RangeTypePGetDatum(X) PointerGetDatum(X)
+static inline RangeType *
+DatumGetRangeTypeP(Datum X)
+{
+ return (RangeType *) PG_DETOAST_DATUM(X);
+}
+
+static inline RangeType *
+DatumGetRangeTypePCopy(Datum X)
+{
+ return (RangeType *) PG_DETOAST_DATUM_COPY(X);
+}
+
+static inline Datum
+RangeTypePGetDatum(const RangeType *X)
+{
+ return PointerGetDatum(X);
+}
+
#define PG_GETARG_RANGE_P(n) DatumGetRangeTypeP(PG_GETARG_DATUM(n))
#define PG_GETARG_RANGE_P_COPY(n) DatumGetRangeTypePCopy(PG_GETARG_DATUM(n))
#define PG_RETURN_RANGE_P(x) return RangeTypePGetDatum(x)
diff --git a/src/include/utils/timestamp.h b/src/include/utils/timestamp.h
index edf3a97318..3602638bdc 100644
--- a/src/include/utils/timestamp.h
+++ b/src/include/utils/timestamp.h
@@ -19,18 +19,46 @@
/*
- * Macros for fmgr-callable functions.
+ * Functions for fmgr-callable functions.
*
* For Timestamp, we make use of the same support routines as for int64.
* Therefore Timestamp is pass-by-reference if and only if int64 is!
*/
-#define DatumGetTimestamp(X) ((Timestamp) DatumGetInt64(X))
-#define DatumGetTimestampTz(X) ((TimestampTz) DatumGetInt64(X))
-#define DatumGetIntervalP(X) ((Interval *) DatumGetPointer(X))
-
-#define TimestampGetDatum(X) Int64GetDatum(X)
-#define TimestampTzGetDatum(X) Int64GetDatum(X)
-#define IntervalPGetDatum(X) PointerGetDatum(X)
+static inline Timestamp
+DatumGetTimestamp(Datum X)
+{
+ return (Timestamp) DatumGetInt64(X);
+}
+
+static inline TimestampTz
+DatumGetTimestampTz(Datum X)
+{
+ return (TimestampTz) DatumGetInt64(X);
+}
+
+static inline Interval *
+DatumGetIntervalP(Datum X)
+{
+ return (Interval *) DatumGetPointer(X);
+}
+
+static inline Datum
+TimestampGetDatum(Timestamp X)
+{
+ return Int64GetDatum(X);
+}
+
+static inline Datum
+TimestampTzGetDatum(TimestampTz X)
+{
+ return Int64GetDatum(X);
+}
+
+static inline Datum
+IntervalPGetDatum(const Interval *X)
+{
+ return PointerGetDatum(X);
+}
#define PG_GETARG_TIMESTAMP(n) DatumGetTimestamp(PG_GETARG_DATUM(n))
#define PG_GETARG_TIMESTAMPTZ(n) DatumGetTimestampTz(PG_GETARG_DATUM(n))
diff --git a/src/include/utils/uuid.h b/src/include/utils/uuid.h
index 0029da4e7f..69f4332d0f 100644
--- a/src/include/utils/uuid.h
+++ b/src/include/utils/uuid.h
@@ -23,9 +23,20 @@ typedef struct pg_uuid_t
} pg_uuid_t;
/* fmgr interface macros */
-#define UUIDPGetDatum(X) PointerGetDatum(X)
+static inline Datum
+UUIDPGetDatum(const pg_uuid_t *X)
+{
+ return PointerGetDatum(X);
+}
+
#define PG_RETURN_UUID_P(X) return UUIDPGetDatum(X)
-#define DatumGetUUIDP(X) ((pg_uuid_t *) DatumGetPointer(X))
+
+static inline pg_uuid_t *
+DatumGetUUIDP(Datum X)
+{
+ return (pg_uuid_t *) DatumGetPointer(X);
+}
+
#define PG_GETARG_UUID_P(X) DatumGetUUIDP(PG_GETARG_DATUM(X))
#endif /* UUID_H */
diff --git a/src/include/utils/varbit.h b/src/include/utils/varbit.h
index 039ba860cf..e65e7f65e7 100644
--- a/src/include/utils/varbit.h
+++ b/src/include/utils/varbit.h
@@ -41,9 +41,24 @@ typedef struct
* BIT and BIT VARYING are toastable varlena types. They are the same
* as far as representation goes, so we just have one set of macros.
*/
-#define DatumGetVarBitP(X) ((VarBit *) PG_DETOAST_DATUM(X))
-#define DatumGetVarBitPCopy(X) ((VarBit *) PG_DETOAST_DATUM_COPY(X))
-#define VarBitPGetDatum(X) PointerGetDatum(X)
+static inline VarBit *
+DatumGetVarBitP(Datum X)
+{
+ return (VarBit *) PG_DETOAST_DATUM(X);
+}
+
+static inline VarBit *
+DatumGetVarBitPCopy(Datum X)
+{
+ return (VarBit *) PG_DETOAST_DATUM_COPY(X);
+}
+
+static inline Datum
+VarBitPGetDatum(const VarBit *X)
+{
+ return PointerGetDatum(X);
+}
+
#define PG_GETARG_VARBIT_P(n) DatumGetVarBitP(PG_GETARG_DATUM(n))
#define PG_GETARG_VARBIT_P_COPY(n) DatumGetVarBitPCopy(PG_GETARG_DATUM(n))
#define PG_RETURN_VARBIT_P(x) return VarBitPGetDatum(x)
diff --git a/src/include/utils/xid8.h b/src/include/utils/xid8.h
index b702fc1a91..9c5ce241db 100644
--- a/src/include/utils/xid8.h
+++ b/src/include/utils/xid8.h
@@ -14,8 +14,18 @@
#include "access/transam.h"
-#define DatumGetFullTransactionId(X) (FullTransactionIdFromU64(DatumGetUInt64(X)))
-#define FullTransactionIdGetDatum(X) (UInt64GetDatum(U64FromFullTransactionId(X)))
+static inline FullTransactionId
+DatumGetFullTransactionId(Datum X)
+{
+ return FullTransactionIdFromU64(DatumGetUInt64(X));
+}
+
+static inline Datum
+FullTransactionIdGetDatum(FullTransactionId X)
+{
+ return UInt64GetDatum(U64FromFullTransactionId(X));
+}
+
#define PG_GETARG_FULLTRANSACTIONID(X) DatumGetFullTransactionId(PG_GETARG_DATUM(X))
#define PG_RETURN_FULLTRANSACTIONID(X) return FullTransactionIdGetDatum(X)
diff --git a/src/include/utils/xml.h b/src/include/utils/xml.h
index 6620a62619..c71174d204 100644
--- a/src/include/utils/xml.h
+++ b/src/include/utils/xml.h
@@ -47,8 +47,17 @@ typedef enum
/* struct PgXmlErrorContext is private to xml.c */
typedef struct PgXmlErrorContext PgXmlErrorContext;
-#define DatumGetXmlP(X) ((xmltype *) PG_DETOAST_DATUM(X))
-#define XmlPGetDatum(X) PointerGetDatum(X)
+static inline xmltype *
+DatumGetXmlP(Datum X)
+{
+ return (xmltype *) PG_DETOAST_DATUM(X);
+}
+
+static inline Datum
+XmlPGetDatum(const xmltype *X)
+{
+ return PointerGetDatum(X);
+}
#define PG_GETARG_XML_P(n) DatumGetXmlP(PG_GETARG_DATUM(n))
#define PG_RETURN_XML_P(x) PG_RETURN_POINTER(x)
--
2.37.2
^ permalink raw reply [nested|flat] 92+ messages in thread
* Re: Convert *GetDatum() and DatumGet*() macros to inline functions
2022-08-28 15:55 Convert *GetDatum() and DatumGet*() macros to inline functions Peter Eisentraut <[email protected]>
2022-08-30 13:36 ` Re: Convert *GetDatum() and DatumGet*() macros to inline functions Aleksander Alekseev <[email protected]>
@ 2022-08-30 13:46 ` Tom Lane <[email protected]>
2022-08-30 13:59 ` Re: Convert *GetDatum() and DatumGet*() macros to inline functions Aleksander Alekseev <[email protected]>
0 siblings, 1 reply; 92+ messages in thread
From: Tom Lane @ 2022-08-30 13:46 UTC (permalink / raw)
To: Aleksander Alekseev <[email protected]>; +Cc: pgsql-hackers; Peter Eisentraut <[email protected]>
Aleksander Alekseev <[email protected]> writes:
> Do you think this should be backported?
Impossible, it's an ABI break.
regards, tom lane
^ permalink raw reply [nested|flat] 92+ messages in thread
* Re: Convert *GetDatum() and DatumGet*() macros to inline functions
2022-08-28 15:55 Convert *GetDatum() and DatumGet*() macros to inline functions Peter Eisentraut <[email protected]>
2022-08-30 13:36 ` Re: Convert *GetDatum() and DatumGet*() macros to inline functions Aleksander Alekseev <[email protected]>
2022-08-30 13:46 ` Re: Convert *GetDatum() and DatumGet*() macros to inline functions Tom Lane <[email protected]>
@ 2022-08-30 13:59 ` Aleksander Alekseev <[email protected]>
2022-08-30 14:13 ` Re: Convert *GetDatum() and DatumGet*() macros to inline functions Tom Lane <[email protected]>
0 siblings, 1 reply; 92+ messages in thread
From: Aleksander Alekseev @ 2022-08-30 13:59 UTC (permalink / raw)
To: pgsql-hackers; +Cc: Tom Lane <[email protected]>; Peter Eisentraut <[email protected]>
Hi Tom,
> Do you think this should be backported?
>> Impossible, it's an ABI break.
OK, got it.
Just to clarify, a break in this case is going to be the fact that we
are adding new functions, although inlined, correct? Or maybe
something else? I'm sorry this is the first time I encounter the
question of ABI compatibility in the context of Postgres, so I would
appreciate it if you could elaborate a bit.
--
Best regards,
Aleksander Alekseev
^ permalink raw reply [nested|flat] 92+ messages in thread
* Re: Convert *GetDatum() and DatumGet*() macros to inline functions
2022-08-28 15:55 Convert *GetDatum() and DatumGet*() macros to inline functions Peter Eisentraut <[email protected]>
2022-08-30 13:36 ` Re: Convert *GetDatum() and DatumGet*() macros to inline functions Aleksander Alekseev <[email protected]>
2022-08-30 13:46 ` Re: Convert *GetDatum() and DatumGet*() macros to inline functions Tom Lane <[email protected]>
2022-08-30 13:59 ` Re: Convert *GetDatum() and DatumGet*() macros to inline functions Aleksander Alekseev <[email protected]>
@ 2022-08-30 14:13 ` Tom Lane <[email protected]>
2022-08-30 14:16 ` Re: Convert *GetDatum() and DatumGet*() macros to inline functions Robert Haas <[email protected]>
0 siblings, 1 reply; 92+ messages in thread
From: Tom Lane @ 2022-08-30 14:13 UTC (permalink / raw)
To: Aleksander Alekseev <[email protected]>; +Cc: pgsql-hackers; Peter Eisentraut <[email protected]>
Aleksander Alekseev <[email protected]> writes:
> Just to clarify, a break in this case is going to be the fact that we
> are adding new functions, although inlined, correct? Or maybe
> something else? I'm sorry this is the first time I encounter the
> question of ABI compatibility in the context of Postgres, so I would
> appreciate it if you could elaborate a bit.
After absorbing a bit more caffeine, I suppose that replacing a
macro with a "static inline" function would not be an ABI break,
at least not with most modern compilers, because the code should
end up the same. I'd still vote against back-patching though.
I don't think the risk-reward ratio is good, especially not for
the pre-C99 branches which don't necessarily have "inline".
regards, tom lane
^ permalink raw reply [nested|flat] 92+ messages in thread
* Re: Convert *GetDatum() and DatumGet*() macros to inline functions
2022-08-28 15:55 Convert *GetDatum() and DatumGet*() macros to inline functions Peter Eisentraut <[email protected]>
2022-08-30 13:36 ` Re: Convert *GetDatum() and DatumGet*() macros to inline functions Aleksander Alekseev <[email protected]>
2022-08-30 13:46 ` Re: Convert *GetDatum() and DatumGet*() macros to inline functions Tom Lane <[email protected]>
2022-08-30 13:59 ` Re: Convert *GetDatum() and DatumGet*() macros to inline functions Aleksander Alekseev <[email protected]>
2022-08-30 14:13 ` Re: Convert *GetDatum() and DatumGet*() macros to inline functions Tom Lane <[email protected]>
@ 2022-08-30 14:16 ` Robert Haas <[email protected]>
2022-08-30 14:25 ` Re: Convert *GetDatum() and DatumGet*() macros to inline functions Aleksander Alekseev <[email protected]>
0 siblings, 1 reply; 92+ messages in thread
From: Robert Haas @ 2022-08-30 14:16 UTC (permalink / raw)
To: Tom Lane <[email protected]>; +Cc: Aleksander Alekseev <[email protected]>; pgsql-hackers; Peter Eisentraut <[email protected]>
On Tue, Aug 30, 2022 at 10:13 AM Tom Lane <[email protected]> wrote:
> Aleksander Alekseev <[email protected]> writes:
> > Just to clarify, a break in this case is going to be the fact that we
> > are adding new functions, although inlined, correct? Or maybe
> > something else? I'm sorry this is the first time I encounter the
> > question of ABI compatibility in the context of Postgres, so I would
> > appreciate it if you could elaborate a bit.
>
> After absorbing a bit more caffeine, I suppose that replacing a
> macro with a "static inline" function would not be an ABI break,
> at least not with most modern compilers, because the code should
> end up the same. I'd still vote against back-patching though.
> I don't think the risk-reward ratio is good, especially not for
> the pre-C99 branches which don't necessarily have "inline".
Yeah, I don't see a reason to back-patch a change like this, certainly
not right away. If over time it turns out that the different
definitions on different branches cause too many headaches, we could
reconsider. However, I'm not sure that will happen, because the whole
point is that the static inline functions are intended to behave in
the same way as the macros, just with better type-checking.
--
Robert Haas
EDB: http://www.enterprisedb.com
^ permalink raw reply [nested|flat] 92+ messages in thread
* Re: Convert *GetDatum() and DatumGet*() macros to inline functions
2022-08-28 15:55 Convert *GetDatum() and DatumGet*() macros to inline functions Peter Eisentraut <[email protected]>
2022-08-30 13:36 ` Re: Convert *GetDatum() and DatumGet*() macros to inline functions Aleksander Alekseev <[email protected]>
2022-08-30 13:46 ` Re: Convert *GetDatum() and DatumGet*() macros to inline functions Tom Lane <[email protected]>
2022-08-30 13:59 ` Re: Convert *GetDatum() and DatumGet*() macros to inline functions Aleksander Alekseev <[email protected]>
2022-08-30 14:13 ` Re: Convert *GetDatum() and DatumGet*() macros to inline functions Tom Lane <[email protected]>
2022-08-30 14:16 ` Re: Convert *GetDatum() and DatumGet*() macros to inline functions Robert Haas <[email protected]>
@ 2022-08-30 14:25 ` Aleksander Alekseev <[email protected]>
2022-08-30 14:27 ` Re: Convert *GetDatum() and DatumGet*() macros to inline functions Robert Haas <[email protected]>
2022-08-30 14:28 ` Re: Convert *GetDatum() and DatumGet*() macros to inline functions Tom Lane <[email protected]>
0 siblings, 2 replies; 92+ messages in thread
From: Aleksander Alekseev @ 2022-08-30 14:25 UTC (permalink / raw)
To: pgsql-hackers; +Cc: Tom Lane <[email protected]>; Robert Haas <[email protected]>; Peter Eisentraut <[email protected]>
Tom, Robert,
> Yeah, I don't see a reason to back-patch a change like this
Maybe we should consider backporting at least 0001 patch, partially
perhaps? I believe if fixes pretty cursed pieces of code, e.g:
```
pg_cryptohash_ctx *context =
- (pg_cryptohash_ctx *) PointerGetDatum(foundres);
+ (pg_cryptohash_ctx *) DatumGetPointer(foundres);
```
This being said, personally I don't have a strong opinion here. After
all, the code works and passes the tests. Maybe I'm just being a
perfectionist here.
--
Best regards,
Aleksander Alekseev
^ permalink raw reply [nested|flat] 92+ messages in thread
* Re: Convert *GetDatum() and DatumGet*() macros to inline functions
2022-08-28 15:55 Convert *GetDatum() and DatumGet*() macros to inline functions Peter Eisentraut <[email protected]>
2022-08-30 13:36 ` Re: Convert *GetDatum() and DatumGet*() macros to inline functions Aleksander Alekseev <[email protected]>
2022-08-30 13:46 ` Re: Convert *GetDatum() and DatumGet*() macros to inline functions Tom Lane <[email protected]>
2022-08-30 13:59 ` Re: Convert *GetDatum() and DatumGet*() macros to inline functions Aleksander Alekseev <[email protected]>
2022-08-30 14:13 ` Re: Convert *GetDatum() and DatumGet*() macros to inline functions Tom Lane <[email protected]>
2022-08-30 14:16 ` Re: Convert *GetDatum() and DatumGet*() macros to inline functions Robert Haas <[email protected]>
2022-08-30 14:25 ` Re: Convert *GetDatum() and DatumGet*() macros to inline functions Aleksander Alekseev <[email protected]>
@ 2022-08-30 14:27 ` Robert Haas <[email protected]>
1 sibling, 0 replies; 92+ messages in thread
From: Robert Haas @ 2022-08-30 14:27 UTC (permalink / raw)
To: Aleksander Alekseev <[email protected]>; +Cc: pgsql-hackers; Tom Lane <[email protected]>; Peter Eisentraut <[email protected]>
On Tue, Aug 30, 2022 at 10:25 AM Aleksander Alekseev
<[email protected]> wrote:
> > Yeah, I don't see a reason to back-patch a change like this
>
> Maybe we should consider backporting at least 0001 patch, partially
> perhaps? I believe if fixes pretty cursed pieces of code, e.g:
>
> ```
> pg_cryptohash_ctx *context =
> - (pg_cryptohash_ctx *) PointerGetDatum(foundres);
> + (pg_cryptohash_ctx *) DatumGetPointer(foundres);
> ```
Sure, back-porting the bug fixes would make sense to me.
--
Robert Haas
EDB: http://www.enterprisedb.com
^ permalink raw reply [nested|flat] 92+ messages in thread
* Re: Convert *GetDatum() and DatumGet*() macros to inline functions
2022-08-28 15:55 Convert *GetDatum() and DatumGet*() macros to inline functions Peter Eisentraut <[email protected]>
2022-08-30 13:36 ` Re: Convert *GetDatum() and DatumGet*() macros to inline functions Aleksander Alekseev <[email protected]>
2022-08-30 13:46 ` Re: Convert *GetDatum() and DatumGet*() macros to inline functions Tom Lane <[email protected]>
2022-08-30 13:59 ` Re: Convert *GetDatum() and DatumGet*() macros to inline functions Aleksander Alekseev <[email protected]>
2022-08-30 14:13 ` Re: Convert *GetDatum() and DatumGet*() macros to inline functions Tom Lane <[email protected]>
2022-08-30 14:16 ` Re: Convert *GetDatum() and DatumGet*() macros to inline functions Robert Haas <[email protected]>
2022-08-30 14:25 ` Re: Convert *GetDatum() and DatumGet*() macros to inline functions Aleksander Alekseev <[email protected]>
@ 2022-08-30 14:28 ` Tom Lane <[email protected]>
2022-08-30 14:52 ` Re: Convert *GetDatum() and DatumGet*() macros to inline functions Aleksander Alekseev <[email protected]>
1 sibling, 1 reply; 92+ messages in thread
From: Tom Lane @ 2022-08-30 14:28 UTC (permalink / raw)
To: Aleksander Alekseev <[email protected]>; +Cc: pgsql-hackers; Robert Haas <[email protected]>; Peter Eisentraut <[email protected]>
Aleksander Alekseev <[email protected]> writes:
> Maybe we should consider backporting at least 0001 patch, partially
> perhaps? I believe if fixes pretty cursed pieces of code, e.g:
Certainly if there are any parts of it that fix actual bugs,
we ought to backport those. I'm not in a big hurry to backport
cosmetic fixes though.
regards, tom lane
^ permalink raw reply [nested|flat] 92+ messages in thread
* Re: Convert *GetDatum() and DatumGet*() macros to inline functions
2022-08-28 15:55 Convert *GetDatum() and DatumGet*() macros to inline functions Peter Eisentraut <[email protected]>
2022-08-30 13:36 ` Re: Convert *GetDatum() and DatumGet*() macros to inline functions Aleksander Alekseev <[email protected]>
2022-08-30 13:46 ` Re: Convert *GetDatum() and DatumGet*() macros to inline functions Tom Lane <[email protected]>
2022-08-30 13:59 ` Re: Convert *GetDatum() and DatumGet*() macros to inline functions Aleksander Alekseev <[email protected]>
2022-08-30 14:13 ` Re: Convert *GetDatum() and DatumGet*() macros to inline functions Tom Lane <[email protected]>
2022-08-30 14:16 ` Re: Convert *GetDatum() and DatumGet*() macros to inline functions Robert Haas <[email protected]>
2022-08-30 14:25 ` Re: Convert *GetDatum() and DatumGet*() macros to inline functions Aleksander Alekseev <[email protected]>
2022-08-30 14:28 ` Re: Convert *GetDatum() and DatumGet*() macros to inline functions Tom Lane <[email protected]>
@ 2022-08-30 14:52 ` Aleksander Alekseev <[email protected]>
2022-08-30 18:15 ` Re: Convert *GetDatum() and DatumGet*() macros to inline functions Aleksander Alekseev <[email protected]>
0 siblings, 1 reply; 92+ messages in thread
From: Aleksander Alekseev @ 2022-08-30 14:52 UTC (permalink / raw)
To: pgsql-hackers; +Cc: Tom Lane <[email protected]>; Robert Haas <[email protected]>; Peter Eisentraut <[email protected]>
Hi hackers,
> Cfbot is making its mind at the moment of writing.
Here is v3 with silenced compiler warnings.
--
Best regards,
Aleksander Alekseev
Attachments:
[application/octet-stream] v3-0002-Convert-GetDatum-and-DatumGet-macros-to-inline-fu.patch (41.3K, ../../CAJ7c6TNJhVWAqRkp+8jLrq3f5wzW_Q3LR04Ast71E_tQvtxjrQ@mail.gmail.com/2-v3-0002-Convert-GetDatum-and-DatumGet-macros-to-inline-fu.patch)
download | inline diff:
From ffd1a1494095c195235ebff78a9e0bc74c779b39 Mon Sep 17 00:00:00 2001
From: Peter Eisentraut <[email protected]>
Date: Sun, 28 Aug 2022 10:47:38 +0200
Subject: [PATCH v3 2/2] Convert *GetDatum() and DatumGet*() macros to inline
functions
The previous macro implementations just cast the argument to a target
type but did not check whether the input type was appropriate. The
function implementation can do better type checking of the input type.
Peter Eisentraut, reviewed by Aleksander Alekseev
Discussion: http://postgr.es/m/8528fb7e-0aa2-6b54-85fb-0c0886dbd6ed%40enterprisedb.com
---
doc/src/sgml/xfunc.sgml | 2 +-
src/backend/access/gist/gistutil.c | 2 +-
src/backend/tsearch/ts_parse.c | 4 +-
src/backend/utils/mb/mbutils.c | 12 +-
src/include/access/gin.h | 14 +-
src/include/funcapi.h | 14 +-
src/include/postgres.h | 275 +++++++++++++++++++---------
src/include/tsearch/ts_type.h | 46 ++++-
src/include/tsearch/ts_utils.h | 14 +-
src/include/utils/cash.h | 14 +-
src/include/utils/date.h | 42 ++++-
src/include/utils/expandeddatum.h | 13 +-
src/include/utils/expandedrecord.h | 16 +-
src/include/utils/geo_decls.h | 98 ++++++++--
src/include/utils/inet.h | 49 ++++-
src/include/utils/jsonb.h | 31 +++-
src/include/utils/jsonpath.h | 14 +-
src/include/utils/multirangetypes.h | 23 ++-
src/include/utils/numeric.h | 21 ++-
src/include/utils/pg_lsn.h | 13 +-
src/include/utils/rangetypes.h | 23 ++-
src/include/utils/timestamp.h | 44 ++++-
src/include/utils/uuid.h | 15 +-
src/include/utils/varbit.h | 21 ++-
src/include/utils/xid8.h | 14 +-
src/include/utils/xml.h | 13 +-
26 files changed, 658 insertions(+), 189 deletions(-)
diff --git a/doc/src/sgml/xfunc.sgml b/doc/src/sgml/xfunc.sgml
index b8cefb9c2c..cf5810b3c1 100644
--- a/doc/src/sgml/xfunc.sgml
+++ b/doc/src/sgml/xfunc.sgml
@@ -2763,7 +2763,7 @@ c_overpaid(PG_FUNCTION_ARGS)
is null. <function>GetAttributeByName</function> returns a <type>Datum</type>
value that you can convert to the proper data type by using the
appropriate <function>DatumGet<replaceable>XXX</replaceable>()</function>
- macro. Note that the return value is meaningless if the null flag is
+ function. Note that the return value is meaningless if the null flag is
set; always check the null flag before trying to do anything with the
result.
</para>
diff --git a/src/backend/access/gist/gistutil.c b/src/backend/access/gist/gistutil.c
index d4bf0c7563..8bdd16fc81 100644
--- a/src/backend/access/gist/gistutil.c
+++ b/src/backend/access/gist/gistutil.c
@@ -280,7 +280,7 @@ gistMakeUnionKey(GISTSTATE *giststate, int attno,
bool
gistKeyIsEQ(GISTSTATE *giststate, int attno, Datum a, Datum b)
{
- bool result;
+ bool result = false; /* silence the compiler warning */
FunctionCall3Coll(&giststate->equalFn[attno],
giststate->supportCollation[attno],
diff --git a/src/backend/tsearch/ts_parse.c b/src/backend/tsearch/ts_parse.c
index 27b2cca2df..ff2f0737c8 100644
--- a/src/backend/tsearch/ts_parse.c
+++ b/src/backend/tsearch/ts_parse.c
@@ -354,7 +354,7 @@ void
parsetext(Oid cfgId, ParsedText *prs, char *buf, int buflen)
{
int type,
- lenlemm;
+ lenlemm = 0; /* silence the compiler warning */
char *lemm = NULL;
LexizeData ldata;
TSLexeme *norms;
@@ -529,7 +529,7 @@ void
hlparsetext(Oid cfgId, HeadlineParsedText *prs, TSQuery query, char *buf, int buflen)
{
int type,
- lenlemm;
+ lenlemm = 0; /* silence the compiler warning */
char *lemm = NULL;
LexizeData ldata;
TSLexeme *norms;
diff --git a/src/backend/utils/mb/mbutils.c b/src/backend/utils/mb/mbutils.c
index 0543c574c6..474ab476f5 100644
--- a/src/backend/utils/mb/mbutils.c
+++ b/src/backend/utils/mb/mbutils.c
@@ -409,8 +409,8 @@ pg_do_encoding_conversion(unsigned char *src, int len,
(void) OidFunctionCall6(proc,
Int32GetDatum(src_encoding),
Int32GetDatum(dest_encoding),
- CStringGetDatum(src),
- CStringGetDatum(result),
+ CStringGetDatum((char *) src),
+ CStringGetDatum((char *) result),
Int32GetDatum(len),
BoolGetDatum(false));
@@ -485,8 +485,8 @@ pg_do_encoding_conversion_buf(Oid proc,
result = OidFunctionCall6(proc,
Int32GetDatum(src_encoding),
Int32GetDatum(dest_encoding),
- CStringGetDatum(src),
- CStringGetDatum(dest),
+ CStringGetDatum((char *) src),
+ CStringGetDatum((char *) dest),
Int32GetDatum(srclen),
BoolGetDatum(noError));
return DatumGetInt32(result);
@@ -910,8 +910,8 @@ pg_unicode_to_server(pg_wchar c, unsigned char *s)
FunctionCall6(Utf8ToServerConvProc,
Int32GetDatum(PG_UTF8),
Int32GetDatum(server_encoding),
- CStringGetDatum(c_as_utf8),
- CStringGetDatum(s),
+ CStringGetDatum((char *) c_as_utf8),
+ CStringGetDatum((char *) s),
Int32GetDatum(c_as_utf8_len),
BoolGetDatum(false));
}
diff --git a/src/include/access/gin.h b/src/include/access/gin.h
index aacc665fdc..fff861e219 100644
--- a/src/include/access/gin.h
+++ b/src/include/access/gin.h
@@ -62,8 +62,18 @@ typedef char GinTernaryValue;
#define GIN_MAYBE 2 /* don't know if item is present / don't know
* if matches */
-#define DatumGetGinTernaryValue(X) ((GinTernaryValue)(X))
-#define GinTernaryValueGetDatum(X) ((Datum)(X))
+static inline GinTernaryValue
+DatumGetGinTernaryValue(Datum X)
+{
+ return (GinTernaryValue) X;
+}
+
+static inline Datum
+GinTernaryValueGetDatum(GinTernaryValue X)
+{
+ return (Datum) X;
+}
+
#define PG_RETURN_GIN_TERNARY_VALUE(x) return GinTernaryValueGetDatum(x)
/* GUC parameters */
diff --git a/src/include/funcapi.h b/src/include/funcapi.h
index dc3d819a1c..8c2521a591 100644
--- a/src/include/funcapi.h
+++ b/src/include/funcapi.h
@@ -204,7 +204,7 @@ extern TupleDesc build_function_result_tupdesc_t(HeapTuple procTuple);
* Datum HeapTupleHeaderGetDatum(HeapTupleHeader tuple) - convert a
* HeapTupleHeader to a Datum.
*
- * Macro declarations:
+ * Inline declarations:
* HeapTupleGetDatum(HeapTuple tuple) - convert a HeapTuple to a Datum.
*
* Obsolete routines and macros:
@@ -217,10 +217,6 @@ extern TupleDesc build_function_result_tupdesc_t(HeapTuple procTuple);
*----------
*/
-#define HeapTupleGetDatum(tuple) HeapTupleHeaderGetDatum((tuple)->t_data)
-/* obsolete version of above */
-#define TupleGetDatum(_slot, _tuple) HeapTupleGetDatum(_tuple)
-
extern TupleDesc RelationNameGetTupleDesc(const char *relname);
extern TupleDesc TypeGetTupleDesc(Oid typeoid, List *colaliases);
@@ -230,6 +226,14 @@ extern AttInMetadata *TupleDescGetAttInMetadata(TupleDesc tupdesc);
extern HeapTuple BuildTupleFromCStrings(AttInMetadata *attinmeta, char **values);
extern Datum HeapTupleHeaderGetDatum(HeapTupleHeader tuple);
+static inline Datum
+HeapTupleGetDatum(const HeapTupleData *tuple)
+{
+ return HeapTupleHeaderGetDatum(tuple->t_data);
+}
+/* obsolete version of above */
+#define TupleGetDatum(_slot, _tuple) HeapTupleGetDatum(_tuple)
+
/*----------
* Support for Set Returning Functions (SRFs)
diff --git a/src/include/postgres.h b/src/include/postgres.h
index 13903fa022..45c9f6563c 100644
--- a/src/include/postgres.h
+++ b/src/include/postgres.h
@@ -24,7 +24,7 @@
* section description
* ------- ------------------------------------------------
* 1) variable-length datatypes (TOAST support)
- * 2) Datum type + support macros
+ * 2) Datum type + support functions
* 3) miscellaneous
*
* NOTES
@@ -395,7 +395,7 @@ typedef struct
/* ----------------------------------------------------------------
- * Section 2: Datum type + support macros
+ * Section 2: Datum type + support functions
* ----------------------------------------------------------------
*/
@@ -405,7 +405,7 @@ typedef struct
*
* sizeof(Datum) == sizeof(void *) == 4 or 8
*
- * The macros below and the analogous macros for other types should be used to
+ * The functions below and the analogous functions for other types should be used to
* convert between a Datum and the appropriate C type.
*/
@@ -434,8 +434,11 @@ typedef struct NullableDatum
*
* Note: any nonzero value will be considered true.
*/
-
-#define DatumGetBool(X) ((bool) ((X) != 0))
+static inline bool
+DatumGetBool(Datum X)
+{
+ return (X != 0);
+}
/*
* BoolGetDatum
@@ -443,162 +446,231 @@ typedef struct NullableDatum
*
* Note: any nonzero value will be considered true.
*/
-
-#define BoolGetDatum(X) ((Datum) ((X) ? 1 : 0))
+static inline Datum
+BoolGetDatum(bool X)
+{
+ return (Datum) (X ? 1 : 0);
+}
/*
* DatumGetChar
* Returns character value of a datum.
*/
-
-#define DatumGetChar(X) ((char) (X))
+static inline char
+DatumGetChar(Datum X)
+{
+ return (char) X;
+}
/*
* CharGetDatum
* Returns datum representation for a character.
*/
-
-#define CharGetDatum(X) ((Datum) (X))
+static inline Datum
+CharGetDatum(char X)
+{
+ return (Datum) X;
+}
/*
* Int8GetDatum
* Returns datum representation for an 8-bit integer.
*/
-
-#define Int8GetDatum(X) ((Datum) (X))
+static inline Datum
+Int8GetDatum(int8 X)
+{
+ return (Datum) X;
+}
/*
* DatumGetUInt8
* Returns 8-bit unsigned integer value of a datum.
*/
-
-#define DatumGetUInt8(X) ((uint8) (X))
+static inline uint8
+DatumGetUInt8(Datum X)
+{
+ return (uint8) X;
+}
/*
* UInt8GetDatum
* Returns datum representation for an 8-bit unsigned integer.
*/
-
-#define UInt8GetDatum(X) ((Datum) (X))
+static inline Datum
+UInt8GetDatum(uint8 X)
+{
+ return (Datum) X;
+}
/*
* DatumGetInt16
* Returns 16-bit integer value of a datum.
*/
-
-#define DatumGetInt16(X) ((int16) (X))
+static inline int16
+DatumGetInt16(Datum X)
+{
+ return (int16) X;
+}
/*
* Int16GetDatum
* Returns datum representation for a 16-bit integer.
*/
-
-#define Int16GetDatum(X) ((Datum) (X))
+static inline Datum
+Int16GetDatum(int16 X)
+{
+ return (Datum) X;
+}
/*
* DatumGetUInt16
* Returns 16-bit unsigned integer value of a datum.
*/
-
-#define DatumGetUInt16(X) ((uint16) (X))
+static inline uint16
+DatumGetUInt16(Datum X)
+{
+ return (uint16) X;
+}
/*
* UInt16GetDatum
* Returns datum representation for a 16-bit unsigned integer.
*/
-
-#define UInt16GetDatum(X) ((Datum) (X))
+static inline Datum
+UInt16GetDatum(uint16 X)
+{
+ return (Datum) X;
+}
/*
* DatumGetInt32
* Returns 32-bit integer value of a datum.
*/
-
-#define DatumGetInt32(X) ((int32) (X))
+static inline int32
+DatumGetInt32(Datum X)
+{
+ return (int32) X;
+}
/*
* Int32GetDatum
* Returns datum representation for a 32-bit integer.
*/
-
-#define Int32GetDatum(X) ((Datum) (X))
+static inline Datum
+Int32GetDatum(int32 X)
+{
+ return (Datum) X;
+}
/*
* DatumGetUInt32
* Returns 32-bit unsigned integer value of a datum.
*/
-
-#define DatumGetUInt32(X) ((uint32) (X))
+static inline uint32
+DatumGetUInt32(Datum X)
+{
+ return (uint32) X;
+}
/*
* UInt32GetDatum
* Returns datum representation for a 32-bit unsigned integer.
*/
-
-#define UInt32GetDatum(X) ((Datum) (X))
+static inline Datum
+UInt32GetDatum(uint32 X)
+{
+ return (Datum) X;
+}
/*
* DatumGetObjectId
* Returns object identifier value of a datum.
*/
-
-#define DatumGetObjectId(X) ((Oid) (X))
+static inline Oid
+DatumGetObjectId(Datum X)
+{
+ return (Oid) X;
+}
/*
* ObjectIdGetDatum
* Returns datum representation for an object identifier.
*/
-
-#define ObjectIdGetDatum(X) ((Datum) (X))
+static inline Datum
+ObjectIdGetDatum(Oid X)
+{
+ return (Datum) X;
+}
/*
* DatumGetTransactionId
* Returns transaction identifier value of a datum.
*/
-
-#define DatumGetTransactionId(X) ((TransactionId) (X))
+static inline TransactionId
+DatumGetTransactionId(Datum X)
+{
+ return (TransactionId) X;
+}
/*
* TransactionIdGetDatum
* Returns datum representation for a transaction identifier.
*/
-
-#define TransactionIdGetDatum(X) ((Datum) (X))
+static inline Datum
+TransactionIdGetDatum(TransactionId X)
+{
+ return (Datum) X;
+}
/*
* MultiXactIdGetDatum
* Returns datum representation for a multixact identifier.
*/
-
-#define MultiXactIdGetDatum(X) ((Datum) (X))
+static inline Datum
+MultiXactIdGetDatum(MultiXactId X)
+{
+ return (Datum) X;
+}
/*
* DatumGetCommandId
* Returns command identifier value of a datum.
*/
-
-#define DatumGetCommandId(X) ((CommandId) (X))
+static inline CommandId
+DatumGetCommandId(Datum X)
+{
+ return (CommandId) X;
+}
/*
* CommandIdGetDatum
* Returns datum representation for a command identifier.
*/
-
-#define CommandIdGetDatum(X) ((Datum) (X))
+static inline Datum
+CommandIdGetDatum(CommandId X)
+{
+ return (Datum) X;
+}
/*
* DatumGetPointer
* Returns pointer value of a datum.
*/
-
-#define DatumGetPointer(X) ((Pointer) (X))
+static inline Pointer
+DatumGetPointer(Datum X)
+{
+ return (Pointer) X;
+}
/*
* PointerGetDatum
* Returns datum representation for a pointer.
*/
-
-#define PointerGetDatum(X) ((Datum) (X))
+static inline Datum
+PointerGetDatum(const void *X)
+{
+ return (Datum) X;
+}
/*
* DatumGetCString
@@ -607,8 +679,11 @@ typedef struct NullableDatum
* Note: C string is not a full-fledged Postgres type at present,
* but type input functions use this conversion for their inputs.
*/
-
-#define DatumGetCString(X) ((char *) DatumGetPointer(X))
+static inline char *
+DatumGetCString(Datum X)
+{
+ return (char *) DatumGetPointer(X);
+}
/*
* CStringGetDatum
@@ -619,15 +694,21 @@ typedef struct NullableDatum
* Note: CString is pass-by-reference; caller must ensure the pointed-to
* value has adequate lifetime.
*/
-
-#define CStringGetDatum(X) PointerGetDatum(X)
+static inline Datum
+CStringGetDatum(const char *X)
+{
+ return PointerGetDatum(X);
+}
/*
* DatumGetName
* Returns name value of a datum.
*/
-
-#define DatumGetName(X) ((Name) DatumGetPointer(X))
+static inline Name
+DatumGetName(Datum X)
+{
+ return (Name) DatumGetPointer(X);
+}
/*
* NameGetDatum
@@ -636,21 +717,27 @@ typedef struct NullableDatum
* Note: Name is pass-by-reference; caller must ensure the pointed-to
* value has adequate lifetime.
*/
-
-#define NameGetDatum(X) CStringGetDatum(NameStr(*(X)))
+static inline Datum
+NameGetDatum(const NameData *X)
+{
+ return CStringGetDatum(NameStr(*X));
+}
/*
* DatumGetInt64
* Returns 64-bit integer value of a datum.
*
- * Note: this macro hides whether int64 is pass by value or by reference.
+ * Note: this function hides whether int64 is pass by value or by reference.
*/
-
+static inline int64
+DatumGetInt64(Datum X)
+{
#ifdef USE_FLOAT8_BYVAL
-#define DatumGetInt64(X) ((int64) (X))
+ return (int64) X;
#else
-#define DatumGetInt64(X) (* ((int64 *) DatumGetPointer(X)))
+ return *((int64 *) DatumGetPointer(X));
#endif
+}
/*
* Int64GetDatum
@@ -659,25 +746,32 @@ typedef struct NullableDatum
* Note: if int64 is pass by reference, this function returns a reference
* to palloc'd space.
*/
-
#ifdef USE_FLOAT8_BYVAL
-#define Int64GetDatum(X) ((Datum) (X))
+static inline Datum
+Int64GetDatum(int64 X)
+{
+ return (Datum) X;
+}
#else
extern Datum Int64GetDatum(int64 X);
#endif
+
/*
* DatumGetUInt64
* Returns 64-bit unsigned integer value of a datum.
*
- * Note: this macro hides whether int64 is pass by value or by reference.
+ * Note: this function hides whether int64 is pass by value or by reference.
*/
-
+static inline uint64
+DatumGetUInt64(Datum X)
+{
#ifdef USE_FLOAT8_BYVAL
-#define DatumGetUInt64(X) ((uint64) (X))
+ return (uint64) X;
#else
-#define DatumGetUInt64(X) (* ((uint64 *) DatumGetPointer(X)))
+ return *((uint64 *) DatumGetPointer(X));
#endif
+}
/*
* UInt64GetDatum
@@ -686,12 +780,15 @@ extern Datum Int64GetDatum(int64 X);
* Note: if int64 is pass by reference, this function returns a reference
* to palloc'd space.
*/
-
+static inline Datum
+UInt64GetDatum(uint64 X)
+{
#ifdef USE_FLOAT8_BYVAL
-#define UInt64GetDatum(X) ((Datum) (X))
+ return (Datum) X;
#else
-#define UInt64GetDatum(X) Int64GetDatum((int64) (X))
+ return Int64GetDatum((int64) X);
#endif
+}
/*
* Float <-> Datum conversions
@@ -739,13 +836,12 @@ Float4GetDatum(float4 X)
* DatumGetFloat8
* Returns 8-byte floating point value of a datum.
*
- * Note: this macro hides whether float8 is pass by value or by reference.
+ * Note: this function hides whether float8 is pass by value or by reference.
*/
-
-#ifdef USE_FLOAT8_BYVAL
static inline float8
DatumGetFloat8(Datum X)
{
+#ifdef USE_FLOAT8_BYVAL
union
{
int64 value;
@@ -754,10 +850,10 @@ DatumGetFloat8(Datum X)
myunion.value = DatumGetInt64(X);
return myunion.retval;
-}
#else
-#define DatumGetFloat8(X) (* ((float8 *) DatumGetPointer(X)))
+ return *((float8 *) DatumGetPointer(X));
#endif
+}
/*
* Float8GetDatum
@@ -766,7 +862,6 @@ DatumGetFloat8(Datum X)
* Note: if float8 is pass by reference, this function returns a reference
* to palloc'd space.
*/
-
#ifdef USE_FLOAT8_BYVAL
static inline Datum
Float8GetDatum(float8 X)
@@ -789,22 +884,34 @@ extern Datum Float8GetDatum(float8 X);
* Int64GetDatumFast
* Float8GetDatumFast
*
- * These macros are intended to allow writing code that does not depend on
+ * These functions are intended to allow writing code that does not depend on
* whether int64 and float8 are pass-by-reference types, while not
* sacrificing performance when they are. The argument must be a variable
* that will exist and have the same value for as long as the Datum is needed.
* In the pass-by-ref case, the address of the variable is taken to use as
* the Datum. In the pass-by-val case, these will be the same as the non-Fast
- * macros.
+ * functions.
*/
+static inline Datum
+Int64GetDatumFast(int64 X)
+{
+#ifdef USE_FLOAT8_BYVAL
+ return Int64GetDatum(X);
+#else
+ return PointerGetDatum(&X);
+#endif
+}
+
+static inline Datum
+Float8GetDatumFast(float8 X)
+{
#ifdef USE_FLOAT8_BYVAL
-#define Int64GetDatumFast(X) Int64GetDatum(X)
-#define Float8GetDatumFast(X) Float8GetDatum(X)
+ return Float8GetDatum(X);
#else
-#define Int64GetDatumFast(X) PointerGetDatum(&(X))
-#define Float8GetDatumFast(X) PointerGetDatum(&(X))
+ return PointerGetDatum(&X);
#endif
+}
/* ----------------------------------------------------------------
diff --git a/src/include/tsearch/ts_type.h b/src/include/tsearch/ts_type.h
index 689b2d1cfb..f1ec84702d 100644
--- a/src/include/tsearch/ts_type.h
+++ b/src/include/tsearch/ts_type.h
@@ -111,12 +111,27 @@ typedef TSVectorData *TSVector;
#define POSDATAPTR(x,e) (_POSVECPTR(x,e)->pos)
/*
- * fmgr interface macros
+ * fmgr interface functions
*/
-#define DatumGetTSVector(X) ((TSVector) PG_DETOAST_DATUM(X))
-#define DatumGetTSVectorCopy(X) ((TSVector) PG_DETOAST_DATUM_COPY(X))
-#define TSVectorGetDatum(X) PointerGetDatum(X)
+static inline TSVector
+DatumGetTSVector(Datum X)
+{
+ return (TSVector) PG_DETOAST_DATUM(X);
+}
+
+static inline TSVector
+DatumGetTSVectorCopy(Datum X)
+{
+ return (TSVector) PG_DETOAST_DATUM_COPY(X);
+}
+
+static inline Datum
+TSVectorGetDatum(const TSVectorData *X)
+{
+ return PointerGetDatum(X);
+}
+
#define PG_GETARG_TSVECTOR(n) DatumGetTSVector(PG_GETARG_DATUM(n))
#define PG_GETARG_TSVECTOR_COPY(n) DatumGetTSVectorCopy(PG_GETARG_DATUM(n))
#define PG_RETURN_TSVECTOR(x) return TSVectorGetDatum(x)
@@ -227,14 +242,29 @@ typedef TSQueryData *TSQuery;
#define GETOPERAND(x) ( (char*)GETQUERY(x) + ((TSQuery)(x))->size * sizeof(QueryItem) )
/*
- * fmgr interface macros
+ * fmgr interface functions
* Note, TSQuery type marked as plain storage, so it can't be toasted
* but PG_DETOAST_DATUM_COPY is used for simplicity
*/
-#define DatumGetTSQuery(X) ((TSQuery) DatumGetPointer(X))
-#define DatumGetTSQueryCopy(X) ((TSQuery) PG_DETOAST_DATUM_COPY(X))
-#define TSQueryGetDatum(X) PointerGetDatum(X)
+static inline TSQuery
+DatumGetTSQuery(Datum X)
+{
+ return (TSQuery) DatumGetPointer(X);
+}
+
+static inline TSQuery
+DatumGetTSQueryCopy(Datum X)
+{
+ return (TSQuery) PG_DETOAST_DATUM_COPY(X);
+}
+
+static inline Datum
+TSQueryGetDatum(const TSQueryData *X)
+{
+ return PointerGetDatum(X);
+}
+
#define PG_GETARG_TSQUERY(n) DatumGetTSQuery(PG_GETARG_DATUM(n))
#define PG_GETARG_TSQUERY_COPY(n) DatumGetTSQueryCopy(PG_GETARG_DATUM(n))
#define PG_RETURN_TSQUERY(x) return TSQueryGetDatum(x)
diff --git a/src/include/tsearch/ts_utils.h b/src/include/tsearch/ts_utils.h
index c36c711dae..26cc415aa8 100644
--- a/src/include/tsearch/ts_utils.h
+++ b/src/include/tsearch/ts_utils.h
@@ -243,8 +243,18 @@ typedef uint64 TSQuerySign;
#define TSQS_SIGLEN (sizeof(TSQuerySign)*BITS_PER_BYTE)
-#define TSQuerySignGetDatum(X) Int64GetDatum((int64) (X))
-#define DatumGetTSQuerySign(X) ((TSQuerySign) DatumGetInt64(X))
+static inline Datum
+TSQuerySignGetDatum(TSQuerySign X)
+{
+ return Int64GetDatum((int64) X);
+}
+
+static inline TSQuerySign
+DatumGetTSQuerySign(Datum X)
+{
+ return (TSQuerySign) DatumGetInt64(X);
+}
+
#define PG_RETURN_TSQUERYSIGN(X) return TSQuerySignGetDatum(X)
#define PG_GETARG_TSQUERYSIGN(n) DatumGetTSQuerySign(PG_GETARG_DATUM(n))
diff --git a/src/include/utils/cash.h b/src/include/utils/cash.h
index 2e332d83b1..55d45fadd4 100644
--- a/src/include/utils/cash.h
+++ b/src/include/utils/cash.h
@@ -17,8 +17,18 @@
typedef int64 Cash;
/* Cash is pass-by-reference if and only if int64 is */
-#define DatumGetCash(X) ((Cash) DatumGetInt64(X))
-#define CashGetDatum(X) Int64GetDatum(X)
+static inline Cash
+DatumGetCash(Datum X)
+{
+ return DatumGetInt64(X);
+}
+
+static inline Datum
+CashGetDatum(Cash X)
+{
+ return Int64GetDatum(X);
+}
+
#define PG_GETARG_CASH(n) DatumGetCash(PG_GETARG_DATUM(n))
#define PG_RETURN_CASH(x) return CashGetDatum(x)
diff --git a/src/include/utils/date.h b/src/include/utils/date.h
index 3991da41f0..0bbe889128 100644
--- a/src/include/utils/date.h
+++ b/src/include/utils/date.h
@@ -45,18 +45,46 @@ typedef struct
#define MAX_TIME_PRECISION 6
/*
- * Macros for fmgr-callable functions.
+ * Functions for fmgr-callable functions.
*
* For TimeADT, we make use of the same support routines as for int64.
* Therefore TimeADT is pass-by-reference if and only if int64 is!
*/
-#define DatumGetDateADT(X) ((DateADT) DatumGetInt32(X))
-#define DatumGetTimeADT(X) ((TimeADT) DatumGetInt64(X))
-#define DatumGetTimeTzADTP(X) ((TimeTzADT *) DatumGetPointer(X))
+static inline DateADT
+DatumGetDateADT(Datum X)
+{
+ return (DateADT) DatumGetInt32(X);
+}
+
+static inline TimeADT
+DatumGetTimeADT(Datum X)
+{
+ return (TimeADT) DatumGetInt64(X);
+}
+
+static inline TimeTzADT *
+DatumGetTimeTzADTP(Datum X)
+{
+ return (TimeTzADT *) DatumGetPointer(X);
+}
-#define DateADTGetDatum(X) Int32GetDatum(X)
-#define TimeADTGetDatum(X) Int64GetDatum(X)
-#define TimeTzADTPGetDatum(X) PointerGetDatum(X)
+static inline Datum
+DateADTGetDatum(DateADT X)
+{
+ return Int32GetDatum(X);
+}
+
+static inline Datum
+TimeADTGetDatum(TimeADT X)
+{
+ return Int64GetDatum(X);
+}
+
+static inline Datum
+TimeTzADTPGetDatum(const TimeTzADT *X)
+{
+ return PointerGetDatum(X);
+}
#define PG_GETARG_DATEADT(n) DatumGetDateADT(PG_GETARG_DATUM(n))
#define PG_GETARG_TIMEADT(n) DatumGetTimeADT(PG_GETARG_DATUM(n))
diff --git a/src/include/utils/expandeddatum.h b/src/include/utils/expandeddatum.h
index ffdb0c45bd..c4818eb054 100644
--- a/src/include/utils/expandeddatum.h
+++ b/src/include/utils/expandeddatum.h
@@ -133,8 +133,17 @@ struct ExpandedObjectHeader
* (More of these might be worth inlining later.)
*/
-#define EOHPGetRWDatum(eohptr) PointerGetDatum((eohptr)->eoh_rw_ptr)
-#define EOHPGetRODatum(eohptr) PointerGetDatum((eohptr)->eoh_ro_ptr)
+static inline Datum
+EOHPGetRWDatum(const struct ExpandedObjectHeader *eohptr)
+{
+ return PointerGetDatum(eohptr->eoh_rw_ptr);
+}
+
+static inline Datum
+EOHPGetRODatum(const struct ExpandedObjectHeader *eohptr)
+{
+ return PointerGetDatum(eohptr->eoh_ro_ptr);
+}
/* Does the Datum represent a writable expanded object? */
#define DatumIsReadWriteExpandedObject(d, isnull, typlen) \
diff --git a/src/include/utils/expandedrecord.h b/src/include/utils/expandedrecord.h
index be60e2ce53..aaf5b6bfd1 100644
--- a/src/include/utils/expandedrecord.h
+++ b/src/include/utils/expandedrecord.h
@@ -138,10 +138,20 @@ typedef struct ExpandedRecordHeader
MemoryContextCallback er_mcb;
} ExpandedRecordHeader;
-/* fmgr macros for expanded record objects */
+/* fmgr functions and macros for expanded record objects */
+static inline Datum
+ExpandedRecordGetDatum(const ExpandedRecordHeader *erh)
+{
+ return EOHPGetRWDatum(&erh->hdr);
+}
+
+static inline Datum
+ExpandedRecordGetRODatum(const ExpandedRecordHeader *erh)
+{
+ return EOHPGetRODatum(&erh->hdr);
+}
+
#define PG_GETARG_EXPANDED_RECORD(n) DatumGetExpandedRecord(PG_GETARG_DATUM(n))
-#define ExpandedRecordGetDatum(erh) EOHPGetRWDatum(&(erh)->hdr)
-#define ExpandedRecordGetRODatum(erh) EOHPGetRODatum(&(erh)->hdr)
#define PG_RETURN_EXPANDED_RECORD(x) PG_RETURN_DATUM(ExpandedRecordGetDatum(x))
/* assorted other macros */
diff --git a/src/include/utils/geo_decls.h b/src/include/utils/geo_decls.h
index 719030777e..05726778ad 100644
--- a/src/include/utils/geo_decls.h
+++ b/src/include/utils/geo_decls.h
@@ -166,48 +166,112 @@ typedef struct
} CIRCLE;
/*
- * fmgr interface macros
+ * fmgr interface functions
*
* Path and Polygon are toastable varlena types, the others are just
* fixed-size pass-by-reference types.
*/
-#define DatumGetPointP(X) ((Point *) DatumGetPointer(X))
-#define PointPGetDatum(X) PointerGetDatum(X)
+static inline Point *
+DatumGetPointP(Datum X)
+{
+ return (Point *) DatumGetPointer(X);
+}
+static inline Datum
+PointPGetDatum(const Point *X)
+{
+ return PointerGetDatum(X);
+}
#define PG_GETARG_POINT_P(n) DatumGetPointP(PG_GETARG_DATUM(n))
#define PG_RETURN_POINT_P(x) return PointPGetDatum(x)
-#define DatumGetLsegP(X) ((LSEG *) DatumGetPointer(X))
-#define LsegPGetDatum(X) PointerGetDatum(X)
+static inline LSEG *
+DatumGetLsegP(Datum X)
+{
+ return (LSEG *) DatumGetPointer(X);
+}
+static inline Datum
+LsegPGetDatum(const LSEG *X)
+{
+ return PointerGetDatum(X);
+}
#define PG_GETARG_LSEG_P(n) DatumGetLsegP(PG_GETARG_DATUM(n))
#define PG_RETURN_LSEG_P(x) return LsegPGetDatum(x)
-#define DatumGetPathP(X) ((PATH *) PG_DETOAST_DATUM(X))
-#define DatumGetPathPCopy(X) ((PATH *) PG_DETOAST_DATUM_COPY(X))
-#define PathPGetDatum(X) PointerGetDatum(X)
+static inline PATH *
+DatumGetPathP(Datum X)
+{
+ return (PATH *) PG_DETOAST_DATUM(X);
+}
+static inline PATH *
+DatumGetPathPCopy(Datum X)
+{
+ return (PATH *) PG_DETOAST_DATUM_COPY(X);
+}
+static inline Datum
+PathPGetDatum(const PATH *X)
+{
+ return PointerGetDatum(X);
+}
#define PG_GETARG_PATH_P(n) DatumGetPathP(PG_GETARG_DATUM(n))
#define PG_GETARG_PATH_P_COPY(n) DatumGetPathPCopy(PG_GETARG_DATUM(n))
#define PG_RETURN_PATH_P(x) return PathPGetDatum(x)
-#define DatumGetLineP(X) ((LINE *) DatumGetPointer(X))
-#define LinePGetDatum(X) PointerGetDatum(X)
+static inline LINE *
+DatumGetLineP(Datum X)
+{
+ return (LINE *) DatumGetPointer(X);
+}
+static inline Datum
+LinePGetDatum(const LINE *X)
+{
+ return PointerGetDatum(X);
+}
#define PG_GETARG_LINE_P(n) DatumGetLineP(PG_GETARG_DATUM(n))
#define PG_RETURN_LINE_P(x) return LinePGetDatum(x)
-#define DatumGetBoxP(X) ((BOX *) DatumGetPointer(X))
-#define BoxPGetDatum(X) PointerGetDatum(X)
+static inline BOX *
+DatumGetBoxP(Datum X)
+{
+ return (BOX *) DatumGetPointer(X);
+}
+static inline Datum
+BoxPGetDatum(const BOX *X)
+{
+ return PointerGetDatum(X);
+}
#define PG_GETARG_BOX_P(n) DatumGetBoxP(PG_GETARG_DATUM(n))
#define PG_RETURN_BOX_P(x) return BoxPGetDatum(x)
-#define DatumGetPolygonP(X) ((POLYGON *) PG_DETOAST_DATUM(X))
-#define DatumGetPolygonPCopy(X) ((POLYGON *) PG_DETOAST_DATUM_COPY(X))
-#define PolygonPGetDatum(X) PointerGetDatum(X)
+static inline POLYGON *
+DatumGetPolygonP(Datum X)
+{
+ return (POLYGON *) PG_DETOAST_DATUM(X);
+}
+static inline POLYGON *
+DatumGetPolygonPCopy(Datum X)
+{
+ return (POLYGON *) PG_DETOAST_DATUM_COPY(X);
+}
+static inline Datum
+PolygonPGetDatum(const POLYGON *X)
+{
+ return PointerGetDatum(X);
+}
#define PG_GETARG_POLYGON_P(n) DatumGetPolygonP(PG_GETARG_DATUM(n))
#define PG_GETARG_POLYGON_P_COPY(n) DatumGetPolygonPCopy(PG_GETARG_DATUM(n))
#define PG_RETURN_POLYGON_P(x) return PolygonPGetDatum(x)
-#define DatumGetCircleP(X) ((CIRCLE *) DatumGetPointer(X))
-#define CirclePGetDatum(X) PointerGetDatum(X)
+static inline CIRCLE *
+DatumGetCircleP(Datum X)
+{
+ return (CIRCLE *) DatumGetPointer(X);
+}
+static inline Datum
+CirclePGetDatum(const CIRCLE *X)
+{
+ return PointerGetDatum(X);
+}
#define PG_GETARG_CIRCLE_P(n) DatumGetCircleP(PG_GETARG_DATUM(n))
#define PG_RETURN_CIRCLE_P(x) return CirclePGetDatum(x)
diff --git a/src/include/utils/inet.h b/src/include/utils/inet.h
index b1ec9723df..5438cfaba5 100644
--- a/src/include/utils/inet.h
+++ b/src/include/utils/inet.h
@@ -119,23 +119,58 @@ typedef struct macaddr8
/*
* fmgr interface macros
*/
-#define DatumGetInetPP(X) ((inet *) PG_DETOAST_DATUM_PACKED(X))
-#define InetPGetDatum(X) PointerGetDatum(X)
+static inline inet *
+DatumGetInetPP(Datum X)
+{
+ return (inet *) PG_DETOAST_DATUM_PACKED(X);
+}
+
+static inline Datum
+InetPGetDatum(const inet *X)
+{
+ return PointerGetDatum(X);
+}
+
#define PG_GETARG_INET_PP(n) DatumGetInetPP(PG_GETARG_DATUM(n))
#define PG_RETURN_INET_P(x) return InetPGetDatum(x)
+
/* obsolescent variants */
-#define DatumGetInetP(X) ((inet *) PG_DETOAST_DATUM(X))
+static inline inet *
+DatumGetInetP(Datum X)
+{
+ return (inet *) PG_DETOAST_DATUM(X);
+}
#define PG_GETARG_INET_P(n) DatumGetInetP(PG_GETARG_DATUM(n))
/* macaddr is a fixed-length pass-by-reference datatype */
-#define DatumGetMacaddrP(X) ((macaddr *) DatumGetPointer(X))
-#define MacaddrPGetDatum(X) PointerGetDatum(X)
+static inline macaddr *
+DatumGetMacaddrP(Datum X)
+{
+ return (macaddr *) DatumGetPointer(X);
+}
+
+static inline Datum
+MacaddrPGetDatum(const macaddr *X)
+{
+ return PointerGetDatum(X);
+}
+
#define PG_GETARG_MACADDR_P(n) DatumGetMacaddrP(PG_GETARG_DATUM(n))
#define PG_RETURN_MACADDR_P(x) return MacaddrPGetDatum(x)
/* macaddr8 is a fixed-length pass-by-reference datatype */
-#define DatumGetMacaddr8P(X) ((macaddr8 *) DatumGetPointer(X))
-#define Macaddr8PGetDatum(X) PointerGetDatum(X)
+static inline macaddr8 *
+DatumGetMacaddr8P(Datum X)
+{
+ return (macaddr8 *) DatumGetPointer(X);
+}
+
+static inline Datum
+Macaddr8PGetDatum(const macaddr8 *X)
+{
+ return PointerGetDatum(X);
+}
+
#define PG_GETARG_MACADDR8_P(n) DatumGetMacaddr8P(PG_GETARG_DATUM(n))
#define PG_RETURN_MACADDR8_P(x) return Macaddr8PGetDatum(x)
diff --git a/src/include/utils/jsonb.h b/src/include/utils/jsonb.h
index bae466b523..536d00b3b4 100644
--- a/src/include/utils/jsonb.h
+++ b/src/include/utils/jsonb.h
@@ -67,14 +67,6 @@ typedef enum
#define JGINFLAG_HASHED 0x10 /* OR'd into flag if value was hashed */
#define JGIN_MAXLENGTH 125 /* max length of text part before hashing */
-/* Convenience macros */
-#define DatumGetJsonbP(d) ((Jsonb *) PG_DETOAST_DATUM(d))
-#define DatumGetJsonbPCopy(d) ((Jsonb *) PG_DETOAST_DATUM_COPY(d))
-#define JsonbPGetDatum(p) PointerGetDatum(p)
-#define PG_GETARG_JSONB_P(x) DatumGetJsonbP(PG_GETARG_DATUM(x))
-#define PG_GETARG_JSONB_P_COPY(x) DatumGetJsonbPCopy(PG_GETARG_DATUM(x))
-#define PG_RETURN_JSONB_P(x) PG_RETURN_POINTER(x)
-
typedef struct JsonbPair JsonbPair;
typedef struct JsonbValue JsonbValue;
@@ -393,6 +385,29 @@ typedef enum /* type categories for datum_to_jsonb */
JSONBTYPE_OTHER /* all else */
} JsonbTypeCategory;
+/* Convenience macros */
+static inline Jsonb *
+DatumGetJsonbP(Datum d)
+{
+ return (Jsonb *) PG_DETOAST_DATUM(d);
+}
+
+static inline Jsonb *
+DatumGetJsonbPCopy(Datum d)
+{
+ return (Jsonb *) PG_DETOAST_DATUM_COPY(d);
+}
+
+static inline Datum
+JsonbPGetDatum(const Jsonb *p)
+{
+ return PointerGetDatum(p);
+}
+
+#define PG_GETARG_JSONB_P(x) DatumGetJsonbP(PG_GETARG_DATUM(x))
+#define PG_GETARG_JSONB_P_COPY(x) DatumGetJsonbPCopy(PG_GETARG_DATUM(x))
+#define PG_RETURN_JSONB_P(x) PG_RETURN_POINTER(x)
+
/* Support functions */
extern uint32 getJsonbOffset(const JsonbContainer *jc, int index);
extern uint32 getJsonbLength(const JsonbContainer *jc, int index);
diff --git a/src/include/utils/jsonpath.h b/src/include/utils/jsonpath.h
index 8e79b8dc9f..ea32a8f072 100644
--- a/src/include/utils/jsonpath.h
+++ b/src/include/utils/jsonpath.h
@@ -32,8 +32,18 @@ typedef struct
#define JSONPATH_LAX (0x80000000)
#define JSONPATH_HDRSZ (offsetof(JsonPath, data))
-#define DatumGetJsonPathP(d) ((JsonPath *) DatumGetPointer(PG_DETOAST_DATUM(d)))
-#define DatumGetJsonPathPCopy(d) ((JsonPath *) DatumGetPointer(PG_DETOAST_DATUM_COPY(d)))
+static inline JsonPath *
+DatumGetJsonPathP(Datum d)
+{
+ return (JsonPath *) PG_DETOAST_DATUM(d);
+}
+
+static inline JsonPath *
+DatumGetJsonPathPCopy(Datum d)
+{
+ return (JsonPath *) PG_DETOAST_DATUM_COPY(d);
+}
+
#define PG_GETARG_JSONPATH_P(x) DatumGetJsonPathP(PG_GETARG_DATUM(x))
#define PG_GETARG_JSONPATH_P_COPY(x) DatumGetJsonPathPCopy(PG_GETARG_DATUM(x))
#define PG_RETURN_JSONPATH_P(p) PG_RETURN_POINTER(p)
diff --git a/src/include/utils/multirangetypes.h b/src/include/utils/multirangetypes.h
index 915330f990..23bce0005f 100644
--- a/src/include/utils/multirangetypes.h
+++ b/src/include/utils/multirangetypes.h
@@ -42,11 +42,26 @@ typedef struct
#define MultirangeIsEmpty(mr) ((mr)->rangeCount == 0)
/*
- * fmgr macros for multirange type objects
+ * fmgr functions for multirange type objects
*/
-#define DatumGetMultirangeTypeP(X) ((MultirangeType *) PG_DETOAST_DATUM(X))
-#define DatumGetMultirangeTypePCopy(X) ((MultirangeType *) PG_DETOAST_DATUM_COPY(X))
-#define MultirangeTypePGetDatum(X) PointerGetDatum(X)
+static inline MultirangeType *
+DatumGetMultirangeTypeP(Datum X)
+{
+ return (MultirangeType *) PG_DETOAST_DATUM(X);
+}
+
+static inline MultirangeType *
+DatumGetMultirangeTypePCopy(Datum X)
+{
+ return (MultirangeType *) PG_DETOAST_DATUM_COPY(X);
+}
+
+static inline Datum
+MultirangeTypePGetDatum(const MultirangeType *X)
+{
+ return PointerGetDatum(X);
+}
+
#define PG_GETARG_MULTIRANGE_P(n) DatumGetMultirangeTypeP(PG_GETARG_DATUM(n))
#define PG_GETARG_MULTIRANGE_P_COPY(n) DatumGetMultirangeTypePCopy(PG_GETARG_DATUM(n))
#define PG_RETURN_MULTIRANGE_P(x) return MultirangeTypePGetDatum(x)
diff --git a/src/include/utils/numeric.h b/src/include/utils/numeric.h
index 3caa74dfe7..88c62c5348 100644
--- a/src/include/utils/numeric.h
+++ b/src/include/utils/numeric.h
@@ -56,9 +56,24 @@ typedef struct NumericData *Numeric;
* fmgr interface macros
*/
-#define DatumGetNumeric(X) ((Numeric) PG_DETOAST_DATUM(X))
-#define DatumGetNumericCopy(X) ((Numeric) PG_DETOAST_DATUM_COPY(X))
-#define NumericGetDatum(X) PointerGetDatum(X)
+static inline Numeric
+DatumGetNumeric(Datum X)
+{
+ return (Numeric) PG_DETOAST_DATUM(X);
+}
+
+static inline Numeric
+DatumGetNumericCopy(Datum X)
+{
+ return (Numeric) PG_DETOAST_DATUM_COPY(X);
+}
+
+static inline Datum
+NumericGetDatum(Numeric X)
+{
+ return PointerGetDatum(X);
+}
+
#define PG_GETARG_NUMERIC(n) DatumGetNumeric(PG_GETARG_DATUM(n))
#define PG_GETARG_NUMERIC_COPY(n) DatumGetNumericCopy(PG_GETARG_DATUM(n))
#define PG_RETURN_NUMERIC(x) return NumericGetDatum(x)
diff --git a/src/include/utils/pg_lsn.h b/src/include/utils/pg_lsn.h
index 7b708f1073..5313afbfe2 100644
--- a/src/include/utils/pg_lsn.h
+++ b/src/include/utils/pg_lsn.h
@@ -18,8 +18,17 @@
#include "access/xlogdefs.h"
#include "fmgr.h"
-#define DatumGetLSN(X) ((XLogRecPtr) DatumGetInt64(X))
-#define LSNGetDatum(X) (Int64GetDatum((int64) (X)))
+static inline XLogRecPtr
+DatumGetLSN(Datum X)
+{
+ return (XLogRecPtr) DatumGetInt64(X);
+}
+
+static inline Datum
+LSNGetDatum(XLogRecPtr X)
+{
+ return Int64GetDatum((int64) X);
+}
#define PG_GETARG_LSN(n) DatumGetLSN(PG_GETARG_DATUM(n))
#define PG_RETURN_LSN(x) return LSNGetDatum(x)
diff --git a/src/include/utils/rangetypes.h b/src/include/utils/rangetypes.h
index 993fad4fc2..661e4892ec 100644
--- a/src/include/utils/rangetypes.h
+++ b/src/include/utils/rangetypes.h
@@ -68,11 +68,26 @@ typedef struct
} RangeBound;
/*
- * fmgr macros for range type objects
+ * fmgr functions for range type objects
*/
-#define DatumGetRangeTypeP(X) ((RangeType *) PG_DETOAST_DATUM(X))
-#define DatumGetRangeTypePCopy(X) ((RangeType *) PG_DETOAST_DATUM_COPY(X))
-#define RangeTypePGetDatum(X) PointerGetDatum(X)
+static inline RangeType *
+DatumGetRangeTypeP(Datum X)
+{
+ return (RangeType *) PG_DETOAST_DATUM(X);
+}
+
+static inline RangeType *
+DatumGetRangeTypePCopy(Datum X)
+{
+ return (RangeType *) PG_DETOAST_DATUM_COPY(X);
+}
+
+static inline Datum
+RangeTypePGetDatum(const RangeType *X)
+{
+ return PointerGetDatum(X);
+}
+
#define PG_GETARG_RANGE_P(n) DatumGetRangeTypeP(PG_GETARG_DATUM(n))
#define PG_GETARG_RANGE_P_COPY(n) DatumGetRangeTypePCopy(PG_GETARG_DATUM(n))
#define PG_RETURN_RANGE_P(x) return RangeTypePGetDatum(x)
diff --git a/src/include/utils/timestamp.h b/src/include/utils/timestamp.h
index edf3a97318..3602638bdc 100644
--- a/src/include/utils/timestamp.h
+++ b/src/include/utils/timestamp.h
@@ -19,18 +19,46 @@
/*
- * Macros for fmgr-callable functions.
+ * Functions for fmgr-callable functions.
*
* For Timestamp, we make use of the same support routines as for int64.
* Therefore Timestamp is pass-by-reference if and only if int64 is!
*/
-#define DatumGetTimestamp(X) ((Timestamp) DatumGetInt64(X))
-#define DatumGetTimestampTz(X) ((TimestampTz) DatumGetInt64(X))
-#define DatumGetIntervalP(X) ((Interval *) DatumGetPointer(X))
-
-#define TimestampGetDatum(X) Int64GetDatum(X)
-#define TimestampTzGetDatum(X) Int64GetDatum(X)
-#define IntervalPGetDatum(X) PointerGetDatum(X)
+static inline Timestamp
+DatumGetTimestamp(Datum X)
+{
+ return (Timestamp) DatumGetInt64(X);
+}
+
+static inline TimestampTz
+DatumGetTimestampTz(Datum X)
+{
+ return (TimestampTz) DatumGetInt64(X);
+}
+
+static inline Interval *
+DatumGetIntervalP(Datum X)
+{
+ return (Interval *) DatumGetPointer(X);
+}
+
+static inline Datum
+TimestampGetDatum(Timestamp X)
+{
+ return Int64GetDatum(X);
+}
+
+static inline Datum
+TimestampTzGetDatum(TimestampTz X)
+{
+ return Int64GetDatum(X);
+}
+
+static inline Datum
+IntervalPGetDatum(const Interval *X)
+{
+ return PointerGetDatum(X);
+}
#define PG_GETARG_TIMESTAMP(n) DatumGetTimestamp(PG_GETARG_DATUM(n))
#define PG_GETARG_TIMESTAMPTZ(n) DatumGetTimestampTz(PG_GETARG_DATUM(n))
diff --git a/src/include/utils/uuid.h b/src/include/utils/uuid.h
index 0029da4e7f..69f4332d0f 100644
--- a/src/include/utils/uuid.h
+++ b/src/include/utils/uuid.h
@@ -23,9 +23,20 @@ typedef struct pg_uuid_t
} pg_uuid_t;
/* fmgr interface macros */
-#define UUIDPGetDatum(X) PointerGetDatum(X)
+static inline Datum
+UUIDPGetDatum(const pg_uuid_t *X)
+{
+ return PointerGetDatum(X);
+}
+
#define PG_RETURN_UUID_P(X) return UUIDPGetDatum(X)
-#define DatumGetUUIDP(X) ((pg_uuid_t *) DatumGetPointer(X))
+
+static inline pg_uuid_t *
+DatumGetUUIDP(Datum X)
+{
+ return (pg_uuid_t *) DatumGetPointer(X);
+}
+
#define PG_GETARG_UUID_P(X) DatumGetUUIDP(PG_GETARG_DATUM(X))
#endif /* UUID_H */
diff --git a/src/include/utils/varbit.h b/src/include/utils/varbit.h
index 039ba860cf..e65e7f65e7 100644
--- a/src/include/utils/varbit.h
+++ b/src/include/utils/varbit.h
@@ -41,9 +41,24 @@ typedef struct
* BIT and BIT VARYING are toastable varlena types. They are the same
* as far as representation goes, so we just have one set of macros.
*/
-#define DatumGetVarBitP(X) ((VarBit *) PG_DETOAST_DATUM(X))
-#define DatumGetVarBitPCopy(X) ((VarBit *) PG_DETOAST_DATUM_COPY(X))
-#define VarBitPGetDatum(X) PointerGetDatum(X)
+static inline VarBit *
+DatumGetVarBitP(Datum X)
+{
+ return (VarBit *) PG_DETOAST_DATUM(X);
+}
+
+static inline VarBit *
+DatumGetVarBitPCopy(Datum X)
+{
+ return (VarBit *) PG_DETOAST_DATUM_COPY(X);
+}
+
+static inline Datum
+VarBitPGetDatum(const VarBit *X)
+{
+ return PointerGetDatum(X);
+}
+
#define PG_GETARG_VARBIT_P(n) DatumGetVarBitP(PG_GETARG_DATUM(n))
#define PG_GETARG_VARBIT_P_COPY(n) DatumGetVarBitPCopy(PG_GETARG_DATUM(n))
#define PG_RETURN_VARBIT_P(x) return VarBitPGetDatum(x)
diff --git a/src/include/utils/xid8.h b/src/include/utils/xid8.h
index b702fc1a91..9c5ce241db 100644
--- a/src/include/utils/xid8.h
+++ b/src/include/utils/xid8.h
@@ -14,8 +14,18 @@
#include "access/transam.h"
-#define DatumGetFullTransactionId(X) (FullTransactionIdFromU64(DatumGetUInt64(X)))
-#define FullTransactionIdGetDatum(X) (UInt64GetDatum(U64FromFullTransactionId(X)))
+static inline FullTransactionId
+DatumGetFullTransactionId(Datum X)
+{
+ return FullTransactionIdFromU64(DatumGetUInt64(X));
+}
+
+static inline Datum
+FullTransactionIdGetDatum(FullTransactionId X)
+{
+ return UInt64GetDatum(U64FromFullTransactionId(X));
+}
+
#define PG_GETARG_FULLTRANSACTIONID(X) DatumGetFullTransactionId(PG_GETARG_DATUM(X))
#define PG_RETURN_FULLTRANSACTIONID(X) return FullTransactionIdGetDatum(X)
diff --git a/src/include/utils/xml.h b/src/include/utils/xml.h
index 6620a62619..c71174d204 100644
--- a/src/include/utils/xml.h
+++ b/src/include/utils/xml.h
@@ -47,8 +47,17 @@ typedef enum
/* struct PgXmlErrorContext is private to xml.c */
typedef struct PgXmlErrorContext PgXmlErrorContext;
-#define DatumGetXmlP(X) ((xmltype *) PG_DETOAST_DATUM(X))
-#define XmlPGetDatum(X) PointerGetDatum(X)
+static inline xmltype *
+DatumGetXmlP(Datum X)
+{
+ return (xmltype *) PG_DETOAST_DATUM(X);
+}
+
+static inline Datum
+XmlPGetDatum(const xmltype *X)
+{
+ return PointerGetDatum(X);
+}
#define PG_GETARG_XML_P(n) DatumGetXmlP(PG_GETARG_DATUM(n))
#define PG_RETURN_XML_P(x) PG_RETURN_POINTER(x)
--
2.37.2
[application/octet-stream] v3-0001-Fix-incorrect-uses-of-Datum-conversion-macros.patch (15.2K, ../../CAJ7c6TNJhVWAqRkp+8jLrq3f5wzW_Q3LR04Ast71E_tQvtxjrQ@mail.gmail.com/3-v3-0001-Fix-incorrect-uses-of-Datum-conversion-macros.patch)
download | inline diff:
From c26047191fe7547ed44360743ade30f24a3ad641 Mon Sep 17 00:00:00 2001
From: Peter Eisentraut <[email protected]>
Date: Sun, 28 Aug 2022 10:47:10 +0200
Subject: [PATCH v3 1/2] Fix incorrect uses of Datum conversion macros
Peter Eisentraut, reviewed by Aleksander Alekseev
Discussion: http://postgr.es/m/8528fb7e-0aa2-6b54-85fb-0c0886dbd6ed%40enterprisedb.com
---
contrib/btree_gist/btree_utils_num.c | 2 +-
contrib/dblink/dblink.c | 2 +-
contrib/hstore/hstore_op.c | 2 +-
contrib/pageinspect/heapfuncs.c | 4 ++--
src/backend/access/brin/brin_bloom.c | 2 +-
src/backend/access/brin/brin_minmax_multi.c | 12 ++++++------
src/backend/access/common/toast_compression.c | 2 +-
src/backend/access/table/toast_helper.c | 2 +-
src/backend/access/transam/xlogfuncs.c | 2 +-
src/backend/statistics/mcv.c | 4 ++--
src/backend/utils/adt/amutils.c | 2 +-
src/backend/utils/adt/multirangetypes.c | 2 +-
src/backend/utils/adt/pg_lsn.c | 4 ++--
src/backend/utils/adt/pgstatfuncs.c | 2 +-
src/backend/utils/adt/rangetypes_spgist.c | 4 ++--
src/backend/utils/adt/regexp.c | 2 +-
src/backend/utils/adt/tsgistidx.c | 2 +-
src/backend/utils/adt/tsquery_op.c | 2 +-
src/backend/utils/adt/varlena.c | 2 +-
src/backend/utils/adt/xml.c | 2 +-
src/backend/utils/resowner/resowner.c | 6 +++---
src/pl/plperl/plperl.c | 2 +-
22 files changed, 33 insertions(+), 33 deletions(-)
diff --git a/contrib/btree_gist/btree_utils_num.c b/contrib/btree_gist/btree_utils_num.c
index 5632ee0586..05c154afa3 100644
--- a/contrib/btree_gist/btree_utils_num.c
+++ b/contrib/btree_gist/btree_utils_num.c
@@ -153,7 +153,7 @@ gbt_num_fetch(GISTENTRY *entry, const gbtree_ninfo *tinfo)
datum = CashGetDatum(*(Cash *) entry->key);
break;
default:
- datum = PointerGetDatum(entry->key);
+ datum = entry->key;
}
retval = palloc(sizeof(GISTENTRY));
diff --git a/contrib/dblink/dblink.c b/contrib/dblink/dblink.c
index e323fdd0e6..7940387920 100644
--- a/contrib/dblink/dblink.c
+++ b/contrib/dblink/dblink.c
@@ -1336,7 +1336,7 @@ dblink_get_connections(PG_FUNCTION_ARGS)
}
if (astate)
- PG_RETURN_ARRAYTYPE_P(makeArrayResult(astate,
+ PG_RETURN_DATUM(makeArrayResult(astate,
CurrentMemoryContext));
else
PG_RETURN_NULL();
diff --git a/contrib/hstore/hstore_op.c b/contrib/hstore/hstore_op.c
index 2f540d7ed6..0d4ec16d1e 100644
--- a/contrib/hstore/hstore_op.c
+++ b/contrib/hstore/hstore_op.c
@@ -1064,7 +1064,7 @@ hstore_each(PG_FUNCTION_ARGS)
tuple = heap_form_tuple(funcctx->tuple_desc, dvalues, nulls);
res = HeapTupleGetDatum(tuple);
- SRF_RETURN_NEXT(funcctx, PointerGetDatum(res));
+ SRF_RETURN_NEXT(funcctx, res);
}
SRF_RETURN_DONE(funcctx);
diff --git a/contrib/pageinspect/heapfuncs.c b/contrib/pageinspect/heapfuncs.c
index 2ff70405cf..aed2753253 100644
--- a/contrib/pageinspect/heapfuncs.c
+++ b/contrib/pageinspect/heapfuncs.c
@@ -383,7 +383,7 @@ tuple_data_split_internal(Oid relid, char *tupdata,
errmsg("unexpected end of tuple data")));
if (attr->attlen == -1 && do_detoast)
- attr_data = DatumGetByteaPCopy(tupdata + off);
+ attr_data = pg_detoast_datum_copy((struct varlena *) (tupdata + off));
else
{
attr_data = (bytea *) palloc(len + VARHDRSZ);
@@ -492,7 +492,7 @@ tuple_data_split(PG_FUNCTION_ARGS)
if (t_bits)
pfree(t_bits);
- PG_RETURN_ARRAYTYPE_P(res);
+ PG_RETURN_DATUM(res);
}
/*
diff --git a/src/backend/access/brin/brin_bloom.c b/src/backend/access/brin/brin_bloom.c
index 6812ca9fd1..6b0af7267d 100644
--- a/src/backend/access/brin/brin_bloom.c
+++ b/src/backend/access/brin/brin_bloom.c
@@ -767,7 +767,7 @@ brin_bloom_summary_out(PG_FUNCTION_ARGS)
StringInfoData str;
/* detoast the data to get value with a full 4B header */
- filter = (BloomFilter *) PG_DETOAST_DATUM(PG_GETARG_BYTEA_PP(0));
+ filter = (BloomFilter *) PG_DETOAST_DATUM_PACKED(PG_GETARG_DATUM(0));
initStringInfo(&str);
appendStringInfoChar(&str, '{');
diff --git a/src/backend/access/brin/brin_minmax_multi.c b/src/backend/access/brin/brin_minmax_multi.c
index a581659fe2..c8f22d5d17 100644
--- a/src/backend/access/brin/brin_minmax_multi.c
+++ b/src/backend/access/brin/brin_minmax_multi.c
@@ -774,12 +774,12 @@ brin_range_deserialize(int maxvalues, SerializedRanges *serialized)
datalen += MAXALIGN(typlen);
else if (typlen == -1) /* varlena */
{
- datalen += MAXALIGN(VARSIZE_ANY(DatumGetPointer(ptr)));
- ptr += VARSIZE_ANY(DatumGetPointer(ptr));
+ datalen += MAXALIGN(VARSIZE_ANY(ptr));
+ ptr += VARSIZE_ANY(ptr);
}
else if (typlen == -2) /* cstring */
{
- Size slen = strlen(DatumGetCString(ptr)) + 1;
+ Size slen = strlen(ptr) + 1;
datalen += MAXALIGN(slen);
ptr += slen;
@@ -3033,7 +3033,7 @@ brin_minmax_multi_summary_out(PG_FUNCTION_ARGS)
* Detoast to get value with full 4B header (can't be stored in a toast
* table, but can use 1B header).
*/
- ranges = (SerializedRanges *) PG_DETOAST_DATUM(PG_GETARG_BYTEA_PP(0));
+ ranges = (SerializedRanges *) PG_DETOAST_DATUM_PACKED(PG_GETARG_DATUM(0));
/* lookup output func for the type */
getTypeOutputInfo(ranges->typid, &outfunc, &isvarlena);
@@ -3081,7 +3081,7 @@ brin_minmax_multi_summary_out(PG_FUNCTION_ARGS)
getTypeOutputInfo(ANYARRAYOID, &typoutput, &typIsVarlena);
- val = PointerGetDatum(makeArrayResult(astate_values, CurrentMemoryContext));
+ val = makeArrayResult(astate_values, CurrentMemoryContext);
extval = OidOutputFunctionCall(typoutput, val);
@@ -3121,7 +3121,7 @@ brin_minmax_multi_summary_out(PG_FUNCTION_ARGS)
getTypeOutputInfo(ANYARRAYOID, &typoutput, &typIsVarlena);
- val = PointerGetDatum(makeArrayResult(astate_values, CurrentMemoryContext));
+ val = makeArrayResult(astate_values, CurrentMemoryContext);
extval = OidOutputFunctionCall(typoutput, val);
diff --git a/src/backend/access/common/toast_compression.c b/src/backend/access/common/toast_compression.c
index f90f9f11e3..7156ae9c47 100644
--- a/src/backend/access/common/toast_compression.c
+++ b/src/backend/access/common/toast_compression.c
@@ -44,7 +44,7 @@ pglz_compress_datum(const struct varlena *value)
len;
struct varlena *tmp = NULL;
- valsize = VARSIZE_ANY_EXHDR(DatumGetPointer(value));
+ valsize = VARSIZE_ANY_EXHDR(value);
/*
* No point in wasting a palloc cycle if value size is outside the allowed
diff --git a/src/backend/access/table/toast_helper.c b/src/backend/access/table/toast_helper.c
index 0cc5a30f9b..74ba2189f0 100644
--- a/src/backend/access/table/toast_helper.c
+++ b/src/backend/access/table/toast_helper.c
@@ -330,7 +330,7 @@ toast_delete_external(Relation rel, Datum *values, bool *isnull,
if (isnull[i])
continue;
- else if (VARATT_IS_EXTERNAL_ONDISK(PointerGetDatum(value)))
+ else if (VARATT_IS_EXTERNAL_ONDISK(value))
toast_delete_datum(rel, value, is_speculative);
}
}
diff --git a/src/backend/access/transam/xlogfuncs.c b/src/backend/access/transam/xlogfuncs.c
index 61e0f4a29c..9cc757f1af 100644
--- a/src/backend/access/transam/xlogfuncs.c
+++ b/src/backend/access/transam/xlogfuncs.c
@@ -555,7 +555,7 @@ pg_wal_lsn_diff(PG_FUNCTION_ARGS)
PG_GETARG_DATUM(0),
PG_GETARG_DATUM(1));
- PG_RETURN_NUMERIC(result);
+ PG_RETURN_DATUM(result);
}
/*
diff --git a/src/backend/statistics/mcv.c b/src/backend/statistics/mcv.c
index 6eeacb0d47..f5a7c31272 100644
--- a/src/backend/statistics/mcv.c
+++ b/src/backend/statistics/mcv.c
@@ -1444,8 +1444,8 @@ pg_stats_ext_mcvlist_items(PG_FUNCTION_ARGS)
}
values[0] = Int32GetDatum(funcctx->call_cntr);
- values[1] = PointerGetDatum(makeArrayResult(astate_values, CurrentMemoryContext));
- values[2] = PointerGetDatum(makeArrayResult(astate_nulls, CurrentMemoryContext));
+ values[1] = makeArrayResult(astate_values, CurrentMemoryContext);
+ values[2] = makeArrayResult(astate_nulls, CurrentMemoryContext);
values[3] = Float8GetDatum(item->frequency);
values[4] = Float8GetDatum(item->base_frequency);
diff --git a/src/backend/utils/adt/amutils.c b/src/backend/utils/adt/amutils.c
index 9d78a90d69..60fd396f24 100644
--- a/src/backend/utils/adt/amutils.c
+++ b/src/backend/utils/adt/amutils.c
@@ -466,5 +466,5 @@ pg_indexam_progress_phasename(PG_FUNCTION_ARGS)
if (!name)
PG_RETURN_NULL();
- PG_RETURN_TEXT_P(CStringGetTextDatum(name));
+ PG_RETURN_DATUM(CStringGetTextDatum(name));
}
diff --git a/src/backend/utils/adt/multirangetypes.c b/src/backend/utils/adt/multirangetypes.c
index da5c7d0906..307d087c97 100644
--- a/src/backend/utils/adt/multirangetypes.c
+++ b/src/backend/utils/adt/multirangetypes.c
@@ -1491,7 +1491,7 @@ multirange_intersect_agg_transfn(PG_FUNCTION_ARGS)
ranges1,
range_count2,
ranges2);
- PG_RETURN_RANGE_P(result);
+ PG_RETURN_MULTIRANGE_P(result);
}
diff --git a/src/backend/utils/adt/pg_lsn.c b/src/backend/utils/adt/pg_lsn.c
index 45408787da..15266f36f5 100644
--- a/src/backend/utils/adt/pg_lsn.c
+++ b/src/backend/utils/adt/pg_lsn.c
@@ -271,7 +271,7 @@ pg_lsn_pli(PG_FUNCTION_ARGS)
/* Add two numerics */
res = DirectFunctionCall2(numeric_add,
- NumericGetDatum(num),
+ num,
NumericGetDatum(nbytes));
/* Convert to pg_lsn */
@@ -305,7 +305,7 @@ pg_lsn_mii(PG_FUNCTION_ARGS)
/* Subtract two numerics */
res = DirectFunctionCall2(numeric_sub,
- NumericGetDatum(num),
+ num,
NumericGetDatum(nbytes));
/* Convert to pg_lsn */
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index 4cca30aae7..be15b4b2e5 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -1119,7 +1119,7 @@ pg_stat_get_backend_client_addr(PG_FUNCTION_ARGS)
clean_ipv6_addr(beentry->st_clientaddr.addr.ss_family, remote_host);
- PG_RETURN_INET_P(DirectFunctionCall1(inet_in,
+ PG_RETURN_DATUM(DirectFunctionCall1(inet_in,
CStringGetDatum(remote_host)));
}
diff --git a/src/backend/utils/adt/rangetypes_spgist.c b/src/backend/utils/adt/rangetypes_spgist.c
index 71a6053b6a..a47f04d975 100644
--- a/src/backend/utils/adt/rangetypes_spgist.c
+++ b/src/backend/utils/adt/rangetypes_spgist.c
@@ -416,7 +416,7 @@ spg_range_quad_inner_consistent(PG_FUNCTION_ARGS)
/* This node has a centroid. Fetch it. */
centroid = DatumGetRangeTypeP(in->prefixDatum);
typcache = range_get_typcache(fcinfo,
- RangeTypeGetOid(DatumGetRangeTypeP(centroid)));
+ RangeTypeGetOid(centroid));
range_deserialize(typcache, centroid, ¢roidLower, ¢roidUpper,
¢roidEmpty);
@@ -557,7 +557,7 @@ spg_range_quad_inner_consistent(PG_FUNCTION_ARGS)
*/
if (in->traversalValue)
{
- prevCentroid = DatumGetRangeTypeP(in->traversalValue);
+ prevCentroid = in->traversalValue;
range_deserialize(typcache, prevCentroid,
&prevLower, &prevUpper, &prevEmpty);
}
diff --git a/src/backend/utils/adt/regexp.c b/src/backend/utils/adt/regexp.c
index 0e0c5d5ce8..dcf6681ef2 100644
--- a/src/backend/utils/adt/regexp.c
+++ b/src/backend/utils/adt/regexp.c
@@ -1757,7 +1757,7 @@ regexp_split_to_array(PG_FUNCTION_ARGS)
splitctx->next_match++;
}
- PG_RETURN_ARRAYTYPE_P(makeArrayResult(astate, CurrentMemoryContext));
+ PG_RETURN_DATUM(makeArrayResult(astate, CurrentMemoryContext));
}
/* This is separate to keep the opr_sanity regression test from complaining */
diff --git a/src/backend/utils/adt/tsgistidx.c b/src/backend/utils/adt/tsgistidx.c
index 47daf8199e..fe438f1c4d 100644
--- a/src/backend/utils/adt/tsgistidx.c
+++ b/src/backend/utils/adt/tsgistidx.c
@@ -102,7 +102,7 @@ static int outbuf_maxlen = 0;
Datum
gtsvectorout(PG_FUNCTION_ARGS)
{
- SignTSVector *key = (SignTSVector *) PG_DETOAST_DATUM(PG_GETARG_POINTER(0));
+ SignTSVector *key = (SignTSVector *) PG_DETOAST_DATUM(PG_GETARG_DATUM(0));
char *outbuf;
if (outbuf_maxlen == 0)
diff --git a/src/backend/utils/adt/tsquery_op.c b/src/backend/utils/adt/tsquery_op.c
index a784157ba5..8d815fa7a8 100644
--- a/src/backend/utils/adt/tsquery_op.c
+++ b/src/backend/utils/adt/tsquery_op.c
@@ -148,7 +148,7 @@ tsquery_phrase_distance(PG_FUNCTION_ARGS)
Datum
tsquery_phrase(PG_FUNCTION_ARGS)
{
- PG_RETURN_POINTER(DirectFunctionCall3(tsquery_phrase_distance,
+ PG_RETURN_DATUM(DirectFunctionCall3(tsquery_phrase_distance,
PG_GETARG_DATUM(0),
PG_GETARG_DATUM(1),
Int32GetDatum(1)));
diff --git a/src/backend/utils/adt/varlena.c b/src/backend/utils/adt/varlena.c
index 8539cef024..6ef317e1f9 100644
--- a/src/backend/utils/adt/varlena.c
+++ b/src/backend/utils/adt/varlena.c
@@ -4779,7 +4779,7 @@ text_to_array(PG_FUNCTION_ARGS)
if (tstate.astate == NULL)
PG_RETURN_ARRAYTYPE_P(construct_empty_array(TEXTOID));
- PG_RETURN_ARRAYTYPE_P(makeArrayResult(tstate.astate,
+ PG_RETURN_DATUM(makeArrayResult(tstate.astate,
CurrentMemoryContext));
}
diff --git a/src/backend/utils/adt/xml.c b/src/backend/utils/adt/xml.c
index 60a85c4697..606088cdfc 100644
--- a/src/backend/utils/adt/xml.c
+++ b/src/backend/utils/adt/xml.c
@@ -4174,7 +4174,7 @@ xpath(PG_FUNCTION_ARGS)
astate = initArrayResult(XMLOID, CurrentMemoryContext, true);
xpath_internal(xpath_expr_text, data, namespaces,
NULL, astate);
- PG_RETURN_ARRAYTYPE_P(makeArrayResult(astate, CurrentMemoryContext));
+ PG_RETURN_DATUM(makeArrayResult(astate, CurrentMemoryContext));
#else
NO_XML_SUPPORT();
return 0;
diff --git a/src/backend/utils/resowner/resowner.c b/src/backend/utils/resowner/resowner.c
index ceb4b0e3f7..ece5d98261 100644
--- a/src/backend/utils/resowner/resowner.c
+++ b/src/backend/utils/resowner/resowner.c
@@ -557,7 +557,7 @@ ResourceOwnerReleaseInternal(ResourceOwner owner,
/* Ditto for JIT contexts */
while (ResourceArrayGetAny(&(owner->jitarr), &foundres))
{
- JitContext *context = (JitContext *) PointerGetDatum(foundres);
+ JitContext *context = (JitContext *) DatumGetPointer(foundres);
jit_release_context(context);
}
@@ -566,7 +566,7 @@ ResourceOwnerReleaseInternal(ResourceOwner owner,
while (ResourceArrayGetAny(&(owner->cryptohasharr), &foundres))
{
pg_cryptohash_ctx *context =
- (pg_cryptohash_ctx *) PointerGetDatum(foundres);
+ (pg_cryptohash_ctx *) DatumGetPointer(foundres);
if (isCommit)
PrintCryptoHashLeakWarning(foundres);
@@ -576,7 +576,7 @@ ResourceOwnerReleaseInternal(ResourceOwner owner,
/* Ditto for HMAC contexts */
while (ResourceArrayGetAny(&(owner->hmacarr), &foundres))
{
- pg_hmac_ctx *context = (pg_hmac_ctx *) PointerGetDatum(foundres);
+ pg_hmac_ctx *context = (pg_hmac_ctx *) DatumGetPointer(foundres);
if (isCommit)
PrintHMACLeakWarning(foundres);
diff --git a/src/pl/plperl/plperl.c b/src/pl/plperl/plperl.c
index 5d192a0ce5..8f21e0d701 100644
--- a/src/pl/plperl/plperl.c
+++ b/src/pl/plperl/plperl.c
@@ -1848,7 +1848,7 @@ plperl_call_handler(PG_FUNCTION_ARGS)
{
current_call_data = &this_call_data;
if (CALLED_AS_TRIGGER(fcinfo))
- retval = PointerGetDatum(plperl_trigger_handler(fcinfo));
+ retval = plperl_trigger_handler(fcinfo);
else if (CALLED_AS_EVENT_TRIGGER(fcinfo))
{
plperl_event_trigger_handler(fcinfo);
--
2.37.2
^ permalink raw reply [nested|flat] 92+ messages in thread
* Re: Convert *GetDatum() and DatumGet*() macros to inline functions
2022-08-28 15:55 Convert *GetDatum() and DatumGet*() macros to inline functions Peter Eisentraut <[email protected]>
2022-08-30 13:36 ` Re: Convert *GetDatum() and DatumGet*() macros to inline functions Aleksander Alekseev <[email protected]>
2022-08-30 13:46 ` Re: Convert *GetDatum() and DatumGet*() macros to inline functions Tom Lane <[email protected]>
2022-08-30 13:59 ` Re: Convert *GetDatum() and DatumGet*() macros to inline functions Aleksander Alekseev <[email protected]>
2022-08-30 14:13 ` Re: Convert *GetDatum() and DatumGet*() macros to inline functions Tom Lane <[email protected]>
2022-08-30 14:16 ` Re: Convert *GetDatum() and DatumGet*() macros to inline functions Robert Haas <[email protected]>
2022-08-30 14:25 ` Re: Convert *GetDatum() and DatumGet*() macros to inline functions Aleksander Alekseev <[email protected]>
2022-08-30 14:28 ` Re: Convert *GetDatum() and DatumGet*() macros to inline functions Tom Lane <[email protected]>
2022-08-30 14:52 ` Re: Convert *GetDatum() and DatumGet*() macros to inline functions Aleksander Alekseev <[email protected]>
@ 2022-08-30 18:15 ` Aleksander Alekseev <[email protected]>
2022-09-05 12:57 ` Re: Convert *GetDatum() and DatumGet*() macros to inline functions Peter Eisentraut <[email protected]>
0 siblings, 1 reply; 92+ messages in thread
From: Aleksander Alekseev @ 2022-08-30 18:15 UTC (permalink / raw)
To: pgsql-hackers
Hi hackers,
> Here is v3 with silenced compiler warnings.
Some more warnings were reported by cfbot, so here is v4. Apologies
for the noise.
--
Best regards,
Aleksander Alekseev
Attachments:
[application/octet-stream] v4-0001-Fix-incorrect-uses-of-Datum-conversion-macros.patch (15.2K, ../../CAJ7c6TPZX9TvXx7L7tc95YwSrrcmwkWY83yEoWeXa7=+aB_6_g@mail.gmail.com/2-v4-0001-Fix-incorrect-uses-of-Datum-conversion-macros.patch)
download | inline diff:
From c26047191fe7547ed44360743ade30f24a3ad641 Mon Sep 17 00:00:00 2001
From: Peter Eisentraut <[email protected]>
Date: Sun, 28 Aug 2022 10:47:10 +0200
Subject: [PATCH v4 1/2] Fix incorrect uses of Datum conversion macros
Peter Eisentraut, reviewed by Aleksander Alekseev
Discussion: http://postgr.es/m/8528fb7e-0aa2-6b54-85fb-0c0886dbd6ed%40enterprisedb.com
---
contrib/btree_gist/btree_utils_num.c | 2 +-
contrib/dblink/dblink.c | 2 +-
contrib/hstore/hstore_op.c | 2 +-
contrib/pageinspect/heapfuncs.c | 4 ++--
src/backend/access/brin/brin_bloom.c | 2 +-
src/backend/access/brin/brin_minmax_multi.c | 12 ++++++------
src/backend/access/common/toast_compression.c | 2 +-
src/backend/access/table/toast_helper.c | 2 +-
src/backend/access/transam/xlogfuncs.c | 2 +-
src/backend/statistics/mcv.c | 4 ++--
src/backend/utils/adt/amutils.c | 2 +-
src/backend/utils/adt/multirangetypes.c | 2 +-
src/backend/utils/adt/pg_lsn.c | 4 ++--
src/backend/utils/adt/pgstatfuncs.c | 2 +-
src/backend/utils/adt/rangetypes_spgist.c | 4 ++--
src/backend/utils/adt/regexp.c | 2 +-
src/backend/utils/adt/tsgistidx.c | 2 +-
src/backend/utils/adt/tsquery_op.c | 2 +-
src/backend/utils/adt/varlena.c | 2 +-
src/backend/utils/adt/xml.c | 2 +-
src/backend/utils/resowner/resowner.c | 6 +++---
src/pl/plperl/plperl.c | 2 +-
22 files changed, 33 insertions(+), 33 deletions(-)
diff --git a/contrib/btree_gist/btree_utils_num.c b/contrib/btree_gist/btree_utils_num.c
index 5632ee0586..05c154afa3 100644
--- a/contrib/btree_gist/btree_utils_num.c
+++ b/contrib/btree_gist/btree_utils_num.c
@@ -153,7 +153,7 @@ gbt_num_fetch(GISTENTRY *entry, const gbtree_ninfo *tinfo)
datum = CashGetDatum(*(Cash *) entry->key);
break;
default:
- datum = PointerGetDatum(entry->key);
+ datum = entry->key;
}
retval = palloc(sizeof(GISTENTRY));
diff --git a/contrib/dblink/dblink.c b/contrib/dblink/dblink.c
index e323fdd0e6..7940387920 100644
--- a/contrib/dblink/dblink.c
+++ b/contrib/dblink/dblink.c
@@ -1336,7 +1336,7 @@ dblink_get_connections(PG_FUNCTION_ARGS)
}
if (astate)
- PG_RETURN_ARRAYTYPE_P(makeArrayResult(astate,
+ PG_RETURN_DATUM(makeArrayResult(astate,
CurrentMemoryContext));
else
PG_RETURN_NULL();
diff --git a/contrib/hstore/hstore_op.c b/contrib/hstore/hstore_op.c
index 2f540d7ed6..0d4ec16d1e 100644
--- a/contrib/hstore/hstore_op.c
+++ b/contrib/hstore/hstore_op.c
@@ -1064,7 +1064,7 @@ hstore_each(PG_FUNCTION_ARGS)
tuple = heap_form_tuple(funcctx->tuple_desc, dvalues, nulls);
res = HeapTupleGetDatum(tuple);
- SRF_RETURN_NEXT(funcctx, PointerGetDatum(res));
+ SRF_RETURN_NEXT(funcctx, res);
}
SRF_RETURN_DONE(funcctx);
diff --git a/contrib/pageinspect/heapfuncs.c b/contrib/pageinspect/heapfuncs.c
index 2ff70405cf..aed2753253 100644
--- a/contrib/pageinspect/heapfuncs.c
+++ b/contrib/pageinspect/heapfuncs.c
@@ -383,7 +383,7 @@ tuple_data_split_internal(Oid relid, char *tupdata,
errmsg("unexpected end of tuple data")));
if (attr->attlen == -1 && do_detoast)
- attr_data = DatumGetByteaPCopy(tupdata + off);
+ attr_data = pg_detoast_datum_copy((struct varlena *) (tupdata + off));
else
{
attr_data = (bytea *) palloc(len + VARHDRSZ);
@@ -492,7 +492,7 @@ tuple_data_split(PG_FUNCTION_ARGS)
if (t_bits)
pfree(t_bits);
- PG_RETURN_ARRAYTYPE_P(res);
+ PG_RETURN_DATUM(res);
}
/*
diff --git a/src/backend/access/brin/brin_bloom.c b/src/backend/access/brin/brin_bloom.c
index 6812ca9fd1..6b0af7267d 100644
--- a/src/backend/access/brin/brin_bloom.c
+++ b/src/backend/access/brin/brin_bloom.c
@@ -767,7 +767,7 @@ brin_bloom_summary_out(PG_FUNCTION_ARGS)
StringInfoData str;
/* detoast the data to get value with a full 4B header */
- filter = (BloomFilter *) PG_DETOAST_DATUM(PG_GETARG_BYTEA_PP(0));
+ filter = (BloomFilter *) PG_DETOAST_DATUM_PACKED(PG_GETARG_DATUM(0));
initStringInfo(&str);
appendStringInfoChar(&str, '{');
diff --git a/src/backend/access/brin/brin_minmax_multi.c b/src/backend/access/brin/brin_minmax_multi.c
index a581659fe2..c8f22d5d17 100644
--- a/src/backend/access/brin/brin_minmax_multi.c
+++ b/src/backend/access/brin/brin_minmax_multi.c
@@ -774,12 +774,12 @@ brin_range_deserialize(int maxvalues, SerializedRanges *serialized)
datalen += MAXALIGN(typlen);
else if (typlen == -1) /* varlena */
{
- datalen += MAXALIGN(VARSIZE_ANY(DatumGetPointer(ptr)));
- ptr += VARSIZE_ANY(DatumGetPointer(ptr));
+ datalen += MAXALIGN(VARSIZE_ANY(ptr));
+ ptr += VARSIZE_ANY(ptr);
}
else if (typlen == -2) /* cstring */
{
- Size slen = strlen(DatumGetCString(ptr)) + 1;
+ Size slen = strlen(ptr) + 1;
datalen += MAXALIGN(slen);
ptr += slen;
@@ -3033,7 +3033,7 @@ brin_minmax_multi_summary_out(PG_FUNCTION_ARGS)
* Detoast to get value with full 4B header (can't be stored in a toast
* table, but can use 1B header).
*/
- ranges = (SerializedRanges *) PG_DETOAST_DATUM(PG_GETARG_BYTEA_PP(0));
+ ranges = (SerializedRanges *) PG_DETOAST_DATUM_PACKED(PG_GETARG_DATUM(0));
/* lookup output func for the type */
getTypeOutputInfo(ranges->typid, &outfunc, &isvarlena);
@@ -3081,7 +3081,7 @@ brin_minmax_multi_summary_out(PG_FUNCTION_ARGS)
getTypeOutputInfo(ANYARRAYOID, &typoutput, &typIsVarlena);
- val = PointerGetDatum(makeArrayResult(astate_values, CurrentMemoryContext));
+ val = makeArrayResult(astate_values, CurrentMemoryContext);
extval = OidOutputFunctionCall(typoutput, val);
@@ -3121,7 +3121,7 @@ brin_minmax_multi_summary_out(PG_FUNCTION_ARGS)
getTypeOutputInfo(ANYARRAYOID, &typoutput, &typIsVarlena);
- val = PointerGetDatum(makeArrayResult(astate_values, CurrentMemoryContext));
+ val = makeArrayResult(astate_values, CurrentMemoryContext);
extval = OidOutputFunctionCall(typoutput, val);
diff --git a/src/backend/access/common/toast_compression.c b/src/backend/access/common/toast_compression.c
index f90f9f11e3..7156ae9c47 100644
--- a/src/backend/access/common/toast_compression.c
+++ b/src/backend/access/common/toast_compression.c
@@ -44,7 +44,7 @@ pglz_compress_datum(const struct varlena *value)
len;
struct varlena *tmp = NULL;
- valsize = VARSIZE_ANY_EXHDR(DatumGetPointer(value));
+ valsize = VARSIZE_ANY_EXHDR(value);
/*
* No point in wasting a palloc cycle if value size is outside the allowed
diff --git a/src/backend/access/table/toast_helper.c b/src/backend/access/table/toast_helper.c
index 0cc5a30f9b..74ba2189f0 100644
--- a/src/backend/access/table/toast_helper.c
+++ b/src/backend/access/table/toast_helper.c
@@ -330,7 +330,7 @@ toast_delete_external(Relation rel, Datum *values, bool *isnull,
if (isnull[i])
continue;
- else if (VARATT_IS_EXTERNAL_ONDISK(PointerGetDatum(value)))
+ else if (VARATT_IS_EXTERNAL_ONDISK(value))
toast_delete_datum(rel, value, is_speculative);
}
}
diff --git a/src/backend/access/transam/xlogfuncs.c b/src/backend/access/transam/xlogfuncs.c
index 61e0f4a29c..9cc757f1af 100644
--- a/src/backend/access/transam/xlogfuncs.c
+++ b/src/backend/access/transam/xlogfuncs.c
@@ -555,7 +555,7 @@ pg_wal_lsn_diff(PG_FUNCTION_ARGS)
PG_GETARG_DATUM(0),
PG_GETARG_DATUM(1));
- PG_RETURN_NUMERIC(result);
+ PG_RETURN_DATUM(result);
}
/*
diff --git a/src/backend/statistics/mcv.c b/src/backend/statistics/mcv.c
index 6eeacb0d47..f5a7c31272 100644
--- a/src/backend/statistics/mcv.c
+++ b/src/backend/statistics/mcv.c
@@ -1444,8 +1444,8 @@ pg_stats_ext_mcvlist_items(PG_FUNCTION_ARGS)
}
values[0] = Int32GetDatum(funcctx->call_cntr);
- values[1] = PointerGetDatum(makeArrayResult(astate_values, CurrentMemoryContext));
- values[2] = PointerGetDatum(makeArrayResult(astate_nulls, CurrentMemoryContext));
+ values[1] = makeArrayResult(astate_values, CurrentMemoryContext);
+ values[2] = makeArrayResult(astate_nulls, CurrentMemoryContext);
values[3] = Float8GetDatum(item->frequency);
values[4] = Float8GetDatum(item->base_frequency);
diff --git a/src/backend/utils/adt/amutils.c b/src/backend/utils/adt/amutils.c
index 9d78a90d69..60fd396f24 100644
--- a/src/backend/utils/adt/amutils.c
+++ b/src/backend/utils/adt/amutils.c
@@ -466,5 +466,5 @@ pg_indexam_progress_phasename(PG_FUNCTION_ARGS)
if (!name)
PG_RETURN_NULL();
- PG_RETURN_TEXT_P(CStringGetTextDatum(name));
+ PG_RETURN_DATUM(CStringGetTextDatum(name));
}
diff --git a/src/backend/utils/adt/multirangetypes.c b/src/backend/utils/adt/multirangetypes.c
index da5c7d0906..307d087c97 100644
--- a/src/backend/utils/adt/multirangetypes.c
+++ b/src/backend/utils/adt/multirangetypes.c
@@ -1491,7 +1491,7 @@ multirange_intersect_agg_transfn(PG_FUNCTION_ARGS)
ranges1,
range_count2,
ranges2);
- PG_RETURN_RANGE_P(result);
+ PG_RETURN_MULTIRANGE_P(result);
}
diff --git a/src/backend/utils/adt/pg_lsn.c b/src/backend/utils/adt/pg_lsn.c
index 45408787da..15266f36f5 100644
--- a/src/backend/utils/adt/pg_lsn.c
+++ b/src/backend/utils/adt/pg_lsn.c
@@ -271,7 +271,7 @@ pg_lsn_pli(PG_FUNCTION_ARGS)
/* Add two numerics */
res = DirectFunctionCall2(numeric_add,
- NumericGetDatum(num),
+ num,
NumericGetDatum(nbytes));
/* Convert to pg_lsn */
@@ -305,7 +305,7 @@ pg_lsn_mii(PG_FUNCTION_ARGS)
/* Subtract two numerics */
res = DirectFunctionCall2(numeric_sub,
- NumericGetDatum(num),
+ num,
NumericGetDatum(nbytes));
/* Convert to pg_lsn */
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index 4cca30aae7..be15b4b2e5 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -1119,7 +1119,7 @@ pg_stat_get_backend_client_addr(PG_FUNCTION_ARGS)
clean_ipv6_addr(beentry->st_clientaddr.addr.ss_family, remote_host);
- PG_RETURN_INET_P(DirectFunctionCall1(inet_in,
+ PG_RETURN_DATUM(DirectFunctionCall1(inet_in,
CStringGetDatum(remote_host)));
}
diff --git a/src/backend/utils/adt/rangetypes_spgist.c b/src/backend/utils/adt/rangetypes_spgist.c
index 71a6053b6a..a47f04d975 100644
--- a/src/backend/utils/adt/rangetypes_spgist.c
+++ b/src/backend/utils/adt/rangetypes_spgist.c
@@ -416,7 +416,7 @@ spg_range_quad_inner_consistent(PG_FUNCTION_ARGS)
/* This node has a centroid. Fetch it. */
centroid = DatumGetRangeTypeP(in->prefixDatum);
typcache = range_get_typcache(fcinfo,
- RangeTypeGetOid(DatumGetRangeTypeP(centroid)));
+ RangeTypeGetOid(centroid));
range_deserialize(typcache, centroid, ¢roidLower, ¢roidUpper,
¢roidEmpty);
@@ -557,7 +557,7 @@ spg_range_quad_inner_consistent(PG_FUNCTION_ARGS)
*/
if (in->traversalValue)
{
- prevCentroid = DatumGetRangeTypeP(in->traversalValue);
+ prevCentroid = in->traversalValue;
range_deserialize(typcache, prevCentroid,
&prevLower, &prevUpper, &prevEmpty);
}
diff --git a/src/backend/utils/adt/regexp.c b/src/backend/utils/adt/regexp.c
index 0e0c5d5ce8..dcf6681ef2 100644
--- a/src/backend/utils/adt/regexp.c
+++ b/src/backend/utils/adt/regexp.c
@@ -1757,7 +1757,7 @@ regexp_split_to_array(PG_FUNCTION_ARGS)
splitctx->next_match++;
}
- PG_RETURN_ARRAYTYPE_P(makeArrayResult(astate, CurrentMemoryContext));
+ PG_RETURN_DATUM(makeArrayResult(astate, CurrentMemoryContext));
}
/* This is separate to keep the opr_sanity regression test from complaining */
diff --git a/src/backend/utils/adt/tsgistidx.c b/src/backend/utils/adt/tsgistidx.c
index 47daf8199e..fe438f1c4d 100644
--- a/src/backend/utils/adt/tsgistidx.c
+++ b/src/backend/utils/adt/tsgistidx.c
@@ -102,7 +102,7 @@ static int outbuf_maxlen = 0;
Datum
gtsvectorout(PG_FUNCTION_ARGS)
{
- SignTSVector *key = (SignTSVector *) PG_DETOAST_DATUM(PG_GETARG_POINTER(0));
+ SignTSVector *key = (SignTSVector *) PG_DETOAST_DATUM(PG_GETARG_DATUM(0));
char *outbuf;
if (outbuf_maxlen == 0)
diff --git a/src/backend/utils/adt/tsquery_op.c b/src/backend/utils/adt/tsquery_op.c
index a784157ba5..8d815fa7a8 100644
--- a/src/backend/utils/adt/tsquery_op.c
+++ b/src/backend/utils/adt/tsquery_op.c
@@ -148,7 +148,7 @@ tsquery_phrase_distance(PG_FUNCTION_ARGS)
Datum
tsquery_phrase(PG_FUNCTION_ARGS)
{
- PG_RETURN_POINTER(DirectFunctionCall3(tsquery_phrase_distance,
+ PG_RETURN_DATUM(DirectFunctionCall3(tsquery_phrase_distance,
PG_GETARG_DATUM(0),
PG_GETARG_DATUM(1),
Int32GetDatum(1)));
diff --git a/src/backend/utils/adt/varlena.c b/src/backend/utils/adt/varlena.c
index 8539cef024..6ef317e1f9 100644
--- a/src/backend/utils/adt/varlena.c
+++ b/src/backend/utils/adt/varlena.c
@@ -4779,7 +4779,7 @@ text_to_array(PG_FUNCTION_ARGS)
if (tstate.astate == NULL)
PG_RETURN_ARRAYTYPE_P(construct_empty_array(TEXTOID));
- PG_RETURN_ARRAYTYPE_P(makeArrayResult(tstate.astate,
+ PG_RETURN_DATUM(makeArrayResult(tstate.astate,
CurrentMemoryContext));
}
diff --git a/src/backend/utils/adt/xml.c b/src/backend/utils/adt/xml.c
index 60a85c4697..606088cdfc 100644
--- a/src/backend/utils/adt/xml.c
+++ b/src/backend/utils/adt/xml.c
@@ -4174,7 +4174,7 @@ xpath(PG_FUNCTION_ARGS)
astate = initArrayResult(XMLOID, CurrentMemoryContext, true);
xpath_internal(xpath_expr_text, data, namespaces,
NULL, astate);
- PG_RETURN_ARRAYTYPE_P(makeArrayResult(astate, CurrentMemoryContext));
+ PG_RETURN_DATUM(makeArrayResult(astate, CurrentMemoryContext));
#else
NO_XML_SUPPORT();
return 0;
diff --git a/src/backend/utils/resowner/resowner.c b/src/backend/utils/resowner/resowner.c
index ceb4b0e3f7..ece5d98261 100644
--- a/src/backend/utils/resowner/resowner.c
+++ b/src/backend/utils/resowner/resowner.c
@@ -557,7 +557,7 @@ ResourceOwnerReleaseInternal(ResourceOwner owner,
/* Ditto for JIT contexts */
while (ResourceArrayGetAny(&(owner->jitarr), &foundres))
{
- JitContext *context = (JitContext *) PointerGetDatum(foundres);
+ JitContext *context = (JitContext *) DatumGetPointer(foundres);
jit_release_context(context);
}
@@ -566,7 +566,7 @@ ResourceOwnerReleaseInternal(ResourceOwner owner,
while (ResourceArrayGetAny(&(owner->cryptohasharr), &foundres))
{
pg_cryptohash_ctx *context =
- (pg_cryptohash_ctx *) PointerGetDatum(foundres);
+ (pg_cryptohash_ctx *) DatumGetPointer(foundres);
if (isCommit)
PrintCryptoHashLeakWarning(foundres);
@@ -576,7 +576,7 @@ ResourceOwnerReleaseInternal(ResourceOwner owner,
/* Ditto for HMAC contexts */
while (ResourceArrayGetAny(&(owner->hmacarr), &foundres))
{
- pg_hmac_ctx *context = (pg_hmac_ctx *) PointerGetDatum(foundres);
+ pg_hmac_ctx *context = (pg_hmac_ctx *) DatumGetPointer(foundres);
if (isCommit)
PrintHMACLeakWarning(foundres);
diff --git a/src/pl/plperl/plperl.c b/src/pl/plperl/plperl.c
index 5d192a0ce5..8f21e0d701 100644
--- a/src/pl/plperl/plperl.c
+++ b/src/pl/plperl/plperl.c
@@ -1848,7 +1848,7 @@ plperl_call_handler(PG_FUNCTION_ARGS)
{
current_call_data = &this_call_data;
if (CALLED_AS_TRIGGER(fcinfo))
- retval = PointerGetDatum(plperl_trigger_handler(fcinfo));
+ retval = plperl_trigger_handler(fcinfo);
else if (CALLED_AS_EVENT_TRIGGER(fcinfo))
{
plperl_event_trigger_handler(fcinfo);
--
2.37.2
[application/octet-stream] v4-0002-Convert-GetDatum-and-DatumGet-macros-to-inline-fu.patch (41.8K, ../../CAJ7c6TPZX9TvXx7L7tc95YwSrrcmwkWY83yEoWeXa7=+aB_6_g@mail.gmail.com/3-v4-0002-Convert-GetDatum-and-DatumGet-macros-to-inline-fu.patch)
download | inline diff:
From c0729f157643479610381802b7318fb4fb009ee4 Mon Sep 17 00:00:00 2001
From: Peter Eisentraut <[email protected]>
Date: Sun, 28 Aug 2022 10:47:38 +0200
Subject: [PATCH v4 2/2] Convert *GetDatum() and DatumGet*() macros to inline
functions
The previous macro implementations just cast the argument to a target
type but did not check whether the input type was appropriate. The
function implementation can do better type checking of the input type.
Peter Eisentraut, reviewed by Aleksander Alekseev
Discussion: http://postgr.es/m/8528fb7e-0aa2-6b54-85fb-0c0886dbd6ed%40enterprisedb.com
---
contrib/intarray/_int_gist.c | 2 +-
doc/src/sgml/xfunc.sgml | 2 +-
src/backend/access/gist/gistutil.c | 2 +-
src/backend/tsearch/ts_parse.c | 4 +-
src/backend/utils/mb/mbutils.c | 12 +-
src/include/access/gin.h | 14 +-
src/include/funcapi.h | 14 +-
src/include/postgres.h | 275 +++++++++++++++++++---------
src/include/tsearch/ts_type.h | 46 ++++-
src/include/tsearch/ts_utils.h | 14 +-
src/include/utils/cash.h | 14 +-
src/include/utils/date.h | 42 ++++-
src/include/utils/expandeddatum.h | 13 +-
src/include/utils/expandedrecord.h | 16 +-
src/include/utils/geo_decls.h | 98 ++++++++--
src/include/utils/inet.h | 49 ++++-
src/include/utils/jsonb.h | 31 +++-
src/include/utils/jsonpath.h | 14 +-
src/include/utils/multirangetypes.h | 23 ++-
src/include/utils/numeric.h | 21 ++-
src/include/utils/pg_lsn.h | 13 +-
src/include/utils/rangetypes.h | 23 ++-
src/include/utils/timestamp.h | 44 ++++-
src/include/utils/uuid.h | 15 +-
src/include/utils/varbit.h | 21 ++-
src/include/utils/xid8.h | 14 +-
src/include/utils/xml.h | 13 +-
27 files changed, 659 insertions(+), 190 deletions(-)
diff --git a/contrib/intarray/_int_gist.c b/contrib/intarray/_int_gist.c
index f1817a6cce..ad75bf8e8b 100644
--- a/contrib/intarray/_int_gist.c
+++ b/contrib/intarray/_int_gist.c
@@ -51,7 +51,7 @@ g_int_consistent(PG_FUNCTION_ARGS)
/* Oid subtype = PG_GETARG_OID(3); */
bool *recheck = (bool *) PG_GETARG_POINTER(4);
- bool retval;
+ bool retval = false; /* silence the compiler warning */
/* this is exact except for RTSameStrategyNumber */
*recheck = (strategy == RTSameStrategyNumber);
diff --git a/doc/src/sgml/xfunc.sgml b/doc/src/sgml/xfunc.sgml
index b8cefb9c2c..cf5810b3c1 100644
--- a/doc/src/sgml/xfunc.sgml
+++ b/doc/src/sgml/xfunc.sgml
@@ -2763,7 +2763,7 @@ c_overpaid(PG_FUNCTION_ARGS)
is null. <function>GetAttributeByName</function> returns a <type>Datum</type>
value that you can convert to the proper data type by using the
appropriate <function>DatumGet<replaceable>XXX</replaceable>()</function>
- macro. Note that the return value is meaningless if the null flag is
+ function. Note that the return value is meaningless if the null flag is
set; always check the null flag before trying to do anything with the
result.
</para>
diff --git a/src/backend/access/gist/gistutil.c b/src/backend/access/gist/gistutil.c
index d4bf0c7563..8bdd16fc81 100644
--- a/src/backend/access/gist/gistutil.c
+++ b/src/backend/access/gist/gistutil.c
@@ -280,7 +280,7 @@ gistMakeUnionKey(GISTSTATE *giststate, int attno,
bool
gistKeyIsEQ(GISTSTATE *giststate, int attno, Datum a, Datum b)
{
- bool result;
+ bool result = false; /* silence the compiler warning */
FunctionCall3Coll(&giststate->equalFn[attno],
giststate->supportCollation[attno],
diff --git a/src/backend/tsearch/ts_parse.c b/src/backend/tsearch/ts_parse.c
index 27b2cca2df..ff2f0737c8 100644
--- a/src/backend/tsearch/ts_parse.c
+++ b/src/backend/tsearch/ts_parse.c
@@ -354,7 +354,7 @@ void
parsetext(Oid cfgId, ParsedText *prs, char *buf, int buflen)
{
int type,
- lenlemm;
+ lenlemm = 0; /* silence the compiler warning */
char *lemm = NULL;
LexizeData ldata;
TSLexeme *norms;
@@ -529,7 +529,7 @@ void
hlparsetext(Oid cfgId, HeadlineParsedText *prs, TSQuery query, char *buf, int buflen)
{
int type,
- lenlemm;
+ lenlemm = 0; /* silence the compiler warning */
char *lemm = NULL;
LexizeData ldata;
TSLexeme *norms;
diff --git a/src/backend/utils/mb/mbutils.c b/src/backend/utils/mb/mbutils.c
index 0543c574c6..474ab476f5 100644
--- a/src/backend/utils/mb/mbutils.c
+++ b/src/backend/utils/mb/mbutils.c
@@ -409,8 +409,8 @@ pg_do_encoding_conversion(unsigned char *src, int len,
(void) OidFunctionCall6(proc,
Int32GetDatum(src_encoding),
Int32GetDatum(dest_encoding),
- CStringGetDatum(src),
- CStringGetDatum(result),
+ CStringGetDatum((char *) src),
+ CStringGetDatum((char *) result),
Int32GetDatum(len),
BoolGetDatum(false));
@@ -485,8 +485,8 @@ pg_do_encoding_conversion_buf(Oid proc,
result = OidFunctionCall6(proc,
Int32GetDatum(src_encoding),
Int32GetDatum(dest_encoding),
- CStringGetDatum(src),
- CStringGetDatum(dest),
+ CStringGetDatum((char *) src),
+ CStringGetDatum((char *) dest),
Int32GetDatum(srclen),
BoolGetDatum(noError));
return DatumGetInt32(result);
@@ -910,8 +910,8 @@ pg_unicode_to_server(pg_wchar c, unsigned char *s)
FunctionCall6(Utf8ToServerConvProc,
Int32GetDatum(PG_UTF8),
Int32GetDatum(server_encoding),
- CStringGetDatum(c_as_utf8),
- CStringGetDatum(s),
+ CStringGetDatum((char *) c_as_utf8),
+ CStringGetDatum((char *) s),
Int32GetDatum(c_as_utf8_len),
BoolGetDatum(false));
}
diff --git a/src/include/access/gin.h b/src/include/access/gin.h
index aacc665fdc..fff861e219 100644
--- a/src/include/access/gin.h
+++ b/src/include/access/gin.h
@@ -62,8 +62,18 @@ typedef char GinTernaryValue;
#define GIN_MAYBE 2 /* don't know if item is present / don't know
* if matches */
-#define DatumGetGinTernaryValue(X) ((GinTernaryValue)(X))
-#define GinTernaryValueGetDatum(X) ((Datum)(X))
+static inline GinTernaryValue
+DatumGetGinTernaryValue(Datum X)
+{
+ return (GinTernaryValue) X;
+}
+
+static inline Datum
+GinTernaryValueGetDatum(GinTernaryValue X)
+{
+ return (Datum) X;
+}
+
#define PG_RETURN_GIN_TERNARY_VALUE(x) return GinTernaryValueGetDatum(x)
/* GUC parameters */
diff --git a/src/include/funcapi.h b/src/include/funcapi.h
index dc3d819a1c..8c2521a591 100644
--- a/src/include/funcapi.h
+++ b/src/include/funcapi.h
@@ -204,7 +204,7 @@ extern TupleDesc build_function_result_tupdesc_t(HeapTuple procTuple);
* Datum HeapTupleHeaderGetDatum(HeapTupleHeader tuple) - convert a
* HeapTupleHeader to a Datum.
*
- * Macro declarations:
+ * Inline declarations:
* HeapTupleGetDatum(HeapTuple tuple) - convert a HeapTuple to a Datum.
*
* Obsolete routines and macros:
@@ -217,10 +217,6 @@ extern TupleDesc build_function_result_tupdesc_t(HeapTuple procTuple);
*----------
*/
-#define HeapTupleGetDatum(tuple) HeapTupleHeaderGetDatum((tuple)->t_data)
-/* obsolete version of above */
-#define TupleGetDatum(_slot, _tuple) HeapTupleGetDatum(_tuple)
-
extern TupleDesc RelationNameGetTupleDesc(const char *relname);
extern TupleDesc TypeGetTupleDesc(Oid typeoid, List *colaliases);
@@ -230,6 +226,14 @@ extern AttInMetadata *TupleDescGetAttInMetadata(TupleDesc tupdesc);
extern HeapTuple BuildTupleFromCStrings(AttInMetadata *attinmeta, char **values);
extern Datum HeapTupleHeaderGetDatum(HeapTupleHeader tuple);
+static inline Datum
+HeapTupleGetDatum(const HeapTupleData *tuple)
+{
+ return HeapTupleHeaderGetDatum(tuple->t_data);
+}
+/* obsolete version of above */
+#define TupleGetDatum(_slot, _tuple) HeapTupleGetDatum(_tuple)
+
/*----------
* Support for Set Returning Functions (SRFs)
diff --git a/src/include/postgres.h b/src/include/postgres.h
index 13903fa022..45c9f6563c 100644
--- a/src/include/postgres.h
+++ b/src/include/postgres.h
@@ -24,7 +24,7 @@
* section description
* ------- ------------------------------------------------
* 1) variable-length datatypes (TOAST support)
- * 2) Datum type + support macros
+ * 2) Datum type + support functions
* 3) miscellaneous
*
* NOTES
@@ -395,7 +395,7 @@ typedef struct
/* ----------------------------------------------------------------
- * Section 2: Datum type + support macros
+ * Section 2: Datum type + support functions
* ----------------------------------------------------------------
*/
@@ -405,7 +405,7 @@ typedef struct
*
* sizeof(Datum) == sizeof(void *) == 4 or 8
*
- * The macros below and the analogous macros for other types should be used to
+ * The functions below and the analogous functions for other types should be used to
* convert between a Datum and the appropriate C type.
*/
@@ -434,8 +434,11 @@ typedef struct NullableDatum
*
* Note: any nonzero value will be considered true.
*/
-
-#define DatumGetBool(X) ((bool) ((X) != 0))
+static inline bool
+DatumGetBool(Datum X)
+{
+ return (X != 0);
+}
/*
* BoolGetDatum
@@ -443,162 +446,231 @@ typedef struct NullableDatum
*
* Note: any nonzero value will be considered true.
*/
-
-#define BoolGetDatum(X) ((Datum) ((X) ? 1 : 0))
+static inline Datum
+BoolGetDatum(bool X)
+{
+ return (Datum) (X ? 1 : 0);
+}
/*
* DatumGetChar
* Returns character value of a datum.
*/
-
-#define DatumGetChar(X) ((char) (X))
+static inline char
+DatumGetChar(Datum X)
+{
+ return (char) X;
+}
/*
* CharGetDatum
* Returns datum representation for a character.
*/
-
-#define CharGetDatum(X) ((Datum) (X))
+static inline Datum
+CharGetDatum(char X)
+{
+ return (Datum) X;
+}
/*
* Int8GetDatum
* Returns datum representation for an 8-bit integer.
*/
-
-#define Int8GetDatum(X) ((Datum) (X))
+static inline Datum
+Int8GetDatum(int8 X)
+{
+ return (Datum) X;
+}
/*
* DatumGetUInt8
* Returns 8-bit unsigned integer value of a datum.
*/
-
-#define DatumGetUInt8(X) ((uint8) (X))
+static inline uint8
+DatumGetUInt8(Datum X)
+{
+ return (uint8) X;
+}
/*
* UInt8GetDatum
* Returns datum representation for an 8-bit unsigned integer.
*/
-
-#define UInt8GetDatum(X) ((Datum) (X))
+static inline Datum
+UInt8GetDatum(uint8 X)
+{
+ return (Datum) X;
+}
/*
* DatumGetInt16
* Returns 16-bit integer value of a datum.
*/
-
-#define DatumGetInt16(X) ((int16) (X))
+static inline int16
+DatumGetInt16(Datum X)
+{
+ return (int16) X;
+}
/*
* Int16GetDatum
* Returns datum representation for a 16-bit integer.
*/
-
-#define Int16GetDatum(X) ((Datum) (X))
+static inline Datum
+Int16GetDatum(int16 X)
+{
+ return (Datum) X;
+}
/*
* DatumGetUInt16
* Returns 16-bit unsigned integer value of a datum.
*/
-
-#define DatumGetUInt16(X) ((uint16) (X))
+static inline uint16
+DatumGetUInt16(Datum X)
+{
+ return (uint16) X;
+}
/*
* UInt16GetDatum
* Returns datum representation for a 16-bit unsigned integer.
*/
-
-#define UInt16GetDatum(X) ((Datum) (X))
+static inline Datum
+UInt16GetDatum(uint16 X)
+{
+ return (Datum) X;
+}
/*
* DatumGetInt32
* Returns 32-bit integer value of a datum.
*/
-
-#define DatumGetInt32(X) ((int32) (X))
+static inline int32
+DatumGetInt32(Datum X)
+{
+ return (int32) X;
+}
/*
* Int32GetDatum
* Returns datum representation for a 32-bit integer.
*/
-
-#define Int32GetDatum(X) ((Datum) (X))
+static inline Datum
+Int32GetDatum(int32 X)
+{
+ return (Datum) X;
+}
/*
* DatumGetUInt32
* Returns 32-bit unsigned integer value of a datum.
*/
-
-#define DatumGetUInt32(X) ((uint32) (X))
+static inline uint32
+DatumGetUInt32(Datum X)
+{
+ return (uint32) X;
+}
/*
* UInt32GetDatum
* Returns datum representation for a 32-bit unsigned integer.
*/
-
-#define UInt32GetDatum(X) ((Datum) (X))
+static inline Datum
+UInt32GetDatum(uint32 X)
+{
+ return (Datum) X;
+}
/*
* DatumGetObjectId
* Returns object identifier value of a datum.
*/
-
-#define DatumGetObjectId(X) ((Oid) (X))
+static inline Oid
+DatumGetObjectId(Datum X)
+{
+ return (Oid) X;
+}
/*
* ObjectIdGetDatum
* Returns datum representation for an object identifier.
*/
-
-#define ObjectIdGetDatum(X) ((Datum) (X))
+static inline Datum
+ObjectIdGetDatum(Oid X)
+{
+ return (Datum) X;
+}
/*
* DatumGetTransactionId
* Returns transaction identifier value of a datum.
*/
-
-#define DatumGetTransactionId(X) ((TransactionId) (X))
+static inline TransactionId
+DatumGetTransactionId(Datum X)
+{
+ return (TransactionId) X;
+}
/*
* TransactionIdGetDatum
* Returns datum representation for a transaction identifier.
*/
-
-#define TransactionIdGetDatum(X) ((Datum) (X))
+static inline Datum
+TransactionIdGetDatum(TransactionId X)
+{
+ return (Datum) X;
+}
/*
* MultiXactIdGetDatum
* Returns datum representation for a multixact identifier.
*/
-
-#define MultiXactIdGetDatum(X) ((Datum) (X))
+static inline Datum
+MultiXactIdGetDatum(MultiXactId X)
+{
+ return (Datum) X;
+}
/*
* DatumGetCommandId
* Returns command identifier value of a datum.
*/
-
-#define DatumGetCommandId(X) ((CommandId) (X))
+static inline CommandId
+DatumGetCommandId(Datum X)
+{
+ return (CommandId) X;
+}
/*
* CommandIdGetDatum
* Returns datum representation for a command identifier.
*/
-
-#define CommandIdGetDatum(X) ((Datum) (X))
+static inline Datum
+CommandIdGetDatum(CommandId X)
+{
+ return (Datum) X;
+}
/*
* DatumGetPointer
* Returns pointer value of a datum.
*/
-
-#define DatumGetPointer(X) ((Pointer) (X))
+static inline Pointer
+DatumGetPointer(Datum X)
+{
+ return (Pointer) X;
+}
/*
* PointerGetDatum
* Returns datum representation for a pointer.
*/
-
-#define PointerGetDatum(X) ((Datum) (X))
+static inline Datum
+PointerGetDatum(const void *X)
+{
+ return (Datum) X;
+}
/*
* DatumGetCString
@@ -607,8 +679,11 @@ typedef struct NullableDatum
* Note: C string is not a full-fledged Postgres type at present,
* but type input functions use this conversion for their inputs.
*/
-
-#define DatumGetCString(X) ((char *) DatumGetPointer(X))
+static inline char *
+DatumGetCString(Datum X)
+{
+ return (char *) DatumGetPointer(X);
+}
/*
* CStringGetDatum
@@ -619,15 +694,21 @@ typedef struct NullableDatum
* Note: CString is pass-by-reference; caller must ensure the pointed-to
* value has adequate lifetime.
*/
-
-#define CStringGetDatum(X) PointerGetDatum(X)
+static inline Datum
+CStringGetDatum(const char *X)
+{
+ return PointerGetDatum(X);
+}
/*
* DatumGetName
* Returns name value of a datum.
*/
-
-#define DatumGetName(X) ((Name) DatumGetPointer(X))
+static inline Name
+DatumGetName(Datum X)
+{
+ return (Name) DatumGetPointer(X);
+}
/*
* NameGetDatum
@@ -636,21 +717,27 @@ typedef struct NullableDatum
* Note: Name is pass-by-reference; caller must ensure the pointed-to
* value has adequate lifetime.
*/
-
-#define NameGetDatum(X) CStringGetDatum(NameStr(*(X)))
+static inline Datum
+NameGetDatum(const NameData *X)
+{
+ return CStringGetDatum(NameStr(*X));
+}
/*
* DatumGetInt64
* Returns 64-bit integer value of a datum.
*
- * Note: this macro hides whether int64 is pass by value or by reference.
+ * Note: this function hides whether int64 is pass by value or by reference.
*/
-
+static inline int64
+DatumGetInt64(Datum X)
+{
#ifdef USE_FLOAT8_BYVAL
-#define DatumGetInt64(X) ((int64) (X))
+ return (int64) X;
#else
-#define DatumGetInt64(X) (* ((int64 *) DatumGetPointer(X)))
+ return *((int64 *) DatumGetPointer(X));
#endif
+}
/*
* Int64GetDatum
@@ -659,25 +746,32 @@ typedef struct NullableDatum
* Note: if int64 is pass by reference, this function returns a reference
* to palloc'd space.
*/
-
#ifdef USE_FLOAT8_BYVAL
-#define Int64GetDatum(X) ((Datum) (X))
+static inline Datum
+Int64GetDatum(int64 X)
+{
+ return (Datum) X;
+}
#else
extern Datum Int64GetDatum(int64 X);
#endif
+
/*
* DatumGetUInt64
* Returns 64-bit unsigned integer value of a datum.
*
- * Note: this macro hides whether int64 is pass by value or by reference.
+ * Note: this function hides whether int64 is pass by value or by reference.
*/
-
+static inline uint64
+DatumGetUInt64(Datum X)
+{
#ifdef USE_FLOAT8_BYVAL
-#define DatumGetUInt64(X) ((uint64) (X))
+ return (uint64) X;
#else
-#define DatumGetUInt64(X) (* ((uint64 *) DatumGetPointer(X)))
+ return *((uint64 *) DatumGetPointer(X));
#endif
+}
/*
* UInt64GetDatum
@@ -686,12 +780,15 @@ extern Datum Int64GetDatum(int64 X);
* Note: if int64 is pass by reference, this function returns a reference
* to palloc'd space.
*/
-
+static inline Datum
+UInt64GetDatum(uint64 X)
+{
#ifdef USE_FLOAT8_BYVAL
-#define UInt64GetDatum(X) ((Datum) (X))
+ return (Datum) X;
#else
-#define UInt64GetDatum(X) Int64GetDatum((int64) (X))
+ return Int64GetDatum((int64) X);
#endif
+}
/*
* Float <-> Datum conversions
@@ -739,13 +836,12 @@ Float4GetDatum(float4 X)
* DatumGetFloat8
* Returns 8-byte floating point value of a datum.
*
- * Note: this macro hides whether float8 is pass by value or by reference.
+ * Note: this function hides whether float8 is pass by value or by reference.
*/
-
-#ifdef USE_FLOAT8_BYVAL
static inline float8
DatumGetFloat8(Datum X)
{
+#ifdef USE_FLOAT8_BYVAL
union
{
int64 value;
@@ -754,10 +850,10 @@ DatumGetFloat8(Datum X)
myunion.value = DatumGetInt64(X);
return myunion.retval;
-}
#else
-#define DatumGetFloat8(X) (* ((float8 *) DatumGetPointer(X)))
+ return *((float8 *) DatumGetPointer(X));
#endif
+}
/*
* Float8GetDatum
@@ -766,7 +862,6 @@ DatumGetFloat8(Datum X)
* Note: if float8 is pass by reference, this function returns a reference
* to palloc'd space.
*/
-
#ifdef USE_FLOAT8_BYVAL
static inline Datum
Float8GetDatum(float8 X)
@@ -789,22 +884,34 @@ extern Datum Float8GetDatum(float8 X);
* Int64GetDatumFast
* Float8GetDatumFast
*
- * These macros are intended to allow writing code that does not depend on
+ * These functions are intended to allow writing code that does not depend on
* whether int64 and float8 are pass-by-reference types, while not
* sacrificing performance when they are. The argument must be a variable
* that will exist and have the same value for as long as the Datum is needed.
* In the pass-by-ref case, the address of the variable is taken to use as
* the Datum. In the pass-by-val case, these will be the same as the non-Fast
- * macros.
+ * functions.
*/
+static inline Datum
+Int64GetDatumFast(int64 X)
+{
+#ifdef USE_FLOAT8_BYVAL
+ return Int64GetDatum(X);
+#else
+ return PointerGetDatum(&X);
+#endif
+}
+
+static inline Datum
+Float8GetDatumFast(float8 X)
+{
#ifdef USE_FLOAT8_BYVAL
-#define Int64GetDatumFast(X) Int64GetDatum(X)
-#define Float8GetDatumFast(X) Float8GetDatum(X)
+ return Float8GetDatum(X);
#else
-#define Int64GetDatumFast(X) PointerGetDatum(&(X))
-#define Float8GetDatumFast(X) PointerGetDatum(&(X))
+ return PointerGetDatum(&X);
#endif
+}
/* ----------------------------------------------------------------
diff --git a/src/include/tsearch/ts_type.h b/src/include/tsearch/ts_type.h
index 689b2d1cfb..f1ec84702d 100644
--- a/src/include/tsearch/ts_type.h
+++ b/src/include/tsearch/ts_type.h
@@ -111,12 +111,27 @@ typedef TSVectorData *TSVector;
#define POSDATAPTR(x,e) (_POSVECPTR(x,e)->pos)
/*
- * fmgr interface macros
+ * fmgr interface functions
*/
-#define DatumGetTSVector(X) ((TSVector) PG_DETOAST_DATUM(X))
-#define DatumGetTSVectorCopy(X) ((TSVector) PG_DETOAST_DATUM_COPY(X))
-#define TSVectorGetDatum(X) PointerGetDatum(X)
+static inline TSVector
+DatumGetTSVector(Datum X)
+{
+ return (TSVector) PG_DETOAST_DATUM(X);
+}
+
+static inline TSVector
+DatumGetTSVectorCopy(Datum X)
+{
+ return (TSVector) PG_DETOAST_DATUM_COPY(X);
+}
+
+static inline Datum
+TSVectorGetDatum(const TSVectorData *X)
+{
+ return PointerGetDatum(X);
+}
+
#define PG_GETARG_TSVECTOR(n) DatumGetTSVector(PG_GETARG_DATUM(n))
#define PG_GETARG_TSVECTOR_COPY(n) DatumGetTSVectorCopy(PG_GETARG_DATUM(n))
#define PG_RETURN_TSVECTOR(x) return TSVectorGetDatum(x)
@@ -227,14 +242,29 @@ typedef TSQueryData *TSQuery;
#define GETOPERAND(x) ( (char*)GETQUERY(x) + ((TSQuery)(x))->size * sizeof(QueryItem) )
/*
- * fmgr interface macros
+ * fmgr interface functions
* Note, TSQuery type marked as plain storage, so it can't be toasted
* but PG_DETOAST_DATUM_COPY is used for simplicity
*/
-#define DatumGetTSQuery(X) ((TSQuery) DatumGetPointer(X))
-#define DatumGetTSQueryCopy(X) ((TSQuery) PG_DETOAST_DATUM_COPY(X))
-#define TSQueryGetDatum(X) PointerGetDatum(X)
+static inline TSQuery
+DatumGetTSQuery(Datum X)
+{
+ return (TSQuery) DatumGetPointer(X);
+}
+
+static inline TSQuery
+DatumGetTSQueryCopy(Datum X)
+{
+ return (TSQuery) PG_DETOAST_DATUM_COPY(X);
+}
+
+static inline Datum
+TSQueryGetDatum(const TSQueryData *X)
+{
+ return PointerGetDatum(X);
+}
+
#define PG_GETARG_TSQUERY(n) DatumGetTSQuery(PG_GETARG_DATUM(n))
#define PG_GETARG_TSQUERY_COPY(n) DatumGetTSQueryCopy(PG_GETARG_DATUM(n))
#define PG_RETURN_TSQUERY(x) return TSQueryGetDatum(x)
diff --git a/src/include/tsearch/ts_utils.h b/src/include/tsearch/ts_utils.h
index c36c711dae..26cc415aa8 100644
--- a/src/include/tsearch/ts_utils.h
+++ b/src/include/tsearch/ts_utils.h
@@ -243,8 +243,18 @@ typedef uint64 TSQuerySign;
#define TSQS_SIGLEN (sizeof(TSQuerySign)*BITS_PER_BYTE)
-#define TSQuerySignGetDatum(X) Int64GetDatum((int64) (X))
-#define DatumGetTSQuerySign(X) ((TSQuerySign) DatumGetInt64(X))
+static inline Datum
+TSQuerySignGetDatum(TSQuerySign X)
+{
+ return Int64GetDatum((int64) X);
+}
+
+static inline TSQuerySign
+DatumGetTSQuerySign(Datum X)
+{
+ return (TSQuerySign) DatumGetInt64(X);
+}
+
#define PG_RETURN_TSQUERYSIGN(X) return TSQuerySignGetDatum(X)
#define PG_GETARG_TSQUERYSIGN(n) DatumGetTSQuerySign(PG_GETARG_DATUM(n))
diff --git a/src/include/utils/cash.h b/src/include/utils/cash.h
index 2e332d83b1..55d45fadd4 100644
--- a/src/include/utils/cash.h
+++ b/src/include/utils/cash.h
@@ -17,8 +17,18 @@
typedef int64 Cash;
/* Cash is pass-by-reference if and only if int64 is */
-#define DatumGetCash(X) ((Cash) DatumGetInt64(X))
-#define CashGetDatum(X) Int64GetDatum(X)
+static inline Cash
+DatumGetCash(Datum X)
+{
+ return DatumGetInt64(X);
+}
+
+static inline Datum
+CashGetDatum(Cash X)
+{
+ return Int64GetDatum(X);
+}
+
#define PG_GETARG_CASH(n) DatumGetCash(PG_GETARG_DATUM(n))
#define PG_RETURN_CASH(x) return CashGetDatum(x)
diff --git a/src/include/utils/date.h b/src/include/utils/date.h
index 3991da41f0..0bbe889128 100644
--- a/src/include/utils/date.h
+++ b/src/include/utils/date.h
@@ -45,18 +45,46 @@ typedef struct
#define MAX_TIME_PRECISION 6
/*
- * Macros for fmgr-callable functions.
+ * Functions for fmgr-callable functions.
*
* For TimeADT, we make use of the same support routines as for int64.
* Therefore TimeADT is pass-by-reference if and only if int64 is!
*/
-#define DatumGetDateADT(X) ((DateADT) DatumGetInt32(X))
-#define DatumGetTimeADT(X) ((TimeADT) DatumGetInt64(X))
-#define DatumGetTimeTzADTP(X) ((TimeTzADT *) DatumGetPointer(X))
+static inline DateADT
+DatumGetDateADT(Datum X)
+{
+ return (DateADT) DatumGetInt32(X);
+}
+
+static inline TimeADT
+DatumGetTimeADT(Datum X)
+{
+ return (TimeADT) DatumGetInt64(X);
+}
+
+static inline TimeTzADT *
+DatumGetTimeTzADTP(Datum X)
+{
+ return (TimeTzADT *) DatumGetPointer(X);
+}
-#define DateADTGetDatum(X) Int32GetDatum(X)
-#define TimeADTGetDatum(X) Int64GetDatum(X)
-#define TimeTzADTPGetDatum(X) PointerGetDatum(X)
+static inline Datum
+DateADTGetDatum(DateADT X)
+{
+ return Int32GetDatum(X);
+}
+
+static inline Datum
+TimeADTGetDatum(TimeADT X)
+{
+ return Int64GetDatum(X);
+}
+
+static inline Datum
+TimeTzADTPGetDatum(const TimeTzADT *X)
+{
+ return PointerGetDatum(X);
+}
#define PG_GETARG_DATEADT(n) DatumGetDateADT(PG_GETARG_DATUM(n))
#define PG_GETARG_TIMEADT(n) DatumGetTimeADT(PG_GETARG_DATUM(n))
diff --git a/src/include/utils/expandeddatum.h b/src/include/utils/expandeddatum.h
index ffdb0c45bd..c4818eb054 100644
--- a/src/include/utils/expandeddatum.h
+++ b/src/include/utils/expandeddatum.h
@@ -133,8 +133,17 @@ struct ExpandedObjectHeader
* (More of these might be worth inlining later.)
*/
-#define EOHPGetRWDatum(eohptr) PointerGetDatum((eohptr)->eoh_rw_ptr)
-#define EOHPGetRODatum(eohptr) PointerGetDatum((eohptr)->eoh_ro_ptr)
+static inline Datum
+EOHPGetRWDatum(const struct ExpandedObjectHeader *eohptr)
+{
+ return PointerGetDatum(eohptr->eoh_rw_ptr);
+}
+
+static inline Datum
+EOHPGetRODatum(const struct ExpandedObjectHeader *eohptr)
+{
+ return PointerGetDatum(eohptr->eoh_ro_ptr);
+}
/* Does the Datum represent a writable expanded object? */
#define DatumIsReadWriteExpandedObject(d, isnull, typlen) \
diff --git a/src/include/utils/expandedrecord.h b/src/include/utils/expandedrecord.h
index be60e2ce53..aaf5b6bfd1 100644
--- a/src/include/utils/expandedrecord.h
+++ b/src/include/utils/expandedrecord.h
@@ -138,10 +138,20 @@ typedef struct ExpandedRecordHeader
MemoryContextCallback er_mcb;
} ExpandedRecordHeader;
-/* fmgr macros for expanded record objects */
+/* fmgr functions and macros for expanded record objects */
+static inline Datum
+ExpandedRecordGetDatum(const ExpandedRecordHeader *erh)
+{
+ return EOHPGetRWDatum(&erh->hdr);
+}
+
+static inline Datum
+ExpandedRecordGetRODatum(const ExpandedRecordHeader *erh)
+{
+ return EOHPGetRODatum(&erh->hdr);
+}
+
#define PG_GETARG_EXPANDED_RECORD(n) DatumGetExpandedRecord(PG_GETARG_DATUM(n))
-#define ExpandedRecordGetDatum(erh) EOHPGetRWDatum(&(erh)->hdr)
-#define ExpandedRecordGetRODatum(erh) EOHPGetRODatum(&(erh)->hdr)
#define PG_RETURN_EXPANDED_RECORD(x) PG_RETURN_DATUM(ExpandedRecordGetDatum(x))
/* assorted other macros */
diff --git a/src/include/utils/geo_decls.h b/src/include/utils/geo_decls.h
index 719030777e..05726778ad 100644
--- a/src/include/utils/geo_decls.h
+++ b/src/include/utils/geo_decls.h
@@ -166,48 +166,112 @@ typedef struct
} CIRCLE;
/*
- * fmgr interface macros
+ * fmgr interface functions
*
* Path and Polygon are toastable varlena types, the others are just
* fixed-size pass-by-reference types.
*/
-#define DatumGetPointP(X) ((Point *) DatumGetPointer(X))
-#define PointPGetDatum(X) PointerGetDatum(X)
+static inline Point *
+DatumGetPointP(Datum X)
+{
+ return (Point *) DatumGetPointer(X);
+}
+static inline Datum
+PointPGetDatum(const Point *X)
+{
+ return PointerGetDatum(X);
+}
#define PG_GETARG_POINT_P(n) DatumGetPointP(PG_GETARG_DATUM(n))
#define PG_RETURN_POINT_P(x) return PointPGetDatum(x)
-#define DatumGetLsegP(X) ((LSEG *) DatumGetPointer(X))
-#define LsegPGetDatum(X) PointerGetDatum(X)
+static inline LSEG *
+DatumGetLsegP(Datum X)
+{
+ return (LSEG *) DatumGetPointer(X);
+}
+static inline Datum
+LsegPGetDatum(const LSEG *X)
+{
+ return PointerGetDatum(X);
+}
#define PG_GETARG_LSEG_P(n) DatumGetLsegP(PG_GETARG_DATUM(n))
#define PG_RETURN_LSEG_P(x) return LsegPGetDatum(x)
-#define DatumGetPathP(X) ((PATH *) PG_DETOAST_DATUM(X))
-#define DatumGetPathPCopy(X) ((PATH *) PG_DETOAST_DATUM_COPY(X))
-#define PathPGetDatum(X) PointerGetDatum(X)
+static inline PATH *
+DatumGetPathP(Datum X)
+{
+ return (PATH *) PG_DETOAST_DATUM(X);
+}
+static inline PATH *
+DatumGetPathPCopy(Datum X)
+{
+ return (PATH *) PG_DETOAST_DATUM_COPY(X);
+}
+static inline Datum
+PathPGetDatum(const PATH *X)
+{
+ return PointerGetDatum(X);
+}
#define PG_GETARG_PATH_P(n) DatumGetPathP(PG_GETARG_DATUM(n))
#define PG_GETARG_PATH_P_COPY(n) DatumGetPathPCopy(PG_GETARG_DATUM(n))
#define PG_RETURN_PATH_P(x) return PathPGetDatum(x)
-#define DatumGetLineP(X) ((LINE *) DatumGetPointer(X))
-#define LinePGetDatum(X) PointerGetDatum(X)
+static inline LINE *
+DatumGetLineP(Datum X)
+{
+ return (LINE *) DatumGetPointer(X);
+}
+static inline Datum
+LinePGetDatum(const LINE *X)
+{
+ return PointerGetDatum(X);
+}
#define PG_GETARG_LINE_P(n) DatumGetLineP(PG_GETARG_DATUM(n))
#define PG_RETURN_LINE_P(x) return LinePGetDatum(x)
-#define DatumGetBoxP(X) ((BOX *) DatumGetPointer(X))
-#define BoxPGetDatum(X) PointerGetDatum(X)
+static inline BOX *
+DatumGetBoxP(Datum X)
+{
+ return (BOX *) DatumGetPointer(X);
+}
+static inline Datum
+BoxPGetDatum(const BOX *X)
+{
+ return PointerGetDatum(X);
+}
#define PG_GETARG_BOX_P(n) DatumGetBoxP(PG_GETARG_DATUM(n))
#define PG_RETURN_BOX_P(x) return BoxPGetDatum(x)
-#define DatumGetPolygonP(X) ((POLYGON *) PG_DETOAST_DATUM(X))
-#define DatumGetPolygonPCopy(X) ((POLYGON *) PG_DETOAST_DATUM_COPY(X))
-#define PolygonPGetDatum(X) PointerGetDatum(X)
+static inline POLYGON *
+DatumGetPolygonP(Datum X)
+{
+ return (POLYGON *) PG_DETOAST_DATUM(X);
+}
+static inline POLYGON *
+DatumGetPolygonPCopy(Datum X)
+{
+ return (POLYGON *) PG_DETOAST_DATUM_COPY(X);
+}
+static inline Datum
+PolygonPGetDatum(const POLYGON *X)
+{
+ return PointerGetDatum(X);
+}
#define PG_GETARG_POLYGON_P(n) DatumGetPolygonP(PG_GETARG_DATUM(n))
#define PG_GETARG_POLYGON_P_COPY(n) DatumGetPolygonPCopy(PG_GETARG_DATUM(n))
#define PG_RETURN_POLYGON_P(x) return PolygonPGetDatum(x)
-#define DatumGetCircleP(X) ((CIRCLE *) DatumGetPointer(X))
-#define CirclePGetDatum(X) PointerGetDatum(X)
+static inline CIRCLE *
+DatumGetCircleP(Datum X)
+{
+ return (CIRCLE *) DatumGetPointer(X);
+}
+static inline Datum
+CirclePGetDatum(const CIRCLE *X)
+{
+ return PointerGetDatum(X);
+}
#define PG_GETARG_CIRCLE_P(n) DatumGetCircleP(PG_GETARG_DATUM(n))
#define PG_RETURN_CIRCLE_P(x) return CirclePGetDatum(x)
diff --git a/src/include/utils/inet.h b/src/include/utils/inet.h
index b1ec9723df..5438cfaba5 100644
--- a/src/include/utils/inet.h
+++ b/src/include/utils/inet.h
@@ -119,23 +119,58 @@ typedef struct macaddr8
/*
* fmgr interface macros
*/
-#define DatumGetInetPP(X) ((inet *) PG_DETOAST_DATUM_PACKED(X))
-#define InetPGetDatum(X) PointerGetDatum(X)
+static inline inet *
+DatumGetInetPP(Datum X)
+{
+ return (inet *) PG_DETOAST_DATUM_PACKED(X);
+}
+
+static inline Datum
+InetPGetDatum(const inet *X)
+{
+ return PointerGetDatum(X);
+}
+
#define PG_GETARG_INET_PP(n) DatumGetInetPP(PG_GETARG_DATUM(n))
#define PG_RETURN_INET_P(x) return InetPGetDatum(x)
+
/* obsolescent variants */
-#define DatumGetInetP(X) ((inet *) PG_DETOAST_DATUM(X))
+static inline inet *
+DatumGetInetP(Datum X)
+{
+ return (inet *) PG_DETOAST_DATUM(X);
+}
#define PG_GETARG_INET_P(n) DatumGetInetP(PG_GETARG_DATUM(n))
/* macaddr is a fixed-length pass-by-reference datatype */
-#define DatumGetMacaddrP(X) ((macaddr *) DatumGetPointer(X))
-#define MacaddrPGetDatum(X) PointerGetDatum(X)
+static inline macaddr *
+DatumGetMacaddrP(Datum X)
+{
+ return (macaddr *) DatumGetPointer(X);
+}
+
+static inline Datum
+MacaddrPGetDatum(const macaddr *X)
+{
+ return PointerGetDatum(X);
+}
+
#define PG_GETARG_MACADDR_P(n) DatumGetMacaddrP(PG_GETARG_DATUM(n))
#define PG_RETURN_MACADDR_P(x) return MacaddrPGetDatum(x)
/* macaddr8 is a fixed-length pass-by-reference datatype */
-#define DatumGetMacaddr8P(X) ((macaddr8 *) DatumGetPointer(X))
-#define Macaddr8PGetDatum(X) PointerGetDatum(X)
+static inline macaddr8 *
+DatumGetMacaddr8P(Datum X)
+{
+ return (macaddr8 *) DatumGetPointer(X);
+}
+
+static inline Datum
+Macaddr8PGetDatum(const macaddr8 *X)
+{
+ return PointerGetDatum(X);
+}
+
#define PG_GETARG_MACADDR8_P(n) DatumGetMacaddr8P(PG_GETARG_DATUM(n))
#define PG_RETURN_MACADDR8_P(x) return Macaddr8PGetDatum(x)
diff --git a/src/include/utils/jsonb.h b/src/include/utils/jsonb.h
index bae466b523..536d00b3b4 100644
--- a/src/include/utils/jsonb.h
+++ b/src/include/utils/jsonb.h
@@ -67,14 +67,6 @@ typedef enum
#define JGINFLAG_HASHED 0x10 /* OR'd into flag if value was hashed */
#define JGIN_MAXLENGTH 125 /* max length of text part before hashing */
-/* Convenience macros */
-#define DatumGetJsonbP(d) ((Jsonb *) PG_DETOAST_DATUM(d))
-#define DatumGetJsonbPCopy(d) ((Jsonb *) PG_DETOAST_DATUM_COPY(d))
-#define JsonbPGetDatum(p) PointerGetDatum(p)
-#define PG_GETARG_JSONB_P(x) DatumGetJsonbP(PG_GETARG_DATUM(x))
-#define PG_GETARG_JSONB_P_COPY(x) DatumGetJsonbPCopy(PG_GETARG_DATUM(x))
-#define PG_RETURN_JSONB_P(x) PG_RETURN_POINTER(x)
-
typedef struct JsonbPair JsonbPair;
typedef struct JsonbValue JsonbValue;
@@ -393,6 +385,29 @@ typedef enum /* type categories for datum_to_jsonb */
JSONBTYPE_OTHER /* all else */
} JsonbTypeCategory;
+/* Convenience macros */
+static inline Jsonb *
+DatumGetJsonbP(Datum d)
+{
+ return (Jsonb *) PG_DETOAST_DATUM(d);
+}
+
+static inline Jsonb *
+DatumGetJsonbPCopy(Datum d)
+{
+ return (Jsonb *) PG_DETOAST_DATUM_COPY(d);
+}
+
+static inline Datum
+JsonbPGetDatum(const Jsonb *p)
+{
+ return PointerGetDatum(p);
+}
+
+#define PG_GETARG_JSONB_P(x) DatumGetJsonbP(PG_GETARG_DATUM(x))
+#define PG_GETARG_JSONB_P_COPY(x) DatumGetJsonbPCopy(PG_GETARG_DATUM(x))
+#define PG_RETURN_JSONB_P(x) PG_RETURN_POINTER(x)
+
/* Support functions */
extern uint32 getJsonbOffset(const JsonbContainer *jc, int index);
extern uint32 getJsonbLength(const JsonbContainer *jc, int index);
diff --git a/src/include/utils/jsonpath.h b/src/include/utils/jsonpath.h
index 8e79b8dc9f..ea32a8f072 100644
--- a/src/include/utils/jsonpath.h
+++ b/src/include/utils/jsonpath.h
@@ -32,8 +32,18 @@ typedef struct
#define JSONPATH_LAX (0x80000000)
#define JSONPATH_HDRSZ (offsetof(JsonPath, data))
-#define DatumGetJsonPathP(d) ((JsonPath *) DatumGetPointer(PG_DETOAST_DATUM(d)))
-#define DatumGetJsonPathPCopy(d) ((JsonPath *) DatumGetPointer(PG_DETOAST_DATUM_COPY(d)))
+static inline JsonPath *
+DatumGetJsonPathP(Datum d)
+{
+ return (JsonPath *) PG_DETOAST_DATUM(d);
+}
+
+static inline JsonPath *
+DatumGetJsonPathPCopy(Datum d)
+{
+ return (JsonPath *) PG_DETOAST_DATUM_COPY(d);
+}
+
#define PG_GETARG_JSONPATH_P(x) DatumGetJsonPathP(PG_GETARG_DATUM(x))
#define PG_GETARG_JSONPATH_P_COPY(x) DatumGetJsonPathPCopy(PG_GETARG_DATUM(x))
#define PG_RETURN_JSONPATH_P(p) PG_RETURN_POINTER(p)
diff --git a/src/include/utils/multirangetypes.h b/src/include/utils/multirangetypes.h
index 915330f990..23bce0005f 100644
--- a/src/include/utils/multirangetypes.h
+++ b/src/include/utils/multirangetypes.h
@@ -42,11 +42,26 @@ typedef struct
#define MultirangeIsEmpty(mr) ((mr)->rangeCount == 0)
/*
- * fmgr macros for multirange type objects
+ * fmgr functions for multirange type objects
*/
-#define DatumGetMultirangeTypeP(X) ((MultirangeType *) PG_DETOAST_DATUM(X))
-#define DatumGetMultirangeTypePCopy(X) ((MultirangeType *) PG_DETOAST_DATUM_COPY(X))
-#define MultirangeTypePGetDatum(X) PointerGetDatum(X)
+static inline MultirangeType *
+DatumGetMultirangeTypeP(Datum X)
+{
+ return (MultirangeType *) PG_DETOAST_DATUM(X);
+}
+
+static inline MultirangeType *
+DatumGetMultirangeTypePCopy(Datum X)
+{
+ return (MultirangeType *) PG_DETOAST_DATUM_COPY(X);
+}
+
+static inline Datum
+MultirangeTypePGetDatum(const MultirangeType *X)
+{
+ return PointerGetDatum(X);
+}
+
#define PG_GETARG_MULTIRANGE_P(n) DatumGetMultirangeTypeP(PG_GETARG_DATUM(n))
#define PG_GETARG_MULTIRANGE_P_COPY(n) DatumGetMultirangeTypePCopy(PG_GETARG_DATUM(n))
#define PG_RETURN_MULTIRANGE_P(x) return MultirangeTypePGetDatum(x)
diff --git a/src/include/utils/numeric.h b/src/include/utils/numeric.h
index 3caa74dfe7..88c62c5348 100644
--- a/src/include/utils/numeric.h
+++ b/src/include/utils/numeric.h
@@ -56,9 +56,24 @@ typedef struct NumericData *Numeric;
* fmgr interface macros
*/
-#define DatumGetNumeric(X) ((Numeric) PG_DETOAST_DATUM(X))
-#define DatumGetNumericCopy(X) ((Numeric) PG_DETOAST_DATUM_COPY(X))
-#define NumericGetDatum(X) PointerGetDatum(X)
+static inline Numeric
+DatumGetNumeric(Datum X)
+{
+ return (Numeric) PG_DETOAST_DATUM(X);
+}
+
+static inline Numeric
+DatumGetNumericCopy(Datum X)
+{
+ return (Numeric) PG_DETOAST_DATUM_COPY(X);
+}
+
+static inline Datum
+NumericGetDatum(Numeric X)
+{
+ return PointerGetDatum(X);
+}
+
#define PG_GETARG_NUMERIC(n) DatumGetNumeric(PG_GETARG_DATUM(n))
#define PG_GETARG_NUMERIC_COPY(n) DatumGetNumericCopy(PG_GETARG_DATUM(n))
#define PG_RETURN_NUMERIC(x) return NumericGetDatum(x)
diff --git a/src/include/utils/pg_lsn.h b/src/include/utils/pg_lsn.h
index 7b708f1073..5313afbfe2 100644
--- a/src/include/utils/pg_lsn.h
+++ b/src/include/utils/pg_lsn.h
@@ -18,8 +18,17 @@
#include "access/xlogdefs.h"
#include "fmgr.h"
-#define DatumGetLSN(X) ((XLogRecPtr) DatumGetInt64(X))
-#define LSNGetDatum(X) (Int64GetDatum((int64) (X)))
+static inline XLogRecPtr
+DatumGetLSN(Datum X)
+{
+ return (XLogRecPtr) DatumGetInt64(X);
+}
+
+static inline Datum
+LSNGetDatum(XLogRecPtr X)
+{
+ return Int64GetDatum((int64) X);
+}
#define PG_GETARG_LSN(n) DatumGetLSN(PG_GETARG_DATUM(n))
#define PG_RETURN_LSN(x) return LSNGetDatum(x)
diff --git a/src/include/utils/rangetypes.h b/src/include/utils/rangetypes.h
index 993fad4fc2..661e4892ec 100644
--- a/src/include/utils/rangetypes.h
+++ b/src/include/utils/rangetypes.h
@@ -68,11 +68,26 @@ typedef struct
} RangeBound;
/*
- * fmgr macros for range type objects
+ * fmgr functions for range type objects
*/
-#define DatumGetRangeTypeP(X) ((RangeType *) PG_DETOAST_DATUM(X))
-#define DatumGetRangeTypePCopy(X) ((RangeType *) PG_DETOAST_DATUM_COPY(X))
-#define RangeTypePGetDatum(X) PointerGetDatum(X)
+static inline RangeType *
+DatumGetRangeTypeP(Datum X)
+{
+ return (RangeType *) PG_DETOAST_DATUM(X);
+}
+
+static inline RangeType *
+DatumGetRangeTypePCopy(Datum X)
+{
+ return (RangeType *) PG_DETOAST_DATUM_COPY(X);
+}
+
+static inline Datum
+RangeTypePGetDatum(const RangeType *X)
+{
+ return PointerGetDatum(X);
+}
+
#define PG_GETARG_RANGE_P(n) DatumGetRangeTypeP(PG_GETARG_DATUM(n))
#define PG_GETARG_RANGE_P_COPY(n) DatumGetRangeTypePCopy(PG_GETARG_DATUM(n))
#define PG_RETURN_RANGE_P(x) return RangeTypePGetDatum(x)
diff --git a/src/include/utils/timestamp.h b/src/include/utils/timestamp.h
index edf3a97318..3602638bdc 100644
--- a/src/include/utils/timestamp.h
+++ b/src/include/utils/timestamp.h
@@ -19,18 +19,46 @@
/*
- * Macros for fmgr-callable functions.
+ * Functions for fmgr-callable functions.
*
* For Timestamp, we make use of the same support routines as for int64.
* Therefore Timestamp is pass-by-reference if and only if int64 is!
*/
-#define DatumGetTimestamp(X) ((Timestamp) DatumGetInt64(X))
-#define DatumGetTimestampTz(X) ((TimestampTz) DatumGetInt64(X))
-#define DatumGetIntervalP(X) ((Interval *) DatumGetPointer(X))
-
-#define TimestampGetDatum(X) Int64GetDatum(X)
-#define TimestampTzGetDatum(X) Int64GetDatum(X)
-#define IntervalPGetDatum(X) PointerGetDatum(X)
+static inline Timestamp
+DatumGetTimestamp(Datum X)
+{
+ return (Timestamp) DatumGetInt64(X);
+}
+
+static inline TimestampTz
+DatumGetTimestampTz(Datum X)
+{
+ return (TimestampTz) DatumGetInt64(X);
+}
+
+static inline Interval *
+DatumGetIntervalP(Datum X)
+{
+ return (Interval *) DatumGetPointer(X);
+}
+
+static inline Datum
+TimestampGetDatum(Timestamp X)
+{
+ return Int64GetDatum(X);
+}
+
+static inline Datum
+TimestampTzGetDatum(TimestampTz X)
+{
+ return Int64GetDatum(X);
+}
+
+static inline Datum
+IntervalPGetDatum(const Interval *X)
+{
+ return PointerGetDatum(X);
+}
#define PG_GETARG_TIMESTAMP(n) DatumGetTimestamp(PG_GETARG_DATUM(n))
#define PG_GETARG_TIMESTAMPTZ(n) DatumGetTimestampTz(PG_GETARG_DATUM(n))
diff --git a/src/include/utils/uuid.h b/src/include/utils/uuid.h
index 0029da4e7f..69f4332d0f 100644
--- a/src/include/utils/uuid.h
+++ b/src/include/utils/uuid.h
@@ -23,9 +23,20 @@ typedef struct pg_uuid_t
} pg_uuid_t;
/* fmgr interface macros */
-#define UUIDPGetDatum(X) PointerGetDatum(X)
+static inline Datum
+UUIDPGetDatum(const pg_uuid_t *X)
+{
+ return PointerGetDatum(X);
+}
+
#define PG_RETURN_UUID_P(X) return UUIDPGetDatum(X)
-#define DatumGetUUIDP(X) ((pg_uuid_t *) DatumGetPointer(X))
+
+static inline pg_uuid_t *
+DatumGetUUIDP(Datum X)
+{
+ return (pg_uuid_t *) DatumGetPointer(X);
+}
+
#define PG_GETARG_UUID_P(X) DatumGetUUIDP(PG_GETARG_DATUM(X))
#endif /* UUID_H */
diff --git a/src/include/utils/varbit.h b/src/include/utils/varbit.h
index 039ba860cf..e65e7f65e7 100644
--- a/src/include/utils/varbit.h
+++ b/src/include/utils/varbit.h
@@ -41,9 +41,24 @@ typedef struct
* BIT and BIT VARYING are toastable varlena types. They are the same
* as far as representation goes, so we just have one set of macros.
*/
-#define DatumGetVarBitP(X) ((VarBit *) PG_DETOAST_DATUM(X))
-#define DatumGetVarBitPCopy(X) ((VarBit *) PG_DETOAST_DATUM_COPY(X))
-#define VarBitPGetDatum(X) PointerGetDatum(X)
+static inline VarBit *
+DatumGetVarBitP(Datum X)
+{
+ return (VarBit *) PG_DETOAST_DATUM(X);
+}
+
+static inline VarBit *
+DatumGetVarBitPCopy(Datum X)
+{
+ return (VarBit *) PG_DETOAST_DATUM_COPY(X);
+}
+
+static inline Datum
+VarBitPGetDatum(const VarBit *X)
+{
+ return PointerGetDatum(X);
+}
+
#define PG_GETARG_VARBIT_P(n) DatumGetVarBitP(PG_GETARG_DATUM(n))
#define PG_GETARG_VARBIT_P_COPY(n) DatumGetVarBitPCopy(PG_GETARG_DATUM(n))
#define PG_RETURN_VARBIT_P(x) return VarBitPGetDatum(x)
diff --git a/src/include/utils/xid8.h b/src/include/utils/xid8.h
index b702fc1a91..9c5ce241db 100644
--- a/src/include/utils/xid8.h
+++ b/src/include/utils/xid8.h
@@ -14,8 +14,18 @@
#include "access/transam.h"
-#define DatumGetFullTransactionId(X) (FullTransactionIdFromU64(DatumGetUInt64(X)))
-#define FullTransactionIdGetDatum(X) (UInt64GetDatum(U64FromFullTransactionId(X)))
+static inline FullTransactionId
+DatumGetFullTransactionId(Datum X)
+{
+ return FullTransactionIdFromU64(DatumGetUInt64(X));
+}
+
+static inline Datum
+FullTransactionIdGetDatum(FullTransactionId X)
+{
+ return UInt64GetDatum(U64FromFullTransactionId(X));
+}
+
#define PG_GETARG_FULLTRANSACTIONID(X) DatumGetFullTransactionId(PG_GETARG_DATUM(X))
#define PG_RETURN_FULLTRANSACTIONID(X) return FullTransactionIdGetDatum(X)
diff --git a/src/include/utils/xml.h b/src/include/utils/xml.h
index 6620a62619..c71174d204 100644
--- a/src/include/utils/xml.h
+++ b/src/include/utils/xml.h
@@ -47,8 +47,17 @@ typedef enum
/* struct PgXmlErrorContext is private to xml.c */
typedef struct PgXmlErrorContext PgXmlErrorContext;
-#define DatumGetXmlP(X) ((xmltype *) PG_DETOAST_DATUM(X))
-#define XmlPGetDatum(X) PointerGetDatum(X)
+static inline xmltype *
+DatumGetXmlP(Datum X)
+{
+ return (xmltype *) PG_DETOAST_DATUM(X);
+}
+
+static inline Datum
+XmlPGetDatum(const xmltype *X)
+{
+ return PointerGetDatum(X);
+}
#define PG_GETARG_XML_P(n) DatumGetXmlP(PG_GETARG_DATUM(n))
#define PG_RETURN_XML_P(x) PG_RETURN_POINTER(x)
--
2.37.2
^ permalink raw reply [nested|flat] 92+ messages in thread
* Re: Convert *GetDatum() and DatumGet*() macros to inline functions
2022-08-28 15:55 Convert *GetDatum() and DatumGet*() macros to inline functions Peter Eisentraut <[email protected]>
2022-08-30 13:36 ` Re: Convert *GetDatum() and DatumGet*() macros to inline functions Aleksander Alekseev <[email protected]>
2022-08-30 13:46 ` Re: Convert *GetDatum() and DatumGet*() macros to inline functions Tom Lane <[email protected]>
2022-08-30 13:59 ` Re: Convert *GetDatum() and DatumGet*() macros to inline functions Aleksander Alekseev <[email protected]>
2022-08-30 14:13 ` Re: Convert *GetDatum() and DatumGet*() macros to inline functions Tom Lane <[email protected]>
2022-08-30 14:16 ` Re: Convert *GetDatum() and DatumGet*() macros to inline functions Robert Haas <[email protected]>
2022-08-30 14:25 ` Re: Convert *GetDatum() and DatumGet*() macros to inline functions Aleksander Alekseev <[email protected]>
2022-08-30 14:28 ` Re: Convert *GetDatum() and DatumGet*() macros to inline functions Tom Lane <[email protected]>
2022-08-30 14:52 ` Re: Convert *GetDatum() and DatumGet*() macros to inline functions Aleksander Alekseev <[email protected]>
2022-08-30 18:15 ` Re: Convert *GetDatum() and DatumGet*() macros to inline functions Aleksander Alekseev <[email protected]>
@ 2022-09-05 12:57 ` Peter Eisentraut <[email protected]>
2022-09-05 14:20 ` Re: Convert *GetDatum() and DatumGet*() macros to inline functions Aleksander Alekseev <[email protected]>
0 siblings, 1 reply; 92+ messages in thread
From: Peter Eisentraut @ 2022-09-05 12:57 UTC (permalink / raw)
To: Aleksander Alekseev <[email protected]>; pgsql-hackers
On 30.08.22 20:15, Aleksander Alekseev wrote:
>> Here is v3 with silenced compiler warnings.
>
> Some more warnings were reported by cfbot, so here is v4. Apologies
> for the noise.
Looking at these warnings you are fixing, I think there is a small
problem we need to address.
I have defined PointerGetDatum() with a const argument:
PointerGetDatum(const void *X)
This is because in some places the thing that is being passed into that
is itself defined as const, so this is the clean way to avoid warnings
about dropping constness.
However, some support functions for gist and text search pass back
return values via pointer arguments, like
DirectFunctionCall3(g_int_same,
entry->key,
PointerGetDatum(query),
PointerGetDatum(&retval));
The compiler you are using apparently thinks that passing &retval to a
const pointer argument cannot change retval, which seems quite
reasonable. But that isn't actually what's happening here, so we're
lying a bit.
(Which compiler is that, by the way?)
I think to resolve that we could either
1. Not define PointerGetDatum() with a const argument, and just sprinkle
in a few unconstify calls where necessary.
2. Maybe add a NonconstPointerGetDatum() for those few cases where
pointer arguments are used for return values.
3. Go with your patch and just fix up the warnings about uninitialized
variables. But that seems the least principled to me.
^ permalink raw reply [nested|flat] 92+ messages in thread
* Re: Convert *GetDatum() and DatumGet*() macros to inline functions
2022-08-28 15:55 Convert *GetDatum() and DatumGet*() macros to inline functions Peter Eisentraut <[email protected]>
2022-08-30 13:36 ` Re: Convert *GetDatum() and DatumGet*() macros to inline functions Aleksander Alekseev <[email protected]>
2022-08-30 13:46 ` Re: Convert *GetDatum() and DatumGet*() macros to inline functions Tom Lane <[email protected]>
2022-08-30 13:59 ` Re: Convert *GetDatum() and DatumGet*() macros to inline functions Aleksander Alekseev <[email protected]>
2022-08-30 14:13 ` Re: Convert *GetDatum() and DatumGet*() macros to inline functions Tom Lane <[email protected]>
2022-08-30 14:16 ` Re: Convert *GetDatum() and DatumGet*() macros to inline functions Robert Haas <[email protected]>
2022-08-30 14:25 ` Re: Convert *GetDatum() and DatumGet*() macros to inline functions Aleksander Alekseev <[email protected]>
2022-08-30 14:28 ` Re: Convert *GetDatum() and DatumGet*() macros to inline functions Tom Lane <[email protected]>
2022-08-30 14:52 ` Re: Convert *GetDatum() and DatumGet*() macros to inline functions Aleksander Alekseev <[email protected]>
2022-08-30 18:15 ` Re: Convert *GetDatum() and DatumGet*() macros to inline functions Aleksander Alekseev <[email protected]>
2022-09-05 12:57 ` Re: Convert *GetDatum() and DatumGet*() macros to inline functions Peter Eisentraut <[email protected]>
@ 2022-09-05 14:20 ` Aleksander Alekseev <[email protected]>
2022-09-08 09:26 ` Re: Convert *GetDatum() and DatumGet*() macros to inline functions Aleksander Alekseev <[email protected]>
0 siblings, 1 reply; 92+ messages in thread
From: Aleksander Alekseev @ 2022-09-05 14:20 UTC (permalink / raw)
To: Peter Eisentraut <[email protected]>; pgsql-hackers
Hi Peter,
> Which compiler is that, by the way?
The warnings were reported by cfbot during the "clang_warning" step.
According to the logs:
```
using compiler=Debian clang version 11.0.1-2
```
Personally I use Clang 14 on MacOS and I don't get these warnings.
> I think to resolve that we could either
>
> 1. Not define PointerGetDatum() with a const argument, and just sprinkle
> in a few unconstify calls where necessary.
>
> 2. Maybe add a NonconstPointerGetDatum() for those few cases where
> pointer arguments are used for return values.
>
> 3. Go with your patch and just fix up the warnings about uninitialized
> variables. But that seems the least principled to me.
IMO the 3rd option is the lesser evil. Initializing four bools/ints in
order to make Clang 11 happy doesn't strike me as such a big deal. At
least until somebody reports a bottleneck for this particular reason.
We can optimize the code when and if this will happen.
--
Best regards,
Aleksander Alekseev
^ permalink raw reply [nested|flat] 92+ messages in thread
* Re: Convert *GetDatum() and DatumGet*() macros to inline functions
2022-08-28 15:55 Convert *GetDatum() and DatumGet*() macros to inline functions Peter Eisentraut <[email protected]>
2022-08-30 13:36 ` Re: Convert *GetDatum() and DatumGet*() macros to inline functions Aleksander Alekseev <[email protected]>
2022-08-30 13:46 ` Re: Convert *GetDatum() and DatumGet*() macros to inline functions Tom Lane <[email protected]>
2022-08-30 13:59 ` Re: Convert *GetDatum() and DatumGet*() macros to inline functions Aleksander Alekseev <[email protected]>
2022-08-30 14:13 ` Re: Convert *GetDatum() and DatumGet*() macros to inline functions Tom Lane <[email protected]>
2022-08-30 14:16 ` Re: Convert *GetDatum() and DatumGet*() macros to inline functions Robert Haas <[email protected]>
2022-08-30 14:25 ` Re: Convert *GetDatum() and DatumGet*() macros to inline functions Aleksander Alekseev <[email protected]>
2022-08-30 14:28 ` Re: Convert *GetDatum() and DatumGet*() macros to inline functions Tom Lane <[email protected]>
2022-08-30 14:52 ` Re: Convert *GetDatum() and DatumGet*() macros to inline functions Aleksander Alekseev <[email protected]>
2022-08-30 18:15 ` Re: Convert *GetDatum() and DatumGet*() macros to inline functions Aleksander Alekseev <[email protected]>
2022-09-05 12:57 ` Re: Convert *GetDatum() and DatumGet*() macros to inline functions Peter Eisentraut <[email protected]>
2022-09-05 14:20 ` Re: Convert *GetDatum() and DatumGet*() macros to inline functions Aleksander Alekseev <[email protected]>
@ 2022-09-08 09:26 ` Aleksander Alekseev <[email protected]>
2022-09-12 15:59 ` Re: Convert *GetDatum() and DatumGet*() macros to inline functions Peter Eisentraut <[email protected]>
0 siblings, 1 reply; 92+ messages in thread
From: Aleksander Alekseev @ 2022-09-08 09:26 UTC (permalink / raw)
To: Peter Eisentraut <[email protected]>; pgsql-hackers
Hi Peter,
> > 3. Go with your patch and just fix up the warnings about uninitialized
> > variables. But that seems the least principled to me.
>
> IMO the 3rd option is the lesser evil. Initializing four bools/ints in
> order to make Clang 11 happy doesn't strike me as such a big deal. At
> least until somebody reports a bottleneck for this particular reason.
> We can optimize the code when and if this will happen.
Since the first patch was applied, cfbot now complains that it can't
apply the patchset. Here is the rebased version.
--
Best regards,
Aleksander Alekseev
Attachments:
[application/octet-stream] v5-0001-Convert-GetDatum-and-DatumGet-macros-to-inline-fu.patch (41.7K, ../../CAJ7c6TNYVEvvRVS9OwZEPEQBDRaFUG+1yr4Sx3Jb_rbAMfohyQ@mail.gmail.com/2-v5-0001-Convert-GetDatum-and-DatumGet-macros-to-inline-fu.patch)
download | inline diff:
From 9094c93a5192b1d0c8695e4e229724cfd74a25c0 Mon Sep 17 00:00:00 2001
From: Peter Eisentraut <[email protected]>
Date: Sun, 28 Aug 2022 10:47:38 +0200
Subject: [PATCH v5] Convert *GetDatum() and DatumGet*() macros to inline
functions
The previous macro implementations just cast the argument to a target
type but did not check whether the input type was appropriate. The
function implementation can do better type checking of the input type.
Peter Eisentraut, reviewed by Aleksander Alekseev
Discussion: http://postgr.es/m/8528fb7e-0aa2-6b54-85fb-0c0886dbd6ed%40enterprisedb.com
---
contrib/intarray/_int_gist.c | 2 +-
doc/src/sgml/xfunc.sgml | 2 +-
src/backend/access/gist/gistutil.c | 2 +-
src/backend/tsearch/ts_parse.c | 4 +-
src/backend/utils/mb/mbutils.c | 12 +-
src/include/access/gin.h | 14 +-
src/include/funcapi.h | 14 +-
src/include/postgres.h | 275 +++++++++++++++++++---------
src/include/tsearch/ts_type.h | 46 ++++-
src/include/tsearch/ts_utils.h | 14 +-
src/include/utils/cash.h | 14 +-
src/include/utils/date.h | 42 ++++-
src/include/utils/expandeddatum.h | 13 +-
src/include/utils/expandedrecord.h | 16 +-
src/include/utils/geo_decls.h | 98 ++++++++--
src/include/utils/inet.h | 49 ++++-
src/include/utils/jsonb.h | 31 +++-
src/include/utils/jsonpath.h | 14 +-
src/include/utils/multirangetypes.h | 23 ++-
src/include/utils/numeric.h | 21 ++-
src/include/utils/pg_lsn.h | 13 +-
src/include/utils/rangetypes.h | 23 ++-
src/include/utils/timestamp.h | 44 ++++-
src/include/utils/uuid.h | 15 +-
src/include/utils/varbit.h | 21 ++-
src/include/utils/xid8.h | 14 +-
src/include/utils/xml.h | 13 +-
27 files changed, 659 insertions(+), 190 deletions(-)
diff --git a/contrib/intarray/_int_gist.c b/contrib/intarray/_int_gist.c
index f1817a6cce..ad75bf8e8b 100644
--- a/contrib/intarray/_int_gist.c
+++ b/contrib/intarray/_int_gist.c
@@ -51,7 +51,7 @@ g_int_consistent(PG_FUNCTION_ARGS)
/* Oid subtype = PG_GETARG_OID(3); */
bool *recheck = (bool *) PG_GETARG_POINTER(4);
- bool retval;
+ bool retval = false; /* silence the compiler warning */
/* this is exact except for RTSameStrategyNumber */
*recheck = (strategy == RTSameStrategyNumber);
diff --git a/doc/src/sgml/xfunc.sgml b/doc/src/sgml/xfunc.sgml
index b8cefb9c2c..cf5810b3c1 100644
--- a/doc/src/sgml/xfunc.sgml
+++ b/doc/src/sgml/xfunc.sgml
@@ -2763,7 +2763,7 @@ c_overpaid(PG_FUNCTION_ARGS)
is null. <function>GetAttributeByName</function> returns a <type>Datum</type>
value that you can convert to the proper data type by using the
appropriate <function>DatumGet<replaceable>XXX</replaceable>()</function>
- macro. Note that the return value is meaningless if the null flag is
+ function. Note that the return value is meaningless if the null flag is
set; always check the null flag before trying to do anything with the
result.
</para>
diff --git a/src/backend/access/gist/gistutil.c b/src/backend/access/gist/gistutil.c
index d4bf0c7563..8bdd16fc81 100644
--- a/src/backend/access/gist/gistutil.c
+++ b/src/backend/access/gist/gistutil.c
@@ -280,7 +280,7 @@ gistMakeUnionKey(GISTSTATE *giststate, int attno,
bool
gistKeyIsEQ(GISTSTATE *giststate, int attno, Datum a, Datum b)
{
- bool result;
+ bool result = false; /* silence the compiler warning */
FunctionCall3Coll(&giststate->equalFn[attno],
giststate->supportCollation[attno],
diff --git a/src/backend/tsearch/ts_parse.c b/src/backend/tsearch/ts_parse.c
index 27b2cca2df..ff2f0737c8 100644
--- a/src/backend/tsearch/ts_parse.c
+++ b/src/backend/tsearch/ts_parse.c
@@ -354,7 +354,7 @@ void
parsetext(Oid cfgId, ParsedText *prs, char *buf, int buflen)
{
int type,
- lenlemm;
+ lenlemm = 0; /* silence the compiler warning */
char *lemm = NULL;
LexizeData ldata;
TSLexeme *norms;
@@ -529,7 +529,7 @@ void
hlparsetext(Oid cfgId, HeadlineParsedText *prs, TSQuery query, char *buf, int buflen)
{
int type,
- lenlemm;
+ lenlemm = 0; /* silence the compiler warning */
char *lemm = NULL;
LexizeData ldata;
TSLexeme *norms;
diff --git a/src/backend/utils/mb/mbutils.c b/src/backend/utils/mb/mbutils.c
index 0543c574c6..474ab476f5 100644
--- a/src/backend/utils/mb/mbutils.c
+++ b/src/backend/utils/mb/mbutils.c
@@ -409,8 +409,8 @@ pg_do_encoding_conversion(unsigned char *src, int len,
(void) OidFunctionCall6(proc,
Int32GetDatum(src_encoding),
Int32GetDatum(dest_encoding),
- CStringGetDatum(src),
- CStringGetDatum(result),
+ CStringGetDatum((char *) src),
+ CStringGetDatum((char *) result),
Int32GetDatum(len),
BoolGetDatum(false));
@@ -485,8 +485,8 @@ pg_do_encoding_conversion_buf(Oid proc,
result = OidFunctionCall6(proc,
Int32GetDatum(src_encoding),
Int32GetDatum(dest_encoding),
- CStringGetDatum(src),
- CStringGetDatum(dest),
+ CStringGetDatum((char *) src),
+ CStringGetDatum((char *) dest),
Int32GetDatum(srclen),
BoolGetDatum(noError));
return DatumGetInt32(result);
@@ -910,8 +910,8 @@ pg_unicode_to_server(pg_wchar c, unsigned char *s)
FunctionCall6(Utf8ToServerConvProc,
Int32GetDatum(PG_UTF8),
Int32GetDatum(server_encoding),
- CStringGetDatum(c_as_utf8),
- CStringGetDatum(s),
+ CStringGetDatum((char *) c_as_utf8),
+ CStringGetDatum((char *) s),
Int32GetDatum(c_as_utf8_len),
BoolGetDatum(false));
}
diff --git a/src/include/access/gin.h b/src/include/access/gin.h
index aacc665fdc..fff861e219 100644
--- a/src/include/access/gin.h
+++ b/src/include/access/gin.h
@@ -62,8 +62,18 @@ typedef char GinTernaryValue;
#define GIN_MAYBE 2 /* don't know if item is present / don't know
* if matches */
-#define DatumGetGinTernaryValue(X) ((GinTernaryValue)(X))
-#define GinTernaryValueGetDatum(X) ((Datum)(X))
+static inline GinTernaryValue
+DatumGetGinTernaryValue(Datum X)
+{
+ return (GinTernaryValue) X;
+}
+
+static inline Datum
+GinTernaryValueGetDatum(GinTernaryValue X)
+{
+ return (Datum) X;
+}
+
#define PG_RETURN_GIN_TERNARY_VALUE(x) return GinTernaryValueGetDatum(x)
/* GUC parameters */
diff --git a/src/include/funcapi.h b/src/include/funcapi.h
index dc3d819a1c..8c2521a591 100644
--- a/src/include/funcapi.h
+++ b/src/include/funcapi.h
@@ -204,7 +204,7 @@ extern TupleDesc build_function_result_tupdesc_t(HeapTuple procTuple);
* Datum HeapTupleHeaderGetDatum(HeapTupleHeader tuple) - convert a
* HeapTupleHeader to a Datum.
*
- * Macro declarations:
+ * Inline declarations:
* HeapTupleGetDatum(HeapTuple tuple) - convert a HeapTuple to a Datum.
*
* Obsolete routines and macros:
@@ -217,10 +217,6 @@ extern TupleDesc build_function_result_tupdesc_t(HeapTuple procTuple);
*----------
*/
-#define HeapTupleGetDatum(tuple) HeapTupleHeaderGetDatum((tuple)->t_data)
-/* obsolete version of above */
-#define TupleGetDatum(_slot, _tuple) HeapTupleGetDatum(_tuple)
-
extern TupleDesc RelationNameGetTupleDesc(const char *relname);
extern TupleDesc TypeGetTupleDesc(Oid typeoid, List *colaliases);
@@ -230,6 +226,14 @@ extern AttInMetadata *TupleDescGetAttInMetadata(TupleDesc tupdesc);
extern HeapTuple BuildTupleFromCStrings(AttInMetadata *attinmeta, char **values);
extern Datum HeapTupleHeaderGetDatum(HeapTupleHeader tuple);
+static inline Datum
+HeapTupleGetDatum(const HeapTupleData *tuple)
+{
+ return HeapTupleHeaderGetDatum(tuple->t_data);
+}
+/* obsolete version of above */
+#define TupleGetDatum(_slot, _tuple) HeapTupleGetDatum(_tuple)
+
/*----------
* Support for Set Returning Functions (SRFs)
diff --git a/src/include/postgres.h b/src/include/postgres.h
index 13903fa022..45c9f6563c 100644
--- a/src/include/postgres.h
+++ b/src/include/postgres.h
@@ -24,7 +24,7 @@
* section description
* ------- ------------------------------------------------
* 1) variable-length datatypes (TOAST support)
- * 2) Datum type + support macros
+ * 2) Datum type + support functions
* 3) miscellaneous
*
* NOTES
@@ -395,7 +395,7 @@ typedef struct
/* ----------------------------------------------------------------
- * Section 2: Datum type + support macros
+ * Section 2: Datum type + support functions
* ----------------------------------------------------------------
*/
@@ -405,7 +405,7 @@ typedef struct
*
* sizeof(Datum) == sizeof(void *) == 4 or 8
*
- * The macros below and the analogous macros for other types should be used to
+ * The functions below and the analogous functions for other types should be used to
* convert between a Datum and the appropriate C type.
*/
@@ -434,8 +434,11 @@ typedef struct NullableDatum
*
* Note: any nonzero value will be considered true.
*/
-
-#define DatumGetBool(X) ((bool) ((X) != 0))
+static inline bool
+DatumGetBool(Datum X)
+{
+ return (X != 0);
+}
/*
* BoolGetDatum
@@ -443,162 +446,231 @@ typedef struct NullableDatum
*
* Note: any nonzero value will be considered true.
*/
-
-#define BoolGetDatum(X) ((Datum) ((X) ? 1 : 0))
+static inline Datum
+BoolGetDatum(bool X)
+{
+ return (Datum) (X ? 1 : 0);
+}
/*
* DatumGetChar
* Returns character value of a datum.
*/
-
-#define DatumGetChar(X) ((char) (X))
+static inline char
+DatumGetChar(Datum X)
+{
+ return (char) X;
+}
/*
* CharGetDatum
* Returns datum representation for a character.
*/
-
-#define CharGetDatum(X) ((Datum) (X))
+static inline Datum
+CharGetDatum(char X)
+{
+ return (Datum) X;
+}
/*
* Int8GetDatum
* Returns datum representation for an 8-bit integer.
*/
-
-#define Int8GetDatum(X) ((Datum) (X))
+static inline Datum
+Int8GetDatum(int8 X)
+{
+ return (Datum) X;
+}
/*
* DatumGetUInt8
* Returns 8-bit unsigned integer value of a datum.
*/
-
-#define DatumGetUInt8(X) ((uint8) (X))
+static inline uint8
+DatumGetUInt8(Datum X)
+{
+ return (uint8) X;
+}
/*
* UInt8GetDatum
* Returns datum representation for an 8-bit unsigned integer.
*/
-
-#define UInt8GetDatum(X) ((Datum) (X))
+static inline Datum
+UInt8GetDatum(uint8 X)
+{
+ return (Datum) X;
+}
/*
* DatumGetInt16
* Returns 16-bit integer value of a datum.
*/
-
-#define DatumGetInt16(X) ((int16) (X))
+static inline int16
+DatumGetInt16(Datum X)
+{
+ return (int16) X;
+}
/*
* Int16GetDatum
* Returns datum representation for a 16-bit integer.
*/
-
-#define Int16GetDatum(X) ((Datum) (X))
+static inline Datum
+Int16GetDatum(int16 X)
+{
+ return (Datum) X;
+}
/*
* DatumGetUInt16
* Returns 16-bit unsigned integer value of a datum.
*/
-
-#define DatumGetUInt16(X) ((uint16) (X))
+static inline uint16
+DatumGetUInt16(Datum X)
+{
+ return (uint16) X;
+}
/*
* UInt16GetDatum
* Returns datum representation for a 16-bit unsigned integer.
*/
-
-#define UInt16GetDatum(X) ((Datum) (X))
+static inline Datum
+UInt16GetDatum(uint16 X)
+{
+ return (Datum) X;
+}
/*
* DatumGetInt32
* Returns 32-bit integer value of a datum.
*/
-
-#define DatumGetInt32(X) ((int32) (X))
+static inline int32
+DatumGetInt32(Datum X)
+{
+ return (int32) X;
+}
/*
* Int32GetDatum
* Returns datum representation for a 32-bit integer.
*/
-
-#define Int32GetDatum(X) ((Datum) (X))
+static inline Datum
+Int32GetDatum(int32 X)
+{
+ return (Datum) X;
+}
/*
* DatumGetUInt32
* Returns 32-bit unsigned integer value of a datum.
*/
-
-#define DatumGetUInt32(X) ((uint32) (X))
+static inline uint32
+DatumGetUInt32(Datum X)
+{
+ return (uint32) X;
+}
/*
* UInt32GetDatum
* Returns datum representation for a 32-bit unsigned integer.
*/
-
-#define UInt32GetDatum(X) ((Datum) (X))
+static inline Datum
+UInt32GetDatum(uint32 X)
+{
+ return (Datum) X;
+}
/*
* DatumGetObjectId
* Returns object identifier value of a datum.
*/
-
-#define DatumGetObjectId(X) ((Oid) (X))
+static inline Oid
+DatumGetObjectId(Datum X)
+{
+ return (Oid) X;
+}
/*
* ObjectIdGetDatum
* Returns datum representation for an object identifier.
*/
-
-#define ObjectIdGetDatum(X) ((Datum) (X))
+static inline Datum
+ObjectIdGetDatum(Oid X)
+{
+ return (Datum) X;
+}
/*
* DatumGetTransactionId
* Returns transaction identifier value of a datum.
*/
-
-#define DatumGetTransactionId(X) ((TransactionId) (X))
+static inline TransactionId
+DatumGetTransactionId(Datum X)
+{
+ return (TransactionId) X;
+}
/*
* TransactionIdGetDatum
* Returns datum representation for a transaction identifier.
*/
-
-#define TransactionIdGetDatum(X) ((Datum) (X))
+static inline Datum
+TransactionIdGetDatum(TransactionId X)
+{
+ return (Datum) X;
+}
/*
* MultiXactIdGetDatum
* Returns datum representation for a multixact identifier.
*/
-
-#define MultiXactIdGetDatum(X) ((Datum) (X))
+static inline Datum
+MultiXactIdGetDatum(MultiXactId X)
+{
+ return (Datum) X;
+}
/*
* DatumGetCommandId
* Returns command identifier value of a datum.
*/
-
-#define DatumGetCommandId(X) ((CommandId) (X))
+static inline CommandId
+DatumGetCommandId(Datum X)
+{
+ return (CommandId) X;
+}
/*
* CommandIdGetDatum
* Returns datum representation for a command identifier.
*/
-
-#define CommandIdGetDatum(X) ((Datum) (X))
+static inline Datum
+CommandIdGetDatum(CommandId X)
+{
+ return (Datum) X;
+}
/*
* DatumGetPointer
* Returns pointer value of a datum.
*/
-
-#define DatumGetPointer(X) ((Pointer) (X))
+static inline Pointer
+DatumGetPointer(Datum X)
+{
+ return (Pointer) X;
+}
/*
* PointerGetDatum
* Returns datum representation for a pointer.
*/
-
-#define PointerGetDatum(X) ((Datum) (X))
+static inline Datum
+PointerGetDatum(const void *X)
+{
+ return (Datum) X;
+}
/*
* DatumGetCString
@@ -607,8 +679,11 @@ typedef struct NullableDatum
* Note: C string is not a full-fledged Postgres type at present,
* but type input functions use this conversion for their inputs.
*/
-
-#define DatumGetCString(X) ((char *) DatumGetPointer(X))
+static inline char *
+DatumGetCString(Datum X)
+{
+ return (char *) DatumGetPointer(X);
+}
/*
* CStringGetDatum
@@ -619,15 +694,21 @@ typedef struct NullableDatum
* Note: CString is pass-by-reference; caller must ensure the pointed-to
* value has adequate lifetime.
*/
-
-#define CStringGetDatum(X) PointerGetDatum(X)
+static inline Datum
+CStringGetDatum(const char *X)
+{
+ return PointerGetDatum(X);
+}
/*
* DatumGetName
* Returns name value of a datum.
*/
-
-#define DatumGetName(X) ((Name) DatumGetPointer(X))
+static inline Name
+DatumGetName(Datum X)
+{
+ return (Name) DatumGetPointer(X);
+}
/*
* NameGetDatum
@@ -636,21 +717,27 @@ typedef struct NullableDatum
* Note: Name is pass-by-reference; caller must ensure the pointed-to
* value has adequate lifetime.
*/
-
-#define NameGetDatum(X) CStringGetDatum(NameStr(*(X)))
+static inline Datum
+NameGetDatum(const NameData *X)
+{
+ return CStringGetDatum(NameStr(*X));
+}
/*
* DatumGetInt64
* Returns 64-bit integer value of a datum.
*
- * Note: this macro hides whether int64 is pass by value or by reference.
+ * Note: this function hides whether int64 is pass by value or by reference.
*/
-
+static inline int64
+DatumGetInt64(Datum X)
+{
#ifdef USE_FLOAT8_BYVAL
-#define DatumGetInt64(X) ((int64) (X))
+ return (int64) X;
#else
-#define DatumGetInt64(X) (* ((int64 *) DatumGetPointer(X)))
+ return *((int64 *) DatumGetPointer(X));
#endif
+}
/*
* Int64GetDatum
@@ -659,25 +746,32 @@ typedef struct NullableDatum
* Note: if int64 is pass by reference, this function returns a reference
* to palloc'd space.
*/
-
#ifdef USE_FLOAT8_BYVAL
-#define Int64GetDatum(X) ((Datum) (X))
+static inline Datum
+Int64GetDatum(int64 X)
+{
+ return (Datum) X;
+}
#else
extern Datum Int64GetDatum(int64 X);
#endif
+
/*
* DatumGetUInt64
* Returns 64-bit unsigned integer value of a datum.
*
- * Note: this macro hides whether int64 is pass by value or by reference.
+ * Note: this function hides whether int64 is pass by value or by reference.
*/
-
+static inline uint64
+DatumGetUInt64(Datum X)
+{
#ifdef USE_FLOAT8_BYVAL
-#define DatumGetUInt64(X) ((uint64) (X))
+ return (uint64) X;
#else
-#define DatumGetUInt64(X) (* ((uint64 *) DatumGetPointer(X)))
+ return *((uint64 *) DatumGetPointer(X));
#endif
+}
/*
* UInt64GetDatum
@@ -686,12 +780,15 @@ extern Datum Int64GetDatum(int64 X);
* Note: if int64 is pass by reference, this function returns a reference
* to palloc'd space.
*/
-
+static inline Datum
+UInt64GetDatum(uint64 X)
+{
#ifdef USE_FLOAT8_BYVAL
-#define UInt64GetDatum(X) ((Datum) (X))
+ return (Datum) X;
#else
-#define UInt64GetDatum(X) Int64GetDatum((int64) (X))
+ return Int64GetDatum((int64) X);
#endif
+}
/*
* Float <-> Datum conversions
@@ -739,13 +836,12 @@ Float4GetDatum(float4 X)
* DatumGetFloat8
* Returns 8-byte floating point value of a datum.
*
- * Note: this macro hides whether float8 is pass by value or by reference.
+ * Note: this function hides whether float8 is pass by value or by reference.
*/
-
-#ifdef USE_FLOAT8_BYVAL
static inline float8
DatumGetFloat8(Datum X)
{
+#ifdef USE_FLOAT8_BYVAL
union
{
int64 value;
@@ -754,10 +850,10 @@ DatumGetFloat8(Datum X)
myunion.value = DatumGetInt64(X);
return myunion.retval;
-}
#else
-#define DatumGetFloat8(X) (* ((float8 *) DatumGetPointer(X)))
+ return *((float8 *) DatumGetPointer(X));
#endif
+}
/*
* Float8GetDatum
@@ -766,7 +862,6 @@ DatumGetFloat8(Datum X)
* Note: if float8 is pass by reference, this function returns a reference
* to palloc'd space.
*/
-
#ifdef USE_FLOAT8_BYVAL
static inline Datum
Float8GetDatum(float8 X)
@@ -789,22 +884,34 @@ extern Datum Float8GetDatum(float8 X);
* Int64GetDatumFast
* Float8GetDatumFast
*
- * These macros are intended to allow writing code that does not depend on
+ * These functions are intended to allow writing code that does not depend on
* whether int64 and float8 are pass-by-reference types, while not
* sacrificing performance when they are. The argument must be a variable
* that will exist and have the same value for as long as the Datum is needed.
* In the pass-by-ref case, the address of the variable is taken to use as
* the Datum. In the pass-by-val case, these will be the same as the non-Fast
- * macros.
+ * functions.
*/
+static inline Datum
+Int64GetDatumFast(int64 X)
+{
+#ifdef USE_FLOAT8_BYVAL
+ return Int64GetDatum(X);
+#else
+ return PointerGetDatum(&X);
+#endif
+}
+
+static inline Datum
+Float8GetDatumFast(float8 X)
+{
#ifdef USE_FLOAT8_BYVAL
-#define Int64GetDatumFast(X) Int64GetDatum(X)
-#define Float8GetDatumFast(X) Float8GetDatum(X)
+ return Float8GetDatum(X);
#else
-#define Int64GetDatumFast(X) PointerGetDatum(&(X))
-#define Float8GetDatumFast(X) PointerGetDatum(&(X))
+ return PointerGetDatum(&X);
#endif
+}
/* ----------------------------------------------------------------
diff --git a/src/include/tsearch/ts_type.h b/src/include/tsearch/ts_type.h
index 689b2d1cfb..f1ec84702d 100644
--- a/src/include/tsearch/ts_type.h
+++ b/src/include/tsearch/ts_type.h
@@ -111,12 +111,27 @@ typedef TSVectorData *TSVector;
#define POSDATAPTR(x,e) (_POSVECPTR(x,e)->pos)
/*
- * fmgr interface macros
+ * fmgr interface functions
*/
-#define DatumGetTSVector(X) ((TSVector) PG_DETOAST_DATUM(X))
-#define DatumGetTSVectorCopy(X) ((TSVector) PG_DETOAST_DATUM_COPY(X))
-#define TSVectorGetDatum(X) PointerGetDatum(X)
+static inline TSVector
+DatumGetTSVector(Datum X)
+{
+ return (TSVector) PG_DETOAST_DATUM(X);
+}
+
+static inline TSVector
+DatumGetTSVectorCopy(Datum X)
+{
+ return (TSVector) PG_DETOAST_DATUM_COPY(X);
+}
+
+static inline Datum
+TSVectorGetDatum(const TSVectorData *X)
+{
+ return PointerGetDatum(X);
+}
+
#define PG_GETARG_TSVECTOR(n) DatumGetTSVector(PG_GETARG_DATUM(n))
#define PG_GETARG_TSVECTOR_COPY(n) DatumGetTSVectorCopy(PG_GETARG_DATUM(n))
#define PG_RETURN_TSVECTOR(x) return TSVectorGetDatum(x)
@@ -227,14 +242,29 @@ typedef TSQueryData *TSQuery;
#define GETOPERAND(x) ( (char*)GETQUERY(x) + ((TSQuery)(x))->size * sizeof(QueryItem) )
/*
- * fmgr interface macros
+ * fmgr interface functions
* Note, TSQuery type marked as plain storage, so it can't be toasted
* but PG_DETOAST_DATUM_COPY is used for simplicity
*/
-#define DatumGetTSQuery(X) ((TSQuery) DatumGetPointer(X))
-#define DatumGetTSQueryCopy(X) ((TSQuery) PG_DETOAST_DATUM_COPY(X))
-#define TSQueryGetDatum(X) PointerGetDatum(X)
+static inline TSQuery
+DatumGetTSQuery(Datum X)
+{
+ return (TSQuery) DatumGetPointer(X);
+}
+
+static inline TSQuery
+DatumGetTSQueryCopy(Datum X)
+{
+ return (TSQuery) PG_DETOAST_DATUM_COPY(X);
+}
+
+static inline Datum
+TSQueryGetDatum(const TSQueryData *X)
+{
+ return PointerGetDatum(X);
+}
+
#define PG_GETARG_TSQUERY(n) DatumGetTSQuery(PG_GETARG_DATUM(n))
#define PG_GETARG_TSQUERY_COPY(n) DatumGetTSQueryCopy(PG_GETARG_DATUM(n))
#define PG_RETURN_TSQUERY(x) return TSQueryGetDatum(x)
diff --git a/src/include/tsearch/ts_utils.h b/src/include/tsearch/ts_utils.h
index c36c711dae..26cc415aa8 100644
--- a/src/include/tsearch/ts_utils.h
+++ b/src/include/tsearch/ts_utils.h
@@ -243,8 +243,18 @@ typedef uint64 TSQuerySign;
#define TSQS_SIGLEN (sizeof(TSQuerySign)*BITS_PER_BYTE)
-#define TSQuerySignGetDatum(X) Int64GetDatum((int64) (X))
-#define DatumGetTSQuerySign(X) ((TSQuerySign) DatumGetInt64(X))
+static inline Datum
+TSQuerySignGetDatum(TSQuerySign X)
+{
+ return Int64GetDatum((int64) X);
+}
+
+static inline TSQuerySign
+DatumGetTSQuerySign(Datum X)
+{
+ return (TSQuerySign) DatumGetInt64(X);
+}
+
#define PG_RETURN_TSQUERYSIGN(X) return TSQuerySignGetDatum(X)
#define PG_GETARG_TSQUERYSIGN(n) DatumGetTSQuerySign(PG_GETARG_DATUM(n))
diff --git a/src/include/utils/cash.h b/src/include/utils/cash.h
index 2e332d83b1..55d45fadd4 100644
--- a/src/include/utils/cash.h
+++ b/src/include/utils/cash.h
@@ -17,8 +17,18 @@
typedef int64 Cash;
/* Cash is pass-by-reference if and only if int64 is */
-#define DatumGetCash(X) ((Cash) DatumGetInt64(X))
-#define CashGetDatum(X) Int64GetDatum(X)
+static inline Cash
+DatumGetCash(Datum X)
+{
+ return DatumGetInt64(X);
+}
+
+static inline Datum
+CashGetDatum(Cash X)
+{
+ return Int64GetDatum(X);
+}
+
#define PG_GETARG_CASH(n) DatumGetCash(PG_GETARG_DATUM(n))
#define PG_RETURN_CASH(x) return CashGetDatum(x)
diff --git a/src/include/utils/date.h b/src/include/utils/date.h
index 3991da41f0..0bbe889128 100644
--- a/src/include/utils/date.h
+++ b/src/include/utils/date.h
@@ -45,18 +45,46 @@ typedef struct
#define MAX_TIME_PRECISION 6
/*
- * Macros for fmgr-callable functions.
+ * Functions for fmgr-callable functions.
*
* For TimeADT, we make use of the same support routines as for int64.
* Therefore TimeADT is pass-by-reference if and only if int64 is!
*/
-#define DatumGetDateADT(X) ((DateADT) DatumGetInt32(X))
-#define DatumGetTimeADT(X) ((TimeADT) DatumGetInt64(X))
-#define DatumGetTimeTzADTP(X) ((TimeTzADT *) DatumGetPointer(X))
+static inline DateADT
+DatumGetDateADT(Datum X)
+{
+ return (DateADT) DatumGetInt32(X);
+}
+
+static inline TimeADT
+DatumGetTimeADT(Datum X)
+{
+ return (TimeADT) DatumGetInt64(X);
+}
+
+static inline TimeTzADT *
+DatumGetTimeTzADTP(Datum X)
+{
+ return (TimeTzADT *) DatumGetPointer(X);
+}
-#define DateADTGetDatum(X) Int32GetDatum(X)
-#define TimeADTGetDatum(X) Int64GetDatum(X)
-#define TimeTzADTPGetDatum(X) PointerGetDatum(X)
+static inline Datum
+DateADTGetDatum(DateADT X)
+{
+ return Int32GetDatum(X);
+}
+
+static inline Datum
+TimeADTGetDatum(TimeADT X)
+{
+ return Int64GetDatum(X);
+}
+
+static inline Datum
+TimeTzADTPGetDatum(const TimeTzADT *X)
+{
+ return PointerGetDatum(X);
+}
#define PG_GETARG_DATEADT(n) DatumGetDateADT(PG_GETARG_DATUM(n))
#define PG_GETARG_TIMEADT(n) DatumGetTimeADT(PG_GETARG_DATUM(n))
diff --git a/src/include/utils/expandeddatum.h b/src/include/utils/expandeddatum.h
index ffdb0c45bd..c4818eb054 100644
--- a/src/include/utils/expandeddatum.h
+++ b/src/include/utils/expandeddatum.h
@@ -133,8 +133,17 @@ struct ExpandedObjectHeader
* (More of these might be worth inlining later.)
*/
-#define EOHPGetRWDatum(eohptr) PointerGetDatum((eohptr)->eoh_rw_ptr)
-#define EOHPGetRODatum(eohptr) PointerGetDatum((eohptr)->eoh_ro_ptr)
+static inline Datum
+EOHPGetRWDatum(const struct ExpandedObjectHeader *eohptr)
+{
+ return PointerGetDatum(eohptr->eoh_rw_ptr);
+}
+
+static inline Datum
+EOHPGetRODatum(const struct ExpandedObjectHeader *eohptr)
+{
+ return PointerGetDatum(eohptr->eoh_ro_ptr);
+}
/* Does the Datum represent a writable expanded object? */
#define DatumIsReadWriteExpandedObject(d, isnull, typlen) \
diff --git a/src/include/utils/expandedrecord.h b/src/include/utils/expandedrecord.h
index be60e2ce53..aaf5b6bfd1 100644
--- a/src/include/utils/expandedrecord.h
+++ b/src/include/utils/expandedrecord.h
@@ -138,10 +138,20 @@ typedef struct ExpandedRecordHeader
MemoryContextCallback er_mcb;
} ExpandedRecordHeader;
-/* fmgr macros for expanded record objects */
+/* fmgr functions and macros for expanded record objects */
+static inline Datum
+ExpandedRecordGetDatum(const ExpandedRecordHeader *erh)
+{
+ return EOHPGetRWDatum(&erh->hdr);
+}
+
+static inline Datum
+ExpandedRecordGetRODatum(const ExpandedRecordHeader *erh)
+{
+ return EOHPGetRODatum(&erh->hdr);
+}
+
#define PG_GETARG_EXPANDED_RECORD(n) DatumGetExpandedRecord(PG_GETARG_DATUM(n))
-#define ExpandedRecordGetDatum(erh) EOHPGetRWDatum(&(erh)->hdr)
-#define ExpandedRecordGetRODatum(erh) EOHPGetRODatum(&(erh)->hdr)
#define PG_RETURN_EXPANDED_RECORD(x) PG_RETURN_DATUM(ExpandedRecordGetDatum(x))
/* assorted other macros */
diff --git a/src/include/utils/geo_decls.h b/src/include/utils/geo_decls.h
index 719030777e..05726778ad 100644
--- a/src/include/utils/geo_decls.h
+++ b/src/include/utils/geo_decls.h
@@ -166,48 +166,112 @@ typedef struct
} CIRCLE;
/*
- * fmgr interface macros
+ * fmgr interface functions
*
* Path and Polygon are toastable varlena types, the others are just
* fixed-size pass-by-reference types.
*/
-#define DatumGetPointP(X) ((Point *) DatumGetPointer(X))
-#define PointPGetDatum(X) PointerGetDatum(X)
+static inline Point *
+DatumGetPointP(Datum X)
+{
+ return (Point *) DatumGetPointer(X);
+}
+static inline Datum
+PointPGetDatum(const Point *X)
+{
+ return PointerGetDatum(X);
+}
#define PG_GETARG_POINT_P(n) DatumGetPointP(PG_GETARG_DATUM(n))
#define PG_RETURN_POINT_P(x) return PointPGetDatum(x)
-#define DatumGetLsegP(X) ((LSEG *) DatumGetPointer(X))
-#define LsegPGetDatum(X) PointerGetDatum(X)
+static inline LSEG *
+DatumGetLsegP(Datum X)
+{
+ return (LSEG *) DatumGetPointer(X);
+}
+static inline Datum
+LsegPGetDatum(const LSEG *X)
+{
+ return PointerGetDatum(X);
+}
#define PG_GETARG_LSEG_P(n) DatumGetLsegP(PG_GETARG_DATUM(n))
#define PG_RETURN_LSEG_P(x) return LsegPGetDatum(x)
-#define DatumGetPathP(X) ((PATH *) PG_DETOAST_DATUM(X))
-#define DatumGetPathPCopy(X) ((PATH *) PG_DETOAST_DATUM_COPY(X))
-#define PathPGetDatum(X) PointerGetDatum(X)
+static inline PATH *
+DatumGetPathP(Datum X)
+{
+ return (PATH *) PG_DETOAST_DATUM(X);
+}
+static inline PATH *
+DatumGetPathPCopy(Datum X)
+{
+ return (PATH *) PG_DETOAST_DATUM_COPY(X);
+}
+static inline Datum
+PathPGetDatum(const PATH *X)
+{
+ return PointerGetDatum(X);
+}
#define PG_GETARG_PATH_P(n) DatumGetPathP(PG_GETARG_DATUM(n))
#define PG_GETARG_PATH_P_COPY(n) DatumGetPathPCopy(PG_GETARG_DATUM(n))
#define PG_RETURN_PATH_P(x) return PathPGetDatum(x)
-#define DatumGetLineP(X) ((LINE *) DatumGetPointer(X))
-#define LinePGetDatum(X) PointerGetDatum(X)
+static inline LINE *
+DatumGetLineP(Datum X)
+{
+ return (LINE *) DatumGetPointer(X);
+}
+static inline Datum
+LinePGetDatum(const LINE *X)
+{
+ return PointerGetDatum(X);
+}
#define PG_GETARG_LINE_P(n) DatumGetLineP(PG_GETARG_DATUM(n))
#define PG_RETURN_LINE_P(x) return LinePGetDatum(x)
-#define DatumGetBoxP(X) ((BOX *) DatumGetPointer(X))
-#define BoxPGetDatum(X) PointerGetDatum(X)
+static inline BOX *
+DatumGetBoxP(Datum X)
+{
+ return (BOX *) DatumGetPointer(X);
+}
+static inline Datum
+BoxPGetDatum(const BOX *X)
+{
+ return PointerGetDatum(X);
+}
#define PG_GETARG_BOX_P(n) DatumGetBoxP(PG_GETARG_DATUM(n))
#define PG_RETURN_BOX_P(x) return BoxPGetDatum(x)
-#define DatumGetPolygonP(X) ((POLYGON *) PG_DETOAST_DATUM(X))
-#define DatumGetPolygonPCopy(X) ((POLYGON *) PG_DETOAST_DATUM_COPY(X))
-#define PolygonPGetDatum(X) PointerGetDatum(X)
+static inline POLYGON *
+DatumGetPolygonP(Datum X)
+{
+ return (POLYGON *) PG_DETOAST_DATUM(X);
+}
+static inline POLYGON *
+DatumGetPolygonPCopy(Datum X)
+{
+ return (POLYGON *) PG_DETOAST_DATUM_COPY(X);
+}
+static inline Datum
+PolygonPGetDatum(const POLYGON *X)
+{
+ return PointerGetDatum(X);
+}
#define PG_GETARG_POLYGON_P(n) DatumGetPolygonP(PG_GETARG_DATUM(n))
#define PG_GETARG_POLYGON_P_COPY(n) DatumGetPolygonPCopy(PG_GETARG_DATUM(n))
#define PG_RETURN_POLYGON_P(x) return PolygonPGetDatum(x)
-#define DatumGetCircleP(X) ((CIRCLE *) DatumGetPointer(X))
-#define CirclePGetDatum(X) PointerGetDatum(X)
+static inline CIRCLE *
+DatumGetCircleP(Datum X)
+{
+ return (CIRCLE *) DatumGetPointer(X);
+}
+static inline Datum
+CirclePGetDatum(const CIRCLE *X)
+{
+ return PointerGetDatum(X);
+}
#define PG_GETARG_CIRCLE_P(n) DatumGetCircleP(PG_GETARG_DATUM(n))
#define PG_RETURN_CIRCLE_P(x) return CirclePGetDatum(x)
diff --git a/src/include/utils/inet.h b/src/include/utils/inet.h
index b1ec9723df..5438cfaba5 100644
--- a/src/include/utils/inet.h
+++ b/src/include/utils/inet.h
@@ -119,23 +119,58 @@ typedef struct macaddr8
/*
* fmgr interface macros
*/
-#define DatumGetInetPP(X) ((inet *) PG_DETOAST_DATUM_PACKED(X))
-#define InetPGetDatum(X) PointerGetDatum(X)
+static inline inet *
+DatumGetInetPP(Datum X)
+{
+ return (inet *) PG_DETOAST_DATUM_PACKED(X);
+}
+
+static inline Datum
+InetPGetDatum(const inet *X)
+{
+ return PointerGetDatum(X);
+}
+
#define PG_GETARG_INET_PP(n) DatumGetInetPP(PG_GETARG_DATUM(n))
#define PG_RETURN_INET_P(x) return InetPGetDatum(x)
+
/* obsolescent variants */
-#define DatumGetInetP(X) ((inet *) PG_DETOAST_DATUM(X))
+static inline inet *
+DatumGetInetP(Datum X)
+{
+ return (inet *) PG_DETOAST_DATUM(X);
+}
#define PG_GETARG_INET_P(n) DatumGetInetP(PG_GETARG_DATUM(n))
/* macaddr is a fixed-length pass-by-reference datatype */
-#define DatumGetMacaddrP(X) ((macaddr *) DatumGetPointer(X))
-#define MacaddrPGetDatum(X) PointerGetDatum(X)
+static inline macaddr *
+DatumGetMacaddrP(Datum X)
+{
+ return (macaddr *) DatumGetPointer(X);
+}
+
+static inline Datum
+MacaddrPGetDatum(const macaddr *X)
+{
+ return PointerGetDatum(X);
+}
+
#define PG_GETARG_MACADDR_P(n) DatumGetMacaddrP(PG_GETARG_DATUM(n))
#define PG_RETURN_MACADDR_P(x) return MacaddrPGetDatum(x)
/* macaddr8 is a fixed-length pass-by-reference datatype */
-#define DatumGetMacaddr8P(X) ((macaddr8 *) DatumGetPointer(X))
-#define Macaddr8PGetDatum(X) PointerGetDatum(X)
+static inline macaddr8 *
+DatumGetMacaddr8P(Datum X)
+{
+ return (macaddr8 *) DatumGetPointer(X);
+}
+
+static inline Datum
+Macaddr8PGetDatum(const macaddr8 *X)
+{
+ return PointerGetDatum(X);
+}
+
#define PG_GETARG_MACADDR8_P(n) DatumGetMacaddr8P(PG_GETARG_DATUM(n))
#define PG_RETURN_MACADDR8_P(x) return Macaddr8PGetDatum(x)
diff --git a/src/include/utils/jsonb.h b/src/include/utils/jsonb.h
index 4cbe6edf21..bdd1c9c75d 100644
--- a/src/include/utils/jsonb.h
+++ b/src/include/utils/jsonb.h
@@ -67,14 +67,6 @@ typedef enum
#define JGINFLAG_HASHED 0x10 /* OR'd into flag if value was hashed */
#define JGIN_MAXLENGTH 125 /* max length of text part before hashing */
-/* Convenience macros */
-#define DatumGetJsonbP(d) ((Jsonb *) PG_DETOAST_DATUM(d))
-#define DatumGetJsonbPCopy(d) ((Jsonb *) PG_DETOAST_DATUM_COPY(d))
-#define JsonbPGetDatum(p) PointerGetDatum(p)
-#define PG_GETARG_JSONB_P(x) DatumGetJsonbP(PG_GETARG_DATUM(x))
-#define PG_GETARG_JSONB_P_COPY(x) DatumGetJsonbPCopy(PG_GETARG_DATUM(x))
-#define PG_RETURN_JSONB_P(x) PG_RETURN_POINTER(x)
-
typedef struct JsonbPair JsonbPair;
typedef struct JsonbValue JsonbValue;
@@ -375,6 +367,29 @@ typedef struct JsonbIterator
} JsonbIterator;
+/* Convenience macros */
+static inline Jsonb *
+DatumGetJsonbP(Datum d)
+{
+ return (Jsonb *) PG_DETOAST_DATUM(d);
+}
+
+static inline Jsonb *
+DatumGetJsonbPCopy(Datum d)
+{
+ return (Jsonb *) PG_DETOAST_DATUM_COPY(d);
+}
+
+static inline Datum
+JsonbPGetDatum(const Jsonb *p)
+{
+ return PointerGetDatum(p);
+}
+
+#define PG_GETARG_JSONB_P(x) DatumGetJsonbP(PG_GETARG_DATUM(x))
+#define PG_GETARG_JSONB_P_COPY(x) DatumGetJsonbPCopy(PG_GETARG_DATUM(x))
+#define PG_RETURN_JSONB_P(x) PG_RETURN_POINTER(x)
+
/* Support functions */
extern uint32 getJsonbOffset(const JsonbContainer *jc, int index);
extern uint32 getJsonbLength(const JsonbContainer *jc, int index);
diff --git a/src/include/utils/jsonpath.h b/src/include/utils/jsonpath.h
index cd0b5d5b61..13f60cdc09 100644
--- a/src/include/utils/jsonpath.h
+++ b/src/include/utils/jsonpath.h
@@ -29,8 +29,18 @@ typedef struct
#define JSONPATH_LAX (0x80000000)
#define JSONPATH_HDRSZ (offsetof(JsonPath, data))
-#define DatumGetJsonPathP(d) ((JsonPath *) DatumGetPointer(PG_DETOAST_DATUM(d)))
-#define DatumGetJsonPathPCopy(d) ((JsonPath *) DatumGetPointer(PG_DETOAST_DATUM_COPY(d)))
+static inline JsonPath *
+DatumGetJsonPathP(Datum d)
+{
+ return (JsonPath *) PG_DETOAST_DATUM(d);
+}
+
+static inline JsonPath *
+DatumGetJsonPathPCopy(Datum d)
+{
+ return (JsonPath *) PG_DETOAST_DATUM_COPY(d);
+}
+
#define PG_GETARG_JSONPATH_P(x) DatumGetJsonPathP(PG_GETARG_DATUM(x))
#define PG_GETARG_JSONPATH_P_COPY(x) DatumGetJsonPathPCopy(PG_GETARG_DATUM(x))
#define PG_RETURN_JSONPATH_P(p) PG_RETURN_POINTER(p)
diff --git a/src/include/utils/multirangetypes.h b/src/include/utils/multirangetypes.h
index 915330f990..23bce0005f 100644
--- a/src/include/utils/multirangetypes.h
+++ b/src/include/utils/multirangetypes.h
@@ -42,11 +42,26 @@ typedef struct
#define MultirangeIsEmpty(mr) ((mr)->rangeCount == 0)
/*
- * fmgr macros for multirange type objects
+ * fmgr functions for multirange type objects
*/
-#define DatumGetMultirangeTypeP(X) ((MultirangeType *) PG_DETOAST_DATUM(X))
-#define DatumGetMultirangeTypePCopy(X) ((MultirangeType *) PG_DETOAST_DATUM_COPY(X))
-#define MultirangeTypePGetDatum(X) PointerGetDatum(X)
+static inline MultirangeType *
+DatumGetMultirangeTypeP(Datum X)
+{
+ return (MultirangeType *) PG_DETOAST_DATUM(X);
+}
+
+static inline MultirangeType *
+DatumGetMultirangeTypePCopy(Datum X)
+{
+ return (MultirangeType *) PG_DETOAST_DATUM_COPY(X);
+}
+
+static inline Datum
+MultirangeTypePGetDatum(const MultirangeType *X)
+{
+ return PointerGetDatum(X);
+}
+
#define PG_GETARG_MULTIRANGE_P(n) DatumGetMultirangeTypeP(PG_GETARG_DATUM(n))
#define PG_GETARG_MULTIRANGE_P_COPY(n) DatumGetMultirangeTypePCopy(PG_GETARG_DATUM(n))
#define PG_RETURN_MULTIRANGE_P(x) return MultirangeTypePGetDatum(x)
diff --git a/src/include/utils/numeric.h b/src/include/utils/numeric.h
index 3caa74dfe7..88c62c5348 100644
--- a/src/include/utils/numeric.h
+++ b/src/include/utils/numeric.h
@@ -56,9 +56,24 @@ typedef struct NumericData *Numeric;
* fmgr interface macros
*/
-#define DatumGetNumeric(X) ((Numeric) PG_DETOAST_DATUM(X))
-#define DatumGetNumericCopy(X) ((Numeric) PG_DETOAST_DATUM_COPY(X))
-#define NumericGetDatum(X) PointerGetDatum(X)
+static inline Numeric
+DatumGetNumeric(Datum X)
+{
+ return (Numeric) PG_DETOAST_DATUM(X);
+}
+
+static inline Numeric
+DatumGetNumericCopy(Datum X)
+{
+ return (Numeric) PG_DETOAST_DATUM_COPY(X);
+}
+
+static inline Datum
+NumericGetDatum(Numeric X)
+{
+ return PointerGetDatum(X);
+}
+
#define PG_GETARG_NUMERIC(n) DatumGetNumeric(PG_GETARG_DATUM(n))
#define PG_GETARG_NUMERIC_COPY(n) DatumGetNumericCopy(PG_GETARG_DATUM(n))
#define PG_RETURN_NUMERIC(x) return NumericGetDatum(x)
diff --git a/src/include/utils/pg_lsn.h b/src/include/utils/pg_lsn.h
index 7b708f1073..5313afbfe2 100644
--- a/src/include/utils/pg_lsn.h
+++ b/src/include/utils/pg_lsn.h
@@ -18,8 +18,17 @@
#include "access/xlogdefs.h"
#include "fmgr.h"
-#define DatumGetLSN(X) ((XLogRecPtr) DatumGetInt64(X))
-#define LSNGetDatum(X) (Int64GetDatum((int64) (X)))
+static inline XLogRecPtr
+DatumGetLSN(Datum X)
+{
+ return (XLogRecPtr) DatumGetInt64(X);
+}
+
+static inline Datum
+LSNGetDatum(XLogRecPtr X)
+{
+ return Int64GetDatum((int64) X);
+}
#define PG_GETARG_LSN(n) DatumGetLSN(PG_GETARG_DATUM(n))
#define PG_RETURN_LSN(x) return LSNGetDatum(x)
diff --git a/src/include/utils/rangetypes.h b/src/include/utils/rangetypes.h
index 993fad4fc2..661e4892ec 100644
--- a/src/include/utils/rangetypes.h
+++ b/src/include/utils/rangetypes.h
@@ -68,11 +68,26 @@ typedef struct
} RangeBound;
/*
- * fmgr macros for range type objects
+ * fmgr functions for range type objects
*/
-#define DatumGetRangeTypeP(X) ((RangeType *) PG_DETOAST_DATUM(X))
-#define DatumGetRangeTypePCopy(X) ((RangeType *) PG_DETOAST_DATUM_COPY(X))
-#define RangeTypePGetDatum(X) PointerGetDatum(X)
+static inline RangeType *
+DatumGetRangeTypeP(Datum X)
+{
+ return (RangeType *) PG_DETOAST_DATUM(X);
+}
+
+static inline RangeType *
+DatumGetRangeTypePCopy(Datum X)
+{
+ return (RangeType *) PG_DETOAST_DATUM_COPY(X);
+}
+
+static inline Datum
+RangeTypePGetDatum(const RangeType *X)
+{
+ return PointerGetDatum(X);
+}
+
#define PG_GETARG_RANGE_P(n) DatumGetRangeTypeP(PG_GETARG_DATUM(n))
#define PG_GETARG_RANGE_P_COPY(n) DatumGetRangeTypePCopy(PG_GETARG_DATUM(n))
#define PG_RETURN_RANGE_P(x) return RangeTypePGetDatum(x)
diff --git a/src/include/utils/timestamp.h b/src/include/utils/timestamp.h
index edf3a97318..3602638bdc 100644
--- a/src/include/utils/timestamp.h
+++ b/src/include/utils/timestamp.h
@@ -19,18 +19,46 @@
/*
- * Macros for fmgr-callable functions.
+ * Functions for fmgr-callable functions.
*
* For Timestamp, we make use of the same support routines as for int64.
* Therefore Timestamp is pass-by-reference if and only if int64 is!
*/
-#define DatumGetTimestamp(X) ((Timestamp) DatumGetInt64(X))
-#define DatumGetTimestampTz(X) ((TimestampTz) DatumGetInt64(X))
-#define DatumGetIntervalP(X) ((Interval *) DatumGetPointer(X))
-
-#define TimestampGetDatum(X) Int64GetDatum(X)
-#define TimestampTzGetDatum(X) Int64GetDatum(X)
-#define IntervalPGetDatum(X) PointerGetDatum(X)
+static inline Timestamp
+DatumGetTimestamp(Datum X)
+{
+ return (Timestamp) DatumGetInt64(X);
+}
+
+static inline TimestampTz
+DatumGetTimestampTz(Datum X)
+{
+ return (TimestampTz) DatumGetInt64(X);
+}
+
+static inline Interval *
+DatumGetIntervalP(Datum X)
+{
+ return (Interval *) DatumGetPointer(X);
+}
+
+static inline Datum
+TimestampGetDatum(Timestamp X)
+{
+ return Int64GetDatum(X);
+}
+
+static inline Datum
+TimestampTzGetDatum(TimestampTz X)
+{
+ return Int64GetDatum(X);
+}
+
+static inline Datum
+IntervalPGetDatum(const Interval *X)
+{
+ return PointerGetDatum(X);
+}
#define PG_GETARG_TIMESTAMP(n) DatumGetTimestamp(PG_GETARG_DATUM(n))
#define PG_GETARG_TIMESTAMPTZ(n) DatumGetTimestampTz(PG_GETARG_DATUM(n))
diff --git a/src/include/utils/uuid.h b/src/include/utils/uuid.h
index 0029da4e7f..69f4332d0f 100644
--- a/src/include/utils/uuid.h
+++ b/src/include/utils/uuid.h
@@ -23,9 +23,20 @@ typedef struct pg_uuid_t
} pg_uuid_t;
/* fmgr interface macros */
-#define UUIDPGetDatum(X) PointerGetDatum(X)
+static inline Datum
+UUIDPGetDatum(const pg_uuid_t *X)
+{
+ return PointerGetDatum(X);
+}
+
#define PG_RETURN_UUID_P(X) return UUIDPGetDatum(X)
-#define DatumGetUUIDP(X) ((pg_uuid_t *) DatumGetPointer(X))
+
+static inline pg_uuid_t *
+DatumGetUUIDP(Datum X)
+{
+ return (pg_uuid_t *) DatumGetPointer(X);
+}
+
#define PG_GETARG_UUID_P(X) DatumGetUUIDP(PG_GETARG_DATUM(X))
#endif /* UUID_H */
diff --git a/src/include/utils/varbit.h b/src/include/utils/varbit.h
index 039ba860cf..e65e7f65e7 100644
--- a/src/include/utils/varbit.h
+++ b/src/include/utils/varbit.h
@@ -41,9 +41,24 @@ typedef struct
* BIT and BIT VARYING are toastable varlena types. They are the same
* as far as representation goes, so we just have one set of macros.
*/
-#define DatumGetVarBitP(X) ((VarBit *) PG_DETOAST_DATUM(X))
-#define DatumGetVarBitPCopy(X) ((VarBit *) PG_DETOAST_DATUM_COPY(X))
-#define VarBitPGetDatum(X) PointerGetDatum(X)
+static inline VarBit *
+DatumGetVarBitP(Datum X)
+{
+ return (VarBit *) PG_DETOAST_DATUM(X);
+}
+
+static inline VarBit *
+DatumGetVarBitPCopy(Datum X)
+{
+ return (VarBit *) PG_DETOAST_DATUM_COPY(X);
+}
+
+static inline Datum
+VarBitPGetDatum(const VarBit *X)
+{
+ return PointerGetDatum(X);
+}
+
#define PG_GETARG_VARBIT_P(n) DatumGetVarBitP(PG_GETARG_DATUM(n))
#define PG_GETARG_VARBIT_P_COPY(n) DatumGetVarBitPCopy(PG_GETARG_DATUM(n))
#define PG_RETURN_VARBIT_P(x) return VarBitPGetDatum(x)
diff --git a/src/include/utils/xid8.h b/src/include/utils/xid8.h
index b702fc1a91..9c5ce241db 100644
--- a/src/include/utils/xid8.h
+++ b/src/include/utils/xid8.h
@@ -14,8 +14,18 @@
#include "access/transam.h"
-#define DatumGetFullTransactionId(X) (FullTransactionIdFromU64(DatumGetUInt64(X)))
-#define FullTransactionIdGetDatum(X) (UInt64GetDatum(U64FromFullTransactionId(X)))
+static inline FullTransactionId
+DatumGetFullTransactionId(Datum X)
+{
+ return FullTransactionIdFromU64(DatumGetUInt64(X));
+}
+
+static inline Datum
+FullTransactionIdGetDatum(FullTransactionId X)
+{
+ return UInt64GetDatum(U64FromFullTransactionId(X));
+}
+
#define PG_GETARG_FULLTRANSACTIONID(X) DatumGetFullTransactionId(PG_GETARG_DATUM(X))
#define PG_RETURN_FULLTRANSACTIONID(X) return FullTransactionIdGetDatum(X)
diff --git a/src/include/utils/xml.h b/src/include/utils/xml.h
index 6620a62619..c71174d204 100644
--- a/src/include/utils/xml.h
+++ b/src/include/utils/xml.h
@@ -47,8 +47,17 @@ typedef enum
/* struct PgXmlErrorContext is private to xml.c */
typedef struct PgXmlErrorContext PgXmlErrorContext;
-#define DatumGetXmlP(X) ((xmltype *) PG_DETOAST_DATUM(X))
-#define XmlPGetDatum(X) PointerGetDatum(X)
+static inline xmltype *
+DatumGetXmlP(Datum X)
+{
+ return (xmltype *) PG_DETOAST_DATUM(X);
+}
+
+static inline Datum
+XmlPGetDatum(const xmltype *X)
+{
+ return PointerGetDatum(X);
+}
#define PG_GETARG_XML_P(n) DatumGetXmlP(PG_GETARG_DATUM(n))
#define PG_RETURN_XML_P(x) PG_RETURN_POINTER(x)
--
2.37.2
^ permalink raw reply [nested|flat] 92+ messages in thread
* Re: Convert *GetDatum() and DatumGet*() macros to inline functions
2022-08-28 15:55 Convert *GetDatum() and DatumGet*() macros to inline functions Peter Eisentraut <[email protected]>
2022-08-30 13:36 ` Re: Convert *GetDatum() and DatumGet*() macros to inline functions Aleksander Alekseev <[email protected]>
2022-08-30 13:46 ` Re: Convert *GetDatum() and DatumGet*() macros to inline functions Tom Lane <[email protected]>
2022-08-30 13:59 ` Re: Convert *GetDatum() and DatumGet*() macros to inline functions Aleksander Alekseev <[email protected]>
2022-08-30 14:13 ` Re: Convert *GetDatum() and DatumGet*() macros to inline functions Tom Lane <[email protected]>
2022-08-30 14:16 ` Re: Convert *GetDatum() and DatumGet*() macros to inline functions Robert Haas <[email protected]>
2022-08-30 14:25 ` Re: Convert *GetDatum() and DatumGet*() macros to inline functions Aleksander Alekseev <[email protected]>
2022-08-30 14:28 ` Re: Convert *GetDatum() and DatumGet*() macros to inline functions Tom Lane <[email protected]>
2022-08-30 14:52 ` Re: Convert *GetDatum() and DatumGet*() macros to inline functions Aleksander Alekseev <[email protected]>
2022-08-30 18:15 ` Re: Convert *GetDatum() and DatumGet*() macros to inline functions Aleksander Alekseev <[email protected]>
2022-09-05 12:57 ` Re: Convert *GetDatum() and DatumGet*() macros to inline functions Peter Eisentraut <[email protected]>
2022-09-05 14:20 ` Re: Convert *GetDatum() and DatumGet*() macros to inline functions Aleksander Alekseev <[email protected]>
2022-09-08 09:26 ` Re: Convert *GetDatum() and DatumGet*() macros to inline functions Aleksander Alekseev <[email protected]>
@ 2022-09-12 15:59 ` Peter Eisentraut <[email protected]>
2022-09-12 17:03 ` Re: Convert *GetDatum() and DatumGet*() macros to inline functions Julien Rouhaud <[email protected]>
0 siblings, 1 reply; 92+ messages in thread
From: Peter Eisentraut @ 2022-09-12 15:59 UTC (permalink / raw)
To: Aleksander Alekseev <[email protected]>; pgsql-hackers
On 08.09.22 11:26, Aleksander Alekseev wrote:
>>> 3. Go with your patch and just fix up the warnings about uninitialized
>>> variables. But that seems the least principled to me.
>>
>> IMO the 3rd option is the lesser evil. Initializing four bools/ints in
>> order to make Clang 11 happy doesn't strike me as such a big deal. At
>> least until somebody reports a bottleneck for this particular reason.
>> We can optimize the code when and if this will happen.
>
> Since the first patch was applied, cfbot now complains that it can't
> apply the patchset. Here is the rebased version.
committed, thanks
^ permalink raw reply [nested|flat] 92+ messages in thread
* Re: Convert *GetDatum() and DatumGet*() macros to inline functions
2022-08-28 15:55 Convert *GetDatum() and DatumGet*() macros to inline functions Peter Eisentraut <[email protected]>
2022-08-30 13:36 ` Re: Convert *GetDatum() and DatumGet*() macros to inline functions Aleksander Alekseev <[email protected]>
2022-08-30 13:46 ` Re: Convert *GetDatum() and DatumGet*() macros to inline functions Tom Lane <[email protected]>
2022-08-30 13:59 ` Re: Convert *GetDatum() and DatumGet*() macros to inline functions Aleksander Alekseev <[email protected]>
2022-08-30 14:13 ` Re: Convert *GetDatum() and DatumGet*() macros to inline functions Tom Lane <[email protected]>
2022-08-30 14:16 ` Re: Convert *GetDatum() and DatumGet*() macros to inline functions Robert Haas <[email protected]>
2022-08-30 14:25 ` Re: Convert *GetDatum() and DatumGet*() macros to inline functions Aleksander Alekseev <[email protected]>
2022-08-30 14:28 ` Re: Convert *GetDatum() and DatumGet*() macros to inline functions Tom Lane <[email protected]>
2022-08-30 14:52 ` Re: Convert *GetDatum() and DatumGet*() macros to inline functions Aleksander Alekseev <[email protected]>
2022-08-30 18:15 ` Re: Convert *GetDatum() and DatumGet*() macros to inline functions Aleksander Alekseev <[email protected]>
2022-09-05 12:57 ` Re: Convert *GetDatum() and DatumGet*() macros to inline functions Peter Eisentraut <[email protected]>
2022-09-05 14:20 ` Re: Convert *GetDatum() and DatumGet*() macros to inline functions Aleksander Alekseev <[email protected]>
2022-09-08 09:26 ` Re: Convert *GetDatum() and DatumGet*() macros to inline functions Aleksander Alekseev <[email protected]>
2022-09-12 15:59 ` Re: Convert *GetDatum() and DatumGet*() macros to inline functions Peter Eisentraut <[email protected]>
@ 2022-09-12 17:03 ` Julien Rouhaud <[email protected]>
2022-09-12 17:59 ` Re: Convert *GetDatum() and DatumGet*() macros to inline functions Peter Eisentraut <[email protected]>
0 siblings, 1 reply; 92+ messages in thread
From: Julien Rouhaud @ 2022-09-12 17:03 UTC (permalink / raw)
To: Peter Eisentraut <[email protected]>; +Cc: Aleksander Alekseev <[email protected]>; pgsql-hackers
Hi,
On Mon, Sep 12, 2022 at 05:59:09PM +0200, Peter Eisentraut wrote:
>
> committed, thanks
FTR lapwing is complaining about this commit:
https://brekka.postgresql.org/cgi-bin/show_log.pl?nm=lapwing&dt=2022-09-12%2016%3A40%3A18.
Snapper is also failing with similar problems:
https://brekka.postgresql.org/cgi-bin/show_log.pl?nm=snapper&dt=2022-09-12%2016%3A42%3A10
^ permalink raw reply [nested|flat] 92+ messages in thread
* Re: Convert *GetDatum() and DatumGet*() macros to inline functions
2022-08-28 15:55 Convert *GetDatum() and DatumGet*() macros to inline functions Peter Eisentraut <[email protected]>
2022-08-30 13:36 ` Re: Convert *GetDatum() and DatumGet*() macros to inline functions Aleksander Alekseev <[email protected]>
2022-08-30 13:46 ` Re: Convert *GetDatum() and DatumGet*() macros to inline functions Tom Lane <[email protected]>
2022-08-30 13:59 ` Re: Convert *GetDatum() and DatumGet*() macros to inline functions Aleksander Alekseev <[email protected]>
2022-08-30 14:13 ` Re: Convert *GetDatum() and DatumGet*() macros to inline functions Tom Lane <[email protected]>
2022-08-30 14:16 ` Re: Convert *GetDatum() and DatumGet*() macros to inline functions Robert Haas <[email protected]>
2022-08-30 14:25 ` Re: Convert *GetDatum() and DatumGet*() macros to inline functions Aleksander Alekseev <[email protected]>
2022-08-30 14:28 ` Re: Convert *GetDatum() and DatumGet*() macros to inline functions Tom Lane <[email protected]>
2022-08-30 14:52 ` Re: Convert *GetDatum() and DatumGet*() macros to inline functions Aleksander Alekseev <[email protected]>
2022-08-30 18:15 ` Re: Convert *GetDatum() and DatumGet*() macros to inline functions Aleksander Alekseev <[email protected]>
2022-09-05 12:57 ` Re: Convert *GetDatum() and DatumGet*() macros to inline functions Peter Eisentraut <[email protected]>
2022-09-05 14:20 ` Re: Convert *GetDatum() and DatumGet*() macros to inline functions Aleksander Alekseev <[email protected]>
2022-09-08 09:26 ` Re: Convert *GetDatum() and DatumGet*() macros to inline functions Aleksander Alekseev <[email protected]>
2022-09-12 15:59 ` Re: Convert *GetDatum() and DatumGet*() macros to inline functions Peter Eisentraut <[email protected]>
2022-09-12 17:03 ` Re: Convert *GetDatum() and DatumGet*() macros to inline functions Julien Rouhaud <[email protected]>
@ 2022-09-12 17:59 ` Peter Eisentraut <[email protected]>
2022-09-26 14:55 ` Re: Convert *GetDatum() and DatumGet*() macros to inline functions Peter Eisentraut <[email protected]>
0 siblings, 1 reply; 92+ messages in thread
From: Peter Eisentraut @ 2022-09-12 17:59 UTC (permalink / raw)
To: Julien Rouhaud <[email protected]>; +Cc: Aleksander Alekseev <[email protected]>; pgsql-hackers
On 12.09.22 19:03, Julien Rouhaud wrote:
> On Mon, Sep 12, 2022 at 05:59:09PM +0200, Peter Eisentraut wrote:
>>
>> committed, thanks
>
> FTR lapwing is complaining about this commit:
> https://brekka.postgresql.org/cgi-bin/show_log.pl?nm=lapwing&dt=2022-09-12%2016%3A40%3A18.
>
> Snapper is also failing with similar problems:
> https://brekka.postgresql.org/cgi-bin/show_log.pl?nm=snapper&dt=2022-09-12%2016%3A42%3A10
Ok, it has problems with 32-bit platforms. I can reproduce it locally.
I'll need to take another look at this. I have reverted the patch for now.
^ permalink raw reply [nested|flat] 92+ messages in thread
* Re: Convert *GetDatum() and DatumGet*() macros to inline functions
2022-08-28 15:55 Convert *GetDatum() and DatumGet*() macros to inline functions Peter Eisentraut <[email protected]>
2022-08-30 13:36 ` Re: Convert *GetDatum() and DatumGet*() macros to inline functions Aleksander Alekseev <[email protected]>
2022-08-30 13:46 ` Re: Convert *GetDatum() and DatumGet*() macros to inline functions Tom Lane <[email protected]>
2022-08-30 13:59 ` Re: Convert *GetDatum() and DatumGet*() macros to inline functions Aleksander Alekseev <[email protected]>
2022-08-30 14:13 ` Re: Convert *GetDatum() and DatumGet*() macros to inline functions Tom Lane <[email protected]>
2022-08-30 14:16 ` Re: Convert *GetDatum() and DatumGet*() macros to inline functions Robert Haas <[email protected]>
2022-08-30 14:25 ` Re: Convert *GetDatum() and DatumGet*() macros to inline functions Aleksander Alekseev <[email protected]>
2022-08-30 14:28 ` Re: Convert *GetDatum() and DatumGet*() macros to inline functions Tom Lane <[email protected]>
2022-08-30 14:52 ` Re: Convert *GetDatum() and DatumGet*() macros to inline functions Aleksander Alekseev <[email protected]>
2022-08-30 18:15 ` Re: Convert *GetDatum() and DatumGet*() macros to inline functions Aleksander Alekseev <[email protected]>
2022-09-05 12:57 ` Re: Convert *GetDatum() and DatumGet*() macros to inline functions Peter Eisentraut <[email protected]>
2022-09-05 14:20 ` Re: Convert *GetDatum() and DatumGet*() macros to inline functions Aleksander Alekseev <[email protected]>
2022-09-08 09:26 ` Re: Convert *GetDatum() and DatumGet*() macros to inline functions Aleksander Alekseev <[email protected]>
2022-09-12 15:59 ` Re: Convert *GetDatum() and DatumGet*() macros to inline functions Peter Eisentraut <[email protected]>
2022-09-12 17:03 ` Re: Convert *GetDatum() and DatumGet*() macros to inline functions Julien Rouhaud <[email protected]>
2022-09-12 17:59 ` Re: Convert *GetDatum() and DatumGet*() macros to inline functions Peter Eisentraut <[email protected]>
@ 2022-09-26 14:55 ` Peter Eisentraut <[email protected]>
2022-09-26 17:34 ` Re: Convert *GetDatum() and DatumGet*() macros to inline functions Tom Lane <[email protected]>
0 siblings, 1 reply; 92+ messages in thread
From: Peter Eisentraut @ 2022-09-26 14:55 UTC (permalink / raw)
To: Julien Rouhaud <[email protected]>; +Cc: Aleksander Alekseev <[email protected]>; pgsql-hackers
On 12.09.22 19:59, Peter Eisentraut wrote:
> On 12.09.22 19:03, Julien Rouhaud wrote:
>> On Mon, Sep 12, 2022 at 05:59:09PM +0200, Peter Eisentraut wrote:
>>>
>>> committed, thanks
>>
>> FTR lapwing is complaining about this commit:
>> https://brekka.postgresql.org/cgi-bin/show_log.pl?nm=lapwing&dt=2022-09-12%2016%3A40%3A18.
>>
>> Snapper is also failing with similar problems:
>> https://brekka.postgresql.org/cgi-bin/show_log.pl?nm=snapper&dt=2022-09-12%2016%3A42%3A10
>
> Ok, it has problems with 32-bit platforms. I can reproduce it locally.
> I'll need to take another look at this. I have reverted the patch for now.
I have tried to analyze these issues, but I'm quite stuck. If anyone
else has any ideas, it would be helpful.
^ permalink raw reply [nested|flat] 92+ messages in thread
* Re: Convert *GetDatum() and DatumGet*() macros to inline functions
2022-08-28 15:55 Convert *GetDatum() and DatumGet*() macros to inline functions Peter Eisentraut <[email protected]>
2022-08-30 13:36 ` Re: Convert *GetDatum() and DatumGet*() macros to inline functions Aleksander Alekseev <[email protected]>
2022-08-30 13:46 ` Re: Convert *GetDatum() and DatumGet*() macros to inline functions Tom Lane <[email protected]>
2022-08-30 13:59 ` Re: Convert *GetDatum() and DatumGet*() macros to inline functions Aleksander Alekseev <[email protected]>
2022-08-30 14:13 ` Re: Convert *GetDatum() and DatumGet*() macros to inline functions Tom Lane <[email protected]>
2022-08-30 14:16 ` Re: Convert *GetDatum() and DatumGet*() macros to inline functions Robert Haas <[email protected]>
2022-08-30 14:25 ` Re: Convert *GetDatum() and DatumGet*() macros to inline functions Aleksander Alekseev <[email protected]>
2022-08-30 14:28 ` Re: Convert *GetDatum() and DatumGet*() macros to inline functions Tom Lane <[email protected]>
2022-08-30 14:52 ` Re: Convert *GetDatum() and DatumGet*() macros to inline functions Aleksander Alekseev <[email protected]>
2022-08-30 18:15 ` Re: Convert *GetDatum() and DatumGet*() macros to inline functions Aleksander Alekseev <[email protected]>
2022-09-05 12:57 ` Re: Convert *GetDatum() and DatumGet*() macros to inline functions Peter Eisentraut <[email protected]>
2022-09-05 14:20 ` Re: Convert *GetDatum() and DatumGet*() macros to inline functions Aleksander Alekseev <[email protected]>
2022-09-08 09:26 ` Re: Convert *GetDatum() and DatumGet*() macros to inline functions Aleksander Alekseev <[email protected]>
2022-09-12 15:59 ` Re: Convert *GetDatum() and DatumGet*() macros to inline functions Peter Eisentraut <[email protected]>
2022-09-12 17:03 ` Re: Convert *GetDatum() and DatumGet*() macros to inline functions Julien Rouhaud <[email protected]>
2022-09-12 17:59 ` Re: Convert *GetDatum() and DatumGet*() macros to inline functions Peter Eisentraut <[email protected]>
2022-09-26 14:55 ` Re: Convert *GetDatum() and DatumGet*() macros to inline functions Peter Eisentraut <[email protected]>
@ 2022-09-26 17:34 ` Tom Lane <[email protected]>
2022-09-27 19:26 ` Re: Convert *GetDatum() and DatumGet*() macros to inline functions Peter Eisentraut <[email protected]>
0 siblings, 1 reply; 92+ messages in thread
From: Tom Lane @ 2022-09-26 17:34 UTC (permalink / raw)
To: Peter Eisentraut <[email protected]>; +Cc: Julien Rouhaud <[email protected]>; Aleksander Alekseev <[email protected]>; pgsql-hackers
Peter Eisentraut <[email protected]> writes:
>> Ok, it has problems with 32-bit platforms. I can reproduce it locally.
>> I'll need to take another look at this. I have reverted the patch for now.
> I have tried to analyze these issues, but I'm quite stuck. If anyone
> else has any ideas, it would be helpful.
It looks to me like the problem is with the rewrite of Int64GetDatumFast
and Float8GetDatumFast:
+static inline Datum
+Int64GetDatumFast(int64 X)
+{
+#ifdef USE_FLOAT8_BYVAL
+ return Int64GetDatum(X);
+#else
+ return PointerGetDatum(&X);
+#endif
+}
In the by-ref code path, this is going to return the address of the
parameter local variable, which of course is broken as soon as the
function exits. To test, I reverted the mods to those two macros,
and I got through check-world OK in a 32-bit VM.
I think we can do this while still having reasonable type-safety
by adding AssertVariableIsOfTypeMacro() checks to the macros.
An advantage of that solution is that we verify that the code
will be safe for a 32-bit build even in 64-bit builds. (Of
course, it's just checking the variable's type not its lifespan,
but this is still a step forward.)
0001 attached is what you committed, 0002 is a proposed delta
to fix the Fast macros.
regards, tom lane
Attachments:
[text/x-diff] 0001-original-commit.patch (40.4K, ../../[email protected]/2-0001-original-commit.patch)
download | inline diff:
commit 595836e99bf1ee6d43405b885fb69bb8c6d3ee23
Author: Peter Eisentraut <[email protected]>
Date: Mon Sep 12 17:35:55 2022 +0200
Convert *GetDatum() and DatumGet*() macros to inline functions
The previous macro implementations just cast the argument to a target
type but did not check whether the input type was appropriate. The
function implementation can do better type checking of the input type.
Reviewed-by: Aleksander Alekseev <[email protected]>
Discussion: https://www.postgresql.org/message-id/flat/8528fb7e-0aa2-6b54-85fb-0c0886dbd6ed%40enterprisedb.com
diff --git a/contrib/intarray/_int_gist.c b/contrib/intarray/_int_gist.c
index f1817a6cce..6378aa74b0 100644
--- a/contrib/intarray/_int_gist.c
+++ b/contrib/intarray/_int_gist.c
@@ -51,7 +51,7 @@ g_int_consistent(PG_FUNCTION_ARGS)
/* Oid subtype = PG_GETARG_OID(3); */
bool *recheck = (bool *) PG_GETARG_POINTER(4);
- bool retval;
+ bool retval = false; /* silence compiler warning */
/* this is exact except for RTSameStrategyNumber */
*recheck = (strategy == RTSameStrategyNumber);
diff --git a/doc/src/sgml/xfunc.sgml b/doc/src/sgml/xfunc.sgml
index b8cefb9c2c..cf5810b3c1 100644
--- a/doc/src/sgml/xfunc.sgml
+++ b/doc/src/sgml/xfunc.sgml
@@ -2763,7 +2763,7 @@ c_overpaid(PG_FUNCTION_ARGS)
is null. <function>GetAttributeByName</function> returns a <type>Datum</type>
value that you can convert to the proper data type by using the
appropriate <function>DatumGet<replaceable>XXX</replaceable>()</function>
- macro. Note that the return value is meaningless if the null flag is
+ function. Note that the return value is meaningless if the null flag is
set; always check the null flag before trying to do anything with the
result.
</para>
diff --git a/src/backend/access/gist/gistutil.c b/src/backend/access/gist/gistutil.c
index d4bf0c7563..1532462317 100644
--- a/src/backend/access/gist/gistutil.c
+++ b/src/backend/access/gist/gistutil.c
@@ -280,7 +280,7 @@ gistMakeUnionKey(GISTSTATE *giststate, int attno,
bool
gistKeyIsEQ(GISTSTATE *giststate, int attno, Datum a, Datum b)
{
- bool result;
+ bool result = false; /* silence compiler warning */
FunctionCall3Coll(&giststate->equalFn[attno],
giststate->supportCollation[attno],
diff --git a/src/backend/tsearch/ts_parse.c b/src/backend/tsearch/ts_parse.c
index 27b2cca2df..92de1f7141 100644
--- a/src/backend/tsearch/ts_parse.c
+++ b/src/backend/tsearch/ts_parse.c
@@ -354,7 +354,7 @@ void
parsetext(Oid cfgId, ParsedText *prs, char *buf, int buflen)
{
int type,
- lenlemm;
+ lenlemm = 0; /* silence compiler warning */
char *lemm = NULL;
LexizeData ldata;
TSLexeme *norms;
@@ -529,7 +529,7 @@ void
hlparsetext(Oid cfgId, HeadlineParsedText *prs, TSQuery query, char *buf, int buflen)
{
int type,
- lenlemm;
+ lenlemm = 0; /* silence compiler warning */
char *lemm = NULL;
LexizeData ldata;
TSLexeme *norms;
diff --git a/src/backend/utils/mb/mbutils.c b/src/backend/utils/mb/mbutils.c
index 0543c574c6..474ab476f5 100644
--- a/src/backend/utils/mb/mbutils.c
+++ b/src/backend/utils/mb/mbutils.c
@@ -409,8 +409,8 @@ pg_do_encoding_conversion(unsigned char *src, int len,
(void) OidFunctionCall6(proc,
Int32GetDatum(src_encoding),
Int32GetDatum(dest_encoding),
- CStringGetDatum(src),
- CStringGetDatum(result),
+ CStringGetDatum((char *) src),
+ CStringGetDatum((char *) result),
Int32GetDatum(len),
BoolGetDatum(false));
@@ -485,8 +485,8 @@ pg_do_encoding_conversion_buf(Oid proc,
result = OidFunctionCall6(proc,
Int32GetDatum(src_encoding),
Int32GetDatum(dest_encoding),
- CStringGetDatum(src),
- CStringGetDatum(dest),
+ CStringGetDatum((char *) src),
+ CStringGetDatum((char *) dest),
Int32GetDatum(srclen),
BoolGetDatum(noError));
return DatumGetInt32(result);
@@ -910,8 +910,8 @@ pg_unicode_to_server(pg_wchar c, unsigned char *s)
FunctionCall6(Utf8ToServerConvProc,
Int32GetDatum(PG_UTF8),
Int32GetDatum(server_encoding),
- CStringGetDatum(c_as_utf8),
- CStringGetDatum(s),
+ CStringGetDatum((char *) c_as_utf8),
+ CStringGetDatum((char *) s),
Int32GetDatum(c_as_utf8_len),
BoolGetDatum(false));
}
diff --git a/src/include/access/gin.h b/src/include/access/gin.h
index aacc665fdc..fff861e219 100644
--- a/src/include/access/gin.h
+++ b/src/include/access/gin.h
@@ -62,8 +62,18 @@ typedef char GinTernaryValue;
#define GIN_MAYBE 2 /* don't know if item is present / don't know
* if matches */
-#define DatumGetGinTernaryValue(X) ((GinTernaryValue)(X))
-#define GinTernaryValueGetDatum(X) ((Datum)(X))
+static inline GinTernaryValue
+DatumGetGinTernaryValue(Datum X)
+{
+ return (GinTernaryValue) X;
+}
+
+static inline Datum
+GinTernaryValueGetDatum(GinTernaryValue X)
+{
+ return (Datum) X;
+}
+
#define PG_RETURN_GIN_TERNARY_VALUE(x) return GinTernaryValueGetDatum(x)
/* GUC parameters */
diff --git a/src/include/funcapi.h b/src/include/funcapi.h
index dc3d819a1c..8c2521a591 100644
--- a/src/include/funcapi.h
+++ b/src/include/funcapi.h
@@ -204,7 +204,7 @@ extern TupleDesc build_function_result_tupdesc_t(HeapTuple procTuple);
* Datum HeapTupleHeaderGetDatum(HeapTupleHeader tuple) - convert a
* HeapTupleHeader to a Datum.
*
- * Macro declarations:
+ * Inline declarations:
* HeapTupleGetDatum(HeapTuple tuple) - convert a HeapTuple to a Datum.
*
* Obsolete routines and macros:
@@ -217,10 +217,6 @@ extern TupleDesc build_function_result_tupdesc_t(HeapTuple procTuple);
*----------
*/
-#define HeapTupleGetDatum(tuple) HeapTupleHeaderGetDatum((tuple)->t_data)
-/* obsolete version of above */
-#define TupleGetDatum(_slot, _tuple) HeapTupleGetDatum(_tuple)
-
extern TupleDesc RelationNameGetTupleDesc(const char *relname);
extern TupleDesc TypeGetTupleDesc(Oid typeoid, List *colaliases);
@@ -230,6 +226,14 @@ extern AttInMetadata *TupleDescGetAttInMetadata(TupleDesc tupdesc);
extern HeapTuple BuildTupleFromCStrings(AttInMetadata *attinmeta, char **values);
extern Datum HeapTupleHeaderGetDatum(HeapTupleHeader tuple);
+static inline Datum
+HeapTupleGetDatum(const HeapTupleData *tuple)
+{
+ return HeapTupleHeaderGetDatum(tuple->t_data);
+}
+/* obsolete version of above */
+#define TupleGetDatum(_slot, _tuple) HeapTupleGetDatum(_tuple)
+
/*----------
* Support for Set Returning Functions (SRFs)
diff --git a/src/include/postgres.h b/src/include/postgres.h
index 13903fa022..45c9f6563c 100644
--- a/src/include/postgres.h
+++ b/src/include/postgres.h
@@ -24,7 +24,7 @@
* section description
* ------- ------------------------------------------------
* 1) variable-length datatypes (TOAST support)
- * 2) Datum type + support macros
+ * 2) Datum type + support functions
* 3) miscellaneous
*
* NOTES
@@ -395,7 +395,7 @@ typedef struct
/* ----------------------------------------------------------------
- * Section 2: Datum type + support macros
+ * Section 2: Datum type + support functions
* ----------------------------------------------------------------
*/
@@ -405,7 +405,7 @@ typedef struct
*
* sizeof(Datum) == sizeof(void *) == 4 or 8
*
- * The macros below and the analogous macros for other types should be used to
+ * The functions below and the analogous functions for other types should be used to
* convert between a Datum and the appropriate C type.
*/
@@ -434,8 +434,11 @@ typedef struct NullableDatum
*
* Note: any nonzero value will be considered true.
*/
-
-#define DatumGetBool(X) ((bool) ((X) != 0))
+static inline bool
+DatumGetBool(Datum X)
+{
+ return (X != 0);
+}
/*
* BoolGetDatum
@@ -443,162 +446,231 @@ typedef struct NullableDatum
*
* Note: any nonzero value will be considered true.
*/
-
-#define BoolGetDatum(X) ((Datum) ((X) ? 1 : 0))
+static inline Datum
+BoolGetDatum(bool X)
+{
+ return (Datum) (X ? 1 : 0);
+}
/*
* DatumGetChar
* Returns character value of a datum.
*/
-
-#define DatumGetChar(X) ((char) (X))
+static inline char
+DatumGetChar(Datum X)
+{
+ return (char) X;
+}
/*
* CharGetDatum
* Returns datum representation for a character.
*/
-
-#define CharGetDatum(X) ((Datum) (X))
+static inline Datum
+CharGetDatum(char X)
+{
+ return (Datum) X;
+}
/*
* Int8GetDatum
* Returns datum representation for an 8-bit integer.
*/
-
-#define Int8GetDatum(X) ((Datum) (X))
+static inline Datum
+Int8GetDatum(int8 X)
+{
+ return (Datum) X;
+}
/*
* DatumGetUInt8
* Returns 8-bit unsigned integer value of a datum.
*/
-
-#define DatumGetUInt8(X) ((uint8) (X))
+static inline uint8
+DatumGetUInt8(Datum X)
+{
+ return (uint8) X;
+}
/*
* UInt8GetDatum
* Returns datum representation for an 8-bit unsigned integer.
*/
-
-#define UInt8GetDatum(X) ((Datum) (X))
+static inline Datum
+UInt8GetDatum(uint8 X)
+{
+ return (Datum) X;
+}
/*
* DatumGetInt16
* Returns 16-bit integer value of a datum.
*/
-
-#define DatumGetInt16(X) ((int16) (X))
+static inline int16
+DatumGetInt16(Datum X)
+{
+ return (int16) X;
+}
/*
* Int16GetDatum
* Returns datum representation for a 16-bit integer.
*/
-
-#define Int16GetDatum(X) ((Datum) (X))
+static inline Datum
+Int16GetDatum(int16 X)
+{
+ return (Datum) X;
+}
/*
* DatumGetUInt16
* Returns 16-bit unsigned integer value of a datum.
*/
-
-#define DatumGetUInt16(X) ((uint16) (X))
+static inline uint16
+DatumGetUInt16(Datum X)
+{
+ return (uint16) X;
+}
/*
* UInt16GetDatum
* Returns datum representation for a 16-bit unsigned integer.
*/
-
-#define UInt16GetDatum(X) ((Datum) (X))
+static inline Datum
+UInt16GetDatum(uint16 X)
+{
+ return (Datum) X;
+}
/*
* DatumGetInt32
* Returns 32-bit integer value of a datum.
*/
-
-#define DatumGetInt32(X) ((int32) (X))
+static inline int32
+DatumGetInt32(Datum X)
+{
+ return (int32) X;
+}
/*
* Int32GetDatum
* Returns datum representation for a 32-bit integer.
*/
-
-#define Int32GetDatum(X) ((Datum) (X))
+static inline Datum
+Int32GetDatum(int32 X)
+{
+ return (Datum) X;
+}
/*
* DatumGetUInt32
* Returns 32-bit unsigned integer value of a datum.
*/
-
-#define DatumGetUInt32(X) ((uint32) (X))
+static inline uint32
+DatumGetUInt32(Datum X)
+{
+ return (uint32) X;
+}
/*
* UInt32GetDatum
* Returns datum representation for a 32-bit unsigned integer.
*/
-
-#define UInt32GetDatum(X) ((Datum) (X))
+static inline Datum
+UInt32GetDatum(uint32 X)
+{
+ return (Datum) X;
+}
/*
* DatumGetObjectId
* Returns object identifier value of a datum.
*/
-
-#define DatumGetObjectId(X) ((Oid) (X))
+static inline Oid
+DatumGetObjectId(Datum X)
+{
+ return (Oid) X;
+}
/*
* ObjectIdGetDatum
* Returns datum representation for an object identifier.
*/
-
-#define ObjectIdGetDatum(X) ((Datum) (X))
+static inline Datum
+ObjectIdGetDatum(Oid X)
+{
+ return (Datum) X;
+}
/*
* DatumGetTransactionId
* Returns transaction identifier value of a datum.
*/
-
-#define DatumGetTransactionId(X) ((TransactionId) (X))
+static inline TransactionId
+DatumGetTransactionId(Datum X)
+{
+ return (TransactionId) X;
+}
/*
* TransactionIdGetDatum
* Returns datum representation for a transaction identifier.
*/
-
-#define TransactionIdGetDatum(X) ((Datum) (X))
+static inline Datum
+TransactionIdGetDatum(TransactionId X)
+{
+ return (Datum) X;
+}
/*
* MultiXactIdGetDatum
* Returns datum representation for a multixact identifier.
*/
-
-#define MultiXactIdGetDatum(X) ((Datum) (X))
+static inline Datum
+MultiXactIdGetDatum(MultiXactId X)
+{
+ return (Datum) X;
+}
/*
* DatumGetCommandId
* Returns command identifier value of a datum.
*/
-
-#define DatumGetCommandId(X) ((CommandId) (X))
+static inline CommandId
+DatumGetCommandId(Datum X)
+{
+ return (CommandId) X;
+}
/*
* CommandIdGetDatum
* Returns datum representation for a command identifier.
*/
-
-#define CommandIdGetDatum(X) ((Datum) (X))
+static inline Datum
+CommandIdGetDatum(CommandId X)
+{
+ return (Datum) X;
+}
/*
* DatumGetPointer
* Returns pointer value of a datum.
*/
-
-#define DatumGetPointer(X) ((Pointer) (X))
+static inline Pointer
+DatumGetPointer(Datum X)
+{
+ return (Pointer) X;
+}
/*
* PointerGetDatum
* Returns datum representation for a pointer.
*/
-
-#define PointerGetDatum(X) ((Datum) (X))
+static inline Datum
+PointerGetDatum(const void *X)
+{
+ return (Datum) X;
+}
/*
* DatumGetCString
@@ -607,8 +679,11 @@ typedef struct NullableDatum
* Note: C string is not a full-fledged Postgres type at present,
* but type input functions use this conversion for their inputs.
*/
-
-#define DatumGetCString(X) ((char *) DatumGetPointer(X))
+static inline char *
+DatumGetCString(Datum X)
+{
+ return (char *) DatumGetPointer(X);
+}
/*
* CStringGetDatum
@@ -619,15 +694,21 @@ typedef struct NullableDatum
* Note: CString is pass-by-reference; caller must ensure the pointed-to
* value has adequate lifetime.
*/
-
-#define CStringGetDatum(X) PointerGetDatum(X)
+static inline Datum
+CStringGetDatum(const char *X)
+{
+ return PointerGetDatum(X);
+}
/*
* DatumGetName
* Returns name value of a datum.
*/
-
-#define DatumGetName(X) ((Name) DatumGetPointer(X))
+static inline Name
+DatumGetName(Datum X)
+{
+ return (Name) DatumGetPointer(X);
+}
/*
* NameGetDatum
@@ -636,21 +717,27 @@ typedef struct NullableDatum
* Note: Name is pass-by-reference; caller must ensure the pointed-to
* value has adequate lifetime.
*/
-
-#define NameGetDatum(X) CStringGetDatum(NameStr(*(X)))
+static inline Datum
+NameGetDatum(const NameData *X)
+{
+ return CStringGetDatum(NameStr(*X));
+}
/*
* DatumGetInt64
* Returns 64-bit integer value of a datum.
*
- * Note: this macro hides whether int64 is pass by value or by reference.
+ * Note: this function hides whether int64 is pass by value or by reference.
*/
-
+static inline int64
+DatumGetInt64(Datum X)
+{
#ifdef USE_FLOAT8_BYVAL
-#define DatumGetInt64(X) ((int64) (X))
+ return (int64) X;
#else
-#define DatumGetInt64(X) (* ((int64 *) DatumGetPointer(X)))
+ return *((int64 *) DatumGetPointer(X));
#endif
+}
/*
* Int64GetDatum
@@ -659,25 +746,32 @@ typedef struct NullableDatum
* Note: if int64 is pass by reference, this function returns a reference
* to palloc'd space.
*/
-
#ifdef USE_FLOAT8_BYVAL
-#define Int64GetDatum(X) ((Datum) (X))
+static inline Datum
+Int64GetDatum(int64 X)
+{
+ return (Datum) X;
+}
#else
extern Datum Int64GetDatum(int64 X);
#endif
+
/*
* DatumGetUInt64
* Returns 64-bit unsigned integer value of a datum.
*
- * Note: this macro hides whether int64 is pass by value or by reference.
+ * Note: this function hides whether int64 is pass by value or by reference.
*/
-
+static inline uint64
+DatumGetUInt64(Datum X)
+{
#ifdef USE_FLOAT8_BYVAL
-#define DatumGetUInt64(X) ((uint64) (X))
+ return (uint64) X;
#else
-#define DatumGetUInt64(X) (* ((uint64 *) DatumGetPointer(X)))
+ return *((uint64 *) DatumGetPointer(X));
#endif
+}
/*
* UInt64GetDatum
@@ -686,12 +780,15 @@ extern Datum Int64GetDatum(int64 X);
* Note: if int64 is pass by reference, this function returns a reference
* to palloc'd space.
*/
-
+static inline Datum
+UInt64GetDatum(uint64 X)
+{
#ifdef USE_FLOAT8_BYVAL
-#define UInt64GetDatum(X) ((Datum) (X))
+ return (Datum) X;
#else
-#define UInt64GetDatum(X) Int64GetDatum((int64) (X))
+ return Int64GetDatum((int64) X);
#endif
+}
/*
* Float <-> Datum conversions
@@ -739,13 +836,12 @@ Float4GetDatum(float4 X)
* DatumGetFloat8
* Returns 8-byte floating point value of a datum.
*
- * Note: this macro hides whether float8 is pass by value or by reference.
+ * Note: this function hides whether float8 is pass by value or by reference.
*/
-
-#ifdef USE_FLOAT8_BYVAL
static inline float8
DatumGetFloat8(Datum X)
{
+#ifdef USE_FLOAT8_BYVAL
union
{
int64 value;
@@ -754,10 +850,10 @@ DatumGetFloat8(Datum X)
myunion.value = DatumGetInt64(X);
return myunion.retval;
-}
#else
-#define DatumGetFloat8(X) (* ((float8 *) DatumGetPointer(X)))
+ return *((float8 *) DatumGetPointer(X));
#endif
+}
/*
* Float8GetDatum
@@ -766,7 +862,6 @@ DatumGetFloat8(Datum X)
* Note: if float8 is pass by reference, this function returns a reference
* to palloc'd space.
*/
-
#ifdef USE_FLOAT8_BYVAL
static inline Datum
Float8GetDatum(float8 X)
@@ -789,22 +884,34 @@ extern Datum Float8GetDatum(float8 X);
* Int64GetDatumFast
* Float8GetDatumFast
*
- * These macros are intended to allow writing code that does not depend on
+ * These functions are intended to allow writing code that does not depend on
* whether int64 and float8 are pass-by-reference types, while not
* sacrificing performance when they are. The argument must be a variable
* that will exist and have the same value for as long as the Datum is needed.
* In the pass-by-ref case, the address of the variable is taken to use as
* the Datum. In the pass-by-val case, these will be the same as the non-Fast
- * macros.
+ * functions.
*/
+static inline Datum
+Int64GetDatumFast(int64 X)
+{
+#ifdef USE_FLOAT8_BYVAL
+ return Int64GetDatum(X);
+#else
+ return PointerGetDatum(&X);
+#endif
+}
+
+static inline Datum
+Float8GetDatumFast(float8 X)
+{
#ifdef USE_FLOAT8_BYVAL
-#define Int64GetDatumFast(X) Int64GetDatum(X)
-#define Float8GetDatumFast(X) Float8GetDatum(X)
+ return Float8GetDatum(X);
#else
-#define Int64GetDatumFast(X) PointerGetDatum(&(X))
-#define Float8GetDatumFast(X) PointerGetDatum(&(X))
+ return PointerGetDatum(&X);
#endif
+}
/* ----------------------------------------------------------------
diff --git a/src/include/tsearch/ts_type.h b/src/include/tsearch/ts_type.h
index 689b2d1cfb..f1ec84702d 100644
--- a/src/include/tsearch/ts_type.h
+++ b/src/include/tsearch/ts_type.h
@@ -111,12 +111,27 @@ typedef TSVectorData *TSVector;
#define POSDATAPTR(x,e) (_POSVECPTR(x,e)->pos)
/*
- * fmgr interface macros
+ * fmgr interface functions
*/
-#define DatumGetTSVector(X) ((TSVector) PG_DETOAST_DATUM(X))
-#define DatumGetTSVectorCopy(X) ((TSVector) PG_DETOAST_DATUM_COPY(X))
-#define TSVectorGetDatum(X) PointerGetDatum(X)
+static inline TSVector
+DatumGetTSVector(Datum X)
+{
+ return (TSVector) PG_DETOAST_DATUM(X);
+}
+
+static inline TSVector
+DatumGetTSVectorCopy(Datum X)
+{
+ return (TSVector) PG_DETOAST_DATUM_COPY(X);
+}
+
+static inline Datum
+TSVectorGetDatum(const TSVectorData *X)
+{
+ return PointerGetDatum(X);
+}
+
#define PG_GETARG_TSVECTOR(n) DatumGetTSVector(PG_GETARG_DATUM(n))
#define PG_GETARG_TSVECTOR_COPY(n) DatumGetTSVectorCopy(PG_GETARG_DATUM(n))
#define PG_RETURN_TSVECTOR(x) return TSVectorGetDatum(x)
@@ -227,14 +242,29 @@ typedef TSQueryData *TSQuery;
#define GETOPERAND(x) ( (char*)GETQUERY(x) + ((TSQuery)(x))->size * sizeof(QueryItem) )
/*
- * fmgr interface macros
+ * fmgr interface functions
* Note, TSQuery type marked as plain storage, so it can't be toasted
* but PG_DETOAST_DATUM_COPY is used for simplicity
*/
-#define DatumGetTSQuery(X) ((TSQuery) DatumGetPointer(X))
-#define DatumGetTSQueryCopy(X) ((TSQuery) PG_DETOAST_DATUM_COPY(X))
-#define TSQueryGetDatum(X) PointerGetDatum(X)
+static inline TSQuery
+DatumGetTSQuery(Datum X)
+{
+ return (TSQuery) DatumGetPointer(X);
+}
+
+static inline TSQuery
+DatumGetTSQueryCopy(Datum X)
+{
+ return (TSQuery) PG_DETOAST_DATUM_COPY(X);
+}
+
+static inline Datum
+TSQueryGetDatum(const TSQueryData *X)
+{
+ return PointerGetDatum(X);
+}
+
#define PG_GETARG_TSQUERY(n) DatumGetTSQuery(PG_GETARG_DATUM(n))
#define PG_GETARG_TSQUERY_COPY(n) DatumGetTSQueryCopy(PG_GETARG_DATUM(n))
#define PG_RETURN_TSQUERY(x) return TSQueryGetDatum(x)
diff --git a/src/include/tsearch/ts_utils.h b/src/include/tsearch/ts_utils.h
index c36c711dae..26cc415aa8 100644
--- a/src/include/tsearch/ts_utils.h
+++ b/src/include/tsearch/ts_utils.h
@@ -243,8 +243,18 @@ typedef uint64 TSQuerySign;
#define TSQS_SIGLEN (sizeof(TSQuerySign)*BITS_PER_BYTE)
-#define TSQuerySignGetDatum(X) Int64GetDatum((int64) (X))
-#define DatumGetTSQuerySign(X) ((TSQuerySign) DatumGetInt64(X))
+static inline Datum
+TSQuerySignGetDatum(TSQuerySign X)
+{
+ return Int64GetDatum((int64) X);
+}
+
+static inline TSQuerySign
+DatumGetTSQuerySign(Datum X)
+{
+ return (TSQuerySign) DatumGetInt64(X);
+}
+
#define PG_RETURN_TSQUERYSIGN(X) return TSQuerySignGetDatum(X)
#define PG_GETARG_TSQUERYSIGN(n) DatumGetTSQuerySign(PG_GETARG_DATUM(n))
diff --git a/src/include/utils/cash.h b/src/include/utils/cash.h
index 2e332d83b1..55d45fadd4 100644
--- a/src/include/utils/cash.h
+++ b/src/include/utils/cash.h
@@ -17,8 +17,18 @@
typedef int64 Cash;
/* Cash is pass-by-reference if and only if int64 is */
-#define DatumGetCash(X) ((Cash) DatumGetInt64(X))
-#define CashGetDatum(X) Int64GetDatum(X)
+static inline Cash
+DatumGetCash(Datum X)
+{
+ return DatumGetInt64(X);
+}
+
+static inline Datum
+CashGetDatum(Cash X)
+{
+ return Int64GetDatum(X);
+}
+
#define PG_GETARG_CASH(n) DatumGetCash(PG_GETARG_DATUM(n))
#define PG_RETURN_CASH(x) return CashGetDatum(x)
diff --git a/src/include/utils/date.h b/src/include/utils/date.h
index 3991da41f0..0bbe889128 100644
--- a/src/include/utils/date.h
+++ b/src/include/utils/date.h
@@ -45,18 +45,46 @@ typedef struct
#define MAX_TIME_PRECISION 6
/*
- * Macros for fmgr-callable functions.
+ * Functions for fmgr-callable functions.
*
* For TimeADT, we make use of the same support routines as for int64.
* Therefore TimeADT is pass-by-reference if and only if int64 is!
*/
-#define DatumGetDateADT(X) ((DateADT) DatumGetInt32(X))
-#define DatumGetTimeADT(X) ((TimeADT) DatumGetInt64(X))
-#define DatumGetTimeTzADTP(X) ((TimeTzADT *) DatumGetPointer(X))
+static inline DateADT
+DatumGetDateADT(Datum X)
+{
+ return (DateADT) DatumGetInt32(X);
+}
+
+static inline TimeADT
+DatumGetTimeADT(Datum X)
+{
+ return (TimeADT) DatumGetInt64(X);
+}
+
+static inline TimeTzADT *
+DatumGetTimeTzADTP(Datum X)
+{
+ return (TimeTzADT *) DatumGetPointer(X);
+}
-#define DateADTGetDatum(X) Int32GetDatum(X)
-#define TimeADTGetDatum(X) Int64GetDatum(X)
-#define TimeTzADTPGetDatum(X) PointerGetDatum(X)
+static inline Datum
+DateADTGetDatum(DateADT X)
+{
+ return Int32GetDatum(X);
+}
+
+static inline Datum
+TimeADTGetDatum(TimeADT X)
+{
+ return Int64GetDatum(X);
+}
+
+static inline Datum
+TimeTzADTPGetDatum(const TimeTzADT *X)
+{
+ return PointerGetDatum(X);
+}
#define PG_GETARG_DATEADT(n) DatumGetDateADT(PG_GETARG_DATUM(n))
#define PG_GETARG_TIMEADT(n) DatumGetTimeADT(PG_GETARG_DATUM(n))
diff --git a/src/include/utils/expandeddatum.h b/src/include/utils/expandeddatum.h
index ffdb0c45bd..c4818eb054 100644
--- a/src/include/utils/expandeddatum.h
+++ b/src/include/utils/expandeddatum.h
@@ -133,8 +133,17 @@ struct ExpandedObjectHeader
* (More of these might be worth inlining later.)
*/
-#define EOHPGetRWDatum(eohptr) PointerGetDatum((eohptr)->eoh_rw_ptr)
-#define EOHPGetRODatum(eohptr) PointerGetDatum((eohptr)->eoh_ro_ptr)
+static inline Datum
+EOHPGetRWDatum(const struct ExpandedObjectHeader *eohptr)
+{
+ return PointerGetDatum(eohptr->eoh_rw_ptr);
+}
+
+static inline Datum
+EOHPGetRODatum(const struct ExpandedObjectHeader *eohptr)
+{
+ return PointerGetDatum(eohptr->eoh_ro_ptr);
+}
/* Does the Datum represent a writable expanded object? */
#define DatumIsReadWriteExpandedObject(d, isnull, typlen) \
diff --git a/src/include/utils/expandedrecord.h b/src/include/utils/expandedrecord.h
index be60e2ce53..aaf5b6bfd1 100644
--- a/src/include/utils/expandedrecord.h
+++ b/src/include/utils/expandedrecord.h
@@ -138,10 +138,20 @@ typedef struct ExpandedRecordHeader
MemoryContextCallback er_mcb;
} ExpandedRecordHeader;
-/* fmgr macros for expanded record objects */
+/* fmgr functions and macros for expanded record objects */
+static inline Datum
+ExpandedRecordGetDatum(const ExpandedRecordHeader *erh)
+{
+ return EOHPGetRWDatum(&erh->hdr);
+}
+
+static inline Datum
+ExpandedRecordGetRODatum(const ExpandedRecordHeader *erh)
+{
+ return EOHPGetRODatum(&erh->hdr);
+}
+
#define PG_GETARG_EXPANDED_RECORD(n) DatumGetExpandedRecord(PG_GETARG_DATUM(n))
-#define ExpandedRecordGetDatum(erh) EOHPGetRWDatum(&(erh)->hdr)
-#define ExpandedRecordGetRODatum(erh) EOHPGetRODatum(&(erh)->hdr)
#define PG_RETURN_EXPANDED_RECORD(x) PG_RETURN_DATUM(ExpandedRecordGetDatum(x))
/* assorted other macros */
diff --git a/src/include/utils/geo_decls.h b/src/include/utils/geo_decls.h
index 719030777e..05726778ad 100644
--- a/src/include/utils/geo_decls.h
+++ b/src/include/utils/geo_decls.h
@@ -166,48 +166,112 @@ typedef struct
} CIRCLE;
/*
- * fmgr interface macros
+ * fmgr interface functions
*
* Path and Polygon are toastable varlena types, the others are just
* fixed-size pass-by-reference types.
*/
-#define DatumGetPointP(X) ((Point *) DatumGetPointer(X))
-#define PointPGetDatum(X) PointerGetDatum(X)
+static inline Point *
+DatumGetPointP(Datum X)
+{
+ return (Point *) DatumGetPointer(X);
+}
+static inline Datum
+PointPGetDatum(const Point *X)
+{
+ return PointerGetDatum(X);
+}
#define PG_GETARG_POINT_P(n) DatumGetPointP(PG_GETARG_DATUM(n))
#define PG_RETURN_POINT_P(x) return PointPGetDatum(x)
-#define DatumGetLsegP(X) ((LSEG *) DatumGetPointer(X))
-#define LsegPGetDatum(X) PointerGetDatum(X)
+static inline LSEG *
+DatumGetLsegP(Datum X)
+{
+ return (LSEG *) DatumGetPointer(X);
+}
+static inline Datum
+LsegPGetDatum(const LSEG *X)
+{
+ return PointerGetDatum(X);
+}
#define PG_GETARG_LSEG_P(n) DatumGetLsegP(PG_GETARG_DATUM(n))
#define PG_RETURN_LSEG_P(x) return LsegPGetDatum(x)
-#define DatumGetPathP(X) ((PATH *) PG_DETOAST_DATUM(X))
-#define DatumGetPathPCopy(X) ((PATH *) PG_DETOAST_DATUM_COPY(X))
-#define PathPGetDatum(X) PointerGetDatum(X)
+static inline PATH *
+DatumGetPathP(Datum X)
+{
+ return (PATH *) PG_DETOAST_DATUM(X);
+}
+static inline PATH *
+DatumGetPathPCopy(Datum X)
+{
+ return (PATH *) PG_DETOAST_DATUM_COPY(X);
+}
+static inline Datum
+PathPGetDatum(const PATH *X)
+{
+ return PointerGetDatum(X);
+}
#define PG_GETARG_PATH_P(n) DatumGetPathP(PG_GETARG_DATUM(n))
#define PG_GETARG_PATH_P_COPY(n) DatumGetPathPCopy(PG_GETARG_DATUM(n))
#define PG_RETURN_PATH_P(x) return PathPGetDatum(x)
-#define DatumGetLineP(X) ((LINE *) DatumGetPointer(X))
-#define LinePGetDatum(X) PointerGetDatum(X)
+static inline LINE *
+DatumGetLineP(Datum X)
+{
+ return (LINE *) DatumGetPointer(X);
+}
+static inline Datum
+LinePGetDatum(const LINE *X)
+{
+ return PointerGetDatum(X);
+}
#define PG_GETARG_LINE_P(n) DatumGetLineP(PG_GETARG_DATUM(n))
#define PG_RETURN_LINE_P(x) return LinePGetDatum(x)
-#define DatumGetBoxP(X) ((BOX *) DatumGetPointer(X))
-#define BoxPGetDatum(X) PointerGetDatum(X)
+static inline BOX *
+DatumGetBoxP(Datum X)
+{
+ return (BOX *) DatumGetPointer(X);
+}
+static inline Datum
+BoxPGetDatum(const BOX *X)
+{
+ return PointerGetDatum(X);
+}
#define PG_GETARG_BOX_P(n) DatumGetBoxP(PG_GETARG_DATUM(n))
#define PG_RETURN_BOX_P(x) return BoxPGetDatum(x)
-#define DatumGetPolygonP(X) ((POLYGON *) PG_DETOAST_DATUM(X))
-#define DatumGetPolygonPCopy(X) ((POLYGON *) PG_DETOAST_DATUM_COPY(X))
-#define PolygonPGetDatum(X) PointerGetDatum(X)
+static inline POLYGON *
+DatumGetPolygonP(Datum X)
+{
+ return (POLYGON *) PG_DETOAST_DATUM(X);
+}
+static inline POLYGON *
+DatumGetPolygonPCopy(Datum X)
+{
+ return (POLYGON *) PG_DETOAST_DATUM_COPY(X);
+}
+static inline Datum
+PolygonPGetDatum(const POLYGON *X)
+{
+ return PointerGetDatum(X);
+}
#define PG_GETARG_POLYGON_P(n) DatumGetPolygonP(PG_GETARG_DATUM(n))
#define PG_GETARG_POLYGON_P_COPY(n) DatumGetPolygonPCopy(PG_GETARG_DATUM(n))
#define PG_RETURN_POLYGON_P(x) return PolygonPGetDatum(x)
-#define DatumGetCircleP(X) ((CIRCLE *) DatumGetPointer(X))
-#define CirclePGetDatum(X) PointerGetDatum(X)
+static inline CIRCLE *
+DatumGetCircleP(Datum X)
+{
+ return (CIRCLE *) DatumGetPointer(X);
+}
+static inline Datum
+CirclePGetDatum(const CIRCLE *X)
+{
+ return PointerGetDatum(X);
+}
#define PG_GETARG_CIRCLE_P(n) DatumGetCircleP(PG_GETARG_DATUM(n))
#define PG_RETURN_CIRCLE_P(x) return CirclePGetDatum(x)
diff --git a/src/include/utils/inet.h b/src/include/utils/inet.h
index b1ec9723df..5438cfaba5 100644
--- a/src/include/utils/inet.h
+++ b/src/include/utils/inet.h
@@ -119,23 +119,58 @@ typedef struct macaddr8
/*
* fmgr interface macros
*/
-#define DatumGetInetPP(X) ((inet *) PG_DETOAST_DATUM_PACKED(X))
-#define InetPGetDatum(X) PointerGetDatum(X)
+static inline inet *
+DatumGetInetPP(Datum X)
+{
+ return (inet *) PG_DETOAST_DATUM_PACKED(X);
+}
+
+static inline Datum
+InetPGetDatum(const inet *X)
+{
+ return PointerGetDatum(X);
+}
+
#define PG_GETARG_INET_PP(n) DatumGetInetPP(PG_GETARG_DATUM(n))
#define PG_RETURN_INET_P(x) return InetPGetDatum(x)
+
/* obsolescent variants */
-#define DatumGetInetP(X) ((inet *) PG_DETOAST_DATUM(X))
+static inline inet *
+DatumGetInetP(Datum X)
+{
+ return (inet *) PG_DETOAST_DATUM(X);
+}
#define PG_GETARG_INET_P(n) DatumGetInetP(PG_GETARG_DATUM(n))
/* macaddr is a fixed-length pass-by-reference datatype */
-#define DatumGetMacaddrP(X) ((macaddr *) DatumGetPointer(X))
-#define MacaddrPGetDatum(X) PointerGetDatum(X)
+static inline macaddr *
+DatumGetMacaddrP(Datum X)
+{
+ return (macaddr *) DatumGetPointer(X);
+}
+
+static inline Datum
+MacaddrPGetDatum(const macaddr *X)
+{
+ return PointerGetDatum(X);
+}
+
#define PG_GETARG_MACADDR_P(n) DatumGetMacaddrP(PG_GETARG_DATUM(n))
#define PG_RETURN_MACADDR_P(x) return MacaddrPGetDatum(x)
/* macaddr8 is a fixed-length pass-by-reference datatype */
-#define DatumGetMacaddr8P(X) ((macaddr8 *) DatumGetPointer(X))
-#define Macaddr8PGetDatum(X) PointerGetDatum(X)
+static inline macaddr8 *
+DatumGetMacaddr8P(Datum X)
+{
+ return (macaddr8 *) DatumGetPointer(X);
+}
+
+static inline Datum
+Macaddr8PGetDatum(const macaddr8 *X)
+{
+ return PointerGetDatum(X);
+}
+
#define PG_GETARG_MACADDR8_P(n) DatumGetMacaddr8P(PG_GETARG_DATUM(n))
#define PG_RETURN_MACADDR8_P(x) return Macaddr8PGetDatum(x)
diff --git a/src/include/utils/jsonb.h b/src/include/utils/jsonb.h
index 4cbe6edf21..bdd1c9c75d 100644
--- a/src/include/utils/jsonb.h
+++ b/src/include/utils/jsonb.h
@@ -67,14 +67,6 @@ typedef enum
#define JGINFLAG_HASHED 0x10 /* OR'd into flag if value was hashed */
#define JGIN_MAXLENGTH 125 /* max length of text part before hashing */
-/* Convenience macros */
-#define DatumGetJsonbP(d) ((Jsonb *) PG_DETOAST_DATUM(d))
-#define DatumGetJsonbPCopy(d) ((Jsonb *) PG_DETOAST_DATUM_COPY(d))
-#define JsonbPGetDatum(p) PointerGetDatum(p)
-#define PG_GETARG_JSONB_P(x) DatumGetJsonbP(PG_GETARG_DATUM(x))
-#define PG_GETARG_JSONB_P_COPY(x) DatumGetJsonbPCopy(PG_GETARG_DATUM(x))
-#define PG_RETURN_JSONB_P(x) PG_RETURN_POINTER(x)
-
typedef struct JsonbPair JsonbPair;
typedef struct JsonbValue JsonbValue;
@@ -375,6 +367,29 @@ typedef struct JsonbIterator
} JsonbIterator;
+/* Convenience macros */
+static inline Jsonb *
+DatumGetJsonbP(Datum d)
+{
+ return (Jsonb *) PG_DETOAST_DATUM(d);
+}
+
+static inline Jsonb *
+DatumGetJsonbPCopy(Datum d)
+{
+ return (Jsonb *) PG_DETOAST_DATUM_COPY(d);
+}
+
+static inline Datum
+JsonbPGetDatum(const Jsonb *p)
+{
+ return PointerGetDatum(p);
+}
+
+#define PG_GETARG_JSONB_P(x) DatumGetJsonbP(PG_GETARG_DATUM(x))
+#define PG_GETARG_JSONB_P_COPY(x) DatumGetJsonbPCopy(PG_GETARG_DATUM(x))
+#define PG_RETURN_JSONB_P(x) PG_RETURN_POINTER(x)
+
/* Support functions */
extern uint32 getJsonbOffset(const JsonbContainer *jc, int index);
extern uint32 getJsonbLength(const JsonbContainer *jc, int index);
diff --git a/src/include/utils/jsonpath.h b/src/include/utils/jsonpath.h
index cd0b5d5b61..13f60cdc09 100644
--- a/src/include/utils/jsonpath.h
+++ b/src/include/utils/jsonpath.h
@@ -29,8 +29,18 @@ typedef struct
#define JSONPATH_LAX (0x80000000)
#define JSONPATH_HDRSZ (offsetof(JsonPath, data))
-#define DatumGetJsonPathP(d) ((JsonPath *) DatumGetPointer(PG_DETOAST_DATUM(d)))
-#define DatumGetJsonPathPCopy(d) ((JsonPath *) DatumGetPointer(PG_DETOAST_DATUM_COPY(d)))
+static inline JsonPath *
+DatumGetJsonPathP(Datum d)
+{
+ return (JsonPath *) PG_DETOAST_DATUM(d);
+}
+
+static inline JsonPath *
+DatumGetJsonPathPCopy(Datum d)
+{
+ return (JsonPath *) PG_DETOAST_DATUM_COPY(d);
+}
+
#define PG_GETARG_JSONPATH_P(x) DatumGetJsonPathP(PG_GETARG_DATUM(x))
#define PG_GETARG_JSONPATH_P_COPY(x) DatumGetJsonPathPCopy(PG_GETARG_DATUM(x))
#define PG_RETURN_JSONPATH_P(p) PG_RETURN_POINTER(p)
diff --git a/src/include/utils/multirangetypes.h b/src/include/utils/multirangetypes.h
index 915330f990..23bce0005f 100644
--- a/src/include/utils/multirangetypes.h
+++ b/src/include/utils/multirangetypes.h
@@ -42,11 +42,26 @@ typedef struct
#define MultirangeIsEmpty(mr) ((mr)->rangeCount == 0)
/*
- * fmgr macros for multirange type objects
+ * fmgr functions for multirange type objects
*/
-#define DatumGetMultirangeTypeP(X) ((MultirangeType *) PG_DETOAST_DATUM(X))
-#define DatumGetMultirangeTypePCopy(X) ((MultirangeType *) PG_DETOAST_DATUM_COPY(X))
-#define MultirangeTypePGetDatum(X) PointerGetDatum(X)
+static inline MultirangeType *
+DatumGetMultirangeTypeP(Datum X)
+{
+ return (MultirangeType *) PG_DETOAST_DATUM(X);
+}
+
+static inline MultirangeType *
+DatumGetMultirangeTypePCopy(Datum X)
+{
+ return (MultirangeType *) PG_DETOAST_DATUM_COPY(X);
+}
+
+static inline Datum
+MultirangeTypePGetDatum(const MultirangeType *X)
+{
+ return PointerGetDatum(X);
+}
+
#define PG_GETARG_MULTIRANGE_P(n) DatumGetMultirangeTypeP(PG_GETARG_DATUM(n))
#define PG_GETARG_MULTIRANGE_P_COPY(n) DatumGetMultirangeTypePCopy(PG_GETARG_DATUM(n))
#define PG_RETURN_MULTIRANGE_P(x) return MultirangeTypePGetDatum(x)
diff --git a/src/include/utils/numeric.h b/src/include/utils/numeric.h
index 3caa74dfe7..88c62c5348 100644
--- a/src/include/utils/numeric.h
+++ b/src/include/utils/numeric.h
@@ -56,9 +56,24 @@ typedef struct NumericData *Numeric;
* fmgr interface macros
*/
-#define DatumGetNumeric(X) ((Numeric) PG_DETOAST_DATUM(X))
-#define DatumGetNumericCopy(X) ((Numeric) PG_DETOAST_DATUM_COPY(X))
-#define NumericGetDatum(X) PointerGetDatum(X)
+static inline Numeric
+DatumGetNumeric(Datum X)
+{
+ return (Numeric) PG_DETOAST_DATUM(X);
+}
+
+static inline Numeric
+DatumGetNumericCopy(Datum X)
+{
+ return (Numeric) PG_DETOAST_DATUM_COPY(X);
+}
+
+static inline Datum
+NumericGetDatum(Numeric X)
+{
+ return PointerGetDatum(X);
+}
+
#define PG_GETARG_NUMERIC(n) DatumGetNumeric(PG_GETARG_DATUM(n))
#define PG_GETARG_NUMERIC_COPY(n) DatumGetNumericCopy(PG_GETARG_DATUM(n))
#define PG_RETURN_NUMERIC(x) return NumericGetDatum(x)
diff --git a/src/include/utils/pg_lsn.h b/src/include/utils/pg_lsn.h
index 7b708f1073..5313afbfe2 100644
--- a/src/include/utils/pg_lsn.h
+++ b/src/include/utils/pg_lsn.h
@@ -18,8 +18,17 @@
#include "access/xlogdefs.h"
#include "fmgr.h"
-#define DatumGetLSN(X) ((XLogRecPtr) DatumGetInt64(X))
-#define LSNGetDatum(X) (Int64GetDatum((int64) (X)))
+static inline XLogRecPtr
+DatumGetLSN(Datum X)
+{
+ return (XLogRecPtr) DatumGetInt64(X);
+}
+
+static inline Datum
+LSNGetDatum(XLogRecPtr X)
+{
+ return Int64GetDatum((int64) X);
+}
#define PG_GETARG_LSN(n) DatumGetLSN(PG_GETARG_DATUM(n))
#define PG_RETURN_LSN(x) return LSNGetDatum(x)
diff --git a/src/include/utils/rangetypes.h b/src/include/utils/rangetypes.h
index 993fad4fc2..661e4892ec 100644
--- a/src/include/utils/rangetypes.h
+++ b/src/include/utils/rangetypes.h
@@ -68,11 +68,26 @@ typedef struct
} RangeBound;
/*
- * fmgr macros for range type objects
+ * fmgr functions for range type objects
*/
-#define DatumGetRangeTypeP(X) ((RangeType *) PG_DETOAST_DATUM(X))
-#define DatumGetRangeTypePCopy(X) ((RangeType *) PG_DETOAST_DATUM_COPY(X))
-#define RangeTypePGetDatum(X) PointerGetDatum(X)
+static inline RangeType *
+DatumGetRangeTypeP(Datum X)
+{
+ return (RangeType *) PG_DETOAST_DATUM(X);
+}
+
+static inline RangeType *
+DatumGetRangeTypePCopy(Datum X)
+{
+ return (RangeType *) PG_DETOAST_DATUM_COPY(X);
+}
+
+static inline Datum
+RangeTypePGetDatum(const RangeType *X)
+{
+ return PointerGetDatum(X);
+}
+
#define PG_GETARG_RANGE_P(n) DatumGetRangeTypeP(PG_GETARG_DATUM(n))
#define PG_GETARG_RANGE_P_COPY(n) DatumGetRangeTypePCopy(PG_GETARG_DATUM(n))
#define PG_RETURN_RANGE_P(x) return RangeTypePGetDatum(x)
diff --git a/src/include/utils/timestamp.h b/src/include/utils/timestamp.h
index edf3a97318..3602638bdc 100644
--- a/src/include/utils/timestamp.h
+++ b/src/include/utils/timestamp.h
@@ -19,18 +19,46 @@
/*
- * Macros for fmgr-callable functions.
+ * Functions for fmgr-callable functions.
*
* For Timestamp, we make use of the same support routines as for int64.
* Therefore Timestamp is pass-by-reference if and only if int64 is!
*/
-#define DatumGetTimestamp(X) ((Timestamp) DatumGetInt64(X))
-#define DatumGetTimestampTz(X) ((TimestampTz) DatumGetInt64(X))
-#define DatumGetIntervalP(X) ((Interval *) DatumGetPointer(X))
-
-#define TimestampGetDatum(X) Int64GetDatum(X)
-#define TimestampTzGetDatum(X) Int64GetDatum(X)
-#define IntervalPGetDatum(X) PointerGetDatum(X)
+static inline Timestamp
+DatumGetTimestamp(Datum X)
+{
+ return (Timestamp) DatumGetInt64(X);
+}
+
+static inline TimestampTz
+DatumGetTimestampTz(Datum X)
+{
+ return (TimestampTz) DatumGetInt64(X);
+}
+
+static inline Interval *
+DatumGetIntervalP(Datum X)
+{
+ return (Interval *) DatumGetPointer(X);
+}
+
+static inline Datum
+TimestampGetDatum(Timestamp X)
+{
+ return Int64GetDatum(X);
+}
+
+static inline Datum
+TimestampTzGetDatum(TimestampTz X)
+{
+ return Int64GetDatum(X);
+}
+
+static inline Datum
+IntervalPGetDatum(const Interval *X)
+{
+ return PointerGetDatum(X);
+}
#define PG_GETARG_TIMESTAMP(n) DatumGetTimestamp(PG_GETARG_DATUM(n))
#define PG_GETARG_TIMESTAMPTZ(n) DatumGetTimestampTz(PG_GETARG_DATUM(n))
diff --git a/src/include/utils/uuid.h b/src/include/utils/uuid.h
index 0029da4e7f..69f4332d0f 100644
--- a/src/include/utils/uuid.h
+++ b/src/include/utils/uuid.h
@@ -23,9 +23,20 @@ typedef struct pg_uuid_t
} pg_uuid_t;
/* fmgr interface macros */
-#define UUIDPGetDatum(X) PointerGetDatum(X)
+static inline Datum
+UUIDPGetDatum(const pg_uuid_t *X)
+{
+ return PointerGetDatum(X);
+}
+
#define PG_RETURN_UUID_P(X) return UUIDPGetDatum(X)
-#define DatumGetUUIDP(X) ((pg_uuid_t *) DatumGetPointer(X))
+
+static inline pg_uuid_t *
+DatumGetUUIDP(Datum X)
+{
+ return (pg_uuid_t *) DatumGetPointer(X);
+}
+
#define PG_GETARG_UUID_P(X) DatumGetUUIDP(PG_GETARG_DATUM(X))
#endif /* UUID_H */
diff --git a/src/include/utils/varbit.h b/src/include/utils/varbit.h
index 039ba860cf..e65e7f65e7 100644
--- a/src/include/utils/varbit.h
+++ b/src/include/utils/varbit.h
@@ -41,9 +41,24 @@ typedef struct
* BIT and BIT VARYING are toastable varlena types. They are the same
* as far as representation goes, so we just have one set of macros.
*/
-#define DatumGetVarBitP(X) ((VarBit *) PG_DETOAST_DATUM(X))
-#define DatumGetVarBitPCopy(X) ((VarBit *) PG_DETOAST_DATUM_COPY(X))
-#define VarBitPGetDatum(X) PointerGetDatum(X)
+static inline VarBit *
+DatumGetVarBitP(Datum X)
+{
+ return (VarBit *) PG_DETOAST_DATUM(X);
+}
+
+static inline VarBit *
+DatumGetVarBitPCopy(Datum X)
+{
+ return (VarBit *) PG_DETOAST_DATUM_COPY(X);
+}
+
+static inline Datum
+VarBitPGetDatum(const VarBit *X)
+{
+ return PointerGetDatum(X);
+}
+
#define PG_GETARG_VARBIT_P(n) DatumGetVarBitP(PG_GETARG_DATUM(n))
#define PG_GETARG_VARBIT_P_COPY(n) DatumGetVarBitPCopy(PG_GETARG_DATUM(n))
#define PG_RETURN_VARBIT_P(x) return VarBitPGetDatum(x)
diff --git a/src/include/utils/xid8.h b/src/include/utils/xid8.h
index b702fc1a91..9c5ce241db 100644
--- a/src/include/utils/xid8.h
+++ b/src/include/utils/xid8.h
@@ -14,8 +14,18 @@
#include "access/transam.h"
-#define DatumGetFullTransactionId(X) (FullTransactionIdFromU64(DatumGetUInt64(X)))
-#define FullTransactionIdGetDatum(X) (UInt64GetDatum(U64FromFullTransactionId(X)))
+static inline FullTransactionId
+DatumGetFullTransactionId(Datum X)
+{
+ return FullTransactionIdFromU64(DatumGetUInt64(X));
+}
+
+static inline Datum
+FullTransactionIdGetDatum(FullTransactionId X)
+{
+ return UInt64GetDatum(U64FromFullTransactionId(X));
+}
+
#define PG_GETARG_FULLTRANSACTIONID(X) DatumGetFullTransactionId(PG_GETARG_DATUM(X))
#define PG_RETURN_FULLTRANSACTIONID(X) return FullTransactionIdGetDatum(X)
diff --git a/src/include/utils/xml.h b/src/include/utils/xml.h
index 6620a62619..c71174d204 100644
--- a/src/include/utils/xml.h
+++ b/src/include/utils/xml.h
@@ -47,8 +47,17 @@ typedef enum
/* struct PgXmlErrorContext is private to xml.c */
typedef struct PgXmlErrorContext PgXmlErrorContext;
-#define DatumGetXmlP(X) ((xmltype *) PG_DETOAST_DATUM(X))
-#define XmlPGetDatum(X) PointerGetDatum(X)
+static inline xmltype *
+DatumGetXmlP(Datum X)
+{
+ return (xmltype *) PG_DETOAST_DATUM(X);
+}
+
+static inline Datum
+XmlPGetDatum(const xmltype *X)
+{
+ return PointerGetDatum(X);
+}
#define PG_GETARG_XML_P(n) DatumGetXmlP(PG_GETARG_DATUM(n))
#define PG_RETURN_XML_P(x) PG_RETURN_POINTER(x)
[text/x-diff] 0002-fix-fast-macros.patch (1.7K, ../../[email protected]/3-0002-fix-fast-macros.patch)
download | inline diff:
diff --git a/src/include/postgres.h b/src/include/postgres.h
index 45c9f6563c..5f6a1e3d5a 100644
--- a/src/include/postgres.h
+++ b/src/include/postgres.h
@@ -884,34 +884,26 @@ extern Datum Float8GetDatum(float8 X);
* Int64GetDatumFast
* Float8GetDatumFast
*
- * These functions are intended to allow writing code that does not depend on
+ * These macros are intended to allow writing code that does not depend on
* whether int64 and float8 are pass-by-reference types, while not
* sacrificing performance when they are. The argument must be a variable
* that will exist and have the same value for as long as the Datum is needed.
* In the pass-by-ref case, the address of the variable is taken to use as
- * the Datum. In the pass-by-val case, these will be the same as the non-Fast
- * functions.
+ * the Datum. In the pass-by-val case, these are the same as the non-Fast
+ * functions, except for asserting that the variable is of the correct type.
*/
-static inline Datum
-Int64GetDatumFast(int64 X)
-{
-#ifdef USE_FLOAT8_BYVAL
- return Int64GetDatum(X);
-#else
- return PointerGetDatum(&X);
-#endif
-}
-
-static inline Datum
-Float8GetDatumFast(float8 X)
-{
#ifdef USE_FLOAT8_BYVAL
- return Float8GetDatum(X);
+#define Int64GetDatumFast(X) \
+ (AssertVariableIsOfTypeMacro(X, int64), Int64GetDatum(X))
+#define Float8GetDatumFast(X) \
+ (AssertVariableIsOfTypeMacro(X, double), Float8GetDatum(X))
#else
- return PointerGetDatum(&X);
+#define Int64GetDatumFast(X) \
+ (AssertVariableIsOfTypeMacro(X, int64), PointerGetDatum(&(X)))
+#define Float8GetDatumFast(X) \
+ (AssertVariableIsOfTypeMacro(X, double), PointerGetDatum(&(X)))
#endif
-}
/* ----------------------------------------------------------------
^ permalink raw reply [nested|flat] 92+ messages in thread
* Re: Convert *GetDatum() and DatumGet*() macros to inline functions
2022-08-28 15:55 Convert *GetDatum() and DatumGet*() macros to inline functions Peter Eisentraut <[email protected]>
2022-08-30 13:36 ` Re: Convert *GetDatum() and DatumGet*() macros to inline functions Aleksander Alekseev <[email protected]>
2022-08-30 13:46 ` Re: Convert *GetDatum() and DatumGet*() macros to inline functions Tom Lane <[email protected]>
2022-08-30 13:59 ` Re: Convert *GetDatum() and DatumGet*() macros to inline functions Aleksander Alekseev <[email protected]>
2022-08-30 14:13 ` Re: Convert *GetDatum() and DatumGet*() macros to inline functions Tom Lane <[email protected]>
2022-08-30 14:16 ` Re: Convert *GetDatum() and DatumGet*() macros to inline functions Robert Haas <[email protected]>
2022-08-30 14:25 ` Re: Convert *GetDatum() and DatumGet*() macros to inline functions Aleksander Alekseev <[email protected]>
2022-08-30 14:28 ` Re: Convert *GetDatum() and DatumGet*() macros to inline functions Tom Lane <[email protected]>
2022-08-30 14:52 ` Re: Convert *GetDatum() and DatumGet*() macros to inline functions Aleksander Alekseev <[email protected]>
2022-08-30 18:15 ` Re: Convert *GetDatum() and DatumGet*() macros to inline functions Aleksander Alekseev <[email protected]>
2022-09-05 12:57 ` Re: Convert *GetDatum() and DatumGet*() macros to inline functions Peter Eisentraut <[email protected]>
2022-09-05 14:20 ` Re: Convert *GetDatum() and DatumGet*() macros to inline functions Aleksander Alekseev <[email protected]>
2022-09-08 09:26 ` Re: Convert *GetDatum() and DatumGet*() macros to inline functions Aleksander Alekseev <[email protected]>
2022-09-12 15:59 ` Re: Convert *GetDatum() and DatumGet*() macros to inline functions Peter Eisentraut <[email protected]>
2022-09-12 17:03 ` Re: Convert *GetDatum() and DatumGet*() macros to inline functions Julien Rouhaud <[email protected]>
2022-09-12 17:59 ` Re: Convert *GetDatum() and DatumGet*() macros to inline functions Peter Eisentraut <[email protected]>
2022-09-26 14:55 ` Re: Convert *GetDatum() and DatumGet*() macros to inline functions Peter Eisentraut <[email protected]>
2022-09-26 17:34 ` Re: Convert *GetDatum() and DatumGet*() macros to inline functions Tom Lane <[email protected]>
@ 2022-09-27 19:26 ` Peter Eisentraut <[email protected]>
2022-09-27 20:13 ` Re: Convert *GetDatum() and DatumGet*() macros to inline functions Tom Lane <[email protected]>
0 siblings, 1 reply; 92+ messages in thread
From: Peter Eisentraut @ 2022-09-27 19:26 UTC (permalink / raw)
To: Tom Lane <[email protected]>; +Cc: Julien Rouhaud <[email protected]>; Aleksander Alekseev <[email protected]>; pgsql-hackers
On 26.09.22 19:34, Tom Lane wrote:
> I think we can do this while still having reasonable type-safety
> by adding AssertVariableIsOfTypeMacro() checks to the macros.
> An advantage of that solution is that we verify that the code
> will be safe for a 32-bit build even in 64-bit builds. (Of
> course, it's just checking the variable's type not its lifespan,
> but this is still a step forward.)
>
> 0001 attached is what you committed, 0002 is a proposed delta
> to fix the Fast macros.
Thanks, I committed it like that.
(I had looked into AssertVariableIsOfTypeMacro() for an earlier variant
of this patch, before I had the idea with the inline functions. It's in
general a bit too strict, such as with short vs int, and signed vs
unsigned, but it should work ok for this limited set of uses.)
^ permalink raw reply [nested|flat] 92+ messages in thread
* Re: Convert *GetDatum() and DatumGet*() macros to inline functions
2022-08-28 15:55 Convert *GetDatum() and DatumGet*() macros to inline functions Peter Eisentraut <[email protected]>
2022-08-30 13:36 ` Re: Convert *GetDatum() and DatumGet*() macros to inline functions Aleksander Alekseev <[email protected]>
2022-08-30 13:46 ` Re: Convert *GetDatum() and DatumGet*() macros to inline functions Tom Lane <[email protected]>
2022-08-30 13:59 ` Re: Convert *GetDatum() and DatumGet*() macros to inline functions Aleksander Alekseev <[email protected]>
2022-08-30 14:13 ` Re: Convert *GetDatum() and DatumGet*() macros to inline functions Tom Lane <[email protected]>
2022-08-30 14:16 ` Re: Convert *GetDatum() and DatumGet*() macros to inline functions Robert Haas <[email protected]>
2022-08-30 14:25 ` Re: Convert *GetDatum() and DatumGet*() macros to inline functions Aleksander Alekseev <[email protected]>
2022-08-30 14:28 ` Re: Convert *GetDatum() and DatumGet*() macros to inline functions Tom Lane <[email protected]>
2022-08-30 14:52 ` Re: Convert *GetDatum() and DatumGet*() macros to inline functions Aleksander Alekseev <[email protected]>
2022-08-30 18:15 ` Re: Convert *GetDatum() and DatumGet*() macros to inline functions Aleksander Alekseev <[email protected]>
2022-09-05 12:57 ` Re: Convert *GetDatum() and DatumGet*() macros to inline functions Peter Eisentraut <[email protected]>
2022-09-05 14:20 ` Re: Convert *GetDatum() and DatumGet*() macros to inline functions Aleksander Alekseev <[email protected]>
2022-09-08 09:26 ` Re: Convert *GetDatum() and DatumGet*() macros to inline functions Aleksander Alekseev <[email protected]>
2022-09-12 15:59 ` Re: Convert *GetDatum() and DatumGet*() macros to inline functions Peter Eisentraut <[email protected]>
2022-09-12 17:03 ` Re: Convert *GetDatum() and DatumGet*() macros to inline functions Julien Rouhaud <[email protected]>
2022-09-12 17:59 ` Re: Convert *GetDatum() and DatumGet*() macros to inline functions Peter Eisentraut <[email protected]>
2022-09-26 14:55 ` Re: Convert *GetDatum() and DatumGet*() macros to inline functions Peter Eisentraut <[email protected]>
2022-09-26 17:34 ` Re: Convert *GetDatum() and DatumGet*() macros to inline functions Tom Lane <[email protected]>
2022-09-27 19:26 ` Re: Convert *GetDatum() and DatumGet*() macros to inline functions Peter Eisentraut <[email protected]>
@ 2022-09-27 20:13 ` Tom Lane <[email protected]>
0 siblings, 0 replies; 92+ messages in thread
From: Tom Lane @ 2022-09-27 20:13 UTC (permalink / raw)
To: Peter Eisentraut <[email protected]>; +Cc: Julien Rouhaud <[email protected]>; Aleksander Alekseev <[email protected]>; pgsql-hackers
Peter Eisentraut <[email protected]> writes:
> On 26.09.22 19:34, Tom Lane wrote:
>> I think we can do this while still having reasonable type-safety
>> by adding AssertVariableIsOfTypeMacro() checks to the macros.
> (I had looked into AssertVariableIsOfTypeMacro() for an earlier variant
> of this patch, before I had the idea with the inline functions. It's in
> general a bit too strict, such as with short vs int, and signed vs
> unsigned, but it should work ok for this limited set of uses.)
Yeah. I had sort of expected to need a UInt64GetDatumFast variant
that would accept uint64, but there doesn't appear to be anyplace
that wants that today. We should be willing to add it if anyone
complains, though.
regards, tom lane
^ permalink raw reply [nested|flat] 92+ messages in thread
end of thread, other threads:[~2022-09-27 20:13 UTC | newest]
Thread overview: 92+ messages (download: mbox mbox.gz follow: Atom feed)
-- links below jump to the message on this page --
2020-01-10 06:08 [PATCH v1 3/3] CatCache expiration feature. Kyotaro Horiguchi <[email protected]>
2020-01-10 06:08 [PATCH v2 3/3] CatCache expiration feature. Kyotaro Horiguchi <[email protected]>
2020-01-10 06:08 [PATCH 4/4] CatCache expiration feature. Kyotaro Horiguchi <[email protected]>
2020-01-10 06:08 [PATCH v3 3/3] CatCache expiration feature. Kyotaro Horiguchi <[email protected]>
2020-11-06 08:27 [PATCH v4] CatCache expiration feature Kyotaro Horiguchi <[email protected]>
2020-11-06 08:27 [PATCH v5] CatCache expiration feature Kyotaro Horiguchi <[email protected]>
2020-11-18 07:54 [PATCH v7 1/3] CatCache expiration feature Kyotaro Horiguchi <[email protected]>
2020-11-18 07:54 [PATCH v6 1/3] CatCache expiration feature Kyotaro Horiguchi <[email protected]>
2020-11-18 07:54 [PATCH v7 1/3] CatCache expiration feature Kyotaro Horiguchi <[email protected]>
2020-11-18 07:54 [PATCH v6 1/3] CatCache expiration feature Kyotaro Horiguchi <[email protected]>
2020-11-18 07:54 [PATCH v7 1/3] CatCache expiration feature Kyotaro Horiguchi <[email protected]>
2020-11-18 07:54 [PATCH v7 1/3] CatCache expiration feature Kyotaro Horiguchi <[email protected]>
2020-11-18 07:54 [PATCH v6 1/3] CatCache expiration feature Kyotaro Horiguchi <[email protected]>
2020-11-18 07:54 [PATCH v6 1/3] CatCache expiration feature Kyotaro Horiguchi <[email protected]>
2020-11-18 07:54 [PATCH v7 1/3] CatCache expiration feature Kyotaro Horiguchi <[email protected]>
2020-11-18 07:54 [PATCH v6 1/3] CatCache expiration feature Kyotaro Horiguchi <[email protected]>
2020-11-18 07:54 [PATCH v7 1/3] CatCache expiration feature Kyotaro Horiguchi <[email protected]>
2020-11-18 07:54 [PATCH v7 1/3] CatCache expiration feature Kyotaro Horiguchi <[email protected]>
2020-11-18 07:54 [PATCH v6 1/3] CatCache expiration feature Kyotaro Horiguchi <[email protected]>
2020-11-18 07:54 [PATCH v6 1/3] CatCache expiration feature Kyotaro Horiguchi <[email protected]>
2020-11-18 07:54 [PATCH v7 1/3] CatCache expiration feature Kyotaro Horiguchi <[email protected]>
2020-11-18 07:54 [PATCH v7 1/3] CatCache expiration feature Kyotaro Horiguchi <[email protected]>
2020-11-18 07:54 [PATCH v6 1/3] CatCache expiration feature Kyotaro Horiguchi <[email protected]>
2020-11-18 07:54 [PATCH v7 1/3] CatCache expiration feature Kyotaro Horiguchi <[email protected]>
2020-11-18 07:54 [PATCH v6 1/3] CatCache expiration feature Kyotaro Horiguchi <[email protected]>
2020-11-18 07:54 [PATCH v7 1/3] CatCache expiration feature Kyotaro Horiguchi <[email protected]>
2020-11-18 07:54 [PATCH v5 1/3] CatCache expiration feature Kyotaro Horiguchi <[email protected]>
2020-11-18 07:54 [PATCH v7 1/3] CatCache expiration feature Kyotaro Horiguchi <[email protected]>
2020-11-18 07:54 [PATCH v6 1/3] CatCache expiration feature Kyotaro Horiguchi <[email protected]>
2020-11-18 07:54 [PATCH v6 1/3] CatCache expiration feature Kyotaro Horiguchi <[email protected]>
2020-11-18 07:54 [PATCH v6 1/3] CatCache expiration feature Kyotaro Horiguchi <[email protected]>
2020-11-18 07:54 [PATCH v7 1/3] CatCache expiration feature Kyotaro Horiguchi <[email protected]>
2020-11-18 07:54 [PATCH v6 1/3] CatCache expiration feature Kyotaro Horiguchi <[email protected]>
2020-11-18 07:54 [PATCH v7 1/3] CatCache expiration feature Kyotaro Horiguchi <[email protected]>
2020-11-18 07:54 [PATCH v6 1/3] CatCache expiration feature Kyotaro Horiguchi <[email protected]>
2020-11-18 07:54 [PATCH v7 1/3] CatCache expiration feature Kyotaro Horiguchi <[email protected]>
2020-11-18 07:54 [PATCH v7 1/3] CatCache expiration feature Kyotaro Horiguchi <[email protected]>
2020-11-18 07:54 [PATCH v6 1/3] CatCache expiration feature Kyotaro Horiguchi <[email protected]>
2020-11-18 07:54 [PATCH v6 1/3] CatCache expiration feature Kyotaro Horiguchi <[email protected]>
2020-11-18 07:54 [PATCH v7 1/3] CatCache expiration feature Kyotaro Horiguchi <[email protected]>
2020-11-18 07:54 [PATCH v6 1/3] CatCache expiration feature Kyotaro Horiguchi <[email protected]>
2020-11-18 07:54 [PATCH v6 1/3] CatCache expiration feature Kyotaro Horiguchi <[email protected]>
2020-11-18 07:54 [PATCH v7 1/3] CatCache expiration feature Kyotaro Horiguchi <[email protected]>
2020-11-18 07:54 [PATCH v6 1/3] CatCache expiration feature Kyotaro Horiguchi <[email protected]>
2020-11-18 07:54 [PATCH v7 1/3] CatCache expiration feature Kyotaro Horiguchi <[email protected]>
2020-11-18 07:54 [PATCH v7 1/3] CatCache expiration feature Kyotaro Horiguchi <[email protected]>
2020-11-18 07:54 [PATCH v6 1/3] CatCache expiration feature Kyotaro Horiguchi <[email protected]>
2020-11-18 07:54 [PATCH v7 1/3] CatCache expiration feature Kyotaro Horiguchi <[email protected]>
2020-11-18 07:54 [PATCH v6 1/3] CatCache expiration feature Kyotaro Horiguchi <[email protected]>
2021-01-27 11:08 [PATCH v8 1/1] CatCache expiration feature Heikki Linnakangas <[email protected]>
2021-01-27 11:08 [PATCH v8 1/1] CatCache expiration feature Heikki Linnakangas <[email protected]>
2021-01-27 11:08 [PATCH v8 1/1] CatCache expiration feature Heikki Linnakangas <[email protected]>
2021-01-27 11:08 [PATCH v8 1/1] CatCache expiration feature Heikki Linnakangas <[email protected]>
2021-01-27 11:08 [PATCH v8 1/1] CatCache expiration feature Heikki Linnakangas <[email protected]>
2021-01-27 11:08 [PATCH v8 1/1] CatCache expiration feature Heikki Linnakangas <[email protected]>
2021-01-27 11:08 [PATCH v8 1/1] CatCache expiration feature Heikki Linnakangas <[email protected]>
2021-01-27 11:08 [PATCH v8 1/1] CatCache expiration feature Heikki Linnakangas <[email protected]>
2021-01-27 11:08 [PATCH v8 1/1] CatCache expiration feature Heikki Linnakangas <[email protected]>
2021-01-27 11:08 [PATCH v8 1/1] CatCache expiration feature Heikki Linnakangas <[email protected]>
2021-01-27 11:08 [PATCH v8 1/1] CatCache expiration feature Heikki Linnakangas <[email protected]>
2021-01-27 11:08 [PATCH v8 1/1] CatCache expiration feature Heikki Linnakangas <[email protected]>
2021-01-27 11:08 [PATCH v8 1/1] CatCache expiration feature Heikki Linnakangas <[email protected]>
2021-01-27 11:08 [PATCH v8 1/1] CatCache expiration feature Heikki Linnakangas <[email protected]>
2021-01-27 11:08 [PATCH v8 1/1] CatCache expiration feature Heikki Linnakangas <[email protected]>
2021-01-27 11:08 [PATCH v8 1/1] CatCache expiration feature Heikki Linnakangas <[email protected]>
2021-01-27 11:08 [PATCH v8 1/1] CatCache expiration feature Heikki Linnakangas <[email protected]>
2021-01-27 11:08 [PATCH v8 1/1] CatCache expiration feature Heikki Linnakangas <[email protected]>
2021-01-27 11:08 [PATCH v8 1/1] CatCache expiration feature Heikki Linnakangas <[email protected]>
2021-01-27 11:08 [PATCH v8 1/1] CatCache expiration feature Heikki Linnakangas <[email protected]>
2021-01-27 11:08 [PATCH v9] CatCache expiration feature Heikki Linnakangas <[email protected]>
2021-01-27 11:08 [PATCH v8 1/1] CatCache expiration feature Heikki Linnakangas <[email protected]>
2022-08-28 15:55 Convert *GetDatum() and DatumGet*() macros to inline functions Peter Eisentraut <[email protected]>
2022-08-30 13:36 ` Re: Convert *GetDatum() and DatumGet*() macros to inline functions Aleksander Alekseev <[email protected]>
2022-08-30 13:46 ` Re: Convert *GetDatum() and DatumGet*() macros to inline functions Tom Lane <[email protected]>
2022-08-30 13:59 ` Re: Convert *GetDatum() and DatumGet*() macros to inline functions Aleksander Alekseev <[email protected]>
2022-08-30 14:13 ` Re: Convert *GetDatum() and DatumGet*() macros to inline functions Tom Lane <[email protected]>
2022-08-30 14:16 ` Re: Convert *GetDatum() and DatumGet*() macros to inline functions Robert Haas <[email protected]>
2022-08-30 14:25 ` Re: Convert *GetDatum() and DatumGet*() macros to inline functions Aleksander Alekseev <[email protected]>
2022-08-30 14:27 ` Re: Convert *GetDatum() and DatumGet*() macros to inline functions Robert Haas <[email protected]>
2022-08-30 14:28 ` Re: Convert *GetDatum() and DatumGet*() macros to inline functions Tom Lane <[email protected]>
2022-08-30 14:52 ` Re: Convert *GetDatum() and DatumGet*() macros to inline functions Aleksander Alekseev <[email protected]>
2022-08-30 18:15 ` Re: Convert *GetDatum() and DatumGet*() macros to inline functions Aleksander Alekseev <[email protected]>
2022-09-05 12:57 ` Re: Convert *GetDatum() and DatumGet*() macros to inline functions Peter Eisentraut <[email protected]>
2022-09-05 14:20 ` Re: Convert *GetDatum() and DatumGet*() macros to inline functions Aleksander Alekseev <[email protected]>
2022-09-08 09:26 ` Re: Convert *GetDatum() and DatumGet*() macros to inline functions Aleksander Alekseev <[email protected]>
2022-09-12 15:59 ` Re: Convert *GetDatum() and DatumGet*() macros to inline functions Peter Eisentraut <[email protected]>
2022-09-12 17:03 ` Re: Convert *GetDatum() and DatumGet*() macros to inline functions Julien Rouhaud <[email protected]>
2022-09-12 17:59 ` Re: Convert *GetDatum() and DatumGet*() macros to inline functions Peter Eisentraut <[email protected]>
2022-09-26 14:55 ` Re: Convert *GetDatum() and DatumGet*() macros to inline functions Peter Eisentraut <[email protected]>
2022-09-26 17:34 ` Re: Convert *GetDatum() and DatumGet*() macros to inline functions Tom Lane <[email protected]>
2022-09-27 19:26 ` Re: Convert *GetDatum() and DatumGet*() macros to inline functions Peter Eisentraut <[email protected]>
2022-09-27 20:13 ` Re: Convert *GetDatum() and DatumGet*() macros to inline functions Tom Lane <[email protected]>
This inbox is served by agora; see mirroring instructions
for how to clone and mirror all data and code used for this inbox