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