Skip to content

Commit a684581

Browse files
committed
Cache by-reference missing values in a long lived context
Attribute missing values might be needed past the lifetime of the tuple descriptors from which they are extracted. To avoid possibly using pointers for by-reference values which might thus be left dangling, we cache a datumCopy'd version of the datum in the TopMemoryContext. Since we first search for the value this only needs to be done once per session for any such value. Original complaint from Tom Lane, idea for mitigation by Andrew Dunstan, tweaked by Tom Lane. Backpatch to version 11 where missing values were introduced. Discussion: https://postgr.es/m/[email protected]
1 parent 757fa45 commit a684581

File tree

2 files changed

+90
-1
lines changed

2 files changed

+90
-1
lines changed

src/backend/access/common/heaptuple.c

Lines changed: 89 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -60,8 +60,12 @@
6060
#include "access/heaptoast.h"
6161
#include "access/sysattr.h"
6262
#include "access/tupdesc_details.h"
63+
#include "common/hashfn.h"
6364
#include "executor/tuptable.h"
65+
#include "utils/datum.h"
6466
#include "utils/expandeddatum.h"
67+
#include "utils/hsearch.h"
68+
#include "utils/memutils.h"
6569

6670

6771
/* Does att's datatype allow packing into the 1-byte-header varlena format? */
@@ -71,6 +75,57 @@
7175
#define VARLENA_ATT_IS_PACKABLE(att) \
7276
((att)->attstorage != TYPSTORAGE_PLAIN)
7377

78+
/*
79+
* Setup for cacheing pass-by-ref missing attributes in a way that survives
80+
* tupleDesc destruction.
81+
*/
82+
83+
typedef struct
84+
{
85+
int len;
86+
Datum value;
87+
} missing_cache_key;
88+
89+
static HTAB *missing_cache = NULL;
90+
91+
static uint32
92+
missing_hash(const void *key, Size keysize)
93+
{
94+
const missing_cache_key *entry = (missing_cache_key *) key;
95+
96+
return hash_bytes((const unsigned char *) entry->value, entry->len);
97+
}
98+
99+
static int
100+
missing_match(const void *key1, const void *key2, Size keysize)
101+
{
102+
const missing_cache_key *entry1 = (missing_cache_key *) key1;
103+
const missing_cache_key *entry2 = (missing_cache_key *) key2;
104+
105+
if (entry1->len != entry2->len)
106+
return entry1->len > entry2->len ? 1 : -1;
107+
108+
return memcmp(DatumGetPointer(entry1->value),
109+
DatumGetPointer(entry2->value),
110+
entry1->len);
111+
}
112+
113+
static void
114+
init_missing_cache()
115+
{
116+
HASHCTL hash_ctl;
117+
118+
hash_ctl.keysize = sizeof(missing_cache_key);
119+
hash_ctl.entrysize = sizeof(missing_cache_key);
120+
hash_ctl.hcxt = TopMemoryContext;
121+
hash_ctl.hash = missing_hash;
122+
hash_ctl.match = missing_match;
123+
missing_cache =
124+
hash_create("Missing Values Cache",
125+
32,
126+
&hash_ctl,
127+
HASH_ELEM | HASH_CONTEXT | HASH_FUNCTION | HASH_COMPARE);
128+
}
74129

75130
/* ----------------------------------------------------------------
76131
* misc support routines
@@ -102,8 +157,41 @@ getmissingattr(TupleDesc tupleDesc,
102157

103158
if (attrmiss->am_present)
104159
{
160+
missing_cache_key key;
161+
missing_cache_key *entry;
162+
bool found;
163+
MemoryContext oldctx;
164+
105165
*isnull = false;
106-
return attrmiss->am_value;
166+
167+
/* no need to cache by-value attributes */
168+
if (att->attbyval)
169+
return attrmiss->am_value;
170+
171+
/* set up cache if required */
172+
if (missing_cache == NULL)
173+
init_missing_cache();
174+
175+
/* check if there's a cache entry */
176+
Assert(att->attlen > 0 || att->attlen == -1);
177+
if (att->attlen > 0)
178+
key.len = att->attlen;
179+
else
180+
key.len = VARSIZE_ANY(attrmiss->am_value);
181+
key.value = attrmiss->am_value;
182+
183+
entry = hash_search(missing_cache, &key, HASH_ENTER, &found);
184+
185+
if (!found)
186+
{
187+
/* cache miss, so we need a non-transient copy of the datum */
188+
oldctx = MemoryContextSwitchTo(TopMemoryContext);
189+
entry->value =
190+
datumCopy(attrmiss->am_value, false, att->attlen);
191+
MemoryContextSwitchTo(oldctx);
192+
}
193+
194+
return entry->value;
107195
}
108196
}
109197

src/tools/pgindent/typedefs.list

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3479,6 +3479,7 @@ mbstr_verifier
34793479
memoize_hash
34803480
memoize_iterator
34813481
metastring
3482+
missing_cache_key
34823483
mix_data_t
34833484
mixedStruct
34843485
mode_t

0 commit comments

Comments
 (0)