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