mirror of https://github.com/postgres/postgres
Remove the 64K limit on the lengths of keys and values within an hstore. (This changes the on-disk format, but the old format can still be read.) Add support for btree/hash opclasses for hstore --- this is not so much for actual indexing purposes as to allow use of GROUP BY, DISTINCT, etc. Add various other new functions and operators. Andrew GierthREL8_5_ALPHA2_BRANCH
parent
1d43e5314e
commit
172eacba43
@ -1,59 +1,197 @@ |
||||
/*
|
||||
* $PostgreSQL: pgsql/contrib/hstore/hstore.h,v 1.8 2009/06/11 14:48:51 momjian Exp $ |
||||
* $PostgreSQL: pgsql/contrib/hstore/hstore.h,v 1.9 2009/09/30 19:50:22 tgl Exp $ |
||||
*/ |
||||
#ifndef __HSTORE_H__ |
||||
#define __HSTORE_H__ |
||||
|
||||
#include "fmgr.h" |
||||
#include "utils/array.h" |
||||
|
||||
|
||||
/*
|
||||
* HEntry: there is one of these for each key _and_ value in an hstore |
||||
* |
||||
* the position offset points to the _end_ so that we can get the length |
||||
* by subtraction from the previous entry. the ISFIRST flag lets us tell |
||||
* whether there is a previous entry. |
||||
*/ |
||||
typedef struct |
||||
{ |
||||
uint16 keylen; |
||||
uint16 vallen; |
||||
uint32 |
||||
valisnull:1, |
||||
pos:31; |
||||
uint32 entry; |
||||
} HEntry; |
||||
|
||||
/* these are determined by the sizes of the keylen and vallen fields */ |
||||
/* in struct HEntry and struct Pairs */ |
||||
#define HSTORE_MAX_KEY_LEN 65535 |
||||
#define HSTORE_MAX_VALUE_LEN 65535 |
||||
#define HENTRY_ISFIRST 0x80000000 |
||||
#define HENTRY_ISNULL 0x40000000 |
||||
#define HENTRY_POSMASK 0x3FFFFFFF |
||||
|
||||
/* note possible multiple evaluations, also access to prior array element */ |
||||
#define HSE_ISFIRST(he_) (((he_).entry & HENTRY_ISFIRST) != 0) |
||||
#define HSE_ISNULL(he_) (((he_).entry & HENTRY_ISNULL) != 0) |
||||
#define HSE_ENDPOS(he_) ((he_).entry & HENTRY_POSMASK) |
||||
#define HSE_OFF(he_) (HSE_ISFIRST(he_) ? 0 : HSE_ENDPOS((&(he_))[-1])) |
||||
#define HSE_LEN(he_) (HSE_ISFIRST(he_) \ |
||||
? HSE_ENDPOS(he_) \
|
||||
: HSE_ENDPOS(he_) - HSE_ENDPOS((&(he_))[-1])) |
||||
|
||||
/*
|
||||
* determined by the size of "endpos" (ie HENTRY_POSMASK), though this is a |
||||
* bit academic since currently varlenas (and hence both the input and the |
||||
* whole hstore) have the same limit |
||||
*/ |
||||
#define HSTORE_MAX_KEY_LEN 0x3FFFFFFF |
||||
#define HSTORE_MAX_VALUE_LEN 0x3FFFFFFF |
||||
|
||||
typedef struct |
||||
{ |
||||
int32 vl_len_; /* varlena header (do not touch directly!) */ |
||||
int4 size; |
||||
char data[1]; |
||||
uint32 size_; /* flags and number of items in hstore */ |
||||
/* array of HEntry follows */ |
||||
} HStore; |
||||
|
||||
#define HSHRDSIZE (VARHDRSZ + sizeof(int4)) |
||||
#define CALCDATASIZE(x, lenstr) ( (x) * sizeof(HEntry) + HSHRDSIZE + (lenstr) ) |
||||
#define ARRPTR(x) ( (HEntry*) ( (char*)(x) + HSHRDSIZE ) ) |
||||
#define STRPTR(x) ( (char*)(x) + HSHRDSIZE + ( sizeof(HEntry) * ((HStore*)x)->size ) ) |
||||
/*
|
||||
* it's not possible to get more than 2^28 items into an hstore, |
||||
* so we reserve the top few bits of the size field. See hstore_compat.c |
||||
* for one reason why. Some bits are left for future use here. |
||||
*/ |
||||
#define HS_FLAG_NEWVERSION 0x80000000 |
||||
|
||||
#define HS_COUNT(hsp_) ((hsp_)->size_ & 0x0FFFFFFF) |
||||
#define HS_SETCOUNT(hsp_,c_) ((hsp_)->size_ = (c_) | HS_FLAG_NEWVERSION) |
||||
|
||||
|
||||
#define HSHRDSIZE (sizeof(HStore)) |
||||
#define CALCDATASIZE(x, lenstr) ( (x) * 2 * sizeof(HEntry) + HSHRDSIZE + (lenstr) ) |
||||
|
||||
/* note multiple evaluations of x */ |
||||
#define ARRPTR(x) ( (HEntry*) ( (HStore*)(x) + 1 ) ) |
||||
#define STRPTR(x) ( (char*)(ARRPTR(x) + HS_COUNT((HStore*)(x)) * 2) ) |
||||
|
||||
/* note multiple/non evaluations */ |
||||
#define HS_KEY(arr_,str_,i_) ((str_) + HSE_OFF((arr_)[2*(i_)])) |
||||
#define HS_VAL(arr_,str_,i_) ((str_) + HSE_OFF((arr_)[2*(i_)+1])) |
||||
#define HS_KEYLEN(arr_,i_) (HSE_LEN((arr_)[2*(i_)])) |
||||
#define HS_VALLEN(arr_,i_) (HSE_LEN((arr_)[2*(i_)+1])) |
||||
#define HS_VALISNULL(arr_,i_) (HSE_ISNULL((arr_)[2*(i_)+1])) |
||||
|
||||
/*
|
||||
* currently, these following macros are the _only_ places that rely |
||||
* on internal knowledge of HEntry. Everything else should be using |
||||
* the above macros. Exception: the in-place upgrade in hstore_compat.c |
||||
* messes with entries directly. |
||||
*/ |
||||
|
||||
/*
|
||||
* copy one key/value pair (which must be contiguous starting at |
||||
* sptr_) into an under-construction hstore; dent_ is an HEntry*, |
||||
* dbuf_ is the destination's string buffer, dptr_ is the current |
||||
* position in the destination. lots of modification and multiple |
||||
* evaluation here. |
||||
*/ |
||||
#define HS_COPYITEM(dent_,dbuf_,dptr_,sptr_,klen_,vlen_,vnull_) \ |
||||
do { \
|
||||
memcpy((dptr_), (sptr_), (klen_)+(vlen_)); \
|
||||
(dptr_) += (klen_)+(vlen_); \
|
||||
(dent_)++->entry = ((dptr_) - (dbuf_) - (vlen_)) & HENTRY_POSMASK; \
|
||||
(dent_)++->entry = ((((dptr_) - (dbuf_)) & HENTRY_POSMASK) \
|
||||
| ((vnull_) ? HENTRY_ISNULL : 0)); \
|
||||
} while(0) |
||||
|
||||
/*
|
||||
* add one key/item pair, from a Pairs structure, into an |
||||
* under-construction hstore |
||||
*/ |
||||
#define HS_ADDITEM(dent_,dbuf_,dptr_,pair_) \ |
||||
do { \
|
||||
memcpy((dptr_), (pair_).key, (pair_).keylen); \
|
||||
(dptr_) += (pair_).keylen; \
|
||||
(dent_)++->entry = ((dptr_) - (dbuf_)) & HENTRY_POSMASK; \
|
||||
if ((pair_).isnull) \
|
||||
(dent_)++->entry = ((((dptr_) - (dbuf_)) & HENTRY_POSMASK) \
|
||||
| HENTRY_ISNULL); \
|
||||
else \
|
||||
{ \
|
||||
memcpy((dptr_), (pair_).val, (pair_).vallen); \
|
||||
(dptr_) += (pair_).vallen; \
|
||||
(dent_)++->entry = ((dptr_) - (dbuf_)) & HENTRY_POSMASK; \
|
||||
} \
|
||||
} while (0) |
||||
|
||||
/* finalize a newly-constructed hstore */ |
||||
#define HS_FINALIZE(hsp_,count_,buf_,ptr_) \ |
||||
do { \
|
||||
int buflen = (ptr_) - (buf_); \
|
||||
if ((count_)) \
|
||||
ARRPTR(hsp_)[0].entry |= HENTRY_ISFIRST; \
|
||||
if ((count_) != HS_COUNT((hsp_))) \
|
||||
{ \
|
||||
HS_SETCOUNT((hsp_),(count_)); \
|
||||
memmove(STRPTR(hsp_), (buf_), buflen); \
|
||||
} \
|
||||
SET_VARSIZE((hsp_), CALCDATASIZE((count_), buflen)); \
|
||||
} while (0) |
||||
|
||||
/* ensure the varlena size of an existing hstore is correct */ |
||||
#define HS_FIXSIZE(hsp_,count_) \ |
||||
do { \
|
||||
int bl = (count_) ? HSE_ENDPOS(ARRPTR(hsp_)[2*(count_)-1]) : 0; \
|
||||
SET_VARSIZE((hsp_), CALCDATASIZE((count_),bl)); \
|
||||
} while (0) |
||||
|
||||
/* DatumGetHStoreP includes support for reading old-format hstore values */ |
||||
extern HStore *hstoreUpgrade(Datum orig); |
||||
|
||||
#define DatumGetHStoreP(d) hstoreUpgrade(d) |
||||
|
||||
#define PG_GETARG_HS(x) ((HStore*)PG_DETOAST_DATUM(PG_GETARG_DATUM(x))) |
||||
#define PG_GETARG_HS(x) DatumGetHStoreP(PG_GETARG_DATUM(x)) |
||||
|
||||
|
||||
/*
|
||||
* Pairs is a "decompressed" representation of one key/value pair. |
||||
* The two strings are not necessarily null-terminated. |
||||
*/ |
||||
typedef struct |
||||
{ |
||||
char *key; |
||||
char *val; |
||||
uint16 keylen; |
||||
uint16 vallen; |
||||
bool isnull; |
||||
bool needfree; |
||||
size_t keylen; |
||||
size_t vallen; |
||||
bool isnull; /* value is null? */ |
||||
bool needfree; /* need to pfree the value? */ |
||||
} Pairs; |
||||
|
||||
int comparePairs(const void *a, const void *b); |
||||
int uniquePairs(Pairs *a, int4 l, int4 *buflen); |
||||
extern int hstoreUniquePairs(Pairs *a, int4 l, int4 *buflen); |
||||
extern HStore *hstorePairs(Pairs *pairs, int4 pcount, int4 buflen); |
||||
|
||||
extern size_t hstoreCheckKeyLen(size_t len); |
||||
extern size_t hstoreCheckValLen(size_t len); |
||||
|
||||
size_t hstoreCheckKeyLen(size_t len); |
||||
size_t hstoreCheckValLen(size_t len); |
||||
extern int hstoreFindKey(HStore *hs, int *lowbound, char *key, int keylen); |
||||
extern Pairs *hstoreArrayToPairs(ArrayType *a, int *npairs); |
||||
|
||||
#define HStoreContainsStrategyNumber 7 |
||||
#define HStoreExistsStrategyNumber 9 |
||||
#define HStoreExistsAnyStrategyNumber 10 |
||||
#define HStoreExistsAllStrategyNumber 11 |
||||
#define HStoreOldContainsStrategyNumber 13 /* backwards compatibility */ |
||||
|
||||
/*
|
||||
* defining HSTORE_POLLUTE_NAMESPACE=0 will prevent use of old function names; |
||||
* for now, we default to on for the benefit of people restoring old dumps |
||||
*/ |
||||
#ifndef HSTORE_POLLUTE_NAMESPACE |
||||
#define HSTORE_POLLUTE_NAMESPACE 1 |
||||
#endif |
||||
|
||||
#if HSTORE_POLLUTE_NAMESPACE |
||||
#define HSTORE_POLLUTE(newname_,oldname_) \ |
||||
PG_FUNCTION_INFO_V1(oldname_); \
|
||||
Datum oldname_(PG_FUNCTION_ARGS); \
|
||||
Datum newname_(PG_FUNCTION_ARGS); \
|
||||
Datum oldname_(PG_FUNCTION_ARGS) { return newname_(fcinfo); } \
|
||||
extern int no_such_variable |
||||
#else |
||||
#define HSTORE_POLLUTE(newname_,oldname_) \ |
||||
extern int no_such_variable |
||||
#endif |
||||
|
||||
#endif /* __HSTORE_H__ */ |
||||
|
||||
@ -0,0 +1,376 @@ |
||||
/*
|
||||
* $PostgreSQL: pgsql/contrib/hstore/hstore_compat.c,v 1.1 2009/09/30 19:50:22 tgl Exp $ |
||||
* |
||||
* Notes on old/new hstore format disambiguation. |
||||
* |
||||
* There are three formats to consider: |
||||
* 1) old contrib/hstore (referred to as hstore-old) |
||||
* 2) prerelease pgfoundry hstore |
||||
* 3) new contrib/hstore |
||||
* |
||||
* (2) and (3) are identical except for the HS_FLAG_NEWVERSION |
||||
* bit, which is set in (3) but not (2). |
||||
* |
||||
* Values that are already in format (3), or which are |
||||
* unambiguously in format (2), are handled by the first |
||||
* "return immediately" test in hstoreUpgrade(). |
||||
* |
||||
* To stress a point: we ONLY get here with possibly-ambiguous |
||||
* values if we're doing some sort of in-place migration from an |
||||
* old prerelease pgfoundry hstore-new; and we explicitly don't |
||||
* support that without fixing up any potentially padded values |
||||
* first. Most of the code here is serious overkill, but the |
||||
* performance penalty isn't serious (especially compared to the |
||||
* palloc() that we have to do anyway) and the belt-and-braces |
||||
* validity checks provide some reassurance. (If for some reason |
||||
* we get a value that would have worked on the old code, but |
||||
* which would be botched by the conversion code, the validity |
||||
* checks will fail it first so we get an error rather than bad |
||||
* data.) |
||||
* |
||||
* Note also that empty hstores are the same in (2) and (3), so |
||||
* there are some special-case paths for them. |
||||
* |
||||
* We tell the difference between formats (2) and (3) as follows (but |
||||
* note that there are some edge cases where we can't tell; see |
||||
* comments in hstoreUpgrade): |
||||
* |
||||
* First, since there must be at least one entry, we look at |
||||
* how the bits line up. The new format looks like: |
||||
* |
||||
* 10kkkkkkkkkkkkkkkkkkkkkkkkkkkkkk (k..k = keylen) |
||||
* 0nvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv (v..v = keylen+vallen) |
||||
* |
||||
* The old format looks like one of these, depending on endianness |
||||
* and bitfield layout: (k..k = keylen, v..v = vallen, p..p = pos, |
||||
* n = isnull) |
||||
* |
||||
* kkkkkkkkkkkkkkkkvvvvvvvvvvvvvvvv |
||||
* nppppppppppppppppppppppppppppppp |
||||
* |
||||
* kkkkkkkkkkkkkkkkvvvvvvvvvvvvvvvv |
||||
* pppppppppppppppppppppppppppppppn |
||||
* |
||||
* vvvvvvvvvvvvvvvvkkkkkkkkkkkkkkkk |
||||
* nppppppppppppppppppppppppppppppp |
||||
* |
||||
* vvvvvvvvvvvvvvvvkkkkkkkkkkkkkkkk |
||||
* pppppppppppppppppppppppppppppppn (usual i386 format) |
||||
* |
||||
* If the entry is in old format, for the first entry "pos" must be 0. |
||||
* We can obviously see that either keylen or vallen must be >32768 |
||||
* for there to be any ambiguity (which is why lengths less than that |
||||
* are fasttracked in hstore.h) Since "pos"==0, the "v" field in the |
||||
* new-format interpretation can only be 0 or 1, which constrains all |
||||
* but three bits of the old-format's k and v fields. But in addition |
||||
* to all of this, the data length implied by the keylen and vallen |
||||
* must fit in the varlena size. So the only ambiguous edge case for |
||||
* hstores with only one entry occurs between a new-format entry with |
||||
* an excess (~32k) of padding, and an old-format entry. But we know |
||||
* which format to use in that case based on how we were compiled, so |
||||
* no actual data corruption can occur. |
||||
* |
||||
* If there is more than one entry, the requirement that keys do not |
||||
* decrease in length, and that positions increase contiguously, and |
||||
* that the end of the data not be beyond the end of the varlena |
||||
* itself, disambiguates in almost all other cases. There is a small |
||||
* set of ambiguous cases which could occur if the old-format value |
||||
* has a large excess of padding and just the right pattern of key |
||||
* sizes, but these are also handled based on how we were compiled. |
||||
* |
||||
* The otherwise undocumented function hstore_version_diag is provided |
||||
* for testing purposes. |
||||
*/ |
||||
#include "postgres.h" |
||||
|
||||
#include "funcapi.h" |
||||
|
||||
#include "hstore.h" |
||||
|
||||
/*
|
||||
* This is the structure used for entries in the old contrib/hstore |
||||
* implementation. Notice that this is the same size as the new entry |
||||
* (two 32-bit words per key/value pair) and that the header is the |
||||
* same, so the old and new versions of ARRPTR, STRPTR, CALCDATASIZE |
||||
* etc. are compatible. |
||||
* |
||||
* If the above statement isn't true on some bizarre platform, we're |
||||
* a bit hosed (see Assert in hstoreValidOldFormat). |
||||
*/ |
||||
typedef struct |
||||
{ |
||||
uint16 keylen; |
||||
uint16 vallen; |
||||
uint32 |
||||
valisnull:1, |
||||
pos:31; |
||||
} HOldEntry; |
||||
|
||||
static int hstoreValidNewFormat(HStore *hs); |
||||
static int hstoreValidOldFormat(HStore *hs); |
||||
|
||||
|
||||
/*
|
||||
* Validity test for a new-format hstore. |
||||
* 0 = not valid |
||||
* 1 = valid but with "slop" in the length |
||||
* 2 = exactly valid |
||||
*/ |
||||
static int |
||||
hstoreValidNewFormat(HStore *hs) |
||||
{ |
||||
int count = HS_COUNT(hs); |
||||
HEntry *entries = ARRPTR(hs); |
||||
int buflen = (count) ? HSE_ENDPOS(entries[2*(count)-1]) : 0; |
||||
int vsize = CALCDATASIZE(count,buflen); |
||||
int i; |
||||
|
||||
if (hs->size_ & HS_FLAG_NEWVERSION) |
||||
return 2; |
||||
|
||||
if (count == 0) |
||||
return 2; |
||||
|
||||
if (!HSE_ISFIRST(entries[0])) |
||||
return 0; |
||||
|
||||
if (vsize > VARSIZE(hs)) |
||||
return 0; |
||||
|
||||
/* entry position must be nondecreasing */ |
||||
|
||||
for (i = 1; i < 2*count; ++i) |
||||
{ |
||||
if (HSE_ISFIRST(entries[i]) |
||||
|| (HSE_ENDPOS(entries[i]) < HSE_ENDPOS(entries[i-1]))) |
||||
return 0; |
||||
} |
||||
|
||||
/* key length must be nondecreasing and keys must not be null */ |
||||
|
||||
for (i = 1; i < count; ++i) |
||||
{ |
||||
if (HS_KEYLEN(entries,i) < HS_KEYLEN(entries,i-1)) |
||||
return 0; |
||||
if (HSE_ISNULL(entries[2*i])) |
||||
return 0; |
||||
} |
||||
|
||||
if (vsize != VARSIZE(hs)) |
||||
return 1; |
||||
|
||||
return 2; |
||||
} |
||||
|
||||
/*
|
||||
* Validity test for an old-format hstore. |
||||
* 0 = not valid |
||||
* 1 = valid but with "slop" in the length |
||||
* 2 = exactly valid |
||||
*/ |
||||
static int |
||||
hstoreValidOldFormat(HStore *hs) |
||||
{ |
||||
int count = hs->size_; |
||||
HOldEntry *entries = (HOldEntry *) ARRPTR(hs); |
||||
int vsize; |
||||
int lastpos = 0; |
||||
int i; |
||||
|
||||
if (hs->size_ & HS_FLAG_NEWVERSION) |
||||
return 0; |
||||
|
||||
Assert(sizeof(HOldEntry) == sizeof(HEntry)); |
||||
|
||||
if (count == 0) |
||||
return 2; |
||||
|
||||
if (count > 0xFFFFFFF) |
||||
return 0; |
||||
|
||||
if (CALCDATASIZE(count,0) > VARSIZE(hs)) |
||||
return 0; |
||||
|
||||
if (entries[0].pos != 0) |
||||
return 0; |
||||
|
||||
/* key length must be nondecreasing */ |
||||
|
||||
for (i = 1; i < count; ++i) |
||||
{ |
||||
if (entries[i].keylen < entries[i-1].keylen) |
||||
return 0; |
||||
} |
||||
|
||||
/*
|
||||
* entry position must be strictly increasing, except for the |
||||
* first entry (which can be ""=>"" and thus zero-length); and |
||||
* all entries must be properly contiguous |
||||
*/ |
||||
|
||||
for (i = 0; i < count; ++i) |
||||
{ |
||||
if (entries[i].pos != lastpos) |
||||
return 0; |
||||
lastpos += (entries[i].keylen |
||||
+ ((entries[i].valisnull) ? 0 : entries[i].vallen)); |
||||
} |
||||
|
||||
vsize = CALCDATASIZE(count,lastpos); |
||||
|
||||
if (vsize > VARSIZE(hs)) |
||||
return 0; |
||||
|
||||
if (vsize != VARSIZE(hs)) |
||||
return 1; |
||||
|
||||
return 2; |
||||
} |
||||
|
||||
|
||||
/*
|
||||
* hstoreUpgrade: PG_DETOAST_DATUM plus support for conversion of old hstores |
||||
*/ |
||||
HStore * |
||||
hstoreUpgrade(Datum orig) |
||||
{ |
||||
HStore *hs = (HStore *) PG_DETOAST_DATUM(orig); |
||||
int valid_new; |
||||
int valid_old; |
||||
bool writable; |
||||
|
||||
/* Return immediately if no conversion needed */ |
||||
if ((hs->size_ & HS_FLAG_NEWVERSION) || |
||||
hs->size_ == 0 || |
||||
(VARSIZE(hs) < 32768 && HSE_ISFIRST((ARRPTR(hs)[0])))) |
||||
return hs; |
||||
|
||||
valid_new = hstoreValidNewFormat(hs); |
||||
valid_old = hstoreValidOldFormat(hs); |
||||
/* Do we have a writable copy? */ |
||||
writable = ((void *) hs != (void *) DatumGetPointer(orig)); |
||||
|
||||
if (!valid_old || hs->size_ == 0) |
||||
{ |
||||
if (valid_new) |
||||
{ |
||||
/*
|
||||
* force the "new version" flag and the correct varlena |
||||
* length, but only if we have a writable copy already |
||||
* (which we almost always will, since short new-format |
||||
* values won't come through here) |
||||
*/ |
||||
if (writable) |
||||
{ |
||||
HS_SETCOUNT(hs,HS_COUNT(hs)); |
||||
HS_FIXSIZE(hs,HS_COUNT(hs)); |
||||
} |
||||
return hs; |
||||
} |
||||
else |
||||
{ |
||||
elog(ERROR,"invalid hstore value found"); |
||||
} |
||||
} |
||||
|
||||
/*
|
||||
* this is the tricky edge case. It is only possible in some |
||||
* quite extreme cases (the hstore must have had a lot |
||||
* of wasted padding space at the end). |
||||
* But the only way a "new" hstore value could get here is if |
||||
* we're upgrading in place from a pre-release version of |
||||
* hstore-new (NOT contrib/hstore), so we work off the following |
||||
* assumptions: |
||||
* 1. If you're moving from old contrib/hstore to hstore-new, |
||||
* you're required to fix up any potential conflicts first, |
||||
* e.g. by running ALTER TABLE ... USING col::text::hstore; |
||||
* on all hstore columns before upgrading. |
||||
* 2. If you're moving from old contrib/hstore to new |
||||
* contrib/hstore, then "new" values are impossible here |
||||
* 3. If you're moving from pre-release hstore-new to hstore-new, |
||||
* then "old" values are impossible here |
||||
* 4. If you're moving from pre-release hstore-new to new |
||||
* contrib/hstore, you're not doing so as an in-place upgrade, |
||||
* so there is no issue |
||||
* So the upshot of all this is that we can treat all the edge |
||||
* cases as "new" if we're being built as hstore-new, and "old" |
||||
* if we're being built as contrib/hstore. |
||||
* |
||||
* XXX the WARNING can probably be downgraded to DEBUG1 once this |
||||
* has been beta-tested. But for now, it would be very useful to |
||||
* know if anyone can actually reach this case in a non-contrived |
||||
* setting. |
||||
*/ |
||||
|
||||
if (valid_new) |
||||
{ |
||||
#if HSTORE_IS_HSTORE_NEW |
||||
elog(WARNING,"ambiguous hstore value resolved as hstore-new"); |
||||
|
||||
/*
|
||||
* force the "new version" flag and the correct varlena |
||||
* length, but only if we have a writable copy already |
||||
* (which we almost always will, since short new-format |
||||
* values won't come through here) |
||||
*/ |
||||
if (writable) |
||||
{ |
||||
HS_SETCOUNT(hs,HS_COUNT(hs)); |
||||
HS_FIXSIZE(hs,HS_COUNT(hs)); |
||||
} |
||||
return hs; |
||||
#else |
||||
elog(WARNING,"ambiguous hstore value resolved as hstore-old"); |
||||
#endif |
||||
} |
||||
|
||||
/*
|
||||
* must have an old-style value. Overwrite it in place as a new-style |
||||
* one, making sure we have a writable copy first. |
||||
*/ |
||||
|
||||
if (!writable) |
||||
hs = (HStore *) PG_DETOAST_DATUM_COPY(orig); |
||||
|
||||
{ |
||||
int count = hs->size_; |
||||
HEntry *new_entries = ARRPTR(hs); |
||||
HOldEntry *old_entries = (HOldEntry *) ARRPTR(hs); |
||||
int i; |
||||
|
||||
for (i = 0; i < count; ++i) |
||||
{ |
||||
uint32 pos = old_entries[i].pos; |
||||
uint32 keylen = old_entries[i].keylen; |
||||
uint32 vallen = old_entries[i].vallen; |
||||
bool isnull = old_entries[i].valisnull; |
||||
|
||||
if (isnull) |
||||
vallen = 0; |
||||
|
||||
new_entries[2*i].entry = (pos + keylen) & HENTRY_POSMASK; |
||||
new_entries[2*i+1].entry = (((pos + keylen + vallen) & HENTRY_POSMASK) |
||||
| ((isnull) ? HENTRY_ISNULL : 0));
|
||||
} |
||||
|
||||
if (count) |
||||
new_entries[0].entry |= HENTRY_ISFIRST; |
||||
HS_SETCOUNT(hs,count); |
||||
HS_FIXSIZE(hs,count); |
||||
} |
||||
|
||||
return hs; |
||||
} |
||||
|
||||
|
||||
PG_FUNCTION_INFO_V1(hstore_version_diag); |
||||
Datum hstore_version_diag(PG_FUNCTION_ARGS); |
||||
Datum |
||||
hstore_version_diag(PG_FUNCTION_ARGS) |
||||
{ |
||||
HStore *hs = (HStore *) PG_DETOAST_DATUM(PG_GETARG_DATUM(0)); |
||||
int valid_new = hstoreValidNewFormat(hs); |
||||
int valid_old = hstoreValidOldFormat(hs); |
||||
|
||||
PG_RETURN_INT32(valid_old*10 + valid_new); |
||||
} |
||||
File diff suppressed because it is too large
Load Diff
Loading…
Reference in new issue