Age Owner Branch data TLA Line data Source code
1 : : /*
2 : : * contrib/hstore/hstore_op.c
3 : : */
4 : : #include "postgres.h"
5 : :
6 : : #include "access/htup_details.h"
7 : : #include "catalog/pg_type.h"
8 : : #include "common/hashfn.h"
9 : : #include "funcapi.h"
10 : : #include "hstore.h"
11 : : #include "utils/builtins.h"
12 : : #include "utils/memutils.h"
13 : :
14 : : /* old names for C functions */
5161 bruce@momjian.us 15 :UBC 0 : HSTORE_POLLUTE(hstore_fetchval, fetchval);
16 : 0 : HSTORE_POLLUTE(hstore_exists, exists);
17 : 0 : HSTORE_POLLUTE(hstore_defined, defined);
18 : 0 : HSTORE_POLLUTE(hstore_delete, delete);
19 : 0 : HSTORE_POLLUTE(hstore_concat, hs_concat);
20 : 0 : HSTORE_POLLUTE(hstore_contains, hs_contains);
21 : 0 : HSTORE_POLLUTE(hstore_contained, hs_contained);
22 : 0 : HSTORE_POLLUTE(hstore_akeys, akeys);
23 : 0 : HSTORE_POLLUTE(hstore_avals, avals);
24 : 0 : HSTORE_POLLUTE(hstore_skeys, skeys);
25 : 0 : HSTORE_POLLUTE(hstore_svals, svals);
26 : 0 : HSTORE_POLLUTE(hstore_each, each);
27 : :
28 : :
29 : : /*
30 : : * We're often finding a sequence of keys in ascending order. The
31 : : * "lowbound" parameter is used to cache lower bounds of searches
32 : : * between calls, based on this assumption. Pass NULL for it for
33 : : * one-off or unordered searches.
34 : : */
35 : : int
5161 bruce@momjian.us 36 :CBC 9456 : hstoreFindKey(HStore *hs, int *lowbound, char *key, int keylen)
37 : : {
5310 tgl@sss.pgh.pa.us 38 : 9456 : HEntry *entries = ARRPTR(hs);
5161 bruce@momjian.us 39 [ + + ]: 9456 : int stopLow = lowbound ? *lowbound : 0;
40 : 9456 : int stopHigh = HS_COUNT(hs);
41 : : int stopMiddle;
6402 42 : 9456 : char *base = STRPTR(hs);
43 : :
5310 tgl@sss.pgh.pa.us 44 [ + + ]: 24849 : while (stopLow < stopHigh)
45 : : {
46 : : int difference;
47 : :
48 : 18556 : stopMiddle = stopLow + (stopHigh - stopLow) / 2;
49 : :
3069 50 [ + + + + ]: 18556 : if (HSTORE_KEYLEN(entries, stopMiddle) == keylen)
51 [ + + ]: 7413 : difference = memcmp(HSTORE_KEY(entries, base, stopMiddle), key, keylen);
52 : : else
53 [ + + + + ]: 11143 : difference = (HSTORE_KEYLEN(entries, stopMiddle) > keylen) ? 1 : -1;
54 : :
6431 teodor@sigaev.ru 55 [ + + ]: 18556 : if (difference == 0)
56 : : {
5310 tgl@sss.pgh.pa.us 57 [ + + ]: 3163 : if (lowbound)
58 : 2519 : *lowbound = stopMiddle + 1;
59 : 3163 : return stopMiddle;
60 : : }
6431 teodor@sigaev.ru 61 [ + + ]: 15393 : else if (difference < 0)
5310 tgl@sss.pgh.pa.us 62 : 8228 : stopLow = stopMiddle + 1;
63 : : else
64 : 7165 : stopHigh = stopMiddle;
65 : : }
66 : :
67 [ + + ]: 6293 : if (lowbound)
68 : 5329 : *lowbound = stopLow;
69 : 6293 : return -1;
70 : : }
71 : :
72 : : Pairs *
73 : 2842 : hstoreArrayToPairs(ArrayType *a, int *npairs)
74 : : {
75 : : Datum *key_datums;
76 : : bool *key_nulls;
77 : : int key_count;
78 : : Pairs *key_pairs;
79 : : int bufsiz;
80 : : int i,
81 : : j;
82 : :
653 peter@eisentraut.org 83 : 2842 : deconstruct_array_builtin(a, TEXTOID, &key_datums, &key_nulls, &key_count);
84 : :
5310 tgl@sss.pgh.pa.us 85 [ + + ]: 2842 : if (key_count == 0)
86 : : {
87 : 5 : *npairs = 0;
88 : 5 : return NULL;
89 : : }
90 : :
91 : : /*
92 : : * A text array uses at least eight bytes per element, so any overflow in
93 : : * "key_count * sizeof(Pairs)" is small enough for palloc() to catch.
94 : : * However, credible improvements to the array format could invalidate
95 : : * that assumption. Therefore, use an explicit check rather than relying
96 : : * on palloc() to complain.
97 : : */
3709 noah@leadboat.com 98 [ - + ]: 2837 : if (key_count > MaxAllocSize / sizeof(Pairs))
3709 noah@leadboat.com 99 [ # # ]:UBC 0 : ereport(ERROR,
100 : : (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
101 : : errmsg("number of pairs (%d) exceeds the maximum allowed (%d)",
102 : : key_count, (int) (MaxAllocSize / sizeof(Pairs)))));
103 : :
5310 tgl@sss.pgh.pa.us 104 :CBC 2837 : key_pairs = palloc(sizeof(Pairs) * key_count);
105 : :
106 [ + + ]: 8516 : for (i = 0, j = 0; i < key_count; i++)
107 : : {
108 [ + - ]: 5679 : if (!key_nulls[i])
109 : : {
110 : 5679 : key_pairs[j].key = VARDATA(key_datums[i]);
111 : 5679 : key_pairs[j].keylen = VARSIZE(key_datums[i]) - VARHDRSZ;
112 : 5679 : key_pairs[j].val = NULL;
113 : 5679 : key_pairs[j].vallen = 0;
114 : 5679 : key_pairs[j].needfree = 0;
115 : 5679 : key_pairs[j].isnull = 1;
116 : 5679 : j++;
117 : : }
118 : : }
119 : :
120 : 2837 : *npairs = hstoreUniquePairs(key_pairs, j, &bufsiz);
121 : :
122 : 2837 : return key_pairs;
123 : : }
124 : :
125 : :
126 : 8 : PG_FUNCTION_INFO_V1(hstore_fetchval);
127 : : Datum
128 : 6 : hstore_fetchval(PG_FUNCTION_ARGS)
129 : : {
2400 130 : 6 : HStore *hs = PG_GETARG_HSTORE_P(0);
5310 131 : 6 : text *key = PG_GETARG_TEXT_PP(1);
132 : 6 : HEntry *entries = ARRPTR(hs);
133 : : text *out;
5161 bruce@momjian.us 134 :UBC 0 : int idx = hstoreFindKey(hs, NULL,
5310 tgl@sss.pgh.pa.us 135 [ - + - - :CBC 12 : VARDATA_ANY(key), VARSIZE_ANY_EXHDR(key));
- - - - -
+ - + ]
136 : :
3069 137 [ + + + + ]: 6 : if (idx < 0 || HSTORE_VALISNULL(entries, idx))
6431 teodor@sigaev.ru 138 : 2 : PG_RETURN_NULL();
139 : :
3069 tgl@sss.pgh.pa.us 140 [ + - ]: 4 : out = cstring_to_text_with_len(HSTORE_VAL(entries, STRPTR(hs), idx),
141 [ - + ]: 4 : HSTORE_VALLEN(entries, idx));
142 : :
5864 143 : 4 : PG_RETURN_TEXT_P(out);
144 : : }
145 : :
146 : :
5310 147 : 16 : PG_FUNCTION_INFO_V1(hstore_exists);
148 : : Datum
149 : 1440 : hstore_exists(PG_FUNCTION_ARGS)
150 : : {
2400 151 : 1440 : HStore *hs = PG_GETARG_HSTORE_P(0);
5310 152 : 1440 : text *key = PG_GETARG_TEXT_PP(1);
5161 bruce@momjian.us 153 :UBC 0 : int idx = hstoreFindKey(hs, NULL,
5310 tgl@sss.pgh.pa.us 154 [ - + - - :CBC 2880 : VARDATA_ANY(key), VARSIZE_ANY_EXHDR(key));
- - - - -
+ - + ]
155 : :
156 : 1440 : PG_RETURN_BOOL(idx >= 0);
157 : : }
158 : :
159 : :
160 : 8 : PG_FUNCTION_INFO_V1(hstore_exists_any);
161 : : Datum
162 : 1727 : hstore_exists_any(PG_FUNCTION_ARGS)
163 : : {
2400 164 : 1727 : HStore *hs = PG_GETARG_HSTORE_P(0);
5310 165 : 1727 : ArrayType *keys = PG_GETARG_ARRAYTYPE_P(1);
166 : : int nkeys;
5161 bruce@momjian.us 167 : 1727 : Pairs *key_pairs = hstoreArrayToPairs(keys, &nkeys);
168 : : int i;
169 : 1727 : int lowbound = 0;
170 : 1727 : bool res = false;
171 : :
172 : : /*
173 : : * we exploit the fact that the pairs list is already sorted into strictly
174 : : * increasing order to narrow the hstoreFindKey search; each search can
175 : : * start one entry past the previous "found" entry, or at the lower bound
176 : : * of the last search.
177 : : */
4844 tgl@sss.pgh.pa.us 178 [ + + ]: 3580 : for (i = 0; i < nkeys; i++)
179 : : {
5161 bruce@momjian.us 180 : 2867 : int idx = hstoreFindKey(hs, &lowbound,
2489 tgl@sss.pgh.pa.us 181 : 2867 : key_pairs[i].key, key_pairs[i].keylen);
182 : :
5310 183 [ + + ]: 2867 : if (idx >= 0)
184 : : {
185 : 1014 : res = true;
4844 186 : 1014 : break;
187 : : }
188 : : }
189 : :
5310 190 : 1727 : PG_RETURN_BOOL(res);
191 : : }
192 : :
193 : :
194 : 8 : PG_FUNCTION_INFO_V1(hstore_exists_all);
195 : : Datum
196 : 1097 : hstore_exists_all(PG_FUNCTION_ARGS)
197 : : {
2400 198 : 1097 : HStore *hs = PG_GETARG_HSTORE_P(0);
5310 199 : 1097 : ArrayType *keys = PG_GETARG_ARRAYTYPE_P(1);
200 : : int nkeys;
5161 bruce@momjian.us 201 : 1097 : Pairs *key_pairs = hstoreArrayToPairs(keys, &nkeys);
202 : : int i;
203 : 1097 : int lowbound = 0;
4844 tgl@sss.pgh.pa.us 204 : 1097 : bool res = true;
205 : :
206 : : /*
207 : : * we exploit the fact that the pairs list is already sorted into strictly
208 : : * increasing order to narrow the hstoreFindKey search; each search can
209 : : * start one entry past the previous "found" entry, or at the lower bound
210 : : * of the last search.
211 : : */
212 [ + + ]: 1507 : for (i = 0; i < nkeys; i++)
213 : : {
5161 bruce@momjian.us 214 : 1378 : int idx = hstoreFindKey(hs, &lowbound,
2489 tgl@sss.pgh.pa.us 215 : 1378 : key_pairs[i].key, key_pairs[i].keylen);
216 : :
5310 217 [ + + ]: 1378 : if (idx < 0)
218 : : {
219 : 968 : res = false;
4844 220 : 968 : break;
221 : : }
222 : : }
223 : :
5310 224 : 1097 : PG_RETURN_BOOL(res);
225 : : }
226 : :
227 : :
228 : 15 : PG_FUNCTION_INFO_V1(hstore_defined);
229 : : Datum
230 : 4 : hstore_defined(PG_FUNCTION_ARGS)
231 : : {
2400 232 : 4 : HStore *hs = PG_GETARG_HSTORE_P(0);
5310 233 : 4 : text *key = PG_GETARG_TEXT_PP(1);
234 : 4 : HEntry *entries = ARRPTR(hs);
5161 bruce@momjian.us 235 :UBC 0 : int idx = hstoreFindKey(hs, NULL,
5310 tgl@sss.pgh.pa.us 236 [ - + - - :CBC 8 : VARDATA_ANY(key), VARSIZE_ANY_EXHDR(key));
- - - - -
+ - + ]
3069 237 [ + + + + ]: 4 : bool res = (idx >= 0 && !HSTORE_VALISNULL(entries, idx));
238 : :
6431 teodor@sigaev.ru 239 : 4 : PG_RETURN_BOOL(res);
240 : : }
241 : :
242 : :
5310 tgl@sss.pgh.pa.us 243 : 8 : PG_FUNCTION_INFO_V1(hstore_delete);
244 : : Datum
245 : 11 : hstore_delete(PG_FUNCTION_ARGS)
246 : : {
2400 247 : 11 : HStore *hs = PG_GETARG_HSTORE_P(0);
5310 248 : 11 : text *key = PG_GETARG_TEXT_PP(1);
5161 bruce@momjian.us 249 [ - + ]: 11 : char *keyptr = VARDATA_ANY(key);
250 [ - + - - : 11 : int keylen = VARSIZE_ANY_EXHDR(key);
- - - - -
+ ]
6255 tgl@sss.pgh.pa.us 251 : 11 : HStore *out = palloc(VARSIZE(hs));
252 : : char *bufs,
253 : : *bufd,
254 : : *ptrd;
255 : : HEntry *es,
256 : : *ed;
257 : : int i;
5161 bruce@momjian.us 258 : 11 : int count = HS_COUNT(hs);
259 : 11 : int outcount = 0;
260 : :
6255 tgl@sss.pgh.pa.us 261 : 11 : SET_VARSIZE(out, VARSIZE(hs));
5161 bruce@momjian.us 262 : 11 : HS_SETCOUNT(out, count); /* temporary! */
263 : :
5310 tgl@sss.pgh.pa.us 264 : 11 : bufs = STRPTR(hs);
6402 bruce@momjian.us 265 : 11 : es = ARRPTR(hs);
5310 tgl@sss.pgh.pa.us 266 : 11 : bufd = ptrd = STRPTR(out);
6402 bruce@momjian.us 267 : 11 : ed = ARRPTR(out);
268 : :
5310 tgl@sss.pgh.pa.us 269 [ + + ]: 44 : for (i = 0; i < count; ++i)
270 : : {
3069 271 [ + + ]: 33 : int len = HSTORE_KEYLEN(es, i);
272 [ + + ]: 33 : char *ptrs = HSTORE_KEY(es, bufs, i);
273 : :
4863 rhaas@postgresql.org 274 [ + - + + ]: 33 : if (!(len == keylen && memcmp(ptrs, keyptr, keylen) == 0))
275 : : {
3069 tgl@sss.pgh.pa.us 276 [ - + ]: 24 : int vallen = HSTORE_VALLEN(es, i);
277 : :
278 : 24 : HS_COPYITEM(ed, bufd, ptrd, ptrs, len, vallen,
279 : : HSTORE_VALISNULL(es, i));
5310 280 : 24 : ++outcount;
281 : : }
282 : : }
283 : :
5161 bruce@momjian.us 284 [ + - + + ]: 11 : HS_FINALIZE(out, outcount, bufd, ptrd);
285 : :
5310 tgl@sss.pgh.pa.us 286 : 11 : PG_RETURN_POINTER(out);
287 : : }
288 : :
289 : :
290 : 8 : PG_FUNCTION_INFO_V1(hstore_delete_array);
291 : : Datum
292 : 12 : hstore_delete_array(PG_FUNCTION_ARGS)
293 : : {
2400 294 : 12 : HStore *hs = PG_GETARG_HSTORE_P(0);
5310 295 : 12 : HStore *out = palloc(VARSIZE(hs));
5161 bruce@momjian.us 296 : 12 : int hs_count = HS_COUNT(hs);
297 : : char *ps,
298 : : *bufd,
299 : : *pd;
300 : : HEntry *es,
301 : : *ed;
302 : : int i,
303 : : j;
304 : 12 : int outcount = 0;
5310 tgl@sss.pgh.pa.us 305 : 12 : ArrayType *key_array = PG_GETARG_ARRAYTYPE_P(1);
306 : : int nkeys;
5161 bruce@momjian.us 307 : 12 : Pairs *key_pairs = hstoreArrayToPairs(key_array, &nkeys);
308 : :
5310 tgl@sss.pgh.pa.us 309 : 12 : SET_VARSIZE(out, VARSIZE(hs));
5161 bruce@momjian.us 310 : 12 : HS_SETCOUNT(out, hs_count); /* temporary! */
311 : :
5310 tgl@sss.pgh.pa.us 312 : 12 : ps = STRPTR(hs);
313 : 12 : es = ARRPTR(hs);
314 : 12 : bufd = pd = STRPTR(out);
315 : 12 : ed = ARRPTR(out);
316 : :
317 [ + + ]: 12 : if (nkeys == 0)
318 : : {
319 : : /* return a copy of the input, unchanged */
320 : 3 : memcpy(out, hs, VARSIZE(hs));
321 [ + - ]: 3 : HS_FIXSIZE(out, hs_count);
322 : 3 : HS_SETCOUNT(out, hs_count);
323 : 3 : PG_RETURN_POINTER(out);
324 : : }
325 : :
326 : : /*
327 : : * this is in effect a merge between hs and key_pairs, both of which are
328 : : * already sorted by (keylen,key); we take keys from hs only
329 : : */
330 : :
5161 bruce@momjian.us 331 [ + + ]: 36 : for (i = j = 0; i < hs_count;)
332 : : {
333 : : int difference;
334 : :
5310 tgl@sss.pgh.pa.us 335 [ - + ]: 27 : if (j >= nkeys)
5310 tgl@sss.pgh.pa.us 336 :UBC 0 : difference = -1;
337 : : else
338 : : {
3069 tgl@sss.pgh.pa.us 339 [ + + ]:CBC 27 : int skeylen = HSTORE_KEYLEN(es, i);
340 : :
5310 341 [ + - ]: 27 : if (skeylen == key_pairs[j].keylen)
3069 342 : 27 : difference = memcmp(HSTORE_KEY(es, ps, i),
4863 rhaas@postgresql.org 343 : 27 : key_pairs[j].key,
344 [ + + ]: 27 : key_pairs[j].keylen);
345 : : else
5310 tgl@sss.pgh.pa.us 346 [ # # ]:UBC 0 : difference = (skeylen > key_pairs[j].keylen) ? 1 : -1;
347 : : }
348 : :
5310 tgl@sss.pgh.pa.us 349 [ - + ]:CBC 27 : if (difference > 0)
5310 tgl@sss.pgh.pa.us 350 :UBC 0 : ++j;
5310 tgl@sss.pgh.pa.us 351 [ + + ]:CBC 27 : else if (difference == 0)
352 : 14 : ++i, ++j;
353 : : else
354 : : {
355 [ + + - + : 13 : HS_COPYITEM(ed, bufd, pd,
+ + + + -
+ - + ]
356 : : HSTORE_KEY(es, ps, i), HSTORE_KEYLEN(es, i),
357 : : HSTORE_VALLEN(es, i), HSTORE_VALISNULL(es, i));
358 : 13 : ++outcount;
359 : 13 : ++i;
360 : : }
361 : : }
362 : :
5161 bruce@momjian.us 363 [ + + + + ]: 9 : HS_FINALIZE(out, outcount, bufd, pd);
364 : :
5310 tgl@sss.pgh.pa.us 365 : 9 : PG_RETURN_POINTER(out);
366 : : }
367 : :
368 : :
369 : 8 : PG_FUNCTION_INFO_V1(hstore_delete_hstore);
370 : : Datum
371 : 12 : hstore_delete_hstore(PG_FUNCTION_ARGS)
372 : : {
2400 373 : 12 : HStore *hs = PG_GETARG_HSTORE_P(0);
374 : 12 : HStore *hs2 = PG_GETARG_HSTORE_P(1);
5310 375 : 12 : HStore *out = palloc(VARSIZE(hs));
5161 bruce@momjian.us 376 : 12 : int hs_count = HS_COUNT(hs);
377 : 12 : int hs2_count = HS_COUNT(hs2);
378 : : char *ps,
379 : : *ps2,
380 : : *bufd,
381 : : *pd;
382 : : HEntry *es,
383 : : *es2,
384 : : *ed;
385 : : int i,
386 : : j;
387 : 12 : int outcount = 0;
388 : :
5310 tgl@sss.pgh.pa.us 389 : 12 : SET_VARSIZE(out, VARSIZE(hs));
5161 bruce@momjian.us 390 : 12 : HS_SETCOUNT(out, hs_count); /* temporary! */
391 : :
5310 tgl@sss.pgh.pa.us 392 : 12 : ps = STRPTR(hs);
393 : 12 : es = ARRPTR(hs);
394 : 12 : ps2 = STRPTR(hs2);
395 : 12 : es2 = ARRPTR(hs2);
396 : 12 : bufd = pd = STRPTR(out);
397 : 12 : ed = ARRPTR(out);
398 : :
399 [ + + ]: 12 : if (hs2_count == 0)
400 : : {
401 : : /* return a copy of the input, unchanged */
402 : 3 : memcpy(out, hs, VARSIZE(hs));
403 [ + - ]: 3 : HS_FIXSIZE(out, hs_count);
404 : 3 : HS_SETCOUNT(out, hs_count);
405 : 3 : PG_RETURN_POINTER(out);
406 : : }
407 : :
408 : : /*
409 : : * this is in effect a merge between hs and hs2, both of which are already
410 : : * sorted by (keylen,key); we take keys from hs only; for equal keys, we
411 : : * take the value from hs unless the values are equal
412 : : */
413 : :
5161 bruce@momjian.us 414 [ + + ]: 36 : for (i = j = 0; i < hs_count;)
415 : : {
416 : : int difference;
417 : :
5310 tgl@sss.pgh.pa.us 418 [ + + ]: 27 : if (j >= hs2_count)
419 : 5 : difference = -1;
420 : : else
421 : : {
3069 422 [ + + ]: 22 : int skeylen = HSTORE_KEYLEN(es, i);
423 [ + + ]: 22 : int s2keylen = HSTORE_KEYLEN(es2, j);
424 : :
5310 425 [ + + ]: 22 : if (skeylen == s2keylen)
3069 426 : 20 : difference = memcmp(HSTORE_KEY(es, ps, i),
427 [ + + + + ]: 20 : HSTORE_KEY(es2, ps2, j),
428 : : skeylen);
429 : : else
5310 430 [ - + ]: 2 : difference = (skeylen > s2keylen) ? 1 : -1;
431 : : }
432 : :
433 [ - + ]: 27 : if (difference > 0)
5310 tgl@sss.pgh.pa.us 434 :UBC 0 : ++j;
5310 tgl@sss.pgh.pa.us 435 [ + + ]:CBC 27 : else if (difference == 0)
436 : : {
3069 437 [ - + ]: 17 : int svallen = HSTORE_VALLEN(es, i);
438 : 17 : int snullval = HSTORE_VALISNULL(es, i);
439 : :
440 [ + + + - ]: 17 : if (snullval != HSTORE_VALISNULL(es2, j) ||
441 [ - + + - ]: 15 : (!snullval && (svallen != HSTORE_VALLEN(es2, j) ||
442 [ + + ]: 15 : memcmp(HSTORE_VAL(es, ps, i),
443 [ + - + - ]: 15 : HSTORE_VAL(es2, ps2, j),
444 : : svallen) != 0)))
445 : : {
5310 446 [ - + + - : 4 : HS_COPYITEM(ed, bufd, pd,
- + - + ]
447 : : HSTORE_KEY(es, ps, i), HSTORE_KEYLEN(es, i),
448 : : svallen, snullval);
449 : 4 : ++outcount;
450 : : }
451 : 17 : ++i, ++j;
452 : : }
453 : : else
454 : : {
455 [ + + - + : 10 : HS_COPYITEM(ed, bufd, pd,
+ + + + -
+ - + ]
456 : : HSTORE_KEY(es, ps, i), HSTORE_KEYLEN(es, i),
457 : : HSTORE_VALLEN(es, i), HSTORE_VALISNULL(es, i));
458 : 10 : ++outcount;
459 : 10 : ++i;
460 : : }
461 : : }
462 : :
5161 bruce@momjian.us 463 [ + + + - ]: 9 : HS_FINALIZE(out, outcount, bufd, pd);
464 : :
6431 teodor@sigaev.ru 465 : 9 : PG_RETURN_POINTER(out);
466 : : }
467 : :
468 : :
5310 tgl@sss.pgh.pa.us 469 : 8 : PG_FUNCTION_INFO_V1(hstore_concat);
470 : : Datum
471 : 30 : hstore_concat(PG_FUNCTION_ARGS)
472 : : {
2400 473 : 30 : HStore *s1 = PG_GETARG_HSTORE_P(0);
474 : 30 : HStore *s2 = PG_GETARG_HSTORE_P(1);
6255 475 : 30 : HStore *out = palloc(VARSIZE(s1) + VARSIZE(s2));
476 : : char *ps1,
477 : : *ps2,
478 : : *bufd,
479 : : *pd;
480 : : HEntry *es1,
481 : : *es2,
482 : : *ed;
483 : : int s1idx;
484 : : int s2idx;
5161 bruce@momjian.us 485 : 30 : int s1count = HS_COUNT(s1);
486 : 30 : int s2count = HS_COUNT(s2);
487 : 30 : int outcount = 0;
488 : :
5310 tgl@sss.pgh.pa.us 489 : 30 : SET_VARSIZE(out, VARSIZE(s1) + VARSIZE(s2) - HSHRDSIZE);
490 : 30 : HS_SETCOUNT(out, s1count + s2count);
491 : :
492 [ + + ]: 30 : if (s1count == 0)
493 : : {
494 : : /* return a copy of the input, unchanged */
495 : 4 : memcpy(out, s2, VARSIZE(s2));
496 [ + + ]: 4 : HS_FIXSIZE(out, s2count);
497 : 4 : HS_SETCOUNT(out, s2count);
498 : 4 : PG_RETURN_POINTER(out);
499 : : }
500 : :
501 [ + + ]: 26 : if (s2count == 0)
502 : : {
503 : : /* return a copy of the input, unchanged */
504 : 2 : memcpy(out, s1, VARSIZE(s1));
505 [ + - ]: 2 : HS_FIXSIZE(out, s1count);
506 : 2 : HS_SETCOUNT(out, s1count);
507 : 2 : PG_RETURN_POINTER(out);
508 : : }
509 : :
6402 bruce@momjian.us 510 : 24 : ps1 = STRPTR(s1);
511 : 24 : ps2 = STRPTR(s2);
5310 tgl@sss.pgh.pa.us 512 : 24 : bufd = pd = STRPTR(out);
6402 bruce@momjian.us 513 : 24 : es1 = ARRPTR(s1);
514 : 24 : es2 = ARRPTR(s2);
515 : 24 : ed = ARRPTR(out);
516 : :
517 : : /*
518 : : * this is in effect a merge between s1 and s2, both of which are already
519 : : * sorted by (keylen,key); we take s2 for equal keys
520 : : */
521 : :
5310 tgl@sss.pgh.pa.us 522 [ + + + + ]: 90 : for (s1idx = s2idx = 0; s1idx < s1count || s2idx < s2count; ++outcount)
523 : : {
524 : : int difference;
525 : :
526 [ + + ]: 66 : if (s1idx >= s1count)
527 : 13 : difference = 1;
528 [ + + ]: 53 : else if (s2idx >= s2count)
529 : 7 : difference = -1;
530 : : else
531 : : {
3069 532 [ + + ]: 46 : int s1keylen = HSTORE_KEYLEN(es1, s1idx);
533 [ + + ]: 46 : int s2keylen = HSTORE_KEYLEN(es2, s2idx);
534 : :
5310 535 [ + + ]: 46 : if (s1keylen == s2keylen)
3069 536 : 41 : difference = memcmp(HSTORE_KEY(es1, ps1, s1idx),
537 [ + + + + ]: 41 : HSTORE_KEY(es2, ps2, s2idx),
538 : : s1keylen);
539 : : else
5310 540 [ + + ]: 5 : difference = (s1keylen > s2keylen) ? 1 : -1;
541 : : }
542 : :
543 [ + + ]: 66 : if (difference >= 0)
544 : : {
545 [ + + - + : 38 : HS_COPYITEM(ed, bufd, pd,
+ + + + -
+ - + ]
546 : : HSTORE_KEY(es2, ps2, s2idx), HSTORE_KEYLEN(es2, s2idx),
547 : : HSTORE_VALLEN(es2, s2idx), HSTORE_VALISNULL(es2, s2idx));
548 : 38 : ++s2idx;
549 [ + + ]: 38 : if (difference == 0)
550 : 19 : ++s1idx;
551 : : }
552 : : else
553 : : {
554 [ + + - + : 28 : HS_COPYITEM(ed, bufd, pd,
+ + + + -
+ - + ]
555 : : HSTORE_KEY(es1, ps1, s1idx), HSTORE_KEYLEN(es1, s1idx),
556 : : HSTORE_VALLEN(es1, s1idx), HSTORE_VALISNULL(es1, s1idx));
557 : 28 : ++s1idx;
558 : : }
559 : : }
560 : :
5161 bruce@momjian.us 561 [ + - + + ]: 24 : HS_FINALIZE(out, outcount, bufd, pd);
562 : :
5310 tgl@sss.pgh.pa.us 563 : 24 : PG_RETURN_POINTER(out);
564 : : }
565 : :
566 : :
567 : 8 : PG_FUNCTION_INFO_V1(hstore_slice_to_array);
568 : : Datum
569 : 4 : hstore_slice_to_array(PG_FUNCTION_ARGS)
570 : : {
2400 571 : 4 : HStore *hs = PG_GETARG_HSTORE_P(0);
5310 572 : 4 : HEntry *entries = ARRPTR(hs);
5161 bruce@momjian.us 573 : 4 : char *ptr = STRPTR(hs);
5310 tgl@sss.pgh.pa.us 574 : 4 : ArrayType *key_array = PG_GETARG_ARRAYTYPE_P(1);
575 : : ArrayType *aout;
576 : : Datum *key_datums;
577 : : bool *key_nulls;
578 : : Datum *out_datums;
579 : : bool *out_nulls;
580 : : int key_count;
581 : : int i;
582 : :
653 peter@eisentraut.org 583 : 4 : deconstruct_array_builtin(key_array, TEXTOID, &key_datums, &key_nulls, &key_count);
584 : :
5310 tgl@sss.pgh.pa.us 585 [ - + ]: 4 : if (key_count == 0)
586 : : {
5310 tgl@sss.pgh.pa.us 587 :UBC 0 : aout = construct_empty_array(TEXTOID);
588 : 0 : PG_RETURN_POINTER(aout);
589 : : }
590 : :
5310 tgl@sss.pgh.pa.us 591 :CBC 4 : out_datums = palloc(sizeof(Datum) * key_count);
592 : 4 : out_nulls = palloc(sizeof(bool) * key_count);
593 : :
594 [ + + ]: 15 : for (i = 0; i < key_count; ++i)
595 : : {
5161 bruce@momjian.us 596 : 11 : text *key = (text *) DatumGetPointer(key_datums[i]);
597 : : int idx;
598 : :
5310 tgl@sss.pgh.pa.us 599 [ + + ]: 11 : if (key_nulls[i])
600 : 1 : idx = -1;
601 : : else
602 : 10 : idx = hstoreFindKey(hs, NULL, VARDATA(key), VARSIZE(key) - VARHDRSZ);
603 : :
3069 604 [ + + + + ]: 11 : if (idx < 0 || HSTORE_VALISNULL(entries, idx))
605 : : {
5310 606 : 2 : out_nulls[i] = true;
607 : 2 : out_datums[i] = (Datum) 0;
608 : : }
609 : : else
610 : : {
1536 alvherre@alvh.no-ip. 611 : 18 : out_datums[i] =
612 [ + - ]: 9 : PointerGetDatum(cstring_to_text_with_len(HSTORE_VAL(entries, ptr, idx),
613 [ - + ]: 9 : HSTORE_VALLEN(entries, idx)));
5310 tgl@sss.pgh.pa.us 614 : 9 : out_nulls[i] = false;
615 : : }
616 : : }
617 : :
618 : 4 : aout = construct_md_array(out_datums, out_nulls,
619 : : ARR_NDIM(key_array),
620 : : ARR_DIMS(key_array),
621 : 4 : ARR_LBOUND(key_array),
622 : : TEXTOID, -1, false, TYPALIGN_INT);
623 : :
624 : 4 : PG_RETURN_POINTER(aout);
625 : : }
626 : :
627 : :
628 : 8 : PG_FUNCTION_INFO_V1(hstore_slice_to_hstore);
629 : : Datum
630 : 6 : hstore_slice_to_hstore(PG_FUNCTION_ARGS)
631 : : {
2400 632 : 6 : HStore *hs = PG_GETARG_HSTORE_P(0);
5310 633 : 6 : HEntry *entries = ARRPTR(hs);
5161 bruce@momjian.us 634 : 6 : char *ptr = STRPTR(hs);
5310 tgl@sss.pgh.pa.us 635 : 6 : ArrayType *key_array = PG_GETARG_ARRAYTYPE_P(1);
636 : : HStore *out;
637 : : int nkeys;
5161 bruce@momjian.us 638 : 6 : Pairs *key_pairs = hstoreArrayToPairs(key_array, &nkeys);
639 : : Pairs *out_pairs;
640 : : int bufsiz;
641 : 6 : int lastidx = 0;
642 : : int i;
643 : 6 : int out_count = 0;
644 : :
5310 tgl@sss.pgh.pa.us 645 [ - + ]: 6 : if (nkeys == 0)
646 : : {
5310 tgl@sss.pgh.pa.us 647 :UBC 0 : out = hstorePairs(NULL, 0, 0);
648 : 0 : PG_RETURN_POINTER(out);
649 : : }
650 : :
651 : : /* hstoreArrayToPairs() checked overflow */
5310 tgl@sss.pgh.pa.us 652 :CBC 6 : out_pairs = palloc(sizeof(Pairs) * nkeys);
653 : 6 : bufsiz = 0;
654 : :
655 : : /*
656 : : * we exploit the fact that the pairs list is already sorted into strictly
657 : : * increasing order to narrow the hstoreFindKey search; each search can
658 : : * start one entry past the previous "found" entry, or at the lower bound
659 : : * of the last search.
660 : : */
661 : :
662 [ + + ]: 21 : for (i = 0; i < nkeys; ++i)
663 : : {
5161 bruce@momjian.us 664 : 15 : int idx = hstoreFindKey(hs, &lastidx,
2489 tgl@sss.pgh.pa.us 665 : 15 : key_pairs[i].key, key_pairs[i].keylen);
666 : :
5310 667 [ + + ]: 15 : if (idx >= 0)
668 : : {
669 : 12 : out_pairs[out_count].key = key_pairs[i].key;
670 : 12 : bufsiz += (out_pairs[out_count].keylen = key_pairs[i].keylen);
3069 671 [ + - ]: 12 : out_pairs[out_count].val = HSTORE_VAL(entries, ptr, idx);
672 [ - + ]: 12 : bufsiz += (out_pairs[out_count].vallen = HSTORE_VALLEN(entries, idx));
673 : 12 : out_pairs[out_count].isnull = HSTORE_VALISNULL(entries, idx);
5310 674 : 12 : out_pairs[out_count].needfree = false;
675 : 12 : ++out_count;
676 : : }
677 : : }
678 : :
679 : : /*
680 : : * we don't use hstoreUniquePairs here because we know that the pairs list
681 : : * is already sorted and uniq'ed.
682 : : */
683 : :
684 : 6 : out = hstorePairs(out_pairs, out_count, bufsiz);
685 : :
6431 teodor@sigaev.ru 686 : 6 : PG_RETURN_POINTER(out);
687 : : }
688 : :
689 : :
5310 tgl@sss.pgh.pa.us 690 : 8 : PG_FUNCTION_INFO_V1(hstore_akeys);
691 : : Datum
692 : 3 : hstore_akeys(PG_FUNCTION_ARGS)
693 : : {
2400 694 : 3 : HStore *hs = PG_GETARG_HSTORE_P(0);
695 : : Datum *d;
696 : : ArrayType *a;
5310 697 : 3 : HEntry *entries = ARRPTR(hs);
6402 bruce@momjian.us 698 : 3 : char *base = STRPTR(hs);
5161 699 : 3 : int count = HS_COUNT(hs);
700 : : int i;
701 : :
5310 tgl@sss.pgh.pa.us 702 [ + + ]: 3 : if (count == 0)
703 : : {
704 : 1 : a = construct_empty_array(TEXTOID);
705 : 1 : PG_RETURN_POINTER(a);
706 : : }
707 : :
708 : 2 : d = (Datum *) palloc(sizeof(Datum) * count);
709 : :
710 [ + + ]: 7 : for (i = 0; i < count; ++i)
711 : : {
3069 712 [ + + ]: 5 : text *t = cstring_to_text_with_len(HSTORE_KEY(entries, base, i),
713 [ + + ]: 5 : HSTORE_KEYLEN(entries, i));
714 : :
715 : 5 : d[i] = PointerGetDatum(t);
716 : : }
717 : :
653 peter@eisentraut.org 718 : 2 : a = construct_array_builtin(d, count, TEXTOID);
719 : :
6431 teodor@sigaev.ru 720 : 2 : PG_RETURN_POINTER(a);
721 : : }
722 : :
723 : :
5310 tgl@sss.pgh.pa.us 724 : 8 : PG_FUNCTION_INFO_V1(hstore_avals);
725 : : Datum
726 : 4 : hstore_avals(PG_FUNCTION_ARGS)
727 : : {
2400 728 : 4 : HStore *hs = PG_GETARG_HSTORE_P(0);
729 : : Datum *d;
730 : : bool *nulls;
731 : : ArrayType *a;
5310 732 : 4 : HEntry *entries = ARRPTR(hs);
6402 bruce@momjian.us 733 : 4 : char *base = STRPTR(hs);
5161 734 : 4 : int count = HS_COUNT(hs);
735 : 4 : int lb = 1;
736 : : int i;
737 : :
5310 tgl@sss.pgh.pa.us 738 [ + + ]: 4 : if (count == 0)
739 : : {
740 : 1 : a = construct_empty_array(TEXTOID);
741 : 1 : PG_RETURN_POINTER(a);
742 : : }
743 : :
744 : 3 : d = (Datum *) palloc(sizeof(Datum) * count);
745 : 3 : nulls = (bool *) palloc(sizeof(bool) * count);
746 : :
747 [ + + ]: 12 : for (i = 0; i < count; ++i)
748 : : {
3069 749 [ + + ]: 9 : if (HSTORE_VALISNULL(entries, i))
750 : : {
5310 751 : 1 : d[i] = (Datum) 0;
752 : 1 : nulls[i] = true;
753 : : }
754 : : else
755 : : {
3069 756 [ + - ]: 8 : text *item = cstring_to_text_with_len(HSTORE_VAL(entries, base, i),
2489 757 [ - + ]: 8 : HSTORE_VALLEN(entries, i));
758 : :
5310 759 : 8 : d[i] = PointerGetDatum(item);
760 : 8 : nulls[i] = false;
761 : : }
762 : : }
763 : :
764 : 3 : a = construct_md_array(d, nulls, 1, &count, &lb,
765 : : TEXTOID, -1, false, TYPALIGN_INT);
766 : :
6431 teodor@sigaev.ru 767 : 3 : PG_RETURN_POINTER(a);
768 : : }
769 : :
770 : :
771 : : static ArrayType *
5310 tgl@sss.pgh.pa.us 772 : 4 : hstore_to_array_internal(HStore *hs, int ndims)
773 : : {
774 : 4 : HEntry *entries = ARRPTR(hs);
775 : 4 : char *base = STRPTR(hs);
5161 bruce@momjian.us 776 : 4 : int count = HS_COUNT(hs);
777 : 4 : int out_size[2] = {0, 2};
778 : 4 : int lb[2] = {1, 1};
779 : : Datum *out_datums;
780 : : bool *out_nulls;
781 : : int i;
782 : :
5310 tgl@sss.pgh.pa.us 783 [ - + ]: 4 : Assert(ndims < 3);
784 : :
785 [ + - - + ]: 4 : if (count == 0 || ndims == 0)
5310 tgl@sss.pgh.pa.us 786 :UBC 0 : return construct_empty_array(TEXTOID);
787 : :
5310 tgl@sss.pgh.pa.us 788 :CBC 4 : out_size[0] = count * 2 / ndims;
789 : 4 : out_datums = palloc(sizeof(Datum) * count * 2);
790 : 4 : out_nulls = palloc(sizeof(bool) * count * 2);
791 : :
792 [ + + ]: 20 : for (i = 0; i < count; ++i)
793 : : {
3069 794 [ + + ]: 16 : text *key = cstring_to_text_with_len(HSTORE_KEY(entries, base, i),
795 [ + + ]: 16 : HSTORE_KEYLEN(entries, i));
796 : :
5161 bruce@momjian.us 797 : 16 : out_datums[i * 2] = PointerGetDatum(key);
798 : 16 : out_nulls[i * 2] = false;
799 : :
3069 tgl@sss.pgh.pa.us 800 [ + + ]: 16 : if (HSTORE_VALISNULL(entries, i))
801 : : {
5161 bruce@momjian.us 802 : 4 : out_datums[i * 2 + 1] = (Datum) 0;
803 : 4 : out_nulls[i * 2 + 1] = true;
804 : : }
805 : : else
806 : : {
3069 tgl@sss.pgh.pa.us 807 [ + - ]: 12 : text *item = cstring_to_text_with_len(HSTORE_VAL(entries, base, i),
2489 808 [ - + ]: 12 : HSTORE_VALLEN(entries, i));
809 : :
5161 bruce@momjian.us 810 : 12 : out_datums[i * 2 + 1] = PointerGetDatum(item);
811 : 12 : out_nulls[i * 2 + 1] = false;
812 : : }
813 : : }
814 : :
5310 tgl@sss.pgh.pa.us 815 : 4 : return construct_md_array(out_datums, out_nulls,
816 : : ndims, out_size, lb,
817 : : TEXTOID, -1, false, TYPALIGN_INT);
818 : : }
819 : :
820 : 8 : PG_FUNCTION_INFO_V1(hstore_to_array);
821 : : Datum
822 : 2 : hstore_to_array(PG_FUNCTION_ARGS)
823 : : {
2400 824 : 2 : HStore *hs = PG_GETARG_HSTORE_P(0);
5310 825 : 2 : ArrayType *out = hstore_to_array_internal(hs, 1);
826 : :
827 : 2 : PG_RETURN_POINTER(out);
828 : : }
829 : :
830 : 8 : PG_FUNCTION_INFO_V1(hstore_to_matrix);
831 : : Datum
832 : 2 : hstore_to_matrix(PG_FUNCTION_ARGS)
833 : : {
2400 834 : 2 : HStore *hs = PG_GETARG_HSTORE_P(0);
5310 835 : 2 : ArrayType *out = hstore_to_array_internal(hs, 2);
836 : :
837 : 2 : PG_RETURN_POINTER(out);
838 : : }
839 : :
840 : : /*
841 : : * Common initialization function for the various set-returning
842 : : * funcs. fcinfo is only passed if the function is to return a
843 : : * composite; it will be used to look up the return tupledesc.
844 : : * we stash a copy of the hstore in the multi-call context in
845 : : * case it was originally toasted. (At least I assume that's why;
846 : : * there was no explanatory comment in the original code. --AG)
847 : : */
848 : :
849 : : static void
5161 bruce@momjian.us 850 : 2010 : setup_firstcall(FuncCallContext *funcctx, HStore *hs,
851 : : FunctionCallInfo fcinfo)
852 : : {
853 : : MemoryContext oldcontext;
854 : : HStore *st;
855 : :
6431 teodor@sigaev.ru 856 : 2010 : oldcontext = MemoryContextSwitchTo(funcctx->multi_call_memory_ctx);
857 : :
5310 tgl@sss.pgh.pa.us 858 : 2010 : st = (HStore *) palloc(VARSIZE(hs));
859 : 2010 : memcpy(st, hs, VARSIZE(hs));
860 : :
6402 bruce@momjian.us 861 : 2010 : funcctx->user_fctx = (void *) st;
862 : :
5310 tgl@sss.pgh.pa.us 863 [ + + ]: 2010 : if (fcinfo)
864 : : {
865 : : TupleDesc tupdesc;
866 : :
867 : : /* Build a tuple descriptor for our result type */
868 [ - + ]: 2003 : if (get_call_result_type(fcinfo, NULL, &tupdesc) != TYPEFUNC_COMPOSITE)
5310 tgl@sss.pgh.pa.us 869 [ # # ]:UBC 0 : elog(ERROR, "return type must be a row type");
870 : :
5310 tgl@sss.pgh.pa.us 871 :CBC 2003 : funcctx->tuple_desc = BlessTupleDesc(tupdesc);
872 : : }
873 : :
6431 teodor@sigaev.ru 874 : 2010 : MemoryContextSwitchTo(oldcontext);
875 : 2010 : }
876 : :
877 : :
5310 tgl@sss.pgh.pa.us 878 : 8 : PG_FUNCTION_INFO_V1(hstore_skeys);
879 : : Datum
880 : 8 : hstore_skeys(PG_FUNCTION_ARGS)
881 : : {
882 : : FuncCallContext *funcctx;
883 : : HStore *hs;
884 : : int i;
885 : :
6402 bruce@momjian.us 886 [ + + ]: 8 : if (SRF_IS_FIRSTCALL())
887 : : {
2400 tgl@sss.pgh.pa.us 888 : 3 : hs = PG_GETARG_HSTORE_P(0);
6431 teodor@sigaev.ru 889 : 3 : funcctx = SRF_FIRSTCALL_INIT();
5310 tgl@sss.pgh.pa.us 890 : 3 : setup_firstcall(funcctx, hs, NULL);
891 : : }
892 : :
6431 teodor@sigaev.ru 893 : 8 : funcctx = SRF_PERCALL_SETUP();
5310 tgl@sss.pgh.pa.us 894 : 8 : hs = (HStore *) funcctx->user_fctx;
895 : 8 : i = funcctx->call_cntr;
896 : :
897 [ + + ]: 8 : if (i < HS_COUNT(hs))
898 : : {
5161 bruce@momjian.us 899 : 5 : HEntry *entries = ARRPTR(hs);
900 : : text *item;
901 : :
3069 tgl@sss.pgh.pa.us 902 [ + + ]: 5 : item = cstring_to_text_with_len(HSTORE_KEY(entries, STRPTR(hs), i),
903 [ + + ]: 5 : HSTORE_KEYLEN(entries, i));
904 : :
6431 teodor@sigaev.ru 905 : 5 : SRF_RETURN_NEXT(funcctx, PointerGetDatum(item));
906 : : }
907 : :
6402 bruce@momjian.us 908 : 3 : SRF_RETURN_DONE(funcctx);
909 : : }
910 : :
911 : :
5310 tgl@sss.pgh.pa.us 912 : 8 : PG_FUNCTION_INFO_V1(hstore_svals);
913 : : Datum
914 : 13 : hstore_svals(PG_FUNCTION_ARGS)
915 : : {
916 : : FuncCallContext *funcctx;
917 : : HStore *hs;
918 : : int i;
919 : :
6402 bruce@momjian.us 920 [ + + ]: 13 : if (SRF_IS_FIRSTCALL())
921 : : {
2400 tgl@sss.pgh.pa.us 922 : 4 : hs = PG_GETARG_HSTORE_P(0);
6431 teodor@sigaev.ru 923 : 4 : funcctx = SRF_FIRSTCALL_INIT();
5310 tgl@sss.pgh.pa.us 924 : 4 : setup_firstcall(funcctx, hs, NULL);
925 : : }
926 : :
6431 teodor@sigaev.ru 927 : 13 : funcctx = SRF_PERCALL_SETUP();
5310 tgl@sss.pgh.pa.us 928 : 13 : hs = (HStore *) funcctx->user_fctx;
929 : 13 : i = funcctx->call_cntr;
930 : :
931 [ + + ]: 13 : if (i < HS_COUNT(hs))
932 : : {
5161 bruce@momjian.us 933 : 9 : HEntry *entries = ARRPTR(hs);
934 : :
3069 tgl@sss.pgh.pa.us 935 [ + + ]: 9 : if (HSTORE_VALISNULL(entries, i))
936 : : {
937 : : ReturnSetInfo *rsi;
938 : :
939 : : /* ugly ugly ugly. why no macro for this? */
6431 teodor@sigaev.ru 940 : 1 : (funcctx)->call_cntr++;
941 : 1 : rsi = (ReturnSetInfo *) fcinfo->resultinfo;
942 : 1 : rsi->isDone = ExprMultipleResult;
943 : 1 : PG_RETURN_NULL();
944 : : }
945 : : else
946 : : {
947 : : text *item;
948 : :
3069 tgl@sss.pgh.pa.us 949 [ + - ]: 8 : item = cstring_to_text_with_len(HSTORE_VAL(entries, STRPTR(hs), i),
950 [ - + ]: 8 : HSTORE_VALLEN(entries, i));
951 : :
6431 teodor@sigaev.ru 952 : 8 : SRF_RETURN_NEXT(funcctx, PointerGetDatum(item));
953 : : }
954 : : }
955 : :
6402 bruce@momjian.us 956 : 4 : SRF_RETURN_DONE(funcctx);
957 : : }
958 : :
959 : :
5310 tgl@sss.pgh.pa.us 960 : 8 : PG_FUNCTION_INFO_V1(hstore_contains);
961 : : Datum
962 : 3560 : hstore_contains(PG_FUNCTION_ARGS)
963 : : {
2400 964 : 3560 : HStore *val = PG_GETARG_HSTORE_P(0);
965 : 3560 : HStore *tmpl = PG_GETARG_HSTORE_P(1);
6402 bruce@momjian.us 966 : 3560 : bool res = true;
967 : 3560 : HEntry *te = ARRPTR(tmpl);
5310 tgl@sss.pgh.pa.us 968 : 3560 : char *tstr = STRPTR(tmpl);
969 : 3560 : HEntry *ve = ARRPTR(val);
970 : 3560 : char *vstr = STRPTR(val);
5161 bruce@momjian.us 971 : 3560 : int tcount = HS_COUNT(tmpl);
972 : 3560 : int lastidx = 0;
973 : : int i;
974 : :
975 : : /*
976 : : * we exploit the fact that keys in "tmpl" are in strictly increasing
977 : : * order to narrow the hstoreFindKey search; each search can start one
978 : : * entry past the previous "found" entry, or at the lower bound of the
979 : : * search
980 : : */
981 : :
5310 tgl@sss.pgh.pa.us 982 [ + + + + ]: 7148 : for (i = 0; res && i < tcount; ++i)
983 : : {
5161 bruce@momjian.us 984 : 3588 : int idx = hstoreFindKey(val, &lastidx,
3069 tgl@sss.pgh.pa.us 985 [ + + ]: 3588 : HSTORE_KEY(te, tstr, i),
986 [ + + ]: 3588 : HSTORE_KEYLEN(te, i));
987 : :
5310 988 [ + + ]: 3588 : if (idx >= 0)
989 : : {
3069 990 : 1083 : bool nullval = HSTORE_VALISNULL(te, i);
991 [ - + ]: 1083 : int vallen = HSTORE_VALLEN(te, i);
992 : :
993 [ + + ]: 1083 : if (nullval != HSTORE_VALISNULL(ve, idx) ||
994 [ + + - + : 513 : (!nullval && (vallen != HSTORE_VALLEN(ve, idx) ||
+ + ]
995 [ + + ]: 305 : memcmp(HSTORE_VAL(te, tstr, i),
996 [ + - + - ]: 305 : HSTORE_VAL(ve, vstr, idx),
997 : : vallen) != 0)))
5995 bruce@momjian.us 998 : 980 : res = false;
999 : : }
1000 : : else
6431 teodor@sigaev.ru 1001 : 2505 : res = false;
1002 : : }
1003 : :
1004 : 3560 : PG_RETURN_BOOL(res);
1005 : : }
1006 : :
1007 : :
5310 tgl@sss.pgh.pa.us 1008 : 7 : PG_FUNCTION_INFO_V1(hstore_contained);
1009 : : Datum
5310 tgl@sss.pgh.pa.us 1010 :UBC 0 : hstore_contained(PG_FUNCTION_ARGS)
1011 : : {
1012 : 0 : PG_RETURN_DATUM(DirectFunctionCall2(hstore_contains,
1013 : : PG_GETARG_DATUM(1),
1014 : : PG_GETARG_DATUM(0)
1015 : : ));
1016 : : }
1017 : :
1018 : :
5310 tgl@sss.pgh.pa.us 1019 :CBC 8 : PG_FUNCTION_INFO_V1(hstore_each);
1020 : : Datum
1021 : 11568 : hstore_each(PG_FUNCTION_ARGS)
1022 : : {
1023 : : FuncCallContext *funcctx;
1024 : : HStore *hs;
1025 : : int i;
1026 : :
6402 bruce@momjian.us 1027 [ + + ]: 11568 : if (SRF_IS_FIRSTCALL())
1028 : : {
2400 tgl@sss.pgh.pa.us 1029 : 2003 : hs = PG_GETARG_HSTORE_P(0);
6431 teodor@sigaev.ru 1030 : 2003 : funcctx = SRF_FIRSTCALL_INIT();
5310 tgl@sss.pgh.pa.us 1031 : 2003 : setup_firstcall(funcctx, hs, fcinfo);
1032 : : }
1033 : :
6431 teodor@sigaev.ru 1034 : 11568 : funcctx = SRF_PERCALL_SETUP();
5310 tgl@sss.pgh.pa.us 1035 : 11568 : hs = (HStore *) funcctx->user_fctx;
1036 : 11568 : i = funcctx->call_cntr;
1037 : :
1038 [ + + ]: 11568 : if (i < HS_COUNT(hs))
1039 : : {
1040 : 9565 : HEntry *entries = ARRPTR(hs);
5161 bruce@momjian.us 1041 : 9565 : char *ptr = STRPTR(hs);
1042 : : Datum res,
1043 : : dvalues[2];
5642 tgl@sss.pgh.pa.us 1044 : 9565 : bool nulls[2] = {false, false};
1045 : : text *item;
1046 : : HeapTuple tuple;
1047 : :
3069 1048 [ + + ]: 9565 : item = cstring_to_text_with_len(HSTORE_KEY(entries, ptr, i),
1049 [ + + ]: 9565 : HSTORE_KEYLEN(entries, i));
6431 teodor@sigaev.ru 1050 : 9565 : dvalues[0] = PointerGetDatum(item);
1051 : :
3069 tgl@sss.pgh.pa.us 1052 [ + + ]: 9565 : if (HSTORE_VALISNULL(entries, i))
1053 : : {
6402 bruce@momjian.us 1054 : 3 : dvalues[1] = (Datum) 0;
5642 tgl@sss.pgh.pa.us 1055 : 3 : nulls[1] = true;
1056 : : }
1057 : : else
1058 : : {
3069 1059 [ + - ]: 9562 : item = cstring_to_text_with_len(HSTORE_VAL(entries, ptr, i),
1060 [ - + ]: 9562 : HSTORE_VALLEN(entries, i));
6431 teodor@sigaev.ru 1061 : 9562 : dvalues[1] = PointerGetDatum(item);
1062 : : }
1063 : :
5310 tgl@sss.pgh.pa.us 1064 : 9565 : tuple = heap_form_tuple(funcctx->tuple_desc, dvalues, nulls);
6076 1065 : 9565 : res = HeapTupleGetDatum(tuple);
1066 : :
595 peter@eisentraut.org 1067 : 9565 : SRF_RETURN_NEXT(funcctx, res);
1068 : : }
1069 : :
6402 bruce@momjian.us 1070 : 2003 : SRF_RETURN_DONE(funcctx);
1071 : : }
1072 : :
1073 : :
1074 : : /*
1075 : : * btree sort order for hstores isn't intended to be useful; we really only
1076 : : * care about equality versus non-equality. we compare the entire string
1077 : : * buffer first, then the entry pos array.
1078 : : */
1079 : :
5310 tgl@sss.pgh.pa.us 1080 : 8 : PG_FUNCTION_INFO_V1(hstore_cmp);
1081 : : Datum
1082 : 43266 : hstore_cmp(PG_FUNCTION_ARGS)
1083 : : {
2400 1084 : 43266 : HStore *hs1 = PG_GETARG_HSTORE_P(0);
1085 : 43266 : HStore *hs2 = PG_GETARG_HSTORE_P(1);
5161 bruce@momjian.us 1086 : 43266 : int hcount1 = HS_COUNT(hs1);
1087 : 43266 : int hcount2 = HS_COUNT(hs2);
1088 : 43266 : int res = 0;
1089 : :
5310 tgl@sss.pgh.pa.us 1090 [ + + + + ]: 43266 : if (hcount1 == 0 || hcount2 == 0)
1091 : : {
1092 : : /*
1093 : : * if either operand is empty, and the other is nonempty, the nonempty
1094 : : * one is larger. If both are empty they are equal.
1095 : : */
1096 [ + + ]: 3676 : if (hcount1 > 0)
1097 : 253 : res = 1;
1098 [ + + ]: 3423 : else if (hcount2 > 0)
1099 : 1635 : res = -1;
1100 : : }
1101 : : else
1102 : : {
1103 : : /* here we know both operands are nonempty */
5161 bruce@momjian.us 1104 : 39590 : char *str1 = STRPTR(hs1);
1105 : 39590 : char *str2 = STRPTR(hs2);
1106 : 39590 : HEntry *ent1 = ARRPTR(hs1);
1107 : 39590 : HEntry *ent2 = ARRPTR(hs2);
1108 : 39590 : size_t len1 = HSE_ENDPOS(ent1[2 * hcount1 - 1]);
1109 : 39590 : size_t len2 = HSE_ENDPOS(ent2[2 * hcount2 - 1]);
1110 : :
1111 : 39590 : res = memcmp(str1, str2, Min(len1, len2));
1112 : :
5310 tgl@sss.pgh.pa.us 1113 [ + + ]: 39590 : if (res == 0)
1114 : : {
1115 [ - + ]: 2770 : if (len1 > len2)
5310 tgl@sss.pgh.pa.us 1116 :UBC 0 : res = 1;
5310 tgl@sss.pgh.pa.us 1117 [ - + ]:CBC 2770 : else if (len1 < len2)
5310 tgl@sss.pgh.pa.us 1118 :UBC 0 : res = -1;
5310 tgl@sss.pgh.pa.us 1119 [ - + ]:CBC 2770 : else if (hcount1 > hcount2)
5310 tgl@sss.pgh.pa.us 1120 :UBC 0 : res = 1;
5310 tgl@sss.pgh.pa.us 1121 [ - + ]:CBC 2770 : else if (hcount2 > hcount1)
5310 tgl@sss.pgh.pa.us 1122 :UBC 0 : res = -1;
1123 : : else
1124 : : {
5161 bruce@momjian.us 1125 :CBC 2770 : int count = hcount1 * 2;
1126 : : int i;
1127 : :
5310 tgl@sss.pgh.pa.us 1128 [ + + ]: 32770 : for (i = 0; i < count; ++i)
1129 [ + - ]: 30000 : if (HSE_ENDPOS(ent1[i]) != HSE_ENDPOS(ent2[i]) ||
1130 [ + - ]: 30000 : HSE_ISNULL(ent1[i]) != HSE_ISNULL(ent2[i]))
1131 : : break;
1132 [ - + ]: 2770 : if (i < count)
1133 : : {
5310 tgl@sss.pgh.pa.us 1134 [ # # ]:UBC 0 : if (HSE_ENDPOS(ent1[i]) < HSE_ENDPOS(ent2[i]))
1135 : 0 : res = -1;
1136 [ # # ]: 0 : else if (HSE_ENDPOS(ent1[i]) > HSE_ENDPOS(ent2[i]))
1137 : 0 : res = 1;
1138 [ # # ]: 0 : else if (HSE_ISNULL(ent1[i]))
1139 : 0 : res = 1;
1140 [ # # ]: 0 : else if (HSE_ISNULL(ent2[i]))
1141 : 0 : res = -1;
1142 : : }
1143 : : }
1144 : : }
1145 : : else
1146 : : {
5310 tgl@sss.pgh.pa.us 1147 [ + + ]:CBC 36820 : res = (res > 0) ? 1 : -1;
1148 : : }
1149 : : }
1150 : :
1151 : : /*
1152 : : * this is a btree support function; this is one of the few places where
1153 : : * memory needs to be explicitly freed.
1154 : : */
5161 bruce@momjian.us 1155 [ + + ]: 43266 : PG_FREE_IF_COPY(hs1, 0);
1156 [ + + ]: 43266 : PG_FREE_IF_COPY(hs2, 1);
5310 tgl@sss.pgh.pa.us 1157 : 43266 : PG_RETURN_INT32(res);
1158 : : }
1159 : :
1160 : :
1161 : 8 : PG_FUNCTION_INFO_V1(hstore_eq);
1162 : : Datum
1163 : 4122 : hstore_eq(PG_FUNCTION_ARGS)
1164 : : {
5161 bruce@momjian.us 1165 : 4122 : int res = DatumGetInt32(DirectFunctionCall2(hstore_cmp,
1166 : : PG_GETARG_DATUM(0),
1167 : : PG_GETARG_DATUM(1)));
1168 : :
5310 tgl@sss.pgh.pa.us 1169 : 4122 : PG_RETURN_BOOL(res == 0);
1170 : : }
1171 : :
1172 : 7 : PG_FUNCTION_INFO_V1(hstore_ne);
1173 : : Datum
5310 tgl@sss.pgh.pa.us 1174 :UBC 0 : hstore_ne(PG_FUNCTION_ARGS)
1175 : : {
5161 bruce@momjian.us 1176 : 0 : int res = DatumGetInt32(DirectFunctionCall2(hstore_cmp,
1177 : : PG_GETARG_DATUM(0),
1178 : : PG_GETARG_DATUM(1)));
1179 : :
5310 tgl@sss.pgh.pa.us 1180 : 0 : PG_RETURN_BOOL(res != 0);
1181 : : }
1182 : :
5310 tgl@sss.pgh.pa.us 1183 :CBC 8 : PG_FUNCTION_INFO_V1(hstore_gt);
1184 : : Datum
1185 : 131 : hstore_gt(PG_FUNCTION_ARGS)
1186 : : {
5161 bruce@momjian.us 1187 : 131 : int res = DatumGetInt32(DirectFunctionCall2(hstore_cmp,
1188 : : PG_GETARG_DATUM(0),
1189 : : PG_GETARG_DATUM(1)));
1190 : :
5310 tgl@sss.pgh.pa.us 1191 : 131 : PG_RETURN_BOOL(res > 0);
1192 : : }
1193 : :
1194 : 7 : PG_FUNCTION_INFO_V1(hstore_ge);
1195 : : Datum
5310 tgl@sss.pgh.pa.us 1196 :UBC 0 : hstore_ge(PG_FUNCTION_ARGS)
1197 : : {
5161 bruce@momjian.us 1198 : 0 : int res = DatumGetInt32(DirectFunctionCall2(hstore_cmp,
1199 : : PG_GETARG_DATUM(0),
1200 : : PG_GETARG_DATUM(1)));
1201 : :
5310 tgl@sss.pgh.pa.us 1202 : 0 : PG_RETURN_BOOL(res >= 0);
1203 : : }
1204 : :
5310 tgl@sss.pgh.pa.us 1205 :CBC 7 : PG_FUNCTION_INFO_V1(hstore_lt);
1206 : : Datum
5310 tgl@sss.pgh.pa.us 1207 :UBC 0 : hstore_lt(PG_FUNCTION_ARGS)
1208 : : {
5161 bruce@momjian.us 1209 : 0 : int res = DatumGetInt32(DirectFunctionCall2(hstore_cmp,
1210 : : PG_GETARG_DATUM(0),
1211 : : PG_GETARG_DATUM(1)));
1212 : :
5310 tgl@sss.pgh.pa.us 1213 : 0 : PG_RETURN_BOOL(res < 0);
1214 : : }
1215 : :
5310 tgl@sss.pgh.pa.us 1216 :CBC 7 : PG_FUNCTION_INFO_V1(hstore_le);
1217 : : Datum
5310 tgl@sss.pgh.pa.us 1218 :UBC 0 : hstore_le(PG_FUNCTION_ARGS)
1219 : : {
5161 bruce@momjian.us 1220 : 0 : int res = DatumGetInt32(DirectFunctionCall2(hstore_cmp,
1221 : : PG_GETARG_DATUM(0),
1222 : : PG_GETARG_DATUM(1)));
1223 : :
5310 tgl@sss.pgh.pa.us 1224 : 0 : PG_RETURN_BOOL(res <= 0);
1225 : : }
1226 : :
1227 : :
5310 tgl@sss.pgh.pa.us 1228 :CBC 8 : PG_FUNCTION_INFO_V1(hstore_hash);
1229 : : Datum
1230 : 2014 : hstore_hash(PG_FUNCTION_ARGS)
1231 : : {
2400 1232 : 2014 : HStore *hs = PG_GETARG_HSTORE_P(0);
5161 bruce@momjian.us 1233 : 2014 : Datum hval = hash_any((unsigned char *) VARDATA(hs),
5310 tgl@sss.pgh.pa.us 1234 : 2014 : VARSIZE(hs) - VARHDRSZ);
1235 : :
1236 : : /*
1237 : : * This (along with hstore_hash_extended) is the only place in the code
1238 : : * that cares whether the overall varlena size exactly matches the true
1239 : : * data size; this assertion should be maintained by all the other code,
1240 : : * but we make it explicit here.
1241 : : */
1242 [ + + - + ]: 2014 : Assert(VARSIZE(hs) ==
1243 : : (HS_COUNT(hs) != 0 ?
1244 : : CALCDATASIZE(HS_COUNT(hs),
1245 : : HSE_ENDPOS(ARRPTR(hs)[2 * HS_COUNT(hs) - 1])) :
1246 : : HSHRDSIZE));
1247 : :
5161 bruce@momjian.us 1248 [ + + ]: 2014 : PG_FREE_IF_COPY(hs, 0);
5310 tgl@sss.pgh.pa.us 1249 : 2014 : PG_RETURN_DATUM(hval);
1250 : : }
1251 : :
1969 1252 : 8 : PG_FUNCTION_INFO_V1(hstore_hash_extended);
1253 : : Datum
1254 : 10 : hstore_hash_extended(PG_FUNCTION_ARGS)
1255 : : {
1256 : 10 : HStore *hs = PG_GETARG_HSTORE_P(0);
1257 : 10 : uint64 seed = PG_GETARG_INT64(1);
1258 : : Datum hval;
1259 : :
1260 : 10 : hval = hash_any_extended((unsigned char *) VARDATA(hs),
1261 : 10 : VARSIZE(hs) - VARHDRSZ,
1262 : : seed);
1263 : :
1264 : : /* See comment in hstore_hash */
1265 [ + + - + ]: 10 : Assert(VARSIZE(hs) ==
1266 : : (HS_COUNT(hs) != 0 ?
1267 : : CALCDATASIZE(HS_COUNT(hs),
1268 : : HSE_ENDPOS(ARRPTR(hs)[2 * HS_COUNT(hs) - 1])) :
1269 : : HSHRDSIZE));
1270 : :
1271 [ - + ]: 10 : PG_FREE_IF_COPY(hs, 0);
1272 : 10 : PG_RETURN_DATUM(hval);
1273 : : }
|