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