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