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