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