Age Owner Branch data TLA Line data Source code
1 : : /*
2 : : * contrib/hstore/hstore_io.c
3 : : */
4 : : #include "postgres.h"
5 : :
6 : : #include <ctype.h>
7 : :
8 : : #include "access/htup_details.h"
9 : : #include "catalog/pg_type.h"
10 : : #include "common/jsonapi.h"
11 : : #include "funcapi.h"
12 : : #include "hstore.h"
13 : : #include "lib/stringinfo.h"
14 : : #include "libpq/pqformat.h"
15 : : #include "nodes/miscnodes.h"
16 : : #include "parser/scansup.h"
17 : : #include "utils/builtins.h"
18 : : #include "utils/json.h"
19 : : #include "utils/jsonb.h"
20 : : #include "utils/lsyscache.h"
21 : : #include "utils/memutils.h"
22 : : #include "utils/typcache.h"
23 : :
519 tgl@sss.pgh.pa.us 24 :CBC 18 : PG_MODULE_MAGIC_EXT(
25 : : .name = "hstore",
26 : : .version = PG_VERSION
27 : : );
28 : :
29 : : /* old names for C functions */
6026 bruce@momjian.us 30 :UBC 0 : HSTORE_POLLUTE(hstore_from_text, tconvert);
31 : :
32 : :
33 : : typedef struct
34 : : {
35 : : char *begin;
36 : : char *ptr;
37 : : char *cur;
38 : : char *word;
39 : : int wordlen;
40 : : Node *escontext;
41 : :
42 : : Pairs *pairs;
43 : : int pcur;
44 : : int plen;
45 : : } HSParser;
46 : :
47 : : static bool hstoreCheckKeyLength(size_t len, HSParser *state);
48 : : static bool hstoreCheckValLength(size_t len, HSParser *state);
49 : :
50 : :
51 : : #define RESIZEPRSBUF \
52 : : do { \
53 : : if ( state->cur - state->word + 1 >= state->wordlen ) \
54 : : { \
55 : : int32 clen = state->cur - state->word; \
56 : : state->wordlen *= 2; \
57 : : state->word = (char*)repalloc( (void*)state->word, state->wordlen ); \
58 : : state->cur = state->word + clen; \
59 : : } \
60 : : } while (0)
61 : :
62 : : #define PRSSYNTAXERROR return prssyntaxerror(state)
63 : :
64 : : static bool
1339 tgl@sss.pgh.pa.us 65 :CBC 4 : prssyntaxerror(HSParser *state)
66 : : {
67 [ + + ]: 4 : errsave(state->escontext,
68 : : (errcode(ERRCODE_SYNTAX_ERROR),
69 : : errmsg("syntax error in hstore, near \"%.*s\" at position %d",
70 : : pg_mblen_cstr(state->ptr), state->ptr,
71 : : (int) (state->ptr - state->begin))));
72 : : /* In soft error situation, return false as convenience for caller */
73 : 3 : return false;
74 : : }
75 : :
76 : : #define PRSEOF return prseof(state)
77 : :
78 : : static bool
79 : 1 : prseof(HSParser *state)
80 : : {
81 [ + - ]: 1 : errsave(state->escontext,
82 : : (errcode(ERRCODE_SYNTAX_ERROR),
83 : : errmsg("syntax error in hstore: unexpected end of string")));
84 : : /* In soft error situation, return false as convenience for caller */
1339 tgl@sss.pgh.pa.us 85 :UBC 0 : return false;
86 : : }
87 : :
88 : :
89 : : #define GV_WAITVAL 0
90 : : #define GV_INVAL 1
91 : : #define GV_INESCVAL 2
92 : : #define GV_WAITESCIN 3
93 : : #define GV_WAITESCESCIN 4
94 : :
95 : : static bool
6286 bruce@momjian.us 96 :CBC 10834 : get_val(HSParser *state, bool ignoreeq, bool *escaped)
97 : : {
7267 98 : 10834 : int st = GV_WAITVAL;
99 : :
100 : 10834 : state->wordlen = 32;
101 : 10834 : state->cur = state->word = palloc(state->wordlen);
102 : 10834 : *escaped = false;
103 : :
104 : : while (1)
105 : : {
106 [ + + ]: 52135 : if (st == GV_WAITVAL)
107 : : {
108 [ + + ]: 15097 : if (*(state->ptr) == '"')
109 : : {
110 : 89 : *escaped = true;
7296 teodor@sigaev.ru 111 : 89 : st = GV_INESCVAL;
112 : : }
7267 bruce@momjian.us 113 [ + + ]: 15008 : else if (*(state->ptr) == '\0')
114 : : {
7296 teodor@sigaev.ru 115 : 146 : return false;
116 : : }
7267 bruce@momjian.us 117 [ + + + - ]: 14862 : else if (*(state->ptr) == '=' && !ignoreeq)
118 : : {
1339 tgl@sss.pgh.pa.us 119 : 2 : PRSSYNTAXERROR;
120 : : }
7267 bruce@momjian.us 121 [ + + ]: 14860 : else if (*(state->ptr) == '\\')
122 : : {
7296 teodor@sigaev.ru 123 : 2 : st = GV_WAITESCIN;
124 : : }
1172 michael@paquier.xyz 125 [ + + ]: 14858 : else if (!scanner_isspace((unsigned char) *(state->ptr)))
126 : : {
7296 teodor@sigaev.ru 127 : 10595 : *(state->cur) = *(state->ptr);
128 : 10595 : state->cur++;
129 : 10595 : st = GV_INVAL;
130 : : }
131 : : }
7267 bruce@momjian.us 132 [ + + ]: 37038 : else if (st == GV_INVAL)
133 : : {
134 [ + + ]: 36637 : if (*(state->ptr) == '\\')
135 : : {
7296 teodor@sigaev.ru 136 : 1 : st = GV_WAITESCIN;
137 : : }
7267 bruce@momjian.us 138 [ + + + + ]: 36636 : else if (*(state->ptr) == '=' && !ignoreeq)
139 : : {
7296 teodor@sigaev.ru 140 : 5265 : state->ptr--;
141 : 5265 : return true;
142 : : }
7267 bruce@momjian.us 143 [ + + + - ]: 31371 : else if (*(state->ptr) == ',' && ignoreeq)
144 : : {
7296 teodor@sigaev.ru 145 : 4131 : state->ptr--;
146 : 4131 : return true;
147 : : }
1172 michael@paquier.xyz 148 [ + + ]: 27240 : else if (scanner_isspace((unsigned char) *(state->ptr)))
149 : : {
7296 teodor@sigaev.ru 150 : 100 : return true;
151 : : }
7267 bruce@momjian.us 152 [ + + ]: 27140 : else if (*(state->ptr) == '\0')
153 : : {
7296 teodor@sigaev.ru 154 : 1101 : state->ptr--;
155 : 1101 : return true;
156 : : }
157 : : else
158 : : {
159 [ - + ]: 26039 : RESIZEPRSBUF;
160 : 26039 : *(state->cur) = *(state->ptr);
161 : 26039 : state->cur++;
162 : : }
163 : : }
7267 bruce@momjian.us 164 [ + + ]: 401 : else if (st == GV_INESCVAL)
165 : : {
166 [ + + ]: 397 : if (*(state->ptr) == '\\')
167 : : {
7296 teodor@sigaev.ru 168 : 1 : st = GV_WAITESCESCIN;
169 : : }
7267 bruce@momjian.us 170 [ + + ]: 396 : else if (*(state->ptr) == '"')
171 : : {
7296 teodor@sigaev.ru 172 : 88 : return true;
173 : : }
7267 bruce@momjian.us 174 [ + + ]: 308 : else if (*(state->ptr) == '\0')
175 : : {
1339 tgl@sss.pgh.pa.us 176 : 1 : PRSEOF;
177 : : }
178 : : else
179 : : {
7296 teodor@sigaev.ru 180 [ - + ]: 307 : RESIZEPRSBUF;
181 : 307 : *(state->cur) = *(state->ptr);
182 : 307 : state->cur++;
183 : : }
184 : : }
7267 bruce@momjian.us 185 [ + + ]: 4 : else if (st == GV_WAITESCIN)
186 : : {
187 [ - + ]: 3 : if (*(state->ptr) == '\0')
1339 tgl@sss.pgh.pa.us 188 :UBC 0 : PRSEOF;
7296 teodor@sigaev.ru 189 [ - + ]:CBC 3 : RESIZEPRSBUF;
190 : 3 : *(state->cur) = *(state->ptr);
191 : 3 : state->cur++;
7267 bruce@momjian.us 192 : 3 : st = GV_INVAL;
193 : : }
194 [ + - ]: 1 : else if (st == GV_WAITESCESCIN)
195 : : {
196 [ - + ]: 1 : if (*(state->ptr) == '\0')
1339 tgl@sss.pgh.pa.us 197 :UBC 0 : PRSEOF;
7296 teodor@sigaev.ru 198 [ - + ]:CBC 1 : RESIZEPRSBUF;
199 : 1 : *(state->cur) = *(state->ptr);
200 : 1 : state->cur++;
201 : 1 : st = GV_INESCVAL;
202 : : }
203 : : else
1339 tgl@sss.pgh.pa.us 204 [ # # ]:UBC 0 : elog(ERROR, "unrecognized get_val state: %d", st);
205 : :
7296 teodor@sigaev.ru 206 :CBC 41301 : state->ptr++;
207 : : }
208 : : }
209 : :
210 : : #define WKEY 0
211 : : #define WVAL 1
212 : : #define WEQ 2
213 : : #define WGT 3
214 : : #define WDEL 4
215 : :
216 : :
217 : : static bool
6286 bruce@momjian.us 218 : 1290 : parse_hstore(HSParser *state)
219 : : {
7267 220 : 1290 : int st = WKEY;
221 : 1290 : bool escaped = false;
222 : :
223 : 1290 : state->plen = 16;
265 michael@paquier.xyz 224 : 1290 : state->pairs = palloc_array(Pairs, state->plen);
7267 bruce@momjian.us 225 : 1290 : state->pcur = 0;
7296 teodor@sigaev.ru 226 : 1290 : state->ptr = state->begin;
7267 bruce@momjian.us 227 : 1290 : state->word = NULL;
228 : :
229 : : while (1)
230 : : {
231 [ + + ]: 26878 : if (st == WKEY)
232 : : {
233 [ + + ]: 5492 : if (!get_val(state, false, &escaped))
234 : : {
1339 tgl@sss.pgh.pa.us 235 [ + + + - : 147 : if (SOFT_ERROR_OCCURRED(state->escontext))
+ - ]
236 : 1 : return false;
237 : 146 : return true; /* EOF, all okay */
238 : : }
7267 bruce@momjian.us 239 [ - + ]: 5344 : if (state->pcur >= state->plen)
240 : : {
7296 teodor@sigaev.ru 241 :UBC 0 : state->plen *= 2;
10 michael@paquier.xyz 242 :UNC 0 : state->pairs = repalloc_array(state->pairs, Pairs, state->plen);
243 : : }
1339 tgl@sss.pgh.pa.us 244 [ - + ]:CBC 5344 : if (!hstoreCheckKeyLength(state->cur - state->word, state))
1339 tgl@sss.pgh.pa.us 245 :UBC 0 : return false;
7267 bruce@momjian.us 246 :CBC 5344 : state->pairs[state->pcur].key = state->word;
1339 tgl@sss.pgh.pa.us 247 : 5344 : state->pairs[state->pcur].keylen = state->cur - state->word;
7267 bruce@momjian.us 248 : 5344 : state->pairs[state->pcur].val = NULL;
249 : 5344 : state->word = NULL;
7296 teodor@sigaev.ru 250 : 5344 : st = WEQ;
251 : : }
7267 bruce@momjian.us 252 [ + + ]: 21386 : else if (st == WEQ)
253 : : {
254 [ + + ]: 5355 : if (*(state->ptr) == '=')
255 : : {
7296 teodor@sigaev.ru 256 : 5344 : st = WGT;
257 : : }
7267 bruce@momjian.us 258 [ - + ]: 11 : else if (*(state->ptr) == '\0')
259 : : {
1339 tgl@sss.pgh.pa.us 260 :UBC 0 : PRSEOF;
261 : : }
1172 michael@paquier.xyz 262 [ - + ]:CBC 11 : else if (!scanner_isspace((unsigned char) *(state->ptr)))
263 : : {
1339 tgl@sss.pgh.pa.us 264 :UBC 0 : PRSSYNTAXERROR;
265 : : }
266 : : }
7267 bruce@momjian.us 267 [ + + ]:CBC 16031 : else if (st == WGT)
268 : : {
269 [ + + ]: 5344 : if (*(state->ptr) == '>')
270 : : {
7296 teodor@sigaev.ru 271 : 5342 : st = WVAL;
272 : : }
7267 bruce@momjian.us 273 [ - + ]: 2 : else if (*(state->ptr) == '\0')
274 : : {
1339 tgl@sss.pgh.pa.us 275 :UBC 0 : PRSEOF;
276 : : }
277 : : else
278 : : {
1339 tgl@sss.pgh.pa.us 279 :CBC 2 : PRSSYNTAXERROR;
280 : : }
281 : : }
7267 bruce@momjian.us 282 [ + + ]: 10687 : else if (st == WVAL)
283 : : {
284 [ - + ]: 5342 : if (!get_val(state, true, &escaped))
285 : : {
1339 tgl@sss.pgh.pa.us 286 [ # # # # :UBC 0 : if (SOFT_ERROR_OCCURRED(state->escontext))
# # ]
287 : 0 : return false;
288 : 0 : PRSEOF;
289 : : }
1339 tgl@sss.pgh.pa.us 290 [ - + ]:CBC 5341 : if (!hstoreCheckValLength(state->cur - state->word, state))
1339 tgl@sss.pgh.pa.us 291 :UBC 0 : return false;
7267 bruce@momjian.us 292 :CBC 5341 : state->pairs[state->pcur].val = state->word;
1339 tgl@sss.pgh.pa.us 293 : 5341 : state->pairs[state->pcur].vallen = state->cur - state->word;
7267 bruce@momjian.us 294 : 5341 : state->pairs[state->pcur].isnull = false;
295 : 5341 : state->pairs[state->pcur].needfree = true;
296 [ + + + + ]: 5341 : if (state->cur - state->word == 4 && !escaped)
297 : : {
7296 teodor@sigaev.ru 298 : 73 : state->word[4] = '\0';
1339 tgl@sss.pgh.pa.us 299 [ + + ]: 73 : if (pg_strcasecmp(state->word, "null") == 0)
7267 bruce@momjian.us 300 : 69 : state->pairs[state->pcur].isnull = true;
301 : : }
302 : 5341 : state->word = NULL;
7296 teodor@sigaev.ru 303 : 5341 : state->pcur++;
304 : 5341 : st = WDEL;
305 : : }
7267 bruce@momjian.us 306 [ + - ]: 5345 : else if (st == WDEL)
307 : : {
308 [ + + ]: 5345 : if (*(state->ptr) == ',')
309 : : {
7296 teodor@sigaev.ru 310 : 4202 : st = WKEY;
311 : : }
7267 bruce@momjian.us 312 [ + + ]: 1143 : else if (*(state->ptr) == '\0')
313 : : {
1339 tgl@sss.pgh.pa.us 314 : 1139 : return true;
315 : : }
1172 michael@paquier.xyz 316 [ - + ]: 4 : else if (!scanner_isspace((unsigned char) *(state->ptr)))
317 : : {
1339 tgl@sss.pgh.pa.us 318 :UBC 0 : PRSSYNTAXERROR;
319 : : }
320 : : }
321 : : else
322 [ # # ]: 0 : elog(ERROR, "unrecognized parse_hstore state: %d", st);
323 : :
7296 teodor@sigaev.ru 324 :CBC 25588 : state->ptr++;
325 : : }
326 : : }
327 : :
328 : : static int
7267 bruce@momjian.us 329 : 12694 : comparePairs(const void *a, const void *b)
330 : : {
5464 peter_e@gmx.net 331 : 12694 : const Pairs *pa = a;
332 : 12694 : const Pairs *pb = b;
333 : :
334 [ + + ]: 12694 : if (pa->keylen == pb->keylen)
335 : : {
336 : 2534 : int res = memcmp(pa->key, pb->key, pa->keylen);
337 : :
7267 bruce@momjian.us 338 [ + - ]: 2534 : if (res)
7296 teodor@sigaev.ru 339 : 2534 : return res;
340 : :
341 : : /* guarantee that needfree will be later */
5464 peter_e@gmx.net 342 [ # # ]:UBC 0 : if (pb->needfree == pa->needfree)
7296 teodor@sigaev.ru 343 : 0 : return 0;
5464 peter_e@gmx.net 344 [ # # ]: 0 : else if (pa->needfree)
7296 teodor@sigaev.ru 345 : 0 : return 1;
346 : : else
7267 bruce@momjian.us 347 : 0 : return -1;
348 : : }
5464 peter_e@gmx.net 349 [ + + ]:CBC 10160 : return (pa->keylen > pb->keylen) ? 1 : -1;
350 : : }
351 : :
352 : : /*
353 : : * Add the string-data length of a pair to *buflen.
354 : : *
355 : : * This is a convenience routine for add_size(), checking if a Pair is null
356 : : * before adding its size. Individual keys and values are each limited to
357 : : * HENTRY_POSMASK bytes.
358 : : */
359 : : static void
9 michael@paquier.xyz 360 :GNC 11116 : hstoreAddPairLen(Size *buflen, const Pairs *pair)
361 : : {
362 [ + + ]: 11116 : Size pairlen = pair->keylen + (pair->isnull ? 0 : pair->vallen);
363 : :
364 : 11116 : *buflen = add_size(*buflen, pairlen);
365 : 11116 : }
366 : :
367 : : /*
368 : : * this code still respects pairs.needfree, even though in general
369 : : * it should never be called in a context where anything needs freeing.
370 : : * we keep it because (a) those calls are in a rare code path anyway,
371 : : * and (b) who knows whether they might be needed by some caller.
372 : : */
373 : : int
374 : 4154 : hstoreUniquePairs(Pairs *a, int32 l, Size *buflen)
375 : : {
376 : : Pairs *ptr,
377 : : *res;
378 : :
7267 bruce@momjian.us 379 :CBC 4154 : *buflen = 0;
380 [ + + ]: 4154 : if (l < 2)
381 : : {
382 [ + + ]: 239 : if (l == 1)
9 michael@paquier.xyz 383 :GNC 91 : hstoreAddPairLen(buflen, a);
7296 teodor@sigaev.ru 384 :CBC 239 : return l;
385 : : }
386 : :
1297 peter@eisentraut.org 387 : 3915 : qsort(a, l, sizeof(Pairs), comparePairs);
388 : :
389 : : /*
390 : : * We can't use qunique here because we have some clean-up code to run on
391 : : * removed elements.
392 : : */
7267 bruce@momjian.us 393 : 3915 : ptr = a + 1;
394 : 3915 : res = a;
395 [ + + ]: 11025 : while (ptr - a < l)
396 : : {
6175 tgl@sss.pgh.pa.us 397 [ + + ]: 7110 : if (ptr->keylen == res->keylen &&
5728 rhaas@postgresql.org 398 [ - + ]: 1876 : memcmp(ptr->key, res->key, res->keylen) == 0)
399 : : {
7267 bruce@momjian.us 400 [ # # ]:UBC 0 : if (ptr->needfree)
401 : : {
7296 teodor@sigaev.ru 402 : 0 : pfree(ptr->key);
191 michael@paquier.xyz 403 [ # # ]: 0 : if (ptr->val != NULL)
404 : 0 : pfree(ptr->val);
405 : : }
406 : : }
407 : : else
408 : : {
9 michael@paquier.xyz 409 :GNC 7110 : hstoreAddPairLen(buflen, res);
7296 teodor@sigaev.ru 410 :CBC 7110 : res++;
3197 tgl@sss.pgh.pa.us 411 [ - + ]: 7110 : if (res != ptr)
3197 tgl@sss.pgh.pa.us 412 :UBC 0 : memcpy(res, ptr, sizeof(Pairs));
413 : : }
414 : :
7296 teodor@sigaev.ru 415 :CBC 7110 : ptr++;
416 : : }
417 : :
9 michael@paquier.xyz 418 :GNC 3915 : hstoreAddPairLen(buflen, res);
7296 teodor@sigaev.ru 419 :CBC 3915 : return res + 1 - a;
420 : : }
421 : :
422 : : size_t
6374 tgl@sss.pgh.pa.us 423 : 138 : hstoreCheckKeyLen(size_t len)
424 : : {
425 [ - + ]: 138 : if (len > HSTORE_MAX_KEY_LEN)
6374 tgl@sss.pgh.pa.us 426 [ # # ]:UBC 0 : ereport(ERROR,
427 : : (errcode(ERRCODE_STRING_DATA_RIGHT_TRUNCATION),
428 : : errmsg("string too long for hstore key")));
6374 tgl@sss.pgh.pa.us 429 :CBC 138 : return len;
430 : : }
431 : :
432 : : static bool
1339 433 : 5344 : hstoreCheckKeyLength(size_t len, HSParser *state)
434 : : {
435 [ - + ]: 5344 : if (len > HSTORE_MAX_KEY_LEN)
1339 tgl@sss.pgh.pa.us 436 [ # # ]:UBC 0 : ereturn(state->escontext, false,
437 : : (errcode(ERRCODE_STRING_DATA_RIGHT_TRUNCATION),
438 : : errmsg("string too long for hstore key")));
1339 tgl@sss.pgh.pa.us 439 :CBC 5344 : return true;
440 : : }
441 : :
442 : : size_t
6374 443 : 100 : hstoreCheckValLen(size_t len)
444 : : {
445 [ - + ]: 100 : if (len > HSTORE_MAX_VALUE_LEN)
6374 tgl@sss.pgh.pa.us 446 [ # # ]:UBC 0 : ereport(ERROR,
447 : : (errcode(ERRCODE_STRING_DATA_RIGHT_TRUNCATION),
448 : : errmsg("string too long for hstore value")));
6374 tgl@sss.pgh.pa.us 449 :CBC 100 : return len;
450 : : }
451 : :
452 : : static bool
1339 453 : 5341 : hstoreCheckValLength(size_t len, HSParser *state)
454 : : {
455 [ - + ]: 5341 : if (len > HSTORE_MAX_VALUE_LEN)
1339 tgl@sss.pgh.pa.us 456 [ # # ]:UBC 0 : ereturn(state->escontext, false,
457 : : (errcode(ERRCODE_STRING_DATA_RIGHT_TRUNCATION),
458 : : errmsg("string too long for hstore value")));
1339 tgl@sss.pgh.pa.us 459 :CBC 5341 : return true;
460 : : }
461 : :
462 : :
463 : : HStore *
9 michael@paquier.xyz 464 :GNC 1362 : hstorePairs(Pairs *pairs, int32 pcount, Size buflen)
465 : : {
466 : : HStore *out;
467 : : HEntry *entry;
468 : : char *ptr;
469 : : char *buf;
470 : : Size len;
471 : : int32 i;
472 : :
473 : 1362 : len = hstoreCalcDataSize(pcount, buflen);
474 : :
6175 tgl@sss.pgh.pa.us 475 :CBC 1362 : out = palloc(len);
476 : 1362 : SET_VARSIZE(out, len);
477 : 1362 : HS_SETCOUNT(out, pcount);
478 : :
479 [ + + ]: 1362 : if (pcount == 0)
480 : 151 : return out;
481 : :
482 : 1211 : entry = ARRPTR(out);
483 : 1211 : buf = ptr = STRPTR(out);
484 : :
485 [ + + ]: 6697 : for (i = 0; i < pcount; i++)
6026 bruce@momjian.us 486 [ + + ]: 5486 : HS_ADDITEM(entry, buf, ptr, pairs[i]);
487 : :
488 [ + - - + ]: 1211 : HS_FINALIZE(out, pcount, buf, ptr);
489 : :
6175 tgl@sss.pgh.pa.us 490 : 1211 : return out;
491 : : }
492 : :
493 : :
7296 teodor@sigaev.ru 494 : 16 : PG_FUNCTION_INFO_V1(hstore_in);
495 : : Datum
7267 bruce@momjian.us 496 : 1290 : hstore_in(PG_FUNCTION_ARGS)
497 : : {
1339 tgl@sss.pgh.pa.us 498 : 1290 : char *str = PG_GETARG_CSTRING(0);
499 : 1290 : Node *escontext = fcinfo->context;
500 : : HSParser state;
501 : : Size buflen;
502 : : HStore *out;
503 : :
504 : 1290 : state.begin = str;
505 : 1290 : state.escontext = escontext;
506 : :
507 [ + + ]: 1290 : if (!parse_hstore(&state))
508 : 3 : PG_RETURN_NULL();
509 : :
6175 510 : 1285 : state.pcur = hstoreUniquePairs(state.pairs, state.pcur, &buflen);
511 : :
512 : 1285 : out = hstorePairs(state.pairs, state.pcur, buflen);
513 : :
514 : 1285 : PG_RETURN_POINTER(out);
515 : : }
516 : :
517 : :
518 : 8 : PG_FUNCTION_INFO_V1(hstore_recv);
519 : : Datum
6175 tgl@sss.pgh.pa.us 520 :UBC 0 : hstore_recv(PG_FUNCTION_ARGS)
521 : : {
522 : : Size buflen;
523 : : HStore *out;
524 : : Pairs *pairs;
525 : : int32 i;
526 : : int32 pcount;
6026 bruce@momjian.us 527 : 0 : StringInfo buf = (StringInfo) PG_GETARG_POINTER(0);
528 : :
6175 tgl@sss.pgh.pa.us 529 : 0 : pcount = pq_getmsgint(buf, 4);
530 : :
531 [ # # ]: 0 : if (pcount == 0)
532 : : {
533 : 0 : out = hstorePairs(NULL, 0, 0);
7296 teodor@sigaev.ru 534 : 0 : PG_RETURN_POINTER(out);
535 : : }
536 : :
4574 noah@leadboat.com 537 [ # # # # ]: 0 : if (pcount < 0 || pcount > MaxAllocSize / sizeof(Pairs))
538 [ # # ]: 0 : ereport(ERROR,
539 : : (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
540 : : errmsg("number of pairs (%d) exceeds the maximum allowed (%d)",
541 : : pcount, (int) (MaxAllocSize / sizeof(Pairs)))));
10 michael@paquier.xyz 542 :UNC 0 : pairs = palloc_array(Pairs, pcount);
543 : :
6175 tgl@sss.pgh.pa.us 544 [ # # ]:UBC 0 : for (i = 0; i < pcount; ++i)
545 : : {
6026 bruce@momjian.us 546 : 0 : int rawlen = pq_getmsgint(buf, 4);
547 : : int len;
548 : :
6175 tgl@sss.pgh.pa.us 549 [ # # ]: 0 : if (rawlen < 0)
550 [ # # ]: 0 : ereport(ERROR,
551 : : (errcode(ERRCODE_NULL_VALUE_NOT_ALLOWED),
552 : : errmsg("null value not allowed for hstore key")));
553 : :
554 : 0 : pairs[i].key = pq_getmsgtext(buf, rawlen, &len);
555 : 0 : pairs[i].keylen = hstoreCheckKeyLen(len);
556 : 0 : pairs[i].needfree = true;
557 : :
558 : 0 : rawlen = pq_getmsgint(buf, 4);
559 [ # # ]: 0 : if (rawlen < 0)
560 : : {
561 : 0 : pairs[i].val = NULL;
562 : 0 : pairs[i].vallen = 0;
563 : 0 : pairs[i].isnull = true;
564 : : }
565 : : else
566 : : {
567 : 0 : pairs[i].val = pq_getmsgtext(buf, rawlen, &len);
568 : 0 : pairs[i].vallen = hstoreCheckValLen(len);
569 : 0 : pairs[i].isnull = false;
570 : : }
571 : : }
572 : :
573 : 0 : pcount = hstoreUniquePairs(pairs, pcount, &buflen);
574 : :
575 : 0 : out = hstorePairs(pairs, pcount, buflen);
576 : :
577 : 0 : PG_RETURN_POINTER(out);
578 : : }
579 : :
580 : :
6175 tgl@sss.pgh.pa.us 581 :CBC 15 : PG_FUNCTION_INFO_V1(hstore_from_text);
582 : : Datum
583 : 36 : hstore_from_text(PG_FUNCTION_ARGS)
584 : : {
585 : : text *key;
6026 bruce@momjian.us 586 : 36 : text *val = NULL;
587 : : Pairs p;
588 : : HStore *out;
589 : :
6175 tgl@sss.pgh.pa.us 590 [ + + ]: 36 : if (PG_ARGISNULL(0))
591 : 1 : PG_RETURN_NULL();
592 : :
593 : 35 : p.needfree = false;
594 : 35 : key = PG_GETARG_TEXT_PP(0);
595 : 35 : p.key = VARDATA_ANY(key);
596 : 35 : p.keylen = hstoreCheckKeyLen(VARSIZE_ANY_EXHDR(key));
597 : :
598 [ + + ]: 35 : if (PG_ARGISNULL(1))
599 : : {
600 : 8 : p.vallen = 0;
601 : 8 : p.isnull = true;
602 : : }
603 : : else
604 : : {
605 : 27 : val = PG_GETARG_TEXT_PP(1);
606 : 27 : p.val = VARDATA_ANY(val);
607 : 27 : p.vallen = hstoreCheckValLen(VARSIZE_ANY_EXHDR(val));
608 : 27 : p.isnull = false;
609 : : }
610 : :
611 : 35 : out = hstorePairs(&p, 1, p.keylen + p.vallen);
612 : :
613 : 35 : PG_RETURN_POINTER(out);
614 : : }
615 : :
616 : :
617 : 8 : PG_FUNCTION_INFO_V1(hstore_from_arrays);
618 : : Datum
619 : 10 : hstore_from_arrays(PG_FUNCTION_ARGS)
620 : : {
621 : : Size buflen;
622 : : HStore *out;
623 : : Pairs *pairs;
624 : : Datum *key_datums;
625 : : bool *key_nulls;
626 : : int key_count;
627 : : Datum *value_datums;
628 : : bool *value_nulls;
629 : : int value_count;
630 : : ArrayType *key_array;
631 : : ArrayType *value_array;
632 : : int i;
633 : :
634 [ - + ]: 10 : if (PG_ARGISNULL(0))
6175 tgl@sss.pgh.pa.us 635 :UBC 0 : PG_RETURN_NULL();
636 : :
6175 tgl@sss.pgh.pa.us 637 :CBC 10 : key_array = PG_GETARG_ARRAYTYPE_P(0);
638 : :
639 [ - + ]: 10 : Assert(ARR_ELEMTYPE(key_array) == TEXTOID);
640 : :
641 : : /*
642 : : * must check >1 rather than != 1 because empty arrays have 0 dimensions,
643 : : * not 1
644 : : */
645 : :
646 [ - + ]: 10 : if (ARR_NDIM(key_array) > 1)
6175 tgl@sss.pgh.pa.us 647 [ # # ]:UBC 0 : ereport(ERROR,
648 : : (errcode(ERRCODE_ARRAY_SUBSCRIPT_ERROR),
649 : : errmsg("wrong number of array subscripts")));
650 : :
1518 peter@eisentraut.org 651 :CBC 10 : deconstruct_array_builtin(key_array, TEXTOID, &key_datums, &key_nulls, &key_count);
652 : :
653 : : /* see discussion in hstoreArrayToPairs() */
4574 noah@leadboat.com 654 [ - + ]: 10 : if (key_count > MaxAllocSize / sizeof(Pairs))
4574 noah@leadboat.com 655 [ # # ]:UBC 0 : ereport(ERROR,
656 : : (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
657 : : errmsg("number of pairs (%d) exceeds the maximum allowed (%d)",
658 : : key_count, (int) (MaxAllocSize / sizeof(Pairs)))));
659 : :
660 : : /* value_array might be NULL */
661 : :
6175 tgl@sss.pgh.pa.us 662 [ + + ]:CBC 10 : if (PG_ARGISNULL(1))
663 : : {
664 : 2 : value_array = NULL;
665 : 2 : value_count = key_count;
666 : 2 : value_datums = NULL;
667 : 2 : value_nulls = NULL;
668 : : }
669 : : else
670 : : {
671 : 8 : value_array = PG_GETARG_ARRAYTYPE_P(1);
672 : :
673 [ - + ]: 8 : Assert(ARR_ELEMTYPE(value_array) == TEXTOID);
674 : :
675 [ - + ]: 8 : if (ARR_NDIM(value_array) > 1)
6175 tgl@sss.pgh.pa.us 676 [ # # ]:UBC 0 : ereport(ERROR,
677 : : (errcode(ERRCODE_ARRAY_SUBSCRIPT_ERROR),
678 : : errmsg("wrong number of array subscripts")));
679 : :
6175 tgl@sss.pgh.pa.us 680 [ + + + + ]:CBC 8 : if ((ARR_NDIM(key_array) > 0 || ARR_NDIM(value_array) > 0) &&
681 [ + + ]: 7 : (ARR_NDIM(key_array) != ARR_NDIM(value_array) ||
682 [ + - ]: 5 : ARR_DIMS(key_array)[0] != ARR_DIMS(value_array)[0] ||
683 [ - + ]: 5 : ARR_LBOUND(key_array)[0] != ARR_LBOUND(value_array)[0]))
684 [ + - ]: 2 : ereport(ERROR,
685 : : (errcode(ERRCODE_ARRAY_SUBSCRIPT_ERROR),
686 : : errmsg("arrays must have same bounds")));
687 : :
1518 peter@eisentraut.org 688 : 6 : deconstruct_array_builtin(value_array, TEXTOID, &value_datums, &value_nulls, &value_count);
689 : :
6175 tgl@sss.pgh.pa.us 690 [ - + ]: 6 : Assert(key_count == value_count);
691 : : }
692 : :
10 michael@paquier.xyz 693 :GNC 8 : pairs = palloc_array(Pairs, key_count);
694 : :
6175 tgl@sss.pgh.pa.us 695 [ + + ]:CBC 28 : for (i = 0; i < key_count; ++i)
696 : : {
697 [ - + ]: 20 : if (key_nulls[i])
6175 tgl@sss.pgh.pa.us 698 [ # # ]:UBC 0 : ereport(ERROR,
699 : : (errcode(ERRCODE_NULL_VALUE_NOT_ALLOWED),
700 : : errmsg("null value not allowed for hstore key")));
701 : :
6175 tgl@sss.pgh.pa.us 702 [ + + + + ]:CBC 20 : if (!value_nulls || value_nulls[i])
703 : : {
387 peter@eisentraut.org 704 : 9 : pairs[i].key = VARDATA(DatumGetPointer(key_datums[i]));
6175 tgl@sss.pgh.pa.us 705 : 9 : pairs[i].val = NULL;
3455 noah@leadboat.com 706 : 18 : pairs[i].keylen =
387 peter@eisentraut.org 707 : 9 : hstoreCheckKeyLen(VARSIZE(DatumGetPointer(key_datums[i])) - VARHDRSZ);
6175 tgl@sss.pgh.pa.us 708 : 9 : pairs[i].vallen = 4;
709 : 9 : pairs[i].isnull = true;
710 : 9 : pairs[i].needfree = false;
711 : : }
712 : : else
713 : : {
387 peter@eisentraut.org 714 : 11 : pairs[i].key = VARDATA(DatumGetPointer(key_datums[i]));
715 : 11 : pairs[i].val = VARDATA(DatumGetPointer(value_datums[i]));
3455 noah@leadboat.com 716 : 22 : pairs[i].keylen =
387 peter@eisentraut.org 717 : 11 : hstoreCheckKeyLen(VARSIZE(DatumGetPointer(key_datums[i])) - VARHDRSZ);
3455 noah@leadboat.com 718 : 22 : pairs[i].vallen =
387 peter@eisentraut.org 719 : 11 : hstoreCheckValLen(VARSIZE(DatumGetPointer(value_datums[i])) - VARHDRSZ);
6175 tgl@sss.pgh.pa.us 720 : 11 : pairs[i].isnull = false;
721 : 11 : pairs[i].needfree = false;
722 : : }
723 : : }
724 : :
725 : 8 : key_count = hstoreUniquePairs(pairs, key_count, &buflen);
726 : :
727 : 8 : out = hstorePairs(pairs, key_count, buflen);
728 : :
729 : 8 : PG_RETURN_POINTER(out);
730 : : }
731 : :
732 : :
733 : 8 : PG_FUNCTION_INFO_V1(hstore_from_array);
734 : : Datum
735 : 14 : hstore_from_array(PG_FUNCTION_ARGS)
736 : : {
737 : 14 : ArrayType *in_array = PG_GETARG_ARRAYTYPE_P(0);
6026 bruce@momjian.us 738 : 14 : int ndims = ARR_NDIM(in_array);
739 : : int count;
740 : : Size buflen;
741 : : HStore *out;
742 : : Pairs *pairs;
743 : : Datum *in_datums;
744 : : bool *in_nulls;
745 : : int in_count;
746 : : int i;
747 : :
6175 tgl@sss.pgh.pa.us 748 [ - + ]: 14 : Assert(ARR_ELEMTYPE(in_array) == TEXTOID);
749 : :
750 [ + + + + ]: 14 : switch (ndims)
751 : : {
752 : 2 : case 0:
753 : 2 : out = hstorePairs(NULL, 0, 0);
754 : 2 : PG_RETURN_POINTER(out);
755 : :
756 : 5 : case 1:
757 [ + + ]: 5 : if ((ARR_DIMS(in_array)[0]) % 2)
758 [ + - ]: 2 : ereport(ERROR,
759 : : (errcode(ERRCODE_ARRAY_SUBSCRIPT_ERROR),
760 : : errmsg("array must have even number of elements")));
761 : 3 : break;
762 : :
763 : 5 : case 2:
764 [ + + ]: 5 : if ((ARR_DIMS(in_array)[1]) != 2)
765 [ + - ]: 2 : ereport(ERROR,
766 : : (errcode(ERRCODE_ARRAY_SUBSCRIPT_ERROR),
767 : : errmsg("array must have two columns")));
768 : 3 : break;
769 : :
770 : 2 : default:
771 [ + - ]: 2 : ereport(ERROR,
772 : : (errcode(ERRCODE_ARRAY_SUBSCRIPT_ERROR),
773 : : errmsg("wrong number of array subscripts")));
774 : : }
775 : :
1518 peter@eisentraut.org 776 : 6 : deconstruct_array_builtin(in_array, TEXTOID, &in_datums, &in_nulls, &in_count);
777 : :
6175 tgl@sss.pgh.pa.us 778 : 6 : count = in_count / 2;
779 : :
780 : : /* see discussion in hstoreArrayToPairs() */
4574 noah@leadboat.com 781 [ - + ]: 6 : if (count > MaxAllocSize / sizeof(Pairs))
4574 noah@leadboat.com 782 [ # # ]:UBC 0 : ereport(ERROR,
783 : : (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
784 : : errmsg("number of pairs (%d) exceeds the maximum allowed (%d)",
785 : : count, (int) (MaxAllocSize / sizeof(Pairs)))));
786 : :
10 michael@paquier.xyz 787 :GNC 6 : pairs = palloc_array(Pairs, count);
788 : :
6175 tgl@sss.pgh.pa.us 789 [ + + ]:CBC 24 : for (i = 0; i < count; ++i)
790 : : {
6026 bruce@momjian.us 791 [ - + ]: 18 : if (in_nulls[i * 2])
6175 tgl@sss.pgh.pa.us 792 [ # # ]:UBC 0 : ereport(ERROR,
793 : : (errcode(ERRCODE_NULL_VALUE_NOT_ALLOWED),
794 : : errmsg("null value not allowed for hstore key")));
795 : :
6026 bruce@momjian.us 796 [ - + ]:CBC 18 : if (in_nulls[i * 2 + 1])
797 : : {
387 peter@eisentraut.org 798 :UBC 0 : pairs[i].key = VARDATA(DatumGetPointer(in_datums[i * 2]));
6175 tgl@sss.pgh.pa.us 799 : 0 : pairs[i].val = NULL;
3455 noah@leadboat.com 800 : 0 : pairs[i].keylen =
387 peter@eisentraut.org 801 : 0 : hstoreCheckKeyLen(VARSIZE(DatumGetPointer(in_datums[i * 2])) - VARHDRSZ);
6175 tgl@sss.pgh.pa.us 802 : 0 : pairs[i].vallen = 4;
803 : 0 : pairs[i].isnull = true;
804 : 0 : pairs[i].needfree = false;
805 : : }
806 : : else
807 : : {
387 peter@eisentraut.org 808 :CBC 18 : pairs[i].key = VARDATA(DatumGetPointer(in_datums[i * 2]));
809 : 18 : pairs[i].val = VARDATA(DatumGetPointer(in_datums[i * 2 + 1]));
3455 noah@leadboat.com 810 : 36 : pairs[i].keylen =
387 peter@eisentraut.org 811 : 18 : hstoreCheckKeyLen(VARSIZE(DatumGetPointer(in_datums[i * 2])) - VARHDRSZ);
3455 noah@leadboat.com 812 : 36 : pairs[i].vallen =
387 peter@eisentraut.org 813 : 18 : hstoreCheckValLen(VARSIZE(DatumGetPointer(in_datums[i * 2 + 1])) - VARHDRSZ);
6175 tgl@sss.pgh.pa.us 814 : 18 : pairs[i].isnull = false;
815 : 18 : pairs[i].needfree = false;
816 : : }
817 : : }
818 : :
819 : 6 : count = hstoreUniquePairs(pairs, count, &buflen);
820 : :
821 : 6 : out = hstorePairs(pairs, count, buflen);
822 : :
823 : 6 : PG_RETURN_POINTER(out);
824 : : }
825 : :
826 : : /* most of hstore_from_record is shamelessly swiped from record_out */
827 : :
828 : : /*
829 : : * structure to cache metadata needed for record I/O
830 : : */
831 : : typedef struct ColumnIOData
832 : : {
833 : : Oid column_type;
834 : : Oid typiofunc;
835 : : Oid typioparam;
836 : : FmgrInfo proc;
837 : : } ColumnIOData;
838 : :
839 : : typedef struct RecordIOData
840 : : {
841 : : Oid record_type;
842 : : int32 record_typmod;
843 : : /* this field is used only if target type is domain over composite: */
844 : : void *domain_info; /* opaque cache for domain checks */
845 : : int ncolumns;
846 : : ColumnIOData columns[FLEXIBLE_ARRAY_MEMBER];
847 : : } RecordIOData;
848 : :
849 : 8 : PG_FUNCTION_INFO_V1(hstore_from_record);
850 : : Datum
851 : 5 : hstore_from_record(PG_FUNCTION_ARGS)
852 : : {
853 : : HeapTupleHeader rec;
854 : : Size buflen;
855 : : HStore *out;
856 : : Pairs *pairs;
857 : : Oid tupType;
858 : : int32 tupTypmod;
859 : : TupleDesc tupdesc;
860 : : HeapTupleData tuple;
861 : : RecordIOData *my_extra;
862 : : int ncolumns;
863 : : int i,
864 : : j;
865 : : Datum *values;
866 : : bool *nulls;
867 : :
868 [ + + ]: 5 : if (PG_ARGISNULL(0))
869 : : {
6026 bruce@momjian.us 870 : 2 : Oid argtype = get_fn_expr_argtype(fcinfo->flinfo, 0);
871 : :
872 : : /*
873 : : * We have no tuple to look at, so the only source of type info is the
874 : : * argtype --- which might be domain over composite, but we don't care
875 : : * here, since we have no need to be concerned about domain
876 : : * constraints. The lookup_rowtype_tupdesc_domain call below will
877 : : * error out if we don't have a known composite type oid here.
878 : : */
6175 tgl@sss.pgh.pa.us 879 : 2 : tupType = argtype;
880 : 2 : tupTypmod = -1;
881 : :
882 : 2 : rec = NULL;
883 : : }
884 : : else
885 : : {
886 : 3 : rec = PG_GETARG_HEAPTUPLEHEADER(0);
887 : :
888 : : /*
889 : : * Extract type info from the tuple itself -- this will work even for
890 : : * anonymous record types.
891 : : */
892 : 3 : tupType = HeapTupleHeaderGetTypeId(rec);
893 : 3 : tupTypmod = HeapTupleHeaderGetTypMod(rec);
894 : : }
895 : :
3227 896 : 5 : tupdesc = lookup_rowtype_tupdesc_domain(tupType, tupTypmod, false);
6175 897 : 5 : ncolumns = tupdesc->natts;
898 : :
899 : : /*
900 : : * We arrange to look up the needed I/O info just once per series of
901 : : * calls, assuming the record type doesn't change underneath us.
902 : : */
903 : 5 : my_extra = (RecordIOData *) fcinfo->flinfo->fn_extra;
904 [ - + ]: 5 : if (my_extra == NULL ||
6175 tgl@sss.pgh.pa.us 905 [ # # ]:UBC 0 : my_extra->ncolumns != ncolumns)
906 : : {
6175 tgl@sss.pgh.pa.us 907 :CBC 10 : fcinfo->flinfo->fn_extra =
908 : 5 : MemoryContextAlloc(fcinfo->flinfo->fn_mcxt,
909 : : offsetof(RecordIOData, columns) +
4206 910 : 5 : ncolumns * sizeof(ColumnIOData));
6175 911 : 5 : my_extra = (RecordIOData *) fcinfo->flinfo->fn_extra;
912 : 5 : my_extra->record_type = InvalidOid;
913 : 5 : my_extra->record_typmod = 0;
914 : : }
915 : :
916 [ - + ]: 5 : if (my_extra->record_type != tupType ||
6175 tgl@sss.pgh.pa.us 917 [ # # ]:UBC 0 : my_extra->record_typmod != tupTypmod)
918 : : {
6175 tgl@sss.pgh.pa.us 919 [ + - + - :CBC 204 : MemSet(my_extra, 0,
+ - + - +
+ ]
920 : : offsetof(RecordIOData, columns) +
921 : : ncolumns * sizeof(ColumnIOData));
922 : 5 : my_extra->record_type = tupType;
923 : 5 : my_extra->record_typmod = tupTypmod;
924 : 5 : my_extra->ncolumns = ncolumns;
925 : : }
926 : :
3354 927 [ - + ]: 5 : Assert(ncolumns <= MaxTupleAttributeNumber); /* thus, no overflow */
10 michael@paquier.xyz 928 :GNC 5 : pairs = palloc_array(Pairs, ncolumns);
929 : :
6175 tgl@sss.pgh.pa.us 930 [ + + ]:CBC 5 : if (rec)
931 : : {
932 : : /* Build a temporary HeapTuple control structure */
933 : 3 : tuple.t_len = HeapTupleHeaderGetDatumLength(rec);
934 : 3 : ItemPointerSetInvalid(&(tuple.t_self));
935 : 3 : tuple.t_tableOid = InvalidOid;
936 : 3 : tuple.t_data = rec;
937 : :
10 michael@paquier.xyz 938 :GNC 3 : values = palloc_array(Datum, ncolumns);
939 : 3 : nulls = palloc_array(bool, ncolumns);
940 : :
941 : : /* Break down the tuple into fields */
6175 tgl@sss.pgh.pa.us 942 :CBC 3 : heap_deform_tuple(&tuple, tupdesc, values, nulls);
943 : : }
944 : : else
945 : : {
946 : 2 : values = NULL;
947 : 2 : nulls = NULL;
948 : : }
949 : :
950 [ + + ]: 28 : for (i = 0, j = 0; i < ncolumns; ++i)
951 : : {
952 : 23 : ColumnIOData *column_info = &my_extra->columns[i];
3294 andres@anarazel.de 953 : 23 : Form_pg_attribute att = TupleDescAttr(tupdesc, i);
954 : 23 : Oid column_type = att->atttypid;
955 : : char *value;
956 : :
957 : : /* Ignore dropped columns in datatype */
958 [ - + ]: 23 : if (att->attisdropped)
6175 tgl@sss.pgh.pa.us 959 :UBC 0 : continue;
960 : :
3294 andres@anarazel.de 961 :CBC 23 : pairs[j].key = NameStr(att->attname);
962 : 23 : pairs[j].keylen = hstoreCheckKeyLen(strlen(NameStr(att->attname)));
963 : :
6175 tgl@sss.pgh.pa.us 964 [ + + - + ]: 23 : if (!nulls || nulls[i])
965 : : {
966 : 9 : pairs[j].val = NULL;
967 : 9 : pairs[j].vallen = 4;
968 : 9 : pairs[j].isnull = true;
969 : 9 : pairs[j].needfree = false;
970 : 9 : ++j;
971 : 9 : continue;
972 : : }
973 : :
974 : : /*
975 : : * Convert the column value to text
976 : : */
977 [ + - ]: 14 : if (column_info->column_type != column_type)
978 : : {
979 : : bool typIsVarlena;
980 : :
981 : 14 : getTypeOutputInfo(column_type,
982 : : &column_info->typiofunc,
983 : : &typIsVarlena);
984 : 14 : fmgr_info_cxt(column_info->typiofunc, &column_info->proc,
985 : 14 : fcinfo->flinfo->fn_mcxt);
986 : 14 : column_info->column_type = column_type;
987 : : }
988 : :
989 : 14 : value = OutputFunctionCall(&column_info->proc, values[i]);
990 : :
991 : 14 : pairs[j].val = value;
992 : 14 : pairs[j].vallen = hstoreCheckValLen(strlen(value));
993 : 14 : pairs[j].isnull = false;
994 : 14 : pairs[j].needfree = false;
995 : 14 : ++j;
996 : : }
997 : :
998 : 5 : ncolumns = hstoreUniquePairs(pairs, j, &buflen);
999 : :
1000 : 5 : out = hstorePairs(pairs, ncolumns, buflen);
1001 : :
1002 [ + - ]: 5 : ReleaseTupleDesc(tupdesc);
1003 : :
7296 teodor@sigaev.ru 1004 : 5 : PG_RETURN_POINTER(out);
1005 : : }
1006 : :
1007 : :
6175 tgl@sss.pgh.pa.us 1008 : 8 : PG_FUNCTION_INFO_V1(hstore_populate_record);
1009 : : Datum
1010 : 33 : hstore_populate_record(PG_FUNCTION_ARGS)
1011 : : {
6026 bruce@momjian.us 1012 : 33 : Oid argtype = get_fn_expr_argtype(fcinfo->flinfo, 0);
1013 : : HStore *hs;
1014 : : HEntry *entries;
1015 : : char *ptr;
1016 : : HeapTupleHeader rec;
1017 : : Oid tupType;
1018 : : int32 tupTypmod;
1019 : : TupleDesc tupdesc;
1020 : : HeapTupleData tuple;
1021 : : HeapTuple rettuple;
1022 : : RecordIOData *my_extra;
1023 : : int ncolumns;
1024 : : int i;
1025 : : Datum *values;
1026 : : bool *nulls;
1027 : :
6175 tgl@sss.pgh.pa.us 1028 [ - + ]: 33 : if (!type_is_rowtype(argtype))
6175 tgl@sss.pgh.pa.us 1029 [ # # ]:UBC 0 : ereport(ERROR,
1030 : : (errcode(ERRCODE_DATATYPE_MISMATCH),
1031 : : errmsg("first argument must be a rowtype")));
1032 : :
6175 tgl@sss.pgh.pa.us 1033 [ + + ]:CBC 33 : if (PG_ARGISNULL(0))
1034 : : {
1035 [ - + ]: 8 : if (PG_ARGISNULL(1))
6175 tgl@sss.pgh.pa.us 1036 :UBC 0 : PG_RETURN_NULL();
1037 : :
6175 tgl@sss.pgh.pa.us 1038 :CBC 8 : rec = NULL;
1039 : :
1040 : : /*
1041 : : * We have no tuple to look at, so the only source of type info is the
1042 : : * argtype. The lookup_rowtype_tupdesc_domain call below will error
1043 : : * out if we don't have a known composite type oid here.
1044 : : */
1045 : 8 : tupType = argtype;
1046 : 8 : tupTypmod = -1;
1047 : : }
1048 : : else
1049 : : {
1050 : 25 : rec = PG_GETARG_HEAPTUPLEHEADER(0);
1051 : :
1052 [ - + ]: 25 : if (PG_ARGISNULL(1))
6175 tgl@sss.pgh.pa.us 1053 :UBC 0 : PG_RETURN_POINTER(rec);
1054 : :
1055 : : /*
1056 : : * Extract type info from the tuple itself -- this will work even for
1057 : : * anonymous record types.
1058 : : */
6175 tgl@sss.pgh.pa.us 1059 :CBC 25 : tupType = HeapTupleHeaderGetTypeId(rec);
1060 : 25 : tupTypmod = HeapTupleHeaderGetTypMod(rec);
1061 : : }
1062 : :
3265 1063 : 33 : hs = PG_GETARG_HSTORE_P(1);
6175 1064 : 33 : entries = ARRPTR(hs);
1065 : 33 : ptr = STRPTR(hs);
1066 : :
1067 : : /*
1068 : : * if the input hstore is empty, we can only skip the rest if we were
1069 : : * passed in a non-null record, since otherwise there may be issues with
1070 : : * domain nulls.
1071 : : */
1072 : :
1073 [ + + + + ]: 33 : if (HS_COUNT(hs) == 0 && rec)
1074 : 4 : PG_RETURN_POINTER(rec);
1075 : :
1076 : : /*
1077 : : * Lookup the input record's tupdesc. For the moment, we don't worry
1078 : : * about whether it is a domain over composite.
1079 : : */
3227 1080 : 29 : tupdesc = lookup_rowtype_tupdesc_domain(tupType, tupTypmod, false);
6175 1081 : 29 : ncolumns = tupdesc->natts;
1082 : :
1083 [ + + ]: 29 : if (rec)
1084 : : {
1085 : : /* Build a temporary HeapTuple control structure */
1086 : 21 : tuple.t_len = HeapTupleHeaderGetDatumLength(rec);
1087 : 21 : ItemPointerSetInvalid(&(tuple.t_self));
1088 : 21 : tuple.t_tableOid = InvalidOid;
1089 : 21 : tuple.t_data = rec;
1090 : : }
1091 : :
1092 : : /*
1093 : : * We arrange to look up the needed I/O info just once per series of
1094 : : * calls, assuming the record type doesn't change underneath us.
1095 : : */
1096 : 29 : my_extra = (RecordIOData *) fcinfo->flinfo->fn_extra;
1097 [ + + ]: 29 : if (my_extra == NULL ||
1098 [ - + ]: 4 : my_extra->ncolumns != ncolumns)
1099 : : {
1100 : 50 : fcinfo->flinfo->fn_extra =
1101 : 25 : MemoryContextAlloc(fcinfo->flinfo->fn_mcxt,
1102 : : offsetof(RecordIOData, columns) +
4206 1103 : 25 : ncolumns * sizeof(ColumnIOData));
6175 1104 : 25 : my_extra = (RecordIOData *) fcinfo->flinfo->fn_extra;
1105 : 25 : my_extra->record_type = InvalidOid;
1106 : 25 : my_extra->record_typmod = 0;
3227 1107 : 25 : my_extra->domain_info = NULL;
1108 : : }
1109 : :
6175 1110 [ + + ]: 29 : if (my_extra->record_type != tupType ||
1111 [ - + ]: 4 : my_extra->record_typmod != tupTypmod)
1112 : : {
1113 [ + - + - : 1068 : MemSet(my_extra, 0,
+ - + - +
+ ]
1114 : : offsetof(RecordIOData, columns) +
1115 : : ncolumns * sizeof(ColumnIOData));
1116 : 25 : my_extra->record_type = tupType;
1117 : 25 : my_extra->record_typmod = tupTypmod;
1118 : 25 : my_extra->ncolumns = ncolumns;
1119 : : }
1120 : :
10 michael@paquier.xyz 1121 :GNC 29 : values = palloc_array(Datum, ncolumns);
1122 : 29 : nulls = palloc_array(bool, ncolumns);
1123 : :
6175 tgl@sss.pgh.pa.us 1124 [ + + ]:CBC 29 : if (rec)
1125 : : {
1126 : : /* Break down the tuple into fields */
1127 : 21 : heap_deform_tuple(&tuple, tupdesc, values, nulls);
1128 : : }
1129 : : else
1130 : : {
1131 [ + + ]: 46 : for (i = 0; i < ncolumns; ++i)
1132 : : {
1133 : 38 : values[i] = (Datum) 0;
1134 : 38 : nulls[i] = true;
1135 : : }
1136 : : }
1137 : :
1138 [ + + ]: 163 : for (i = 0; i < ncolumns; ++i)
1139 : : {
1140 : 141 : ColumnIOData *column_info = &my_extra->columns[i];
3294 andres@anarazel.de 1141 : 141 : Form_pg_attribute att = TupleDescAttr(tupdesc, i);
1142 : 141 : Oid column_type = att->atttypid;
1143 : : char *value;
1144 : : int idx;
1145 : : int vallen;
1146 : :
1147 : : /* Ignore dropped columns in datatype */
1148 [ - + ]: 141 : if (att->attisdropped)
1149 : : {
6175 tgl@sss.pgh.pa.us 1150 :UBC 0 : nulls[i] = true;
1151 : 0 : continue;
1152 : : }
1153 : :
6175 tgl@sss.pgh.pa.us 1154 :CBC 141 : idx = hstoreFindKey(hs, 0,
3294 andres@anarazel.de 1155 : 141 : NameStr(att->attname),
1156 : 141 : strlen(NameStr(att->attname)));
1157 : :
1158 : : /*
1159 : : * we can't just skip here if the key wasn't found since we might have
1160 : : * a domain to deal with. If we were passed in a non-null record
1161 : : * datum, we assume that the existing values are valid (if they're
1162 : : * not, then it's not our fault), but if we were passed in a null,
1163 : : * then every field which we don't populate needs to be run through
1164 : : * the input function just in case it's a domain type.
1165 : : */
6175 tgl@sss.pgh.pa.us 1166 [ + + + + ]: 141 : if (idx < 0 && rec)
1167 : 79 : continue;
1168 : :
1169 : : /*
1170 : : * Prepare to convert the column value from text
1171 : : */
1172 [ + + ]: 62 : if (column_info->column_type != column_type)
1173 : : {
1174 : 61 : getTypeInputInfo(column_type,
1175 : : &column_info->typiofunc,
1176 : : &column_info->typioparam);
1177 : 61 : fmgr_info_cxt(column_info->typiofunc, &column_info->proc,
1178 : 61 : fcinfo->flinfo->fn_mcxt);
1179 : 61 : column_info->column_type = column_type;
1180 : : }
1181 : :
3934 1182 [ + + + + ]: 62 : if (idx < 0 || HSTORE_VALISNULL(entries, idx))
1183 : : {
1184 : : /*
1185 : : * need InputFunctionCall to happen even for nulls, so that domain
1186 : : * checks are done
1187 : : */
6175 1188 : 36 : values[i] = InputFunctionCall(&column_info->proc, NULL,
1189 : : column_info->typioparam,
1190 : : att->atttypmod);
1191 : 29 : nulls[i] = true;
1192 : : }
1193 : : else
1194 : : {
3934 1195 [ - + ]: 26 : vallen = HSTORE_VALLEN(entries, idx);
6175 1196 : 26 : value = palloc(1 + vallen);
3934 1197 [ + - ]: 26 : memcpy(value, HSTORE_VAL(entries, ptr, idx), vallen);
6175 1198 : 26 : value[vallen] = 0;
1199 : :
1200 : 26 : values[i] = InputFunctionCall(&column_info->proc, value,
1201 : : column_info->typioparam,
1202 : : att->atttypmod);
1203 : 26 : nulls[i] = false;
1204 : : }
1205 : : }
1206 : :
1207 : 22 : rettuple = heap_form_tuple(tupdesc, values, nulls);
1208 : :
1209 : : /*
1210 : : * If the target type is domain over composite, all we know at this point
1211 : : * is that we've made a valid value of the base composite type. Must
1212 : : * check domain constraints before deciding we're done.
1213 : : */
3227 1214 [ - + ]: 22 : if (argtype != tupdesc->tdtypeid)
3227 tgl@sss.pgh.pa.us 1215 :UBC 0 : domain_check(HeapTupleGetDatum(rettuple), false,
1216 : : argtype,
1217 : : &my_extra->domain_info,
1218 : 0 : fcinfo->flinfo->fn_mcxt);
1219 : :
6175 tgl@sss.pgh.pa.us 1220 [ + - ]:CBC 22 : ReleaseTupleDesc(tupdesc);
1221 : :
1222 : 22 : PG_RETURN_DATUM(HeapTupleGetDatum(rettuple));
1223 : : }
1224 : :
1225 : :
1226 : : static char *
7267 bruce@momjian.us 1227 : 510 : cpw(char *dst, char *src, int len)
1228 : : {
1229 : 510 : char *ptr = src;
1230 : :
1231 [ + + ]: 1474 : while (ptr - src < len)
1232 : : {
1233 [ + + - + ]: 964 : if (*ptr == '"' || *ptr == '\\')
1234 : 3 : *dst++ = '\\';
7296 teodor@sigaev.ru 1235 : 964 : *dst++ = *ptr++;
1236 : : }
1237 : 510 : return dst;
1238 : : }
1239 : :
1240 : 17 : PG_FUNCTION_INFO_V1(hstore_out);
1241 : : Datum
7267 bruce@momjian.us 1242 : 148 : hstore_out(PG_FUNCTION_ARGS)
1243 : : {
3265 tgl@sss.pgh.pa.us 1244 : 148 : HStore *in = PG_GETARG_HSTORE_P(0);
1245 : : Size buflen;
1246 : : int i;
6026 bruce@momjian.us 1247 : 148 : int count = HS_COUNT(in);
1248 : : char *out,
1249 : : *ptr;
7267 1250 : 148 : char *base = STRPTR(in);
1251 : 148 : HEntry *entries = ARRPTR(in);
1252 : :
6175 tgl@sss.pgh.pa.us 1253 [ + + ]: 148 : if (count == 0)
4616 peter_e@gmx.net 1254 : 13 : PG_RETURN_CSTRING(pstrdup(""));
1255 : :
6175 tgl@sss.pgh.pa.us 1256 : 135 : buflen = 0;
1257 : :
1258 : : /*
1259 : : * this loop overestimates due to pessimistic assumptions about escaping,
1260 : : * so very large hstore values can't be output. this could be fixed, but
1261 : : * many other data types probably have the same issue. This replaced code
1262 : : * that used the original varlena size for calculations, which was wrong
1263 : : * in some subtle ways.
1264 : : */
1265 : :
1266 [ + + ]: 408 : for (i = 0; i < count; i++)
1267 : : {
1268 : : /* include "", => and comma-space */
9 michael@paquier.xyz 1269 :GNC 273 : buflen = add_size(buflen, 6);
1270 [ + + ]: 273 : buflen = add_size(buflen, mul_size(2, HSTORE_KEYLEN(entries, i)));
1271 : :
1272 : : /* include "" only if nonnull */
1273 [ + + ]: 273 : if (HSTORE_VALISNULL(entries, i))
1274 : 36 : buflen = add_size(buflen, 4);
1275 : : else
1276 : : {
1277 : 237 : buflen = add_size(buflen, 2);
1278 [ - + ]: 237 : buflen = add_size(buflen, mul_size(2, HSTORE_VALLEN(entries, i)));
1279 : : }
1280 : : }
1281 : :
7267 bruce@momjian.us 1282 :CBC 135 : out = ptr = palloc(buflen);
1283 : :
6175 tgl@sss.pgh.pa.us 1284 [ + + ]: 408 : for (i = 0; i < count; i++)
1285 : : {
7267 bruce@momjian.us 1286 : 273 : *ptr++ = '"';
3934 tgl@sss.pgh.pa.us 1287 [ + + + + ]: 273 : ptr = cpw(ptr, HSTORE_KEY(entries, base, i), HSTORE_KEYLEN(entries, i));
7267 bruce@momjian.us 1288 : 273 : *ptr++ = '"';
1289 : 273 : *ptr++ = '=';
1290 : 273 : *ptr++ = '>';
3934 tgl@sss.pgh.pa.us 1291 [ + + ]: 273 : if (HSTORE_VALISNULL(entries, i))
1292 : : {
7267 bruce@momjian.us 1293 : 36 : *ptr++ = 'N';
1294 : 36 : *ptr++ = 'U';
1295 : 36 : *ptr++ = 'L';
1296 : 36 : *ptr++ = 'L';
1297 : : }
1298 : : else
1299 : : {
1300 : 237 : *ptr++ = '"';
3934 tgl@sss.pgh.pa.us 1301 [ - + + - ]: 237 : ptr = cpw(ptr, HSTORE_VAL(entries, base, i), HSTORE_VALLEN(entries, i));
7267 bruce@momjian.us 1302 : 237 : *ptr++ = '"';
1303 : : }
1304 : :
6175 tgl@sss.pgh.pa.us 1305 [ + + ]: 273 : if (i + 1 != count)
1306 : : {
7267 bruce@momjian.us 1307 : 138 : *ptr++ = ',';
1308 : 138 : *ptr++ = ' ';
1309 : : }
1310 : : }
1311 : 135 : *ptr = '\0';
1312 : :
7296 teodor@sigaev.ru 1313 : 135 : PG_RETURN_CSTRING(out);
1314 : : }
1315 : :
1316 : :
6175 tgl@sss.pgh.pa.us 1317 : 7 : PG_FUNCTION_INFO_V1(hstore_send);
1318 : : Datum
6175 tgl@sss.pgh.pa.us 1319 :UBC 0 : hstore_send(PG_FUNCTION_ARGS)
1320 : : {
3265 1321 : 0 : HStore *in = PG_GETARG_HSTORE_P(0);
1322 : : int i;
6026 bruce@momjian.us 1323 : 0 : int count = HS_COUNT(in);
6175 tgl@sss.pgh.pa.us 1324 : 0 : char *base = STRPTR(in);
1325 : 0 : HEntry *entries = ARRPTR(in);
1326 : : StringInfoData buf;
1327 : :
1328 : 0 : pq_begintypsend(&buf);
1329 : :
3242 andres@anarazel.de 1330 : 0 : pq_sendint32(&buf, count);
1331 : :
6175 tgl@sss.pgh.pa.us 1332 [ # # ]: 0 : for (i = 0; i < count; i++)
1333 : : {
3934 1334 [ # # ]: 0 : int32 keylen = HSTORE_KEYLEN(entries, i);
1335 : :
3242 andres@anarazel.de 1336 : 0 : pq_sendint32(&buf, keylen);
3934 tgl@sss.pgh.pa.us 1337 [ # # ]: 0 : pq_sendtext(&buf, HSTORE_KEY(entries, base, i), keylen);
1338 [ # # ]: 0 : if (HSTORE_VALISNULL(entries, i))
1339 : : {
3242 andres@anarazel.de 1340 : 0 : pq_sendint32(&buf, -1);
1341 : : }
1342 : : else
1343 : : {
3934 tgl@sss.pgh.pa.us 1344 [ # # ]: 0 : int32 vallen = HSTORE_VALLEN(entries, i);
1345 : :
3242 andres@anarazel.de 1346 : 0 : pq_sendint32(&buf, vallen);
3934 tgl@sss.pgh.pa.us 1347 [ # # ]: 0 : pq_sendtext(&buf, HSTORE_VAL(entries, base, i), vallen);
1348 : : }
1349 : : }
1350 : :
6175 1351 : 0 : PG_RETURN_BYTEA_P(pq_endtypsend(&buf));
1352 : : }
1353 : :
1354 : :
1355 : : /*
1356 : : * hstore_to_json_loose
1357 : : *
1358 : : * This is a heuristic conversion to json which treats
1359 : : * 't' and 'f' as booleans and strings that look like numbers as numbers,
1360 : : * as long as they don't start with a leading zero followed by another digit
1361 : : * (think zip codes or phone numbers starting with 0).
1362 : : */
4918 andrew@dunslane.net 1363 :CBC 8 : PG_FUNCTION_INFO_V1(hstore_to_json_loose);
1364 : : Datum
1365 : 3 : hstore_to_json_loose(PG_FUNCTION_ARGS)
1366 : : {
3265 tgl@sss.pgh.pa.us 1367 : 3 : HStore *in = PG_GETARG_HSTORE_P(0);
1368 : : int i;
4918 andrew@dunslane.net 1369 : 3 : int count = HS_COUNT(in);
1370 : 3 : char *base = STRPTR(in);
1371 : 3 : HEntry *entries = ARRPTR(in);
1372 : : StringInfoData dst;
1373 : :
1374 [ - + ]: 3 : if (count == 0)
4496 bruce@momjian.us 1375 :UBC 0 : PG_RETURN_TEXT_P(cstring_to_text_with_len("{}", 2));
1376 : :
4570 heikki.linnakangas@i 1377 :CBC 3 : initStringInfo(&dst);
1378 : :
1379 : 3 : appendStringInfoChar(&dst, '{');
1380 : :
4918 andrew@dunslane.net 1381 [ + + ]: 25 : for (i = 0; i < count; i++)
1382 : : {
761 drowley@postgresql.o 1383 : 66 : escape_json_with_len(&dst,
1384 [ + + ]: 22 : HSTORE_KEY(entries, base, i),
1385 [ + + ]: 22 : HSTORE_KEYLEN(entries, i));
4570 heikki.linnakangas@i 1386 : 22 : appendStringInfoString(&dst, ": ");
3934 tgl@sss.pgh.pa.us 1387 [ + + ]: 22 : if (HSTORE_VALISNULL(entries, i))
4570 heikki.linnakangas@i 1388 : 2 : appendStringInfoString(&dst, "null");
1389 : : /* guess that values of 't' or 'f' are booleans */
3934 tgl@sss.pgh.pa.us 1390 [ - + - - : 20 : else if (HSTORE_VALLEN(entries, i) == 1 &&
+ + ]
1391 [ + - + + ]: 6 : *(HSTORE_VAL(entries, base, i)) == 't')
4570 heikki.linnakangas@i 1392 : 2 : appendStringInfoString(&dst, "true");
3934 tgl@sss.pgh.pa.us 1393 [ - + - - : 18 : else if (HSTORE_VALLEN(entries, i) == 1 &&
+ + ]
1394 [ + - + + ]: 4 : *(HSTORE_VAL(entries, base, i)) == 'f')
4570 heikki.linnakangas@i 1395 : 1 : appendStringInfoString(&dst, "false");
1396 : : else
1397 : : {
761 drowley@postgresql.o 1398 [ + - ]: 17 : char *str = HSTORE_VAL(entries, base, i);
1399 [ - + ]: 17 : int len = HSTORE_VALLEN(entries, i);
1400 : :
1401 [ + + ]: 17 : if (IsValidJsonNumber(str, len))
1402 : 12 : appendBinaryStringInfo(&dst, str, len);
1403 : : else
1404 : 5 : escape_json_with_len(&dst, str, len);
1405 : : }
1406 : :
4918 andrew@dunslane.net 1407 [ + + ]: 22 : if (i + 1 != count)
4570 heikki.linnakangas@i 1408 : 19 : appendStringInfoString(&dst, ", ");
1409 : : }
1410 : 3 : appendStringInfoChar(&dst, '}');
1411 : :
1451 drowley@postgresql.o 1412 : 3 : PG_RETURN_TEXT_P(cstring_to_text_with_len(dst.data, dst.len));
1413 : : }
1414 : :
4918 andrew@dunslane.net 1415 : 8 : PG_FUNCTION_INFO_V1(hstore_to_json);
1416 : : Datum
1417 : 4 : hstore_to_json(PG_FUNCTION_ARGS)
1418 : : {
3265 tgl@sss.pgh.pa.us 1419 : 4 : HStore *in = PG_GETARG_HSTORE_P(0);
1420 : : int i;
4918 andrew@dunslane.net 1421 : 4 : int count = HS_COUNT(in);
1422 : 4 : char *base = STRPTR(in);
1423 : 4 : HEntry *entries = ARRPTR(in);
1424 : : StringInfoData dst;
1425 : :
1426 [ - + ]: 4 : if (count == 0)
4496 bruce@momjian.us 1427 :UBC 0 : PG_RETURN_TEXT_P(cstring_to_text_with_len("{}", 2));
1428 : :
4570 heikki.linnakangas@i 1429 :CBC 4 : initStringInfo(&dst);
1430 : :
1431 : 4 : appendStringInfoChar(&dst, '{');
1432 : :
4918 andrew@dunslane.net 1433 [ + + ]: 32 : for (i = 0; i < count; i++)
1434 : : {
761 drowley@postgresql.o 1435 : 84 : escape_json_with_len(&dst,
1436 [ + + ]: 28 : HSTORE_KEY(entries, base, i),
1437 [ + + ]: 28 : HSTORE_KEYLEN(entries, i));
4570 heikki.linnakangas@i 1438 : 28 : appendStringInfoString(&dst, ": ");
3934 tgl@sss.pgh.pa.us 1439 [ + + ]: 28 : if (HSTORE_VALISNULL(entries, i))
4570 heikki.linnakangas@i 1440 : 3 : appendStringInfoString(&dst, "null");
1441 : : else
1442 : : {
761 drowley@postgresql.o 1443 : 75 : escape_json_with_len(&dst,
1444 [ + - ]: 25 : HSTORE_VAL(entries, base, i),
1445 [ - + ]: 25 : HSTORE_VALLEN(entries, i));
1446 : : }
1447 : :
4918 andrew@dunslane.net 1448 [ + + ]: 28 : if (i + 1 != count)
4570 heikki.linnakangas@i 1449 : 24 : appendStringInfoString(&dst, ", ");
1450 : : }
1451 : 4 : appendStringInfoChar(&dst, '}');
1452 : :
1451 drowley@postgresql.o 1453 : 4 : PG_RETURN_TEXT_P(cstring_to_text_with_len(dst.data, dst.len));
1454 : : }
1455 : :
4540 andrew@dunslane.net 1456 : 8 : PG_FUNCTION_INFO_V1(hstore_to_jsonb);
1457 : : Datum
1458 : 2 : hstore_to_jsonb(PG_FUNCTION_ARGS)
1459 : : {
3265 tgl@sss.pgh.pa.us 1460 : 2 : HStore *in = PG_GETARG_HSTORE_P(0);
1461 : : int i;
4540 andrew@dunslane.net 1462 : 2 : int count = HS_COUNT(in);
1463 : 2 : char *base = STRPTR(in);
1464 : 2 : HEntry *entries = ARRPTR(in);
263 tgl@sss.pgh.pa.us 1465 : 2 : JsonbInState state = {0};
1466 : :
1467 : 2 : pushJsonbValue(&state, WJB_BEGIN_OBJECT, NULL);
1468 : :
4540 andrew@dunslane.net 1469 [ + + ]: 16 : for (i = 0; i < count; i++)
1470 : : {
1471 : : JsonbValue key,
1472 : : val;
1473 : :
1474 : 14 : key.type = jbvString;
3934 tgl@sss.pgh.pa.us 1475 [ + + ]: 14 : key.val.string.len = HSTORE_KEYLEN(entries, i);
1476 [ + + ]: 14 : key.val.string.val = HSTORE_KEY(entries, base, i);
1477 : :
263 1478 : 14 : pushJsonbValue(&state, WJB_KEY, &key);
1479 : :
3934 1480 [ + + ]: 14 : if (HSTORE_VALISNULL(entries, i))
1481 : : {
4540 andrew@dunslane.net 1482 : 2 : val.type = jbvNull;
1483 : : }
1484 : : else
1485 : : {
1486 : 12 : val.type = jbvString;
3934 tgl@sss.pgh.pa.us 1487 [ - + ]: 12 : val.val.string.len = HSTORE_VALLEN(entries, i);
1488 [ + - ]: 12 : val.val.string.val = HSTORE_VAL(entries, base, i);
1489 : : }
263 1490 : 14 : pushJsonbValue(&state, WJB_VALUE, &val);
1491 : : }
1492 : :
1493 : 2 : pushJsonbValue(&state, WJB_END_OBJECT, NULL);
1494 : :
1495 : 2 : PG_RETURN_POINTER(JsonbValueToJsonb(state.result));
1496 : : }
1497 : :
4540 andrew@dunslane.net 1498 : 8 : PG_FUNCTION_INFO_V1(hstore_to_jsonb_loose);
1499 : : Datum
1500 : 1 : hstore_to_jsonb_loose(PG_FUNCTION_ARGS)
1501 : : {
3265 tgl@sss.pgh.pa.us 1502 : 1 : HStore *in = PG_GETARG_HSTORE_P(0);
1503 : : int i;
4540 andrew@dunslane.net 1504 : 1 : int count = HS_COUNT(in);
1505 : 1 : char *base = STRPTR(in);
1506 : 1 : HEntry *entries = ARRPTR(in);
263 tgl@sss.pgh.pa.us 1507 : 1 : JsonbInState state = {0};
1508 : : StringInfoData tmp;
1509 : :
4540 andrew@dunslane.net 1510 : 1 : initStringInfo(&tmp);
1511 : :
263 tgl@sss.pgh.pa.us 1512 : 1 : pushJsonbValue(&state, WJB_BEGIN_OBJECT, NULL);
1513 : :
4540 andrew@dunslane.net 1514 [ + + ]: 9 : for (i = 0; i < count; i++)
1515 : : {
1516 : : JsonbValue key,
1517 : : val;
1518 : :
1519 : 8 : key.type = jbvString;
3934 tgl@sss.pgh.pa.us 1520 [ + + ]: 8 : key.val.string.len = HSTORE_KEYLEN(entries, i);
1521 [ + + ]: 8 : key.val.string.val = HSTORE_KEY(entries, base, i);
1522 : :
263 1523 : 8 : pushJsonbValue(&state, WJB_KEY, &key);
1524 : :
3934 1525 [ + + ]: 8 : if (HSTORE_VALISNULL(entries, i))
1526 : : {
4540 andrew@dunslane.net 1527 : 1 : val.type = jbvNull;
1528 : : }
1529 : : /* guess that values of 't' or 'f' are booleans */
3934 tgl@sss.pgh.pa.us 1530 [ - + - - : 7 : else if (HSTORE_VALLEN(entries, i) == 1 &&
+ + ]
1531 [ + - + + ]: 2 : *(HSTORE_VAL(entries, base, i)) == 't')
1532 : : {
4540 andrew@dunslane.net 1533 : 1 : val.type = jbvBool;
4530 tgl@sss.pgh.pa.us 1534 : 1 : val.val.boolean = true;
1535 : : }
3934 1536 [ - + - - : 6 : else if (HSTORE_VALLEN(entries, i) == 1 &&
+ + ]
1537 [ + - - + ]: 1 : *(HSTORE_VAL(entries, base, i)) == 'f')
1538 : : {
4540 andrew@dunslane.net 1539 :UBC 0 : val.type = jbvBool;
4530 tgl@sss.pgh.pa.us 1540 : 0 : val.val.boolean = false;
1541 : : }
1542 : : else
1543 : : {
4540 andrew@dunslane.net 1544 :CBC 6 : resetStringInfo(&tmp);
3934 tgl@sss.pgh.pa.us 1545 [ + - ]: 6 : appendBinaryStringInfo(&tmp, HSTORE_VAL(entries, base, i),
1546 [ - + ]: 6 : HSTORE_VALLEN(entries, i));
3858 1547 [ + + ]: 6 : if (IsValidJsonNumber(tmp.data, tmp.len))
1548 : : {
1549 : : Datum numd;
1550 : :
4540 andrew@dunslane.net 1551 : 4 : val.type = jbvNumeric;
3039 tgl@sss.pgh.pa.us 1552 : 4 : numd = DirectFunctionCall3(numeric_in,
1553 : : CStringGetDatum(tmp.data),
1554 : : ObjectIdGetDatum(InvalidOid),
1555 : : Int32GetDatum(-1));
1556 : 4 : val.val.numeric = DatumGetNumeric(numd);
1557 : : }
1558 : : else
1559 : : {
4540 andrew@dunslane.net 1560 : 2 : val.type = jbvString;
3934 tgl@sss.pgh.pa.us 1561 [ - + ]: 2 : val.val.string.len = HSTORE_VALLEN(entries, i);
1562 [ + - ]: 2 : val.val.string.val = HSTORE_VAL(entries, base, i);
1563 : : }
1564 : : }
263 1565 : 8 : pushJsonbValue(&state, WJB_VALUE, &val);
1566 : : }
1567 : :
1568 : 1 : pushJsonbValue(&state, WJB_END_OBJECT, NULL);
1569 : :
1570 : 1 : PG_RETURN_POINTER(JsonbValueToJsonb(state.result));
1571 : : }
|