PostgreSQL Source Code  git master
 All Data Structures Namespaces Files Functions Variables Typedefs Enumerations Enumerator Macros
hstore_compat.c File Reference
#include "postgres.h"
#include "hstore.h"
Include dependency graph for hstore_compat.c:

Go to the source code of this file.

Data Structures

struct  HOldEntry
 

Functions

static int hstoreValidNewFormat (HStore *hs)
 
static int hstoreValidOldFormat (HStore *hs)
 
HStorehstoreUpgrade (Datum orig)
 
 PG_FUNCTION_INFO_V1 (hstore_version_diag)
 
Datum hstore_version_diag (PG_FUNCTION_ARGS)
 

Function Documentation

Datum hstore_version_diag ( PG_FUNCTION_ARGS  )

Definition at line 361 of file hstore_compat.c.

References hstoreValidNewFormat(), hstoreValidOldFormat(), PG_DETOAST_DATUM, PG_GETARG_DATUM, and PG_RETURN_INT32.

362 {
364  int valid_new = hstoreValidNewFormat(hs);
365  int valid_old = hstoreValidOldFormat(hs);
366 
367  PG_RETURN_INT32(valid_old * 10 + valid_new);
368 }
Definition: hstore.h:44
#define PG_GETARG_DATUM(n)
Definition: fmgr.h:224
#define PG_RETURN_INT32(x)
Definition: fmgr.h:298
static int hstoreValidNewFormat(HStore *hs)
static int hstoreValidOldFormat(HStore *hs)
#define PG_DETOAST_DATUM(datum)
Definition: fmgr.h:196
HStore* hstoreUpgrade ( Datum  orig)

Definition at line 236 of file hstore_compat.c.

References ARRPTR, DatumGetPointer, elog, HEntry::entry, ERROR, HENTRY_ISFIRST, HENTRY_ISNULL, HENTRY_POSMASK, HS_COUNT, HS_FIXSIZE, HS_FLAG_NEWVERSION, HS_SETCOUNT, HSE_ISFIRST, hstoreValidNewFormat(), hstoreValidOldFormat(), i, HOldEntry::keylen, PG_DETOAST_DATUM, PG_DETOAST_DATUM_COPY, HOldEntry::pos, HStore::size_, HOldEntry::valisnull, HOldEntry::vallen, VARSIZE, and WARNING.

237 {
238  HStore *hs = (HStore *) PG_DETOAST_DATUM(orig);
239  int valid_new;
240  int valid_old;
241  bool writable;
242 
243  /* Return immediately if no conversion needed */
244  if ((hs->size_ & HS_FLAG_NEWVERSION) ||
245  hs->size_ == 0 ||
246  (VARSIZE(hs) < 32768 && HSE_ISFIRST((ARRPTR(hs)[0]))))
247  return hs;
248 
249  valid_new = hstoreValidNewFormat(hs);
250  valid_old = hstoreValidOldFormat(hs);
251  /* Do we have a writable copy? */
252  writable = ((void *) hs != (void *) DatumGetPointer(orig));
253 
254  if (!valid_old || hs->size_ == 0)
255  {
256  if (valid_new)
257  {
258  /*
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)
263  */
264  if (writable)
265  {
266  HS_SETCOUNT(hs, HS_COUNT(hs));
267  HS_FIXSIZE(hs, HS_COUNT(hs));
268  }
269  return hs;
270  }
271  else
272  {
273  elog(ERROR, "invalid hstore value found");
274  }
275  }
276 
277  /*
278  * this is the tricky edge case. It is only possible in some quite extreme
279  * cases (the hstore must have had a lot of wasted padding space at the
280  * end). But the only way a "new" hstore value could get here is if we're
281  * upgrading in place from a pre-release version of hstore-new (NOT
282  * contrib/hstore), so we work off the following assumptions: 1. If you're
283  * moving from old contrib/hstore to hstore-new, you're required to fix up
284  * any potential conflicts first, e.g. by running ALTER TABLE ... USING
285  * col::text::hstore; on all hstore columns before upgrading. 2. If you're
286  * moving from old contrib/hstore to new contrib/hstore, then "new" values
287  * are impossible here 3. If you're moving from pre-release hstore-new to
288  * hstore-new, then "old" values are impossible here 4. If you're moving
289  * from pre-release hstore-new to new contrib/hstore, you're not doing so
290  * as an in-place upgrade, so there is no issue So the upshot of all this
291  * is that we can treat all the edge cases as "new" if we're being built
292  * as hstore-new, and "old" if we're being built as contrib/hstore.
293  *
294  * XXX the WARNING can probably be downgraded to DEBUG1 once this has been
295  * beta-tested. But for now, it would be very useful to know if anyone can
296  * actually reach this case in a non-contrived setting.
297  */
298 
299  if (valid_new)
300  {
301 #if HSTORE_IS_HSTORE_NEW
302  elog(WARNING, "ambiguous hstore value resolved as hstore-new");
303 
304  /*
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)
308  */
309  if (writable)
310  {
311  HS_SETCOUNT(hs, HS_COUNT(hs));
312  HS_FIXSIZE(hs, HS_COUNT(hs));
313  }
314  return hs;
315 #else
316  elog(WARNING, "ambiguous hstore value resolved as hstore-old");
317 #endif
318  }
319 
320  /*
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.
323  */
324 
325  if (!writable)
326  hs = (HStore *) PG_DETOAST_DATUM_COPY(orig);
327 
328  {
329  int count = hs->size_;
330  HEntry *new_entries = ARRPTR(hs);
331  HOldEntry *old_entries = (HOldEntry *) ARRPTR(hs);
332  int i;
333 
334  for (i = 0; i < count; ++i)
335  {
336  uint32 pos = old_entries[i].pos;
337  uint32 keylen = old_entries[i].keylen;
338  uint32 vallen = old_entries[i].vallen;
339  bool isnull = old_entries[i].valisnull;
340 
341  if (isnull)
342  vallen = 0;
343 
344  new_entries[2 * i].entry = (pos + keylen) & HENTRY_POSMASK;
345  new_entries[2 * i + 1].entry = (((pos + keylen + vallen) & HENTRY_POSMASK)
346  | ((isnull) ? HENTRY_ISNULL : 0));
347  }
348 
349  if (count)
350  new_entries[0].entry |= HENTRY_ISFIRST;
351  HS_SETCOUNT(hs, count);
352  HS_FIXSIZE(hs, count);
353  }
354 
355  return hs;
356 }
Definition: hstore.h:44
#define HENTRY_POSMASK
Definition: hstore.h:25
#define PG_DETOAST_DATUM_COPY(datum)
Definition: fmgr.h:198
#define VARSIZE(PTR)
Definition: postgres.h:306
#define HSE_ISFIRST(he_)
Definition: hstore.h:28
#define HENTRY_ISNULL
Definition: hstore.h:24
static int hstoreValidNewFormat(HStore *hs)
uint16 keylen
#define ERROR
Definition: elog.h:43
#define HS_FLAG_NEWVERSION
Definition: hstore.h:59
#define HS_COUNT(hsp_)
Definition: hstore.h:61
uint16 vallen
uint32 entry
Definition: hstore.h:20
unsigned int uint32
Definition: c.h:265
#define WARNING
Definition: elog.h:40
static int hstoreValidOldFormat(HStore *hs)
#define HENTRY_ISFIRST
Definition: hstore.h:23
uint32 valisnull
uint32 pos
Definition: hstore.h:18
#define DatumGetPointer(X)
Definition: postgres.h:557
uint32 size_
Definition: hstore.h:47
int i
#define PG_DETOAST_DATUM(datum)
Definition: fmgr.h:196
#define ARRPTR(x)
Definition: cube.c:26
#define elog
Definition: elog.h:219
#define HS_SETCOUNT(hsp_, c_)
Definition: hstore.h:62
#define HS_FIXSIZE(hsp_, count_)
Definition: hstore.h:143
static int hstoreValidNewFormat ( HStore hs)
static

Definition at line 119 of file hstore_compat.c.

References ARRPTR, CALCDATASIZE, HS_COUNT, HS_FLAG_NEWVERSION, HSE_ENDPOS, HSE_ISFIRST, HSE_ISNULL, HSTORE_KEYLEN, i, HStore::size_, and VARSIZE.

Referenced by hstore_version_diag(), and hstoreUpgrade().

120 {
121  int count = HS_COUNT(hs);
122  HEntry *entries = ARRPTR(hs);
123  int buflen = (count) ? HSE_ENDPOS(entries[2 * (count) - 1]) : 0;
124  int vsize = CALCDATASIZE(count, buflen);
125  int i;
126 
127  if (hs->size_ & HS_FLAG_NEWVERSION)
128  return 2;
129 
130  if (count == 0)
131  return 2;
132 
133  if (!HSE_ISFIRST(entries[0]))
134  return 0;
135 
136  if (vsize > VARSIZE(hs))
137  return 0;
138 
139  /* entry position must be nondecreasing */
140 
141  for (i = 1; i < 2 * count; ++i)
142  {
143  if (HSE_ISFIRST(entries[i])
144  || (HSE_ENDPOS(entries[i]) < HSE_ENDPOS(entries[i - 1])))
145  return 0;
146  }
147 
148  /* key length must be nondecreasing and keys must not be null */
149 
150  for (i = 1; i < count; ++i)
151  {
152  if (HSTORE_KEYLEN(entries, i) < HSTORE_KEYLEN(entries, i - 1))
153  return 0;
154  if (HSE_ISNULL(entries[2 * i]))
155  return 0;
156  }
157 
158  if (vsize != VARSIZE(hs))
159  return 1;
160 
161  return 2;
162 }
#define HSE_ENDPOS(he_)
Definition: hstore.h:30
#define VARSIZE(PTR)
Definition: postgres.h:306
#define HSTORE_KEYLEN(arr_, i_)
Definition: hstore.h:81
#define HSE_ISFIRST(he_)
Definition: hstore.h:28
#define HSE_ISNULL(he_)
Definition: hstore.h:29
#define HS_FLAG_NEWVERSION
Definition: hstore.h:59
#define HS_COUNT(hsp_)
Definition: hstore.h:61
#define CALCDATASIZE(x, lenstr)
Definition: hstore.h:72
Definition: hstore.h:18
uint32 size_
Definition: hstore.h:47
int i
#define ARRPTR(x)
Definition: cube.c:26
static int hstoreValidOldFormat ( HStore hs)
static

Definition at line 171 of file hstore_compat.c.

References ARRPTR, CALCDATASIZE, HS_FLAG_NEWVERSION, i, HOldEntry::keylen, HStore::size_, StaticAssertStmt, HOldEntry::valisnull, and VARSIZE.

Referenced by hstore_version_diag(), and hstoreUpgrade().

172 {
173  int count = hs->size_;
174  HOldEntry *entries = (HOldEntry *) ARRPTR(hs);
175  int vsize;
176  int lastpos = 0;
177  int i;
178 
179  if (hs->size_ & HS_FLAG_NEWVERSION)
180  return 0;
181 
182  /* New format uses an HEntry for key and another for value */
183  StaticAssertStmt(sizeof(HOldEntry) == 2 * sizeof(HEntry),
184  "old hstore format is not upward-compatible");
185 
186  if (count == 0)
187  return 2;
188 
189  if (count > 0xFFFFFFF)
190  return 0;
191 
192  if (CALCDATASIZE(count, 0) > VARSIZE(hs))
193  return 0;
194 
195  if (entries[0].pos != 0)
196  return 0;
197 
198  /* key length must be nondecreasing */
199 
200  for (i = 1; i < count; ++i)
201  {
202  if (entries[i].keylen < entries[i - 1].keylen)
203  return 0;
204  }
205 
206  /*
207  * entry position must be strictly increasing, except for the first entry
208  * (which can be ""=>"" and thus zero-length); and all entries must be
209  * properly contiguous
210  */
211 
212  for (i = 0; i < count; ++i)
213  {
214  if (entries[i].pos != lastpos)
215  return 0;
216  lastpos += (entries[i].keylen
217  + ((entries[i].valisnull) ? 0 : entries[i].vallen));
218  }
219 
220  vsize = CALCDATASIZE(count, lastpos);
221 
222  if (vsize > VARSIZE(hs))
223  return 0;
224 
225  if (vsize != VARSIZE(hs))
226  return 1;
227 
228  return 2;
229 }
#define VARSIZE(PTR)
Definition: postgres.h:306
#define StaticAssertStmt(condition, errmessage)
Definition: c.h:753
uint16 keylen
#define HS_FLAG_NEWVERSION
Definition: hstore.h:59
#define CALCDATASIZE(x, lenstr)
Definition: hstore.h:72
uint32 valisnull
Definition: hstore.h:18
uint32 size_
Definition: hstore.h:47
int i
#define ARRPTR(x)
Definition: cube.c:26
PG_FUNCTION_INFO_V1 ( hstore_version_diag  )