fix-missing-value-corruption.patch
text/x-patch
Filename: fix-missing-value-corruption.patch
Type: text/x-patch
Part: 0
Patch
Format: unified
| File | + | − |
|---|---|---|
| src/backend/access/common/heaptuple.c | 103 | 2 |
| src/backend/access/transam/xact.c | 2 | 0 |
| src/include/access/htup_details.h | 1 | 0 |
| src/tools/pgindent/typedefs.list | 1 | 0 |
diff --git a/src/backend/access/common/heaptuple.c b/src/backend/access/common/heaptuple.c
index cb2291ee36..acd801a07c 100644
--- a/src/backend/access/common/heaptuple.c
+++ b/src/backend/access/common/heaptuple.c
@@ -60,9 +60,12 @@
#include "access/heaptoast.h"
#include "access/sysattr.h"
#include "access/tupdesc_details.h"
+#include "common/hashfn.h"
#include "executor/tuptable.h"
+#include "utils/datum.h"
#include "utils/expandeddatum.h"
-
+#include "utils/hsearch.h"
+#include "utils/memutils.h"
/* Does att's datatype allow packing into the 1-byte-header varlena format? */
#define ATT_IS_PACKABLE(att) \
@@ -71,6 +74,68 @@
#define VARLENA_ATT_IS_PACKABLE(att) \
((att)->attstorage != TYPSTORAGE_PLAIN)
+/*
+ * Setup for cacheing pass-by-ref missing attributes in a way that survives
+ * tupleDesc destruction.
+ */
+
+typedef struct
+{
+ int len;
+ Datum value;
+} missing_cache_key;
+
+static HTAB *missing_cache = NULL;
+
+static uint32
+missing_hash(const void *key, Size keysize)
+{
+ const missing_cache_key *entry = (missing_cache_key *) key;
+
+ return hash_bytes((const unsigned char *) entry->value, entry->len);
+}
+
+static int
+missing_match(const void *key1, const void *key2, Size keysize)
+{
+ const missing_cache_key *entry1 = (missing_cache_key *) key1;
+ const missing_cache_key *entry2 = (missing_cache_key *) key2;
+
+ if (entry1->len != entry2->len)
+ return entry1->len > entry2->len ? 1 : -1;
+
+ return memcmp(DatumGetPointer(entry1->value),
+ DatumGetPointer(entry2->value),
+ entry1->len);
+}
+
+static void
+init_missing_cache()
+{
+ HASHCTL hash_ctl;
+
+ hash_ctl.keysize = sizeof(missing_cache_key);
+ hash_ctl.entrysize = sizeof(missing_cache_key);
+ hash_ctl.hcxt = TopTransactionContext;
+ hash_ctl.hash = missing_hash;
+ hash_ctl.match = missing_match;
+ missing_cache =
+ hash_create("Missing Values Cache",
+ 32,
+ &hash_ctl,
+ HASH_ELEM | HASH_CONTEXT | HASH_FUNCTION | HASH_COMPARE);
+}
+
+/* end of transaction routine to clear the missing values cache */
+void
+AtEOXact_MissCache(void)
+{
+ /*
+ * just set the cache pointer to NULL, the context cleanup will do the
+ * rest
+ */
+ missing_cache = NULL;
+}
/* ----------------------------------------------------------------
* misc support routines
@@ -102,8 +167,44 @@ getmissingattr(TupleDesc tupleDesc,
if (attrmiss->am_present)
{
+ missing_cache_key key;
+ missing_cache_key *entry;
+ bool found;
+ MemoryContext oldctx;
+ Datum copy;
+
*isnull = false;
- return attrmiss->am_value;
+
+ /* no need to cache by-value attributes */
+ if (att->attbyval)
+ return attrmiss->am_value;
+
+ /* set up cache if required */
+ if (missing_cache == NULL)
+ init_missing_cache();
+
+ /* check if there's a cache entry */
+ Assert(att->attlen > 0 || att->attlen == -1);
+ if (att->attlen > 0)
+ key.len = att->attlen;
+ else
+ key.len = VARSIZE_ANY(attrmiss->am_value);
+ key.value = attrmiss->am_value;
+
+ entry = hash_search(missing_cache, &key, HASH_ENTER, &found);
+
+ /* cache hit, so return it */
+ if (found)
+ return entry->value;
+
+ /* cache miss, so we need a transaction-stable copy of the datum */
+ oldctx = MemoryContextSwitchTo(TopTransactionContext);
+ copy = datumCopy(attrmiss->am_value, false, att->attlen);
+ MemoryContextSwitchTo(oldctx);
+
+ entry->value = copy;
+
+ return copy;
}
}
diff --git a/src/backend/access/transam/xact.c b/src/backend/access/transam/xact.c
index 8daaa535ed..b2946b955e 100644
--- a/src/backend/access/transam/xact.c
+++ b/src/backend/access/transam/xact.c
@@ -21,6 +21,7 @@
#include <unistd.h>
#include "access/commit_ts.h"
+#include "access/htup_details.h"
#include "access/multixact.h"
#include "access/parallel.h"
#include "access/subtrans.h"
@@ -2362,6 +2363,7 @@ CommitTransaction(void)
AtEOXact_GUC(true, 1);
AtEOXact_SPI(true);
AtEOXact_Enum();
+ AtEOXact_MissCache();
AtEOXact_on_commit_actions(true);
AtEOXact_Namespace(true, is_parallel_worker);
AtEOXact_SMgr();
diff --git a/src/include/access/htup_details.h b/src/include/access/htup_details.h
index e01f4f35c8..027c4f5436 100644
--- a/src/include/access/htup_details.h
+++ b/src/include/access/htup_details.h
@@ -736,6 +736,7 @@ extern MinimalTuple minimal_tuple_from_heap_tuple(HeapTuple htup);
extern size_t varsize_any(void *p);
extern HeapTuple heap_expand_tuple(HeapTuple sourceTuple, TupleDesc tupleDesc);
extern MinimalTuple minimal_expand_tuple(HeapTuple sourceTuple, TupleDesc tupleDesc);
+extern void AtEOXact_MissCache(void);
#ifndef FRONTEND
/*
diff --git a/src/tools/pgindent/typedefs.list b/src/tools/pgindent/typedefs.list
index 260854747b..91d57159db 100644
--- a/src/tools/pgindent/typedefs.list
+++ b/src/tools/pgindent/typedefs.list
@@ -3471,6 +3471,7 @@ mbstr_verifier
memoize_hash
memoize_iterator
metastring
+missing_cache_key
mix_data_t
mixedStruct
mode_t