Location via proxy:   [ UP ]  
[Report a bug]   [Manage cookies]                
Skip to content

Commit d5890f4

Browse files
committed
Fix hstore hash function for empty hstores upgraded from 8.4.
Hstore data generated on pg 8.4 and pg_upgraded to current versions remains in its original on-disk format unless modified. The same goes for values generated by the addon hstore-new module on pre-9.0 versions. (The hstoreUpgrade function converts old values on the fly when read in, but the on-disk value is not modified by this.) Since old-format empty hstores (and hstore-new hstores) have representations compatible with the new format, hstoreUpgrade thought it could get away without modifying such values; but this breaks hstore_hash (and the new hstore_hash_extended) which assumes bit-perfect matching between semantically identical hstore values. Only one bit actually differs (the "new version" flag in the count field) but that of course is enough to break the hash. Fix by making hstoreUpgrade unconditionally convert all old values to new format. Backpatch all the way, even though this changes a hash value in some cases, because in those cases the hash value is already failing - for example, a hash join between old- and new-format empty hstores will be failing to match, or a hash index on an hstore column containing an old-format empty value will be failing to find the value since it will be searching for a hash derived from a new-format datum. (There are no known field reports of this happening, probably because hashing of hstores has only been useful in limited circumstances and there probably isn't much upgraded data being used this way.) Per concerns arising from discussion of commit eb6f291. Original bug is my fault. Discussion: https://postgr.es/m/60b1fd3b-7332-40f0-7e7f-f2f04f777747%402ndquadrant.com
1 parent 757c518 commit d5890f4

File tree

1 file changed

+19
-28
lines changed

1 file changed

+19
-28
lines changed

contrib/hstore/hstore_compat.c

Lines changed: 19 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -238,34 +238,35 @@ hstoreUpgrade(Datum orig)
238238
HStore *hs = (HStore *) PG_DETOAST_DATUM(orig);
239239
int valid_new;
240240
int valid_old;
241-
bool writable;
242241

243242
/* Return immediately if no conversion needed */
244-
if ((hs->size_ & HS_FLAG_NEWVERSION) ||
245-
hs->size_ == 0 ||
243+
if (hs->size_ & HS_FLAG_NEWVERSION)
244+
return hs;
245+
246+
/* Do we have a writable copy? If not, make one. */
247+
if ((void *) hs == (void *) DatumGetPointer(orig))
248+
hs = (HStore *) PG_DETOAST_DATUM_COPY(orig);
249+
250+
if (hs->size_ == 0 ||
246251
(VARSIZE(hs) < 32768 && HSE_ISFIRST((ARRPTR(hs)[0]))))
252+
{
253+
HS_SETCOUNT(hs, HS_COUNT(hs));
254+
HS_FIXSIZE(hs, HS_COUNT(hs));
247255
return hs;
256+
}
248257

249258
valid_new = hstoreValidNewFormat(hs);
250259
valid_old = hstoreValidOldFormat(hs);
251-
/* Do we have a writable copy? */
252-
writable = ((void *) hs != (void *) DatumGetPointer(orig));
253260

254261
if (!valid_old || hs->size_ == 0)
255262
{
256263
if (valid_new)
257264
{
258265
/*
259-
* force the "new version" flag and the correct varlena length,
260-
* but only if we have a writable copy already (which we almost
261-
* always will, since short new-format values won't come through
262-
* here)
266+
* force the "new version" flag and the correct varlena length.
263267
*/
264-
if (writable)
265-
{
266-
HS_SETCOUNT(hs, HS_COUNT(hs));
267-
HS_FIXSIZE(hs, HS_COUNT(hs));
268-
}
268+
HS_SETCOUNT(hs, HS_COUNT(hs));
269+
HS_FIXSIZE(hs, HS_COUNT(hs));
269270
return hs;
270271
}
271272
else
@@ -302,29 +303,19 @@ hstoreUpgrade(Datum orig)
302303
elog(WARNING, "ambiguous hstore value resolved as hstore-new");
303304

304305
/*
305-
* force the "new version" flag and the correct varlena length, but
306-
* only if we have a writable copy already (which we almost always
307-
* will, since short new-format values won't come through here)
306+
* force the "new version" flag and the correct varlena length.
308307
*/
309-
if (writable)
310-
{
311-
HS_SETCOUNT(hs, HS_COUNT(hs));
312-
HS_FIXSIZE(hs, HS_COUNT(hs));
313-
}
308+
HS_SETCOUNT(hs, HS_COUNT(hs));
309+
HS_FIXSIZE(hs, HS_COUNT(hs));
314310
return hs;
315311
#else
316312
elog(WARNING, "ambiguous hstore value resolved as hstore-old");
317313
#endif
318314
}
319315

320316
/*
321-
* must have an old-style value. Overwrite it in place as a new-style one,
322-
* making sure we have a writable copy first.
317+
* must have an old-style value. Overwrite it in place as a new-style one.
323318
*/
324-
325-
if (!writable)
326-
hs = (HStore *) PG_DETOAST_DATUM_COPY(orig);
327-
328319
{
329320
int count = hs->size_;
330321
HEntry *new_entries = ARRPTR(hs);

0 commit comments

Comments
 (0)