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