2.0
[hstore.git] / hstore_op.c
1 /*
2  * contrib/hstore/hstore_op.c
3  */
4 #include "postgres.h"
5
6 #include "access/hash.h"
7 #include "access/htup_details.h"
8 #include "catalog/pg_type.h"
9 #include "funcapi.h"
10 #include "utils/builtins.h"
11 #include "utils/memutils.h"
12 #include "utils/pg_crc.h"
13
14 #include "hstore.h"
15
16 #ifndef SRF_RETURN_NEXT_NULL
17 #define SRF_RETURN_NEXT_NULL(_funcctx) \
18         do { \
19                 ReturnSetInfo      *rsi; \
20                 (_funcctx)->call_cntr++; \
21                 rsi = (ReturnSetInfo *) fcinfo->resultinfo; \
22                 rsi->isDone = ExprMultipleResult; \
23                 PG_RETURN_NULL(); \
24         } while (0)
25 #endif
26
27 /* old names for C functions */
28 HSTORE_POLLUTE(hstore_fetchval, fetchval);
29 HSTORE_POLLUTE(hstore_exists, exists);
30 HSTORE_POLLUTE(hstore_defined, defined);
31 HSTORE_POLLUTE(hstore_delete, delete);
32 HSTORE_POLLUTE(hstore_concat, hs_concat);
33 HSTORE_POLLUTE(hstore_contains, hs_contains);
34 HSTORE_POLLUTE(hstore_contained, hs_contained);
35 HSTORE_POLLUTE(hstore_akeys, akeys);
36 HSTORE_POLLUTE(hstore_avals, avals);
37 HSTORE_POLLUTE(hstore_skeys, skeys);
38 HSTORE_POLLUTE(hstore_svals, svals);
39 HSTORE_POLLUTE(hstore_each, each);
40
41 static HStoreValue*
42 arrayToHStoreSortedArray(ArrayType *a)
43 {
44         Datum                   *key_datums;
45         bool                    *key_nulls;
46         int                             key_count;
47         HStoreValue             *v;
48         int                             i,
49                                         j;
50         bool                    hasNonUniq = false;
51
52         deconstruct_array(a,
53                                           TEXTOID, -1, false, 'i',
54                                           &key_datums, &key_nulls, &key_count);
55
56         if (key_count == 0)
57                 return NULL;
58
59         /*
60          * A text array uses at least eight bytes per element, so any overflow in
61          * "key_count * sizeof(Pairs)" is small enough for palloc() to catch.
62          * However, credible improvements to the array format could invalidate
63          * that assumption.  Therefore, use an explicit check rather than relying
64          * on palloc() to complain.
65          */
66         if (key_count > MaxAllocSize / sizeof(HStorePair))
67                 ereport(ERROR,
68                                 (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
69                           errmsg("number of pairs (%d) exceeds the maximum allowed (%d)",
70                                          key_count, (int) (MaxAllocSize / sizeof(HStorePair)))));
71
72         v = palloc(sizeof(*v));
73         v->type = hsvArray;
74         v->array.scalar = false;
75
76         v->array.elems = palloc(sizeof(*v->hash.pairs) * key_count);
77
78         for (i = 0, j = 0; i < key_count; i++)
79         {
80                 if (!key_nulls[i])
81                 {
82                         v->array.elems[j].type = hsvString;
83                         v->array.elems[j].string.val = VARDATA(key_datums[i]);
84                         v->array.elems[j].string.len = VARSIZE(key_datums[i]) - VARHDRSZ;
85                         j++;
86                 }
87         }
88         v->array.nelems = j;
89
90         if (v->array.nelems > 1)
91                 qsort_arg(v->array.elems, v->array.nelems, sizeof(*v->array.elems),
92                                   compareHStoreStringValue, &hasNonUniq);
93
94         if (hasNonUniq)
95         {
96                 HStoreValue     *ptr = v->array.elems + 1,
97                                         *res = v->array.elems;
98
99                 while (ptr - v->array.elems < v->array.nelems)
100                 {
101                         if (!(ptr->string.len == res->string.len &&
102                                   memcmp(ptr->string.val, res->string.val, ptr->string.len) == 0))
103                         {
104                                 res++;
105                                 *res = *ptr;
106                         }
107
108                         ptr++;
109                 }
110
111                 v->array.nelems = res + 1 - v->array.elems;
112         }
113
114         return v;
115 }
116
117 static HStoreValue*
118 findInHStoreSortedArray(HStoreValue *a, uint32 *lowbound,
119                                                 char *key, uint32 keylen)
120 {
121         HStoreValue             *stopLow = a->array.elems + ((lowbound) ? *lowbound : 0),
122                                         *stopHigh = a->array.elems + a->array.nelems,
123                                         *stopMiddle;
124
125         while (stopLow < stopHigh)
126         {
127                 int diff;
128
129                 stopMiddle = stopLow + (stopHigh - stopLow) / 2;
130
131                 if (keylen == stopMiddle->string.len)
132                         diff = memcmp(stopMiddle->string.val, key, keylen);
133                 else
134                         diff = (stopMiddle->string.len > keylen) ? 1 : -1;
135
136                 if (diff == 0)
137                 {
138                         if (lowbound)
139                                 *lowbound = (stopMiddle - a->array.elems) + 1;
140                         return stopMiddle;
141                 }
142                 else if (diff < 0)
143                 {
144                         stopLow = stopMiddle + 1;
145                 }
146                 else
147                 {
148                         stopHigh = stopMiddle;
149                 }
150         }
151
152         if (lowbound)
153                 *lowbound = (stopLow - a->array.elems) + 1;
154
155         return NULL;
156 }
157
158 PG_FUNCTION_INFO_V1(hstore_fetchval);
159 Datum           hstore_fetchval(PG_FUNCTION_ARGS);
160 Datum
161 hstore_fetchval(PG_FUNCTION_ARGS)
162 {
163         HStore          *hs = PG_GETARG_HS(0);
164         text            *key = PG_GETARG_TEXT_PP(1);
165         HStoreValue     *v = NULL;
166         text            *out;
167
168         if (!HS_ISEMPTY(hs))
169                 v = findUncompressedHStoreValue(VARDATA(hs),
170                                                                                 HS_FLAG_HSTORE | HS_FLAG_ARRAY,
171                                                                                 NULL,
172                                                                                 VARDATA_ANY(key),
173                                                                                 VARSIZE_ANY_EXHDR(key));
174
175         if ((out = HStoreValueToText(v)) == NULL)
176                 PG_RETURN_NULL();
177         else
178                 PG_RETURN_TEXT_P(out);
179 }
180
181 PG_FUNCTION_INFO_V1(hstore_fetchval_numeric);
182 Datum           hstore_fetchval_numeric(PG_FUNCTION_ARGS);
183 Datum
184 hstore_fetchval_numeric(PG_FUNCTION_ARGS)
185 {
186         HStore          *hs = PG_GETARG_HS(0);
187         text            *key = PG_GETARG_TEXT_PP(1);
188         HStoreValue     *v = NULL;
189
190         if (!HS_ISEMPTY(hs))
191                 v = findUncompressedHStoreValue(VARDATA(hs),
192                                                                                 HS_FLAG_HSTORE | HS_FLAG_ARRAY,
193                                                                                 NULL,
194                                                                                 VARDATA_ANY(key),
195                                                                                 VARSIZE_ANY_EXHDR(key));
196
197         if (v && v->type == hsvNumeric)
198         {
199                 Numeric         out = palloc(VARSIZE_ANY(v->numeric));
200
201                 memcpy(out, v->numeric, VARSIZE_ANY(v->numeric));
202                 PG_RETURN_NUMERIC(out);
203         }
204
205         PG_RETURN_NULL();
206 }
207
208 PG_FUNCTION_INFO_V1(hstore_fetchval_boolean);
209 Datum           hstore_fetchval_boolean(PG_FUNCTION_ARGS);
210 Datum
211 hstore_fetchval_boolean(PG_FUNCTION_ARGS)
212 {
213         HStore          *hs = PG_GETARG_HS(0);
214         text            *key = PG_GETARG_TEXT_PP(1);
215         HStoreValue     *v = NULL;
216
217         if (!HS_ISEMPTY(hs))
218                 v = findUncompressedHStoreValue(VARDATA(hs),
219                                                                                 HS_FLAG_HSTORE | HS_FLAG_ARRAY,
220                                                                                 NULL,
221                                                                                 VARDATA_ANY(key),
222                                                                                 VARSIZE_ANY_EXHDR(key));
223
224         if (v && v->type == hsvBool)
225                 PG_RETURN_BOOL(v->boolean);
226
227         PG_RETURN_NULL();
228 }
229
230 PG_FUNCTION_INFO_V1(hstore_fetchval_n);
231 Datum           hstore_fetchval_n(PG_FUNCTION_ARGS);
232 Datum
233 hstore_fetchval_n(PG_FUNCTION_ARGS)
234 {
235         HStore          *hs = PG_GETARG_HS(0);
236         int                     i = PG_GETARG_INT32(1);
237         HStoreValue     *v = NULL;
238         text            *out;
239
240         if (!HS_ISEMPTY(hs))
241                 v = getHStoreValue(VARDATA(hs), HS_FLAG_HSTORE | HS_FLAG_ARRAY, i);
242
243         if ((out = HStoreValueToText(v)) == NULL)
244                 PG_RETURN_NULL();
245         else
246                 PG_RETURN_TEXT_P(out);
247 }
248
249 PG_FUNCTION_INFO_V1(hstore_fetchval_n_numeric);
250 Datum           hstore_fetchval_n_numeric(PG_FUNCTION_ARGS);
251 Datum
252 hstore_fetchval_n_numeric(PG_FUNCTION_ARGS)
253 {
254         HStore          *hs = PG_GETARG_HS(0);
255         int                     i = PG_GETARG_INT32(1);
256         HStoreValue     *v = NULL;
257
258         if (!HS_ISEMPTY(hs))
259                 v = getHStoreValue(VARDATA(hs), HS_FLAG_HSTORE | HS_FLAG_ARRAY, i);
260
261         if (v && v->type == hsvNumeric)
262         {
263                 Numeric         out = palloc(VARSIZE_ANY(v->numeric));
264
265                 memcpy(out, v->numeric, VARSIZE_ANY(v->numeric));
266                 PG_RETURN_NUMERIC(out);
267         }
268
269         PG_RETURN_NULL();
270 }
271
272 PG_FUNCTION_INFO_V1(hstore_fetchval_n_boolean);
273 Datum           hstore_fetchval_n_boolean(PG_FUNCTION_ARGS);
274 Datum
275 hstore_fetchval_n_boolean(PG_FUNCTION_ARGS)
276 {
277         HStore          *hs = PG_GETARG_HS(0);
278         int                     i = PG_GETARG_INT32(1);
279         HStoreValue     *v = NULL;
280
281         if (!HS_ISEMPTY(hs))
282                 v = getHStoreValue(VARDATA(hs), HS_FLAG_HSTORE | HS_FLAG_ARRAY, i);
283
284         if (v && v->type == hsvBool)
285                 PG_RETURN_BOOL(v->boolean);
286
287         PG_RETURN_NULL();
288 }
289
290 static bool
291 h_atoi(char *c, int l, int *acc)
292 {
293         bool    negative = false;
294         char    *p = c;
295
296         *acc = 0;
297
298         while(isspace(*p) && p - c < l)
299                 p++;
300
301         if (p - c >= l)
302                 return false;
303
304         if (*p == '-')
305         {
306                 negative = true;
307                 p++;
308         }
309         else if (*p == '+')
310         {
311                 p++;
312         }
313
314         if (p - c >= l)
315                 return false;
316
317
318         while(p - c < l)
319         {
320                 if (!isdigit(*p))
321                         return false;
322
323                 *acc *= 10;
324                 *acc += (*p - '0');
325                 p++;
326         }
327
328         if (negative)
329                 *acc = - *acc;
330
331         return true;
332 }
333
334 static HStoreValue*
335 hstoreDeepFetch(HStore *in, ArrayType *path)
336 {
337         HStoreValue                     *v = NULL;
338         static HStoreValue      init /* could be returned */;
339         Datum                           *path_elems;
340         bool                            *path_nulls;
341         int                                     path_len, i;
342
343         Assert(ARR_ELEMTYPE(path) == TEXTOID);
344
345         if (ARR_NDIM(path) > 1)
346                 ereport(ERROR,
347                                 (errcode(ERRCODE_ARRAY_SUBSCRIPT_ERROR),
348                                  errmsg("wrong number of array subscripts")));
349
350         if (HS_ROOT_COUNT(in) == 0)
351                 return NULL;
352
353         deconstruct_array(path, TEXTOID, -1, false, 'i',
354                                           &path_elems, &path_nulls, &path_len);
355
356         init.type = hsvBinary;
357         init.size = VARSIZE(in);
358         init.binary.data = VARDATA(in);
359         init.binary.len = VARSIZE_ANY_EXHDR(in);
360
361         v = &init;
362
363         if (path_len == 0)
364                 return v;
365
366         for(i=0; v != NULL && i<path_len; i++)
367         {
368                 uint32  header;
369
370                 if (v->type != hsvBinary || path_nulls[i])
371                         return NULL;
372
373                 header = *(uint32*)v->binary.data;
374
375                 if (header & HS_FLAG_HSTORE)
376                 {
377                         v = findUncompressedHStoreValue(v->binary.data, HS_FLAG_HSTORE,
378                                                                                         NULL,
379                                                                                         VARDATA_ANY(path_elems[i]),
380                                                                                         VARSIZE_ANY_EXHDR(path_elems[i]));
381                 }
382                 else if (header & HS_FLAG_ARRAY)
383                 {
384                         int ith;
385
386                         if (h_atoi(VARDATA_ANY(path_elems[i]),
387                                            VARSIZE_ANY_EXHDR(path_elems[i]), &ith) == false)
388                                 return NULL;
389
390                         if (ith < 0)
391                         {
392                                 if (-ith > (int)(header & HS_COUNT_MASK))
393                                         return NULL;
394                                 else
395                                         ith = ((int)(header & HS_COUNT_MASK)) + ith;
396                         }
397                         else
398                         {
399                                 if (ith >= (int)(header & HS_COUNT_MASK))
400                                         return NULL;
401                         }
402
403                         v = getHStoreValue(v->binary.data, HS_FLAG_ARRAY, ith);
404                 }
405                 else
406                 {
407                         elog(PANIC,"wrong header type: %08x", header);
408                 }
409         }
410
411         return v;
412 }
413
414 PG_FUNCTION_INFO_V1(hstore_fetchval_path);
415 Datum           hstore_fetchval_path(PG_FUNCTION_ARGS);
416 Datum
417 hstore_fetchval_path(PG_FUNCTION_ARGS)
418 {
419         HStore          *hs = PG_GETARG_HS(0);
420         ArrayType       *path = PG_GETARG_ARRAYTYPE_P(1);
421         text            *out;
422
423         if ((out = HStoreValueToText(hstoreDeepFetch(hs, path))) == NULL)
424                 PG_RETURN_NULL();
425         else
426                 PG_RETURN_TEXT_P(out);
427 }
428
429 PG_FUNCTION_INFO_V1(hstore_fetchval_path_numeric);
430 Datum           hstore_fetchval_path_numeric(PG_FUNCTION_ARGS);
431 Datum
432 hstore_fetchval_path_numeric(PG_FUNCTION_ARGS)
433 {
434         HStore          *hs = PG_GETARG_HS(0);
435         ArrayType       *path = PG_GETARG_ARRAYTYPE_P(1);
436         HStoreValue     *v = NULL;
437
438         if (!HS_ISEMPTY(hs))
439                 v = hstoreDeepFetch(hs, path);
440
441         if (v && v->type == hsvNumeric)
442         {
443                 Numeric         out = palloc(VARSIZE_ANY(v->numeric));
444
445                 memcpy(out, v->numeric, VARSIZE_ANY(v->numeric));
446                 PG_RETURN_NUMERIC(out);
447         }
448
449         PG_RETURN_NULL();
450 }
451
452 PG_FUNCTION_INFO_V1(hstore_fetchval_path_boolean);
453 Datum           hstore_fetchval_path_boolean(PG_FUNCTION_ARGS);
454 Datum
455 hstore_fetchval_path_boolean(PG_FUNCTION_ARGS)
456 {
457         HStore          *hs = PG_GETARG_HS(0);
458         ArrayType       *path = PG_GETARG_ARRAYTYPE_P(1);
459         HStoreValue     *v = NULL;
460
461         if (!HS_ISEMPTY(hs))
462                 v = hstoreDeepFetch(hs, path);
463
464         if (v && v->type == hsvBool)
465                 PG_RETURN_BOOL(v->boolean);
466
467         PG_RETURN_NULL();
468 }
469
470 static HStore *
471 HStoreValueToHStore(HStoreValue *v)
472 {
473         HStore                  *out;
474
475         if (v == NULL || v->type == hsvNull)
476         {
477                 out = NULL;
478         }
479         else if (v->type == hsvString || v->type == hsvBool ||
480                          v->type == hsvNumeric)
481         {
482                 ToHStoreState   *state = NULL;
483                 HStoreValue             *res;
484                 int                             r;
485                 HStoreValue             scalarArray;
486
487                 scalarArray.type = hsvArray;
488                 scalarArray.array.scalar = true;
489                 scalarArray.array.nelems = 1;
490
491                 pushHStoreValue(&state, WHS_BEGIN_ARRAY, &scalarArray);
492                 pushHStoreValue(&state, WHS_ELEM, v);
493                 res = pushHStoreValue(&state, WHS_END_ARRAY, NULL);
494
495                 out = palloc(VARHDRSZ + res->size);
496                 SET_VARSIZE(out, VARHDRSZ + res->size);
497                 r = compressHStore(res, VARDATA(out));
498                 Assert(r <= res->size);
499                 SET_VARSIZE(out, r + VARHDRSZ);
500         }
501         else
502         {
503                 out = palloc(VARHDRSZ + v->size);
504
505                 Assert(v->type == hsvBinary);
506                 SET_VARSIZE(out, VARHDRSZ + v->binary.len);
507                 memcpy(VARDATA(out), v->binary.data, v->binary.len);
508         }
509
510         return out;
511 }
512
513 PG_FUNCTION_INFO_V1(hstore_fetchval_hstore);
514 Datum           hstore_fetchval_hstore(PG_FUNCTION_ARGS);
515 Datum
516 hstore_fetchval_hstore(PG_FUNCTION_ARGS)
517 {
518         HStore          *hs = PG_GETARG_HS(0);
519         text            *key = PG_GETARG_TEXT_PP(1);
520         HStoreValue     *v = NULL;
521         HStore          *out;
522
523         if (!HS_ISEMPTY(hs))
524                 v = findUncompressedHStoreValue(VARDATA(hs),
525                                                                                 HS_FLAG_HSTORE | HS_FLAG_ARRAY,
526                                                                                 NULL,
527                                                                                 VARDATA_ANY(key),
528                                                                                 VARSIZE_ANY_EXHDR(key));
529
530
531         if ((out = HStoreValueToHStore(v)) == NULL)
532                 PG_RETURN_NULL();
533         else
534                 PG_RETURN_POINTER(out);
535 }
536
537 PG_FUNCTION_INFO_V1(hstore_fetchval_n_hstore);
538 Datum           hstore_fetchval_n_hstore(PG_FUNCTION_ARGS);
539 Datum
540 hstore_fetchval_n_hstore(PG_FUNCTION_ARGS)
541 {
542         HStore          *hs = PG_GETARG_HS(0);
543         int                     i = PG_GETARG_INT32(1);
544         HStoreValue     *v = NULL;
545         HStore          *out;
546
547         if (!HS_ISEMPTY(hs))
548                 v = getHStoreValue(VARDATA(hs), HS_FLAG_HSTORE | HS_FLAG_ARRAY, i);
549
550         if ((out = HStoreValueToHStore(v)) == NULL)
551                 PG_RETURN_NULL();
552         else
553                 PG_RETURN_POINTER(out);
554 }
555
556 PG_FUNCTION_INFO_V1(hstore_fetchval_path_hstore);
557 Datum           hstore_fetchval_path_hstore(PG_FUNCTION_ARGS);
558 Datum
559 hstore_fetchval_path_hstore(PG_FUNCTION_ARGS)
560 {
561         HStore          *hs = PG_GETARG_HS(0);
562         ArrayType       *path = PG_GETARG_ARRAYTYPE_P(1);
563         HStore          *out;
564
565         if ((out = HStoreValueToHStore(hstoreDeepFetch(hs, path))) == NULL)
566                 PG_RETURN_NULL();
567         else
568                 PG_RETURN_POINTER(out);
569 }
570
571 PG_FUNCTION_INFO_V1(hstore_exists);
572 Datum           hstore_exists(PG_FUNCTION_ARGS);
573 Datum
574 hstore_exists(PG_FUNCTION_ARGS)
575 {
576         HStore          *hs = PG_GETARG_HS(0);
577         text            *key = PG_GETARG_TEXT_PP(1);
578         HStoreValue     *v = NULL;
579
580         if (!HS_ISEMPTY(hs))
581                 v = findUncompressedHStoreValue(VARDATA(hs),
582                                                                                 HS_FLAG_HSTORE | HS_FLAG_ARRAY,
583                                                                                 NULL,
584                                                                                 VARDATA_ANY(key),
585                                                                                 VARSIZE_ANY_EXHDR(key));
586
587         PG_RETURN_BOOL(v != NULL);
588 }
589
590
591 PG_FUNCTION_INFO_V1(hstore_exists_idx);
592 Datum           hstore_exists_idx(PG_FUNCTION_ARGS);
593 Datum
594 hstore_exists_idx(PG_FUNCTION_ARGS)
595 {
596         HStore          *hs = PG_GETARG_HS(0);
597         int                     ith = PG_GETARG_INT32(1);
598         HStoreValue     *v = NULL;
599
600         if (!HS_ISEMPTY(hs))
601                 v = getHStoreValue(VARDATA(hs), HS_FLAG_HSTORE | HS_FLAG_ARRAY, ith);
602
603         PG_RETURN_BOOL(v != NULL);
604 }
605
606 PG_FUNCTION_INFO_V1(hstore_exists_path);
607 Datum           hstore_exists_path(PG_FUNCTION_ARGS);
608 Datum
609 hstore_exists_path(PG_FUNCTION_ARGS)
610 {
611         HStore          *hs = PG_GETARG_HS(0);
612         ArrayType       *path = PG_GETARG_ARRAYTYPE_P(1);
613
614         PG_RETURN_BOOL(hstoreDeepFetch(hs, path) != NULL);
615 }
616
617
618
619 PG_FUNCTION_INFO_V1(hstore_exists_any);
620 Datum           hstore_exists_any(PG_FUNCTION_ARGS);
621 Datum
622 hstore_exists_any(PG_FUNCTION_ARGS)
623 {
624         HStore                  *hs = PG_GETARG_HS(0);
625         ArrayType               *keys = PG_GETARG_ARRAYTYPE_P(1);
626         HStoreValue             *v = arrayToHStoreSortedArray(keys);
627         int                             i;
628         uint32                  *plowbound = NULL, lowbound = 0;
629         bool                    res = false;
630
631         if (HS_ISEMPTY(hs) || v == NULL || v->hash.npairs == 0)
632                 PG_RETURN_BOOL(false);
633
634         if (HS_ROOT_IS_HASH(hs))
635                 plowbound = &lowbound;
636         /*
637          * we exploit the fact that the pairs list is already sorted into strictly
638          * increasing order to narrow the findUncompressedHStoreValue search; each search can
639          * start one entry past the previous "found" entry, or at the lower bound
640          * of the last search.
641          */
642         for (i = 0; i < v->array.nelems; i++)
643         {
644                 if (findUncompressedHStoreValueByValue(VARDATA(hs), HS_FLAG_HSTORE | HS_FLAG_ARRAY, plowbound,
645                                                                                            v->array.elems + i) != NULL)
646                 {
647                         res = true;
648                         break;
649                 }
650         }
651
652         PG_RETURN_BOOL(res);
653 }
654
655
656 PG_FUNCTION_INFO_V1(hstore_exists_all);
657 Datum           hstore_exists_all(PG_FUNCTION_ARGS);
658 Datum
659 hstore_exists_all(PG_FUNCTION_ARGS)
660 {
661         HStore                  *hs = PG_GETARG_HS(0);
662         ArrayType               *keys = PG_GETARG_ARRAYTYPE_P(1);
663         HStoreValue             *v = arrayToHStoreSortedArray(keys);
664         int                             i;
665         uint32                  *plowbound = NULL, lowbound = 0;
666         bool                    res = true;
667
668         if (HS_ISEMPTY(hs) || v == NULL || v->array.nelems == 0)
669         {
670
671                 if (v == NULL || v->array.nelems == 0)
672                         PG_RETURN_BOOL(true); /* compatibility */
673                 else
674                         PG_RETURN_BOOL(false);
675         }
676
677         if (HS_ROOT_IS_HASH(hs))
678                 plowbound = &lowbound;
679         /*
680          * we exploit the fact that the pairs list is already sorted into strictly
681          * increasing order to narrow the findUncompressedHStoreValue search;
682          * each search can start one entry past the previous "found" entry,
683          * or at the lower bound of the last search.
684          */
685         for (i = 0; i < v->array.nelems; i++)
686         {
687                 if (findUncompressedHStoreValueByValue(VARDATA(hs),
688                                                                                            HS_FLAG_HSTORE | HS_FLAG_ARRAY,
689                                                                                            plowbound,
690                                                                                            v->array.elems + i) == NULL)
691                 {
692                         res = false;
693                         break;
694                 }
695         }
696
697         PG_RETURN_BOOL(res);
698 }
699
700
701 PG_FUNCTION_INFO_V1(hstore_defined);
702 Datum           hstore_defined(PG_FUNCTION_ARGS);
703 Datum
704 hstore_defined(PG_FUNCTION_ARGS)
705 {
706         HStore          *hs = PG_GETARG_HS(0);
707         text            *key = PG_GETARG_TEXT_PP(1);
708         HStoreValue     *v = NULL;
709
710         if (!HS_ISEMPTY(hs))
711                 v = findUncompressedHStoreValue(VARDATA(hs),
712                                                                                 HS_FLAG_HSTORE | HS_FLAG_ARRAY,
713                                                                                 NULL,
714                                                                                 VARDATA_ANY(key),
715                                                                                 VARSIZE_ANY_EXHDR(key));
716
717         PG_RETURN_BOOL(!(v == NULL || v->type == hsvNull));
718 }
719
720
721 PG_FUNCTION_INFO_V1(hstore_delete);
722 Datum           hstore_delete(PG_FUNCTION_ARGS);
723 Datum
724 hstore_delete(PG_FUNCTION_ARGS)
725 {
726         HStore                  *in = PG_GETARG_HS(0);
727         text                    *key = PG_GETARG_TEXT_PP(1);
728         char                    *keyptr = VARDATA_ANY(key);
729         int                             keylen = VARSIZE_ANY_EXHDR(key);
730         HStore                  *out = palloc(VARSIZE(in));
731         ToHStoreState   *toState = NULL;
732         HStoreIterator  *it;
733         uint32                  r;
734         HStoreValue             v, *res = NULL;
735         bool                    skipNested = false;
736
737         SET_VARSIZE(out, VARSIZE(in));
738
739         if (HS_ISEMPTY(in))
740                 PG_RETURN_POINTER(out);
741
742         it = HStoreIteratorInit(VARDATA(in));
743
744         while((r = HStoreIteratorGet(&it, &v, skipNested)) != 0)
745         {
746                 skipNested = true;
747
748                 if ((r == WHS_ELEM || r == WHS_KEY) &&
749                         (v.type == hsvString && keylen == v.string.len &&
750                          memcmp(keyptr, v.string.val, keylen) == 0))
751                 {
752                         if (r == WHS_KEY)
753                                 /* skip corresponding value */
754                                 HStoreIteratorGet(&it, &v, true);
755
756                         continue;
757                 }
758
759                 res = pushHStoreValue(&toState, r, &v);
760         }
761
762         if (res == NULL || (res->type == hsvArray && res->array.nelems == 0) ||
763                                            (res->type == hsvHash && res->hash.npairs == 0) )
764         {
765                 SET_VARSIZE(out, VARHDRSZ);
766         }
767         else
768         {
769                 r = compressHStore(res, VARDATA(out));
770                 SET_VARSIZE(out, r + VARHDRSZ);
771         }
772
773         PG_RETURN_POINTER(out);
774 }
775
776 PG_FUNCTION_INFO_V1(hstore_delete_array);
777 Datum           hstore_delete_array(PG_FUNCTION_ARGS);
778 Datum
779 hstore_delete_array(PG_FUNCTION_ARGS)
780 {
781         HStore                  *in = PG_GETARG_HS(0);
782         HStore                  *out = palloc(VARSIZE(in));
783         HStoreValue     *a = arrayToHStoreSortedArray(PG_GETARG_ARRAYTYPE_P(1));
784         HStoreIterator  *it;
785         ToHStoreState   *toState = NULL;
786         uint32                  r, i = 0;
787         HStoreValue             v, *res = NULL;
788         bool                    skipNested = false;
789         bool                    isHash = false;
790
791
792         if (HS_ISEMPTY(in) || a == NULL || a->array.nelems == 0)
793         {
794                 memcpy(out, in, VARSIZE(in));
795                 PG_RETURN_POINTER(out);
796         }
797
798         it = HStoreIteratorInit(VARDATA(in));
799
800         while((r = HStoreIteratorGet(&it, &v, skipNested)) != 0)
801         {
802
803                 if (skipNested == false)
804                 {
805                         Assert(v.type == hsvArray || v.type == hsvHash);
806                         isHash = (v.type == hsvArray) ? false : true;
807                         skipNested = true;
808                 }
809
810                 if ((r == WHS_ELEM || r == WHS_KEY) && v.type == hsvString &&
811                         i < a->array.nelems)
812                 {
813                         int diff;
814
815                         if (isHash)
816                         {
817                                 do {
818                                         diff = compareHStoreStringValue(&v, a->array.elems + i,
819                                                                                                         NULL);
820
821                                         if (diff >= 0)
822                                                 i++;
823                                 } while(diff > 0 && i < a->array.nelems);
824                         }
825                         else
826                         {
827                                 diff = (findInHStoreSortedArray(a, NULL,
828                                                                                                 v.string.val,
829                                                                                                 v.string.len) == NULL) ? 1 : 0;
830                         }
831
832                         if (diff == 0)
833                         {
834                                 if (r == WHS_KEY)
835                                         /* skip corresponding value */
836                                         HStoreIteratorGet(&it, &v, true);
837
838                                 continue;
839                         }
840                 }
841
842                 res = pushHStoreValue(&toState, r, &v);
843         }
844
845         if (res == NULL || (res->type == hsvArray && res->array.nelems == 0) ||
846                                            (res->type == hsvHash && res->hash.npairs == 0) )
847         {
848                 SET_VARSIZE(out, VARHDRSZ);
849         }
850         else
851         {
852                 r = compressHStore(res, VARDATA(out));
853                 SET_VARSIZE(out, r + VARHDRSZ);
854         }
855
856         PG_RETURN_POINTER(out);
857 }
858
859
860 PG_FUNCTION_INFO_V1(hstore_delete_hstore);
861 Datum           hstore_delete_hstore(PG_FUNCTION_ARGS);
862 Datum
863 hstore_delete_hstore(PG_FUNCTION_ARGS)
864 {
865         HStore                  *hs1 = PG_GETARG_HS(0);
866         HStore                  *hs2 = PG_GETARG_HS(1);
867         HStore                  *out = palloc(VARSIZE(hs1));
868         HStoreIterator  *it1, *it2;
869         ToHStoreState   *toState = NULL;
870         uint32                  r1, r2;
871         HStoreValue             v1, v2, *res = NULL;
872         bool                    isHash1, isHash2;
873
874         if (HS_ISEMPTY(hs1) || HS_ISEMPTY(hs2))
875         {
876                 memcpy(out, hs1, VARSIZE(hs1));
877                 PG_RETURN_POINTER(out);
878         }
879
880         it1 = HStoreIteratorInit(VARDATA(hs1));
881         r1 = HStoreIteratorGet(&it1, &v1, false);
882         isHash1 = (v1.type == hsvArray) ? false : true;
883
884         it2 = HStoreIteratorInit(VARDATA(hs2));
885         r2 = HStoreIteratorGet(&it2, &v2, false);
886         isHash2 = (v2.type == hsvArray) ? false : true;
887
888         res = pushHStoreValue(&toState, r1, &v1);
889
890         if (isHash1 == true && isHash2 == true)
891         {
892                 bool                    fin2 = false,
893                                                 keyIsDef = false;
894
895                 while((r1 = HStoreIteratorGet(&it1, &v1, true)) != 0)
896                 {
897                         if (r1 == WHS_KEY && fin2 == false)
898                         {
899                                 int diff  = 1;
900
901                                 if (keyIsDef)
902                                         r2 = WHS_KEY;
903
904                                 while(keyIsDef ||
905                                           (r2 = HStoreIteratorGet(&it2, &v2, true)) != 0)
906                                 {
907                                         if (r2 != WHS_KEY)
908                                                 continue;
909
910                                         diff = compareHStoreStringValue(&v1, &v2, NULL);
911
912                                         if (diff > 0 && keyIsDef)
913                                                 keyIsDef = false;
914                                         if (diff <= 0)
915                                                 break;
916                                 }
917
918                                 if (r2 == 0)
919                                 {
920                                         fin2 = true;
921                                 }
922                                 else if (diff == 0)
923                                 {
924                                         HStoreValue             vk;
925
926                                         keyIsDef = false;
927
928                                         r1 = HStoreIteratorGet(&it1, &vk, true);
929                                         r2 = HStoreIteratorGet(&it2, &v2, true);
930
931                                         Assert(r1 == WHS_VALUE && r2 == WHS_VALUE);
932
933                                         if (compareHStoreValue(&vk, &v2) != 0)
934                                         {
935                                                 res = pushHStoreValue(&toState, WHS_KEY, &v1);
936                                                 res = pushHStoreValue(&toState, WHS_VALUE, &vk);
937                                         }
938
939                                         continue;
940                                 }
941                                 else
942                                 {
943                                         keyIsDef = true;
944                                 }
945                         }
946
947                         res = pushHStoreValue(&toState, r1, &v1);
948                 }
949         }
950         else
951         {
952                 while((r1 = HStoreIteratorGet(&it1, &v1, true)) != 0)
953                 {
954
955                         if (r1 == WHS_ELEM || r1 == WHS_KEY)
956                         {
957                                 int diff = 1;
958
959                                 it2 = HStoreIteratorInit(VARDATA(hs2));
960
961                                 r2 = HStoreIteratorGet(&it2, &v2, false);
962
963                                 while(diff && (r2 = HStoreIteratorGet(&it2, &v2, true)) != 0)
964                                 {
965                                         if (r2 == WHS_KEY || r2 == WHS_VALUE || r2 == WHS_ELEM)
966                                                 diff = compareHStoreValue(&v1, &v2);
967                                 }
968
969                                 if (diff == 0)
970                                 {
971                                         if (r1 == WHS_KEY)
972                                                 HStoreIteratorGet(&it1, &v1, true);
973                                         continue;
974                                 }
975                         }
976
977                         res = pushHStoreValue(&toState, r1, &v1);
978                 }
979         }
980
981         if (res == NULL || (res->type == hsvArray && res->array.nelems == 0) ||
982                                            (res->type == hsvHash && res->hash.npairs == 0) )
983         {
984                 SET_VARSIZE(out, VARHDRSZ);
985         }
986         else
987         {
988                 int r = compressHStore(res, VARDATA(out));
989                 SET_VARSIZE(out, r + VARHDRSZ);
990         }
991
992         PG_RETURN_POINTER(out);
993 }
994
995 static HStoreValue*
996 deletePathDo(HStoreIterator **it, Datum *path_elems,
997                          bool *path_nulls, int path_len,
998                          ToHStoreState  **st, int level)
999 {
1000         HStoreValue     v, *res = NULL;
1001         int                     r;
1002
1003         r = HStoreIteratorGet(it, &v, false);
1004
1005         if (r == WHS_BEGIN_ARRAY)
1006         {
1007                 int     skipIdx, i;
1008                 uint32  n = v.array.nelems;
1009
1010                 skipIdx = n;
1011                 if (level >= path_len || path_nulls[level] ||
1012                         h_atoi(VARDATA_ANY(path_elems[level]),
1013                                    VARSIZE_ANY_EXHDR(path_elems[level]), &skipIdx) == false)
1014                 {
1015                         skipIdx = n;
1016                 }
1017                 else if (skipIdx < 0)
1018                 {
1019                         if (-skipIdx > n)
1020                                 skipIdx = n;
1021                         else
1022                                 skipIdx = n + skipIdx;
1023                 }
1024
1025                 if (skipIdx > n)
1026                         skipIdx = n;
1027
1028                 if (skipIdx == 0 && n == 1)
1029                 {
1030                         r = HStoreIteratorGet(it, &v, true);
1031                         Assert(r == WHS_ELEM);
1032                         r = HStoreIteratorGet(it, &v, true);
1033                         Assert(r == WHS_END_ARRAY);
1034                         return NULL;
1035                 }
1036
1037                 pushHStoreValue(st, r, &v);
1038
1039                 for(i=0; i<skipIdx; i++) {
1040                         r = HStoreIteratorGet(it, &v, true);
1041                         Assert(r == WHS_ELEM);
1042                         res = pushHStoreValue(st, r, &v);
1043                 }
1044
1045                 if (level >= path_len || skipIdx == n) {
1046                         r = HStoreIteratorGet(it, &v, true);
1047                         Assert(r == WHS_END_ARRAY);
1048                         res = pushHStoreValue(st, r, &v);
1049                         return res;
1050                 }
1051
1052                 if (level == path_len - 1)
1053                 {
1054                         /* last level in path, skip all elem */
1055                         r = HStoreIteratorGet(it, &v, true);
1056                         Assert(r == WHS_ELEM);
1057                 }
1058                 else
1059                 {
1060                         res = deletePathDo(it, path_elems, path_nulls, path_len, st,
1061                                                            level + 1);
1062                 }
1063
1064                 for(i = skipIdx + 1; i<n; i++) {
1065                         r = HStoreIteratorGet(it, &v, true);
1066                         Assert(r == WHS_ELEM);
1067                         res = pushHStoreValue(st, r, &v);
1068                 }
1069
1070                 r = HStoreIteratorGet(it, &v, true);
1071                 Assert(r == WHS_END_ARRAY);
1072                 res = pushHStoreValue(st, r, &v);
1073         }
1074         else if (r == WHS_BEGIN_HASH)
1075         {
1076                 int                     i;
1077                 uint32          n = v.hash.npairs;
1078                 HStoreValue     k;
1079                 bool            done = false;
1080
1081                 if (n == 1 && level == path_len - 1)
1082                 {
1083                         r = HStoreIteratorGet(it, &k, false);
1084                         Assert(r == WHS_KEY);
1085
1086                         if ( path_nulls[level] == false &&
1087                                  k.string.len == VARSIZE_ANY_EXHDR(path_elems[level]) &&
1088                                  memcmp(k.string.val, VARDATA_ANY(path_elems[level]),
1089                                                 k.string.len) == 0)
1090                         {
1091                                 r = HStoreIteratorGet(it, &v, true);
1092                                 Assert(r == WHS_VALUE);
1093                                 r = HStoreIteratorGet(it, &v, true);
1094                                 Assert(r == WHS_END_HASH);
1095                                 return NULL;
1096                         }
1097
1098                         pushHStoreValue(st, WHS_BEGIN_HASH, &v);
1099                         pushHStoreValue(st, WHS_KEY, &k);
1100                         r = HStoreIteratorGet(it, &v, true);
1101                         Assert(r == WHS_VALUE);
1102                         pushHStoreValue(st, r, &v);
1103                         r = HStoreIteratorGet(it, &v, true);
1104                         Assert(r == WHS_END_HASH);
1105                         return pushHStoreValue(st, r, &v);
1106                 }
1107
1108                 pushHStoreValue(st, WHS_BEGIN_HASH, &v);
1109
1110                 if (level >= path_len || path_nulls[level])
1111                         done = true;
1112
1113                 for(i=0; i<n; i++)
1114                 {
1115                         r = HStoreIteratorGet(it, &k, false);
1116                         Assert(r == WHS_KEY);
1117
1118                         if (done == false &&
1119                                 k.string.len == VARSIZE_ANY_EXHDR(path_elems[level]) &&
1120                                 memcmp(k.string.val, VARDATA_ANY(path_elems[level]),
1121                                            k.string.len) == 0)
1122                         {
1123                                 done = true;
1124
1125                                 if (level == path_len - 1)
1126                                 {
1127                                         r = HStoreIteratorGet(it, &v, true);
1128                                         Assert(r == WHS_VALUE);
1129                                 }
1130                                 else
1131                                 {
1132                                         pushHStoreValue(st, r, &k);
1133                                         res = deletePathDo(it, path_elems, path_nulls, path_len,
1134                                                                            st, level + 1);
1135                                         if (res == NULL)
1136                                         {
1137                                                 v.type = hsvNull;
1138                                                 pushHStoreValue(st, WHS_VALUE, &v);
1139                                         }
1140                                 }
1141
1142                                 continue;
1143                         }
1144
1145                         pushHStoreValue(st, r, &k);
1146                         r = HStoreIteratorGet(it, &v, true);
1147                         Assert(r == WHS_VALUE);
1148                         pushHStoreValue(st, r, &v);
1149                 }
1150
1151                 r = HStoreIteratorGet(it, &v, true);
1152                 Assert(r == WHS_END_HASH);
1153                 res = pushHStoreValue(st, r, &v);
1154         }
1155         else if (r == WHS_ELEM || r == WHS_VALUE) /* just a string or null */
1156         {
1157                 pushHStoreValue(st, r, &v);
1158                 res = (void*)0x01; /* dummy value */
1159         }
1160         else
1161         {
1162                 elog(PANIC, "impossible state");
1163         }
1164
1165         return res;
1166 }
1167
1168
1169 PG_FUNCTION_INFO_V1(hstore_delete_path);
1170 Datum           hstore_delete_path(PG_FUNCTION_ARGS);
1171 Datum
1172 hstore_delete_path(PG_FUNCTION_ARGS)
1173 {
1174         HStore                  *in = PG_GETARG_HS(0);
1175         HStore                  *out = palloc(VARSIZE(in));
1176         ArrayType               *path = PG_GETARG_ARRAYTYPE_P(1);
1177         HStoreValue             *res = NULL;
1178         Datum                   *path_elems;
1179         bool                    *path_nulls;
1180         int                             path_len;
1181         HStoreIterator  *it;
1182         ToHStoreState   *st = NULL;
1183
1184         Assert(ARR_ELEMTYPE(path) == TEXTOID);
1185
1186         if (ARR_NDIM(path) > 1)
1187                 ereport(ERROR,
1188                                 (errcode(ERRCODE_ARRAY_SUBSCRIPT_ERROR),
1189                                  errmsg("wrong number of array subscripts")));
1190
1191         if (HS_ROOT_COUNT(in) == 0)
1192         {
1193                 memcpy(out, in, VARSIZE(in));
1194                 PG_RETURN_POINTER(out);
1195         }
1196
1197         deconstruct_array(path, TEXTOID, -1, false, 'i',
1198                                           &path_elems, &path_nulls, &path_len);
1199
1200         if (path_len == 0)
1201         {
1202                 memcpy(out, in, VARSIZE(in));
1203                 PG_RETURN_POINTER(out);
1204         }
1205
1206         it = HStoreIteratorInit(VARDATA(in));
1207
1208         res = deletePathDo(&it, path_elems, path_nulls, path_len, &st, 0);
1209
1210         if (res == NULL)
1211         {
1212                 SET_VARSIZE(out, VARHDRSZ);
1213         }
1214         else
1215         {
1216                 int                             sz;
1217
1218                 sz = compressHStore(res, VARDATA(out));
1219                 SET_VARSIZE(out, sz + VARHDRSZ);
1220         }
1221
1222         PG_RETURN_POINTER(out);
1223 }
1224
1225 PG_FUNCTION_INFO_V1(hstore_delete_idx);
1226 Datum           hstore_delete_idx(PG_FUNCTION_ARGS);
1227 Datum
1228 hstore_delete_idx(PG_FUNCTION_ARGS)
1229 {
1230         HStore                  *in = PG_GETARG_HS(0);
1231         int                             idx = PG_GETARG_INT32(1);
1232         HStore                  *out = palloc(VARSIZE(in));
1233         ToHStoreState   *toState = NULL;
1234         HStoreIterator  *it;
1235         uint32                  r, i = 0, n;
1236         HStoreValue             v, *res = NULL;
1237
1238         if (HS_ISEMPTY(in))
1239         {
1240                 memcpy(out, in, VARSIZE(in));
1241                 PG_RETURN_POINTER(out);
1242         }
1243
1244         it = HStoreIteratorInit(VARDATA(in));
1245
1246         r = HStoreIteratorGet(&it, &v, false);
1247         if (r == WHS_BEGIN_ARRAY)
1248                 n = v.array.nelems;
1249         else
1250                 n = v.hash.npairs;
1251
1252         if (idx < 0)
1253         {
1254                 if (-idx > n)
1255                         idx = n;
1256                 else
1257                         idx = n + idx;
1258         }
1259
1260         if (idx >= n)
1261         {
1262                 memcpy(out, in, VARSIZE(in));
1263                 PG_RETURN_POINTER(out);
1264         }
1265
1266         pushHStoreValue(&toState, r, &v);
1267
1268         while((r = HStoreIteratorGet(&it, &v, true)) != 0)
1269         {
1270                 if (r == WHS_ELEM || r == WHS_KEY)
1271                 {
1272                         if (i++ == idx)
1273                         {
1274                                 if (r == WHS_KEY)
1275                                         HStoreIteratorGet(&it, &v, true); /* skip value */
1276                                 continue;
1277                         }
1278                 }
1279
1280                 res = pushHStoreValue(&toState, r, &v);
1281         }
1282
1283         if (res == NULL || (res->type == hsvArray && res->array.nelems == 0) ||
1284                                            (res->type == hsvHash && res->hash.npairs == 0) )
1285         {
1286                 SET_VARSIZE(out, VARHDRSZ);
1287         }
1288         else
1289         {
1290                 r = compressHStore(res, VARDATA(out));
1291                 SET_VARSIZE(out, r + VARHDRSZ);
1292         }
1293
1294         PG_RETURN_POINTER(out);
1295 }
1296
1297 static void
1298 convertScalarToString(HStoreValue *v)
1299 {
1300         switch(v->type) {
1301                 case hsvNull:
1302                         elog(ERROR, "key in hstore type could not be a NULL");
1303                         break;
1304                 case hsvBool:
1305                         v->type = hsvString;
1306                         v->string.val = pnstrdup((v->boolean) ? "t" : "f", 1);
1307                         v->string.len = 1;
1308                         v->size = sizeof(HEntry) + v->string.len;
1309                         break;
1310                 case hsvNumeric:
1311                         v->type = hsvString;
1312                         v->string.val = DatumGetCString(
1313                                                         DirectFunctionCall1(numeric_out,
1314                                                                                                 PointerGetDatum(v->numeric)));
1315                         v->string.len = strlen(v->string.val);
1316                         v->size = sizeof(HEntry) + v->string.len;
1317                         break;
1318                 case hsvString:
1319                         break;
1320                 default:
1321                         elog(PANIC,"Could not convert to string");
1322         }
1323 }
1324
1325 static HStoreValue *
1326 IteratorConcat(HStoreIterator **it1, HStoreIterator **it2,
1327                            ToHStoreState **toState)
1328 {
1329         uint32                  r1, r2, rk1, rk2;
1330         HStoreValue             v1, v2, *res = NULL;
1331
1332         r1 = rk1 = HStoreIteratorGet(it1, &v1, false);
1333         r2 = rk2 = HStoreIteratorGet(it2, &v2, false);
1334
1335         if (rk1 == WHS_BEGIN_HASH && rk2 == WHS_BEGIN_HASH)
1336         {
1337                 bool                    fin2 = false,
1338                                                 keyIsDef = false;
1339
1340                 res = pushHStoreValue(toState, r1, &v1);
1341
1342                 for(;;)
1343                 {
1344                         r1 = HStoreIteratorGet(it1, &v1, true);
1345
1346                         Assert(r1 == WHS_KEY || r1 == WHS_VALUE || r1 == WHS_END_HASH);
1347
1348                         if (r1 == WHS_KEY && fin2 == false)
1349                         {
1350                                 int diff  = 1;
1351
1352                                 if (keyIsDef)
1353                                         r2 = WHS_KEY;
1354
1355                                 while(keyIsDef || (r2 = HStoreIteratorGet(it2, &v2, true)) != 0)
1356                                 {
1357                                         if (r2 != WHS_KEY)
1358                                                 continue;
1359
1360                                         diff = compareHStoreStringValue(&v1, &v2, NULL);
1361
1362                                         if (diff > 0)
1363                                         {
1364                                                 if (keyIsDef)
1365                                                         keyIsDef = false;
1366
1367                                                 pushHStoreValue(toState, r2, &v2);
1368                                                 r2 = HStoreIteratorGet(it2, &v2, true);
1369                                                 Assert(r2 == WHS_VALUE);
1370                                                 pushHStoreValue(toState, r2, &v2);
1371                                         }
1372                                         else if (diff <= 0)
1373                                         {
1374                                                 break;
1375                                         }
1376                                 }
1377
1378                                 if (r2 == 0)
1379                                 {
1380                                         fin2 = true;
1381                                 }
1382                                 else if (diff == 0)
1383                                 {
1384                                         keyIsDef = false;
1385
1386                                         pushHStoreValue(toState, r1, &v1);
1387
1388                                         r1 = HStoreIteratorGet(it1, &v1, true); /* ignore */
1389                                         r2 = HStoreIteratorGet(it2, &v2, true); /* new val */
1390
1391                                         Assert(r1 == WHS_VALUE && r2 == WHS_VALUE);
1392                                         pushHStoreValue(toState, r2, &v2);
1393
1394                                         continue;
1395                                 }
1396                                 else
1397                                 {
1398                                         keyIsDef = true;
1399                                 }
1400                         }
1401                         else if (r1 == WHS_END_HASH)
1402                         {
1403                                 if (r2 != 0)
1404                                 {
1405                                         if (keyIsDef)
1406                                                 r2 = WHS_KEY;
1407
1408                                         while(keyIsDef ||
1409                                                   (r2 = HStoreIteratorGet(it2, &v2, true)) != 0)
1410                                         {
1411                                                 if (r2 != WHS_KEY)
1412                                                         continue;
1413
1414                                                 pushHStoreValue(toState, r2, &v2);
1415                                                 r2 = HStoreIteratorGet(it2, &v2, true);
1416                                                 Assert(r2 == WHS_VALUE);
1417                                                 pushHStoreValue(toState, r2, &v2);
1418                                                 keyIsDef = false;
1419                                         }
1420                                 }
1421
1422                                 res = pushHStoreValue(toState, r1, &v1);
1423                                 break;
1424                         }
1425
1426                         res = pushHStoreValue(toState, r1, &v1);
1427                 }
1428         }
1429         else if ((rk1 == WHS_BEGIN_HASH || rk1 == WHS_BEGIN_ARRAY) &&
1430                          (rk2 == WHS_BEGIN_HASH || rk2 == WHS_BEGIN_ARRAY))
1431         {
1432                 if (rk1 == WHS_BEGIN_HASH && rk2 == WHS_BEGIN_ARRAY &&
1433                         v2.array.nelems % 2 != 0)
1434                         elog(ERROR, "hstore's array must have even number of elements");
1435
1436                 res = pushHStoreValue(toState, r1, &v1);
1437
1438                 for(;;)
1439                 {
1440                         r1 = HStoreIteratorGet(it1, &v1, true);
1441                         if (r1 == WHS_END_HASH || r1 == WHS_END_ARRAY)
1442                                 break;
1443                         Assert(r1 == WHS_KEY || r1 == WHS_VALUE || r1 == WHS_ELEM);
1444                         pushHStoreValue(toState, r1, &v1);
1445                 }
1446
1447                 while((r2 = HStoreIteratorGet(it2, &v2, true)) != 0)
1448                 {
1449                         if (!(r2 == WHS_END_HASH || r2 == WHS_END_ARRAY))
1450                         {
1451                                 if (rk1 == WHS_BEGIN_HASH)
1452                                 {
1453                                         convertScalarToString(&v2);
1454                                         pushHStoreValue(toState, WHS_KEY, &v2);
1455                                         r2 = HStoreIteratorGet(it2, &v2, true);
1456                                         Assert(r2 == WHS_ELEM);
1457                                         pushHStoreValue(toState, WHS_VALUE, &v2);
1458                                 }
1459                                 else
1460                                 {
1461                                         pushHStoreValue(toState, WHS_ELEM, &v2);
1462                                 }
1463                         }
1464                 }
1465
1466                 res = pushHStoreValue(toState,
1467                                                           (rk1 == WHS_BEGIN_HASH) ? WHS_END_HASH : WHS_END_ARRAY,
1468                                                           NULL/* signal to sort */);
1469         }
1470         else if ((rk1 & (WHS_VALUE | WHS_ELEM)) != 0)
1471         {
1472                 if (v2.type == hsvArray && v2.array.scalar)
1473                 {
1474                         Assert(v2.array.nelems == 1);
1475                         r2 = HStoreIteratorGet(it2, &v2, false);
1476                         pushHStoreValue(toState, r1, &v2);
1477                 }
1478                 else
1479                 {
1480                         res = pushHStoreValue(toState, r2, &v2);
1481                         while((r2 = HStoreIteratorGet(it2, &v2, true)) != 0)
1482                                 res = pushHStoreValue(toState, r2, &v2);
1483                 }
1484         }
1485         else
1486         {
1487                 elog(ERROR, "invalid concatnation of hstores");
1488         }
1489
1490         return res;
1491 }
1492
1493 PG_FUNCTION_INFO_V1(hstore_concat);
1494 Datum           hstore_concat(PG_FUNCTION_ARGS);
1495 Datum
1496 hstore_concat(PG_FUNCTION_ARGS)
1497 {
1498         HStore                  *hs1 = PG_GETARG_HS(0);
1499         HStore                  *hs2 = PG_GETARG_HS(1);
1500         HStore                  *out = palloc(VARSIZE(hs1) + VARSIZE(hs2));
1501         ToHStoreState   *toState = NULL;
1502         HStoreValue             *res;
1503         HStoreIterator  *it1, *it2;
1504
1505         if (HS_ISEMPTY(hs1))
1506         {
1507                 memcpy(out, hs2, VARSIZE(hs2));
1508                 PG_RETURN_POINTER(out);
1509         }
1510         else if (HS_ISEMPTY(hs2))
1511         {
1512                 memcpy(out, hs1, VARSIZE(hs1));
1513                 PG_RETURN_POINTER(out);
1514         }
1515
1516         it1 = HStoreIteratorInit(VARDATA(hs1));
1517         it2 = HStoreIteratorInit(VARDATA(hs2));
1518
1519         res = IteratorConcat(&it1, &it2, &toState);
1520
1521         if (res == NULL || (res->type == hsvArray && res->array.nelems == 0) ||
1522                                            (res->type == hsvHash && res->hash.npairs == 0) )
1523         {
1524                 SET_VARSIZE(out, VARHDRSZ);
1525         }
1526         else
1527         {
1528                 uint32 r;
1529
1530                 if (res->type == hsvArray && res->array.nelems > 1)
1531                         res->array.scalar = false;
1532
1533                 r = compressHStore(res, VARDATA(out));
1534                 SET_VARSIZE(out, r + VARHDRSZ);
1535         }
1536
1537         PG_RETURN_POINTER(out);
1538 }
1539
1540
1541 PG_FUNCTION_INFO_V1(hstore_slice_to_array);
1542 Datum           hstore_slice_to_array(PG_FUNCTION_ARGS);
1543 Datum
1544 hstore_slice_to_array(PG_FUNCTION_ARGS)
1545 {
1546         HStore     *hs = PG_GETARG_HS(0);
1547         ArrayType  *key_array = PG_GETARG_ARRAYTYPE_P(1);
1548         ArrayType  *aout;
1549         Datum      *key_datums;
1550         bool       *key_nulls;
1551         Datum      *out_datums;
1552         bool       *out_nulls;
1553         int                     key_count;
1554         int                     i;
1555
1556         deconstruct_array(key_array,
1557                                           TEXTOID, -1, false, 'i',
1558                                           &key_datums, &key_nulls, &key_count);
1559
1560         if (key_count == 0 || HS_ISEMPTY(hs))
1561         {
1562                 aout = construct_empty_array(TEXTOID);
1563                 PG_RETURN_POINTER(aout);
1564         }
1565
1566         out_datums = palloc(sizeof(Datum) * key_count);
1567         out_nulls = palloc(sizeof(bool) * key_count);
1568
1569         for (i = 0; i < key_count; ++i)
1570         {
1571                 text       *key = (text *) DatumGetPointer(key_datums[i]);
1572                 HStoreValue     *v = NULL;
1573
1574                 if (key_nulls[i] == false)
1575                         v = findUncompressedHStoreValue(VARDATA(hs),
1576                                                                                         HS_FLAG_HSTORE | HS_FLAG_ARRAY,
1577                                                                                         NULL,
1578                                                                                         VARDATA(key),
1579                                                                                         VARSIZE(key) - VARHDRSZ);
1580
1581                 out_datums[i] = PointerGetDatum(HStoreValueToText(v));
1582                 out_nulls[i] = (DatumGetPointer(out_datums[i]) == NULL) ? true : false;
1583         }
1584
1585         aout = construct_md_array(out_datums, out_nulls,
1586                                                           ARR_NDIM(key_array),
1587                                                           ARR_DIMS(key_array),
1588                                                           ARR_LBOUND(key_array),
1589                                                           TEXTOID, -1, false, 'i');
1590
1591         PG_RETURN_POINTER(aout);
1592 }
1593
1594
1595 PG_FUNCTION_INFO_V1(hstore_slice_to_hstore);
1596 Datum           hstore_slice_to_hstore(PG_FUNCTION_ARGS);
1597 Datum
1598 hstore_slice_to_hstore(PG_FUNCTION_ARGS)
1599 {
1600         HStore             *hs = PG_GETARG_HS(0);
1601         HStoreValue        *a = arrayToHStoreSortedArray(PG_GETARG_ARRAYTYPE_P(1));
1602         uint32                  lowbound = 0,
1603                                    *plowbound;
1604         HStoreValue             *res = NULL;
1605         ToHStoreState   *state = NULL;
1606         text                    *out;
1607         uint32                  i;
1608
1609         out = palloc(VARSIZE(hs));
1610
1611         if (a == NULL || a->array.nelems == 0 || HS_ISEMPTY(hs))
1612         {
1613                 memcpy(out, hs, VARSIZE(hs));
1614                 PG_RETURN_POINTER(out);
1615         }
1616
1617         if (HS_ROOT_IS_HASH(hs))
1618         {
1619                 plowbound = &lowbound;
1620                 pushHStoreValue(&state, WHS_BEGIN_HASH, NULL);
1621         }
1622         else
1623         {
1624                 plowbound = NULL;
1625                 pushHStoreValue(&state, WHS_BEGIN_ARRAY, NULL);
1626         }
1627
1628         for (i = 0; i < a->array.nelems; ++i)
1629         {
1630                 HStoreValue     *v = findUncompressedHStoreValueByValue(VARDATA(hs),
1631                                                                                                                         HS_FLAG_HSTORE | HS_FLAG_ARRAY,
1632                                                                                                                         plowbound,
1633                                                                                                                         a->array.elems + i);
1634
1635                 if (v)
1636                 {
1637                         if (plowbound)
1638                         {
1639                                 pushHStoreValue(&state, WHS_KEY, a->array.elems + i);
1640                                 pushHStoreValue(&state, WHS_VALUE, v);
1641                         }
1642                         else
1643                         {
1644                                 pushHStoreValue(&state, WHS_ELEM, v);
1645                         }
1646                 }
1647         }
1648
1649         if (plowbound)
1650                 res = pushHStoreValue(&state, WHS_END_HASH, a /* any non-null value */);
1651         else
1652                 res = pushHStoreValue(&state, WHS_END_ARRAY, NULL);
1653
1654
1655         if (res == NULL || (res->type == hsvArray && res->array.nelems == 0) ||
1656                                                 (res->type == hsvHash && res->hash.npairs == 0) )
1657         {
1658                 SET_VARSIZE(out, VARHDRSZ);
1659         }
1660         else
1661         {
1662                 int r = compressHStore(res, VARDATA(out));
1663                 SET_VARSIZE(out, r + VARHDRSZ);
1664         }
1665
1666         PG_RETURN_POINTER(out);
1667 }
1668
1669 static HStoreValue*
1670 replacePathDo(HStoreIterator **it, Datum *path_elems,
1671                           bool *path_nulls, int path_len,
1672                           ToHStoreState  **st, int level, HStoreValue *newval)
1673 {
1674         HStoreValue v, *res = NULL;
1675         int                     r;
1676
1677         r = HStoreIteratorGet(it, &v, false);
1678
1679         if (r == WHS_BEGIN_ARRAY)
1680         {
1681                 int             idx, i;
1682                 uint32  n = v.array.nelems;
1683
1684                 idx = n;
1685                 if (level >= path_len || path_nulls[level] ||
1686                         h_atoi(VARDATA_ANY(path_elems[level]),
1687                                    VARSIZE_ANY_EXHDR(path_elems[level]), &idx) == false)
1688                 {
1689                         idx = n;
1690                 }
1691                 else if (idx < 0)
1692                 {
1693                         if (-idx > n)
1694                                 idx = n;
1695                         else
1696                                 idx = n + idx;
1697                 }
1698
1699                 if (idx > n)
1700                         idx = n;
1701
1702                 pushHStoreValue(st, r, &v);
1703
1704                 for(i=0; i<n; i++)
1705                 {
1706                         if (i == idx && level < path_len)
1707                         {
1708                                 if (level == path_len - 1)
1709                                 {
1710                                         r = HStoreIteratorGet(it, &v, true); /* skip */
1711                                         Assert(r == WHS_ELEM);
1712                                         res = pushHStoreValue(st, r, newval);
1713                                 }
1714                                 else
1715                                 {
1716                                         res = replacePathDo(it, path_elems, path_nulls, path_len,
1717                                                                                 st, level + 1, newval);
1718                                 }
1719                         }
1720                         else
1721                         {
1722                                 r = HStoreIteratorGet(it, &v, true);
1723                                 Assert(r == WHS_ELEM);
1724                                 res = pushHStoreValue(st, r, &v);
1725                         }
1726                 }
1727
1728                 r = HStoreIteratorGet(it, &v, true);
1729                 Assert(r == WHS_END_ARRAY);
1730                 res = pushHStoreValue(st, r, &v);
1731         }
1732         else if (r == WHS_BEGIN_HASH)
1733         {
1734                 int                     i;
1735                 uint32          n = v.hash.npairs;
1736                 HStoreValue     k;
1737                 bool            done = false;
1738
1739                 pushHStoreValue(st, WHS_BEGIN_HASH, &v);
1740
1741                 if (level >= path_len || path_nulls[level])
1742                         done = true;
1743
1744                 for(i=0; i<n; i++)
1745                 {
1746                         r = HStoreIteratorGet(it, &k, false);
1747                         Assert(r == WHS_KEY);
1748                         res = pushHStoreValue(st, r, &k);
1749
1750                         if (done == false &&
1751                                 k.string.len == VARSIZE_ANY_EXHDR(path_elems[level]) &&
1752                                 memcmp(k.string.val, VARDATA_ANY(path_elems[level]),
1753                                            k.string.len) == 0)
1754                         {
1755                                 if (level == path_len - 1)
1756                                 {
1757                                         r = HStoreIteratorGet(it, &v, true); /* skip */
1758                                         Assert(r == WHS_VALUE);
1759                                         res = pushHStoreValue(st, r, newval);
1760                                 }
1761                                 else
1762                                 {
1763                                         res = replacePathDo(it, path_elems, path_nulls, path_len,
1764                                                                                 st, level + 1, newval);
1765                                 }
1766                         }
1767                         else
1768                         {
1769                                 r = HStoreIteratorGet(it, &v, true);
1770                                 Assert(r == WHS_VALUE);
1771                                 res = pushHStoreValue(st, r, &v);
1772                         }
1773                 }
1774
1775                 r = HStoreIteratorGet(it, &v, true);
1776                 Assert(r == WHS_END_HASH);
1777                 res = pushHStoreValue(st, r, &v);
1778         }
1779         else if (r == WHS_ELEM || r == WHS_VALUE)
1780         {
1781                 pushHStoreValue(st, r, &v);
1782                 res = (void*)0x01; /* dummy value */
1783         }
1784         else
1785         {
1786                 elog(PANIC, "impossible state");
1787         }
1788
1789         return res;
1790 }
1791
1792 PG_FUNCTION_INFO_V1(hstore_replace);
1793 Datum           hstore_replace(PG_FUNCTION_ARGS);
1794 Datum
1795 hstore_replace(PG_FUNCTION_ARGS)
1796 {
1797         HStore                  *in = PG_GETARG_HS(0);
1798         ArrayType               *path = PG_GETARG_ARRAYTYPE_P(1);
1799         HStore                  *newval = PG_GETARG_HS(2);
1800         HStore                  *out = palloc(VARSIZE(in) + VARSIZE(newval));
1801         HStoreValue             *res = NULL;
1802         HStoreValue             value;
1803         Datum                   *path_elems;
1804         bool                    *path_nulls;
1805         int                             path_len;
1806         HStoreIterator  *it;
1807         ToHStoreState   *st = NULL;
1808
1809         Assert(ARR_ELEMTYPE(path) == TEXTOID);
1810
1811         if (ARR_NDIM(path) > 1)
1812                 ereport(ERROR,
1813                                 (errcode(ERRCODE_ARRAY_SUBSCRIPT_ERROR),
1814                                  errmsg("wrong number of array subscripts")));
1815
1816         if (HS_ROOT_COUNT(in) == 0)
1817         {
1818                 memcpy(out, in, VARSIZE(in));
1819                 PG_RETURN_POINTER(out);
1820         }
1821
1822         deconstruct_array(path, TEXTOID, -1, false, 'i',
1823                                           &path_elems, &path_nulls, &path_len);
1824
1825         if (path_len == 0)
1826         {
1827                 memcpy(out, in, VARSIZE(in));
1828                 PG_RETURN_POINTER(out);
1829         }
1830
1831         if (HS_ROOT_COUNT(newval) == 0)
1832         {
1833                 value.type = hsvNull;
1834                 value.size = sizeof(HEntry);
1835         }
1836         else
1837         {
1838                 value.type = hsvBinary;
1839                 value.binary.data = VARDATA(newval);
1840                 value.binary.len = VARSIZE_ANY_EXHDR(newval);
1841                 value.size = value.binary.len + sizeof(HEntry);
1842         }
1843
1844         it = HStoreIteratorInit(VARDATA(in));
1845
1846         res = replacePathDo(&it, path_elems, path_nulls, path_len, &st, 0, &value);
1847
1848         if (res == NULL)
1849         {
1850                 SET_VARSIZE(out, VARHDRSZ);
1851         }
1852         else
1853         {
1854                 int                             sz;
1855
1856                 sz = compressHStore(res, VARDATA(out));
1857                 SET_VARSIZE(out, sz + VARHDRSZ);
1858         }
1859
1860         PG_RETURN_POINTER(out);
1861 }
1862
1863 static HStoreValue*
1864 concatPathDo(HStoreIterator **it, Datum *path_elems,
1865                          bool *path_nulls, int path_len,
1866                          ToHStoreState  **st, int level, HStoreIterator *toConcat)
1867 {
1868         HStoreValue v, *res = NULL;
1869         int                     r;
1870
1871         r = HStoreIteratorGet(it, &v, false);
1872
1873         if (r == WHS_BEGIN_ARRAY)
1874         {
1875                 int             idx, i;
1876                 uint32  n = v.array.nelems;
1877
1878                 idx = n;
1879                 if (level >= path_len || path_nulls[level] ||
1880                         h_atoi(VARDATA_ANY(path_elems[level]),
1881                                    VARSIZE_ANY_EXHDR(path_elems[level]), &idx) == false)
1882                 {
1883                         idx = n;
1884                 }
1885                 else if (idx < 0)
1886                 {
1887                         if (-idx > n)
1888                                 idx = n;
1889                         else
1890                                 idx = n + idx;
1891                 }
1892
1893                 if (idx > n)
1894                         idx = n;
1895
1896                 pushHStoreValue(st, r, &v);
1897
1898                 for(i=0; i<n; i++)
1899                 {
1900                         if (i == idx && level < path_len)
1901                         {
1902                                 if (level == path_len - 1)
1903                                         res = IteratorConcat(it, &toConcat, st);
1904                                 else
1905                                         res = concatPathDo(it, path_elems, path_nulls, path_len,
1906                                                                            st, level + 1, toConcat);
1907                         }
1908                         else
1909                         {
1910                                 r = HStoreIteratorGet(it, &v, true);
1911                                 Assert(r == WHS_ELEM);
1912                                 res = pushHStoreValue(st, r, &v);
1913                         }
1914                 }
1915
1916                 r = HStoreIteratorGet(it, &v, true);
1917                 Assert(r == WHS_END_ARRAY);
1918                 res = pushHStoreValue(st, r, &v);
1919         }
1920         else if (r == WHS_BEGIN_HASH)
1921         {
1922                 int                     i;
1923                 uint32          n = v.hash.npairs;
1924                 HStoreValue     k;
1925                 bool            done = false;
1926
1927                 pushHStoreValue(st, WHS_BEGIN_HASH, &v);
1928
1929                 if (level >= path_len || path_nulls[level])
1930                         done = true;
1931
1932                 for(i=0; i<n; i++)
1933                 {
1934                         r = HStoreIteratorGet(it, &k, false);
1935                         Assert(r == WHS_KEY);
1936                         res = pushHStoreValue(st, r, &k);
1937
1938                         if (done == false && level < path_len &&
1939                                 k.string.len == VARSIZE_ANY_EXHDR(path_elems[level]) &&
1940                                 memcmp(k.string.val, VARDATA_ANY(path_elems[level]),
1941                                            k.string.len) == 0)
1942                         {
1943                                 if (level == path_len - 1)
1944                                         res = IteratorConcat(it, &toConcat, st);
1945                                 else
1946                                         res = concatPathDo(it, path_elems, path_nulls, path_len,
1947                                                                            st, level + 1, toConcat);
1948                         }
1949                         else
1950                         {
1951                                 r = HStoreIteratorGet(it, &v, true);
1952                                 Assert(r == WHS_VALUE);
1953                                 res = pushHStoreValue(st, r, &v);
1954                         }
1955                 }
1956
1957                 r = HStoreIteratorGet(it, &v, true);
1958                 Assert(r == WHS_END_HASH);
1959                 res = pushHStoreValue(st, r, &v);
1960         }
1961         else if (r == WHS_ELEM || r == WHS_VALUE)
1962         {
1963                 pushHStoreValue(st, r, &v);
1964                 res = (void*)0x01; /* dummy value */
1965         }
1966         else
1967         {
1968                 elog(PANIC, "impossible state");
1969         }
1970
1971         return res;
1972 }
1973
1974 PG_FUNCTION_INFO_V1(hstore_deep_concat);
1975 Datum           hstore_deep_concat(PG_FUNCTION_ARGS);
1976 Datum
1977 hstore_deep_concat(PG_FUNCTION_ARGS)
1978 {
1979         HStore                  *in = PG_GETARG_HS(0);
1980         ArrayType               *path = PG_GETARG_ARRAYTYPE_P(1);
1981         HStore                  *newval = PG_GETARG_HS(2);
1982         HStore                  *out = palloc(VARSIZE(in) + VARSIZE(newval));
1983         HStoreValue             *res = NULL;
1984         Datum                   *path_elems;
1985         bool                    *path_nulls;
1986         int                             path_len;
1987         HStoreIterator  *it1, *it2;
1988         ToHStoreState   *st = NULL;
1989
1990         Assert(ARR_ELEMTYPE(path) == TEXTOID);
1991
1992         if (ARR_NDIM(path) > 1)
1993                 ereport(ERROR,
1994                                 (errcode(ERRCODE_ARRAY_SUBSCRIPT_ERROR),
1995                                  errmsg("wrong number of array subscripts")));
1996
1997         if (HS_ROOT_COUNT(in) == 0 || HS_ROOT_COUNT(newval) == 0)
1998         {
1999                 memcpy(out, in, VARSIZE(in));
2000                 PG_RETURN_POINTER(out);
2001         }
2002
2003         deconstruct_array(path, TEXTOID, -1, false, 'i',
2004                                           &path_elems, &path_nulls, &path_len);
2005
2006         it1 = HStoreIteratorInit(VARDATA(in));
2007         it2 = HStoreIteratorInit(VARDATA(newval));
2008
2009         if (path_len == 0)
2010                 res = IteratorConcat(&it1, &it2, &st);
2011         else
2012                 res = concatPathDo(&it1, path_elems, path_nulls, path_len, &st, 0, it2);
2013
2014         if (res == NULL)
2015         {
2016                 SET_VARSIZE(out, VARHDRSZ);
2017         }
2018         else
2019         {
2020                 int                             sz;
2021
2022                 if (res->type == hsvArray && res->array.nelems > 1)
2023                         res->array.scalar = false;
2024
2025                 sz = compressHStore(res, VARDATA(out));
2026                 SET_VARSIZE(out, sz + VARHDRSZ);
2027         }
2028
2029         PG_RETURN_POINTER(out);
2030 }
2031
2032 PG_FUNCTION_INFO_V1(hstore_akeys);
2033 Datum           hstore_akeys(PG_FUNCTION_ARGS);
2034 Datum
2035 hstore_akeys(PG_FUNCTION_ARGS)
2036 {
2037         HStore                  *hs = PG_GETARG_HS(0);
2038         Datum                   *d;
2039         ArrayType               *a;
2040         int                             i = 0, r = 0;
2041         HStoreIterator  *it;
2042         HStoreValue             v;
2043         bool                    skipNested = false;
2044
2045         if (HS_ISEMPTY(hs))
2046         {
2047                 a = construct_empty_array(TEXTOID);
2048                 PG_RETURN_POINTER(a);
2049         }
2050
2051         d = (Datum *) palloc(sizeof(Datum) * HS_ROOT_COUNT(hs));
2052
2053         it = HStoreIteratorInit(VARDATA(hs));
2054
2055         while((r = HStoreIteratorGet(&it, &v, skipNested)) != 0)
2056         {
2057                 skipNested = true;
2058
2059                 if ((r == WHS_ELEM && v.type != hsvNull) || r == WHS_KEY)
2060                         d[i++] = PointerGetDatum(HStoreValueToText(&v));
2061         }
2062
2063         a = construct_array(d, i,
2064                                                 TEXTOID, -1, false, 'i');
2065
2066         PG_RETURN_POINTER(a);
2067 }
2068
2069
2070 PG_FUNCTION_INFO_V1(hstore_avals);
2071 Datum           hstore_avals(PG_FUNCTION_ARGS);
2072 Datum
2073 hstore_avals(PG_FUNCTION_ARGS)
2074 {
2075         HStore                  *hs = PG_GETARG_HS(0);
2076         Datum                   *d;
2077         ArrayType               *a;
2078         int                             i = 0, r = 0;
2079         HStoreIterator  *it;
2080         HStoreValue             v;
2081         bool                    skipNested = false;
2082         bool               *nulls;
2083         int                             lb = 1;
2084
2085         if (HS_ISEMPTY(hs))
2086         {
2087                 a = construct_empty_array(TEXTOID);
2088                 PG_RETURN_POINTER(a);
2089         }
2090
2091         d = (Datum *) palloc(sizeof(Datum) * HS_ROOT_COUNT(hs));
2092         nulls = (bool *) palloc(sizeof(bool) * HS_ROOT_COUNT(hs));
2093
2094         it = HStoreIteratorInit(VARDATA(hs));
2095
2096         while((r = HStoreIteratorGet(&it, &v, skipNested)) != 0)
2097         {
2098                 skipNested = true;
2099
2100                 if (r == WHS_ELEM || r == WHS_VALUE)
2101                 {
2102                         d[i] = PointerGetDatum(HStoreValueToText(&v));
2103                         nulls[i] = (DatumGetPointer(d[i]) == NULL) ? true : false;
2104                         i++;
2105                 }
2106         }
2107
2108         a = construct_md_array(d, nulls, 1, &i, &lb,
2109                                                    TEXTOID, -1, false, 'i');
2110
2111         PG_RETURN_POINTER(a);
2112 }
2113
2114
2115 static ArrayType *
2116 hstore_to_array_internal(HStore *hs, int ndims)
2117 {
2118         int                             count = HS_ROOT_COUNT(hs);
2119         int                             out_size[2] = {0, 2};
2120         int                             lb[2] = {1, 1};
2121         Datum              *out_datums;
2122         bool                    *out_nulls;
2123         bool                    isHash = HS_ROOT_IS_HASH(hs) ? true : false;
2124         int                             i = 0, r = 0;
2125         HStoreIterator  *it;
2126         HStoreValue             v;
2127         bool                    skipNested = false;
2128
2129         Assert(ndims < 3);
2130
2131         if (count == 0 || ndims == 0)
2132                 return construct_empty_array(TEXTOID);
2133
2134         if (isHash == false && ndims == 2 && count % 2 != 0)
2135                 elog(ERROR, "hstore's array should have even number of elements");
2136
2137         out_size[0] = count * (isHash ? 2 : 1) / ndims;
2138         out_datums = palloc(sizeof(Datum) * count * 2);
2139         out_nulls = palloc(sizeof(bool) * count * 2);
2140
2141         it = HStoreIteratorInit(VARDATA(hs));
2142
2143         while((r = HStoreIteratorGet(&it, &v, skipNested)) != 0)
2144         {
2145                 skipNested = true;
2146
2147                 switch(r)
2148                 {
2149                         case WHS_ELEM:
2150                                 out_datums[i] = PointerGetDatum(HStoreValueToText(&v));
2151                                 out_nulls[i] = (DatumGetPointer(out_datums[i]) == NULL) ? true : false;
2152                                 i++;
2153                                 break;
2154                         case WHS_KEY:
2155                                 out_datums[i * 2] = PointerGetDatum(HStoreValueToText(&v));
2156                                 out_nulls[i * 2] = (DatumGetPointer(out_datums[i * 2]) == NULL) ? true : false;
2157                                 break;
2158                         case WHS_VALUE:
2159                                 out_datums[i * 2 + 1] = PointerGetDatum(HStoreValueToText(&v));
2160                                 out_nulls[i * 2 + 1] = (DatumGetPointer(out_datums[i * 2 + 1]) == NULL) ? true : false;
2161                                 i++;
2162                                 break;
2163                         default:
2164                                 break;
2165                 }
2166         }
2167
2168         return construct_md_array(out_datums, out_nulls,
2169                                                           ndims, out_size, lb,
2170                                                           TEXTOID, -1, false, 'i');
2171 }
2172
2173 PG_FUNCTION_INFO_V1(hstore_to_array);
2174 Datum           hstore_to_array(PG_FUNCTION_ARGS);
2175 Datum
2176 hstore_to_array(PG_FUNCTION_ARGS)
2177 {
2178         HStore     *hs = PG_GETARG_HS(0);
2179         ArrayType  *out = hstore_to_array_internal(hs, 1);
2180
2181         PG_RETURN_POINTER(out);
2182 }
2183
2184 PG_FUNCTION_INFO_V1(hstore_to_matrix);
2185 Datum           hstore_to_matrix(PG_FUNCTION_ARGS);
2186 Datum
2187 hstore_to_matrix(PG_FUNCTION_ARGS)
2188 {
2189         HStore     *hs = PG_GETARG_HS(0);
2190         ArrayType  *out = hstore_to_array_internal(hs, 2);
2191
2192         PG_RETURN_POINTER(out);
2193 }
2194
2195 /*
2196  * Common initialization function for the various set-returning
2197  * funcs. fcinfo is only passed if the function is to return a
2198  * composite; it will be used to look up the return tupledesc.
2199  * we stash a copy of the hstore in the multi-call context in
2200  * case it was originally toasted. (At least I assume that's why;
2201  * there was no explanatory comment in the original code. --AG)
2202  */
2203
2204 typedef struct SetReturningState
2205 {
2206         HStore                  *hs;
2207         HStoreIterator  *it;
2208         MemoryContext   ctx;
2209
2210         HStoreValue             init;
2211         int                             path_len;
2212         int                             level;
2213         struct {
2214                 HStoreValue             v;
2215                 Datum           varStr;
2216                 int                             varInt;
2217                 enum {
2218                         pathStr,
2219                         pathInt,
2220                         pathAny
2221                 }                               varKind;
2222                 int                             i;
2223         }                               *path;
2224 } SetReturningState;
2225
2226 static SetReturningState*
2227 setup_firstcall(FuncCallContext *funcctx, HStore *hs, ArrayType *path,
2228                                 FunctionCallInfoData *fcinfo)
2229 {
2230         MemoryContext                   oldcontext;
2231         SetReturningState          *st;
2232
2233         oldcontext = MemoryContextSwitchTo(funcctx->multi_call_memory_ctx);
2234
2235         st = palloc(sizeof(*st));
2236
2237         st->ctx = funcctx->multi_call_memory_ctx;
2238
2239         st->hs = (HStore *) palloc(VARSIZE(hs));
2240         memcpy(st->hs, hs, VARSIZE(hs));
2241         if (HS_ISEMPTY(hs) || path)
2242                 st->it = NULL;
2243         else
2244                 st->it = HStoreIteratorInit(VARDATA(st->hs));
2245
2246         funcctx->user_fctx = (void *) st;
2247
2248         if (fcinfo)
2249         {
2250                 TupleDesc       tupdesc;
2251
2252                 /* Build a tuple descriptor for our result type */
2253                 if (get_call_result_type(fcinfo, NULL, &tupdesc) != TYPEFUNC_COMPOSITE)
2254                         elog(ERROR, "return type must be a row type");
2255
2256                 funcctx->tuple_desc = BlessTupleDesc(tupdesc);
2257         }
2258
2259         st->path_len = st->level = 0;
2260         if (path)
2261         {
2262                 Datum           *path_elems;
2263                 bool            *path_nulls;
2264                 int                     i;
2265
2266                 Assert(ARR_ELEMTYPE(path) == TEXTOID);
2267                 if (ARR_NDIM(path) > 1)
2268                         ereport(ERROR,
2269                                         (errcode(ERRCODE_ARRAY_SUBSCRIPT_ERROR),
2270                                          errmsg("wrong number of array subscripts")));
2271
2272                 deconstruct_array(path, TEXTOID, -1, false, 'i',
2273                                                   &path_elems, &path_nulls, &st->path_len);
2274
2275                 st->init.type = hsvBinary;
2276                 st->init.size = VARSIZE(st->hs);
2277                 st->init.binary.data = VARDATA(st->hs);
2278                 st->init.binary.len = VARSIZE_ANY_EXHDR(st->hs);
2279
2280                 if (st->path_len > 0)
2281                 {
2282                         st->path = palloc(sizeof(*st->path) * st->path_len);
2283                         st->path[0].v = st->init;
2284                 }
2285
2286                 for(i=0; i<st->path_len; i++)
2287                 {
2288                         st->path[i].varStr = path_elems[i];
2289                         st->path[i].i = 0;
2290
2291                         if (path_nulls[i])
2292                                 st->path[i].varKind = pathAny;
2293                         else if (h_atoi(VARDATA_ANY(path_elems[i]),
2294                                                         VARSIZE_ANY_EXHDR(path_elems[i]),
2295                                                         &st->path[i].varInt))
2296                                 st->path[i].varKind = pathInt;
2297                         else
2298                                 st->path[i].varKind = pathStr;
2299                 }
2300         }
2301
2302         MemoryContextSwitchTo(oldcontext);
2303
2304         return st;
2305 }
2306
2307 static uint32
2308 HStoreIteratorGetCtx(SetReturningState *st, HStoreValue *v, bool skipNested)
2309 {
2310         int                     r;
2311         MemoryContext   oldctx;
2312
2313         oldctx = MemoryContextSwitchTo(st->ctx);
2314         r = HStoreIteratorGet(&st->it, v, skipNested);
2315         MemoryContextSwitchTo(oldctx);
2316
2317         return r;
2318 }
2319
2320 PG_FUNCTION_INFO_V1(hstore_skeys);
2321 Datum           hstore_skeys(PG_FUNCTION_ARGS);
2322 Datum
2323 hstore_skeys(PG_FUNCTION_ARGS)
2324 {
2325         FuncCallContext         *funcctx;
2326         SetReturningState       *st;
2327         int                                     r;
2328         HStoreValue                     v;
2329
2330         if (SRF_IS_FIRSTCALL())
2331         {
2332                 funcctx = SRF_FIRSTCALL_INIT();
2333                 st = setup_firstcall(funcctx, PG_GETARG_HS(0), NULL, NULL);
2334         }
2335
2336         funcctx = SRF_PERCALL_SETUP();
2337         st = (SetReturningState *) funcctx->user_fctx;
2338
2339         while(st->it && (r = HStoreIteratorGetCtx(st, &v, true)) != 0)
2340         {
2341                 if (r == WHS_KEY || r == WHS_ELEM)
2342                 {
2343                         text       *item = HStoreValueToText(&v);
2344
2345                         if (item == NULL)
2346                                 SRF_RETURN_NEXT_NULL(funcctx);
2347                         else
2348                                 SRF_RETURN_NEXT(funcctx, PointerGetDatum(item));
2349                 }
2350         }
2351
2352         SRF_RETURN_DONE(funcctx);
2353 }
2354
2355 PG_FUNCTION_INFO_V1(hstore_svals);
2356 Datum           hstore_svals(PG_FUNCTION_ARGS);
2357 Datum
2358 hstore_svals(PG_FUNCTION_ARGS)
2359 {
2360         FuncCallContext         *funcctx;
2361         SetReturningState       *st;
2362         int                                     r;
2363         HStoreValue                     v;
2364
2365         if (SRF_IS_FIRSTCALL())
2366         {
2367                 funcctx = SRF_FIRSTCALL_INIT();
2368                 st = setup_firstcall(funcctx, PG_GETARG_HS(0), NULL, NULL);
2369         }
2370
2371         funcctx = SRF_PERCALL_SETUP();
2372         st = (SetReturningState *) funcctx->user_fctx;
2373
2374         while(st->it && (r = HStoreIteratorGetCtx(st, &v, true)) != 0)
2375         {
2376                 if (r == WHS_VALUE || r == WHS_ELEM)
2377                 {
2378                         text       *item = HStoreValueToText(&v);
2379
2380                         if (item == NULL)
2381                                 SRF_RETURN_NEXT_NULL(funcctx);
2382                         else
2383                                 SRF_RETURN_NEXT(funcctx, PointerGetDatum(item));
2384                 }
2385         }
2386
2387         SRF_RETURN_DONE(funcctx);
2388 }
2389
2390 PG_FUNCTION_INFO_V1(hstore_hvals);
2391 Datum           hstore_hvals(PG_FUNCTION_ARGS);
2392 Datum
2393 hstore_hvals(PG_FUNCTION_ARGS)
2394 {
2395         FuncCallContext         *funcctx;
2396         SetReturningState       *st;
2397         int                                     r;
2398         HStoreValue                     v;
2399
2400         if (SRF_IS_FIRSTCALL())
2401         {
2402                 funcctx = SRF_FIRSTCALL_INIT();
2403                 st = setup_firstcall(funcctx, PG_GETARG_HS(0), NULL, NULL);
2404         }
2405
2406         funcctx = SRF_PERCALL_SETUP();
2407         st = (SetReturningState *) funcctx->user_fctx;
2408
2409         while(st->it && (r = HStoreIteratorGetCtx(st, &v, true)) != 0)
2410         {
2411                 if (r == WHS_VALUE || r == WHS_ELEM)
2412                 {
2413                         HStore     *item = HStoreValueToHStore(&v);
2414
2415                         if (item == NULL)
2416                                 SRF_RETURN_NEXT_NULL(funcctx);
2417                         else
2418                                 SRF_RETURN_NEXT(funcctx, PointerGetDatum(item));
2419                 }
2420         }
2421
2422         SRF_RETURN_DONE(funcctx);
2423 }
2424
2425 static HStoreValue*
2426 getNextValsPath(SetReturningState *st)
2427 {
2428         HStoreValue             *v = NULL;
2429
2430         if (st->path_len == 0)
2431         {
2432                 /* empty path */
2433                 if (st->level == 0)
2434                 {
2435                         v = &st->init;
2436                         st->level ++;
2437                 }
2438
2439                 return v;
2440         }
2441
2442         while(st->level >= 0)
2443         {
2444                 uint32  header;
2445
2446                 v = NULL;
2447                 if (st->path[st->level].v.type != hsvBinary)
2448                 {
2449                         st->level--;
2450                         continue;
2451                 }
2452
2453                 header = *(uint32*)st->path[st->level].v.binary.data;
2454
2455                 if (header & HS_FLAG_HSTORE)
2456                 {
2457                         if (st->path[st->level].varKind == pathAny)
2458                         {
2459                                 v = getHStoreValue(st->path[st->level].v.binary.data,
2460                                                                    HS_FLAG_HSTORE,
2461                                                                    st->path[st->level].i++);
2462                         }
2463                         else
2464                         {
2465                                 v = findUncompressedHStoreValue(st->path[st->level].v.binary.data,
2466                                                                                                 HS_FLAG_HSTORE, NULL,
2467                                                                                                 VARDATA_ANY(st->path[st->level].varStr),
2468                                                                                                 VARSIZE_ANY_EXHDR(st->path[st->level].varStr));
2469                         }
2470                 }
2471                 else if (header & HS_FLAG_ARRAY)
2472                 {
2473                         if (st->path[st->level].varKind == pathAny)
2474                         {
2475                                 v = getHStoreValue(st->path[st->level].v.binary.data,
2476                                                                    HS_FLAG_ARRAY, st->path[st->level].i++);
2477                         }
2478                         else if (st->path[st->level].varKind == pathInt)
2479                         {
2480                                 int     ith = st->path[st->level].varInt;
2481
2482                                 if (ith < 0)
2483                                 {
2484                                         if (-ith > (int)(header & HS_COUNT_MASK))
2485                                         {
2486                                                 st->level--;
2487                                                 continue;
2488                                         }
2489                                         else
2490                                         {
2491                                                 ith = ((int)(header & HS_COUNT_MASK)) + ith;
2492                                         }
2493                                 }
2494                                 else
2495                                 {
2496                                         if (ith >= (int)(header & HS_COUNT_MASK))
2497                                         {
2498                                                 st->level--;
2499                                                 continue;
2500                                         }
2501                                 }
2502
2503                                 v = getHStoreValue(st->path[st->level].v.binary.data,
2504                                                                    HS_FLAG_ARRAY, ith);
2505                         }
2506                         else
2507                         {
2508                                 st->level--;
2509                                 continue;
2510                         }
2511                 }
2512                 else
2513                 {
2514                         elog(PANIC, "impossible state");
2515                 }
2516
2517                 if (v == NULL)
2518                 {
2519                         st->level--;
2520                 }
2521                 else if (st->level == st->path_len - 1)
2522                 {
2523                         if (st->path[st->level].varKind != pathAny)
2524                         {
2525                                 st->path[st->level].v.type = hsvNull;
2526                                 st->level--;
2527                         }
2528                         break;
2529                 }
2530                 else
2531                 {
2532                         if (st->path[st->level].varKind != pathAny)
2533                                 st->path[st->level].v.type = hsvNull;
2534                         st->level++;
2535                         st->path[st->level].v = *v;
2536                         st->path[st->level].i = 0;
2537                 }
2538         }
2539
2540         return v;
2541 }
2542
2543 PG_FUNCTION_INFO_V1(hstore_svals_path);
2544 Datum           hstore_svals_path(PG_FUNCTION_ARGS);
2545 Datum
2546 hstore_svals_path(PG_FUNCTION_ARGS)
2547 {
2548         FuncCallContext         *funcctx;
2549         SetReturningState       *st;
2550         HStoreValue                     *v;
2551
2552         if (SRF_IS_FIRSTCALL())
2553         {
2554                 funcctx = SRF_FIRSTCALL_INIT();
2555                 st = setup_firstcall(funcctx, PG_GETARG_HS(0), PG_GETARG_ARRAYTYPE_P(1), NULL);
2556         }
2557
2558         funcctx = SRF_PERCALL_SETUP();
2559         st = (SetReturningState *) funcctx->user_fctx;
2560
2561         if ((v = getNextValsPath(st)) != NULL)
2562         {
2563                 text    *item = HStoreValueToText(v);
2564
2565                 if (item == NULL)
2566                         SRF_RETURN_NEXT_NULL(funcctx);
2567                 else
2568                         SRF_RETURN_NEXT(funcctx, PointerGetDatum(item));
2569         }
2570
2571         SRF_RETURN_DONE(funcctx);
2572 }
2573
2574 PG_FUNCTION_INFO_V1(hstore_hvals_path);
2575 Datum           hstore_hvals_path(PG_FUNCTION_ARGS);
2576 Datum
2577 hstore_hvals_path(PG_FUNCTION_ARGS)
2578 {
2579         FuncCallContext         *funcctx;
2580         SetReturningState       *st;
2581         HStoreValue             *v;
2582
2583         if (SRF_IS_FIRSTCALL())
2584         {
2585                 funcctx = SRF_FIRSTCALL_INIT();
2586                 st = setup_firstcall(funcctx, PG_GETARG_HS(0),
2587                                                          PG_GETARG_ARRAYTYPE_P(1), NULL);
2588         }
2589
2590         funcctx = SRF_PERCALL_SETUP();
2591         st = (SetReturningState *) funcctx->user_fctx;
2592
2593         if ((v = getNextValsPath(st)) != NULL)
2594         {
2595                 HStore     *item = HStoreValueToHStore(v);
2596
2597                 if (item == NULL)
2598                         SRF_RETURN_NEXT_NULL(funcctx);
2599                 else
2600                         SRF_RETURN_NEXT(funcctx, PointerGetDatum(item));
2601         }
2602
2603         SRF_RETURN_DONE(funcctx);
2604 }
2605
2606 PG_FUNCTION_INFO_V1(hstore_each);
2607 Datum           hstore_each(PG_FUNCTION_ARGS);
2608 Datum
2609 hstore_each(PG_FUNCTION_ARGS)
2610 {
2611         FuncCallContext         *funcctx;
2612         SetReturningState       *st;
2613         int                                     r;
2614         HStoreValue                     v;
2615
2616         if (SRF_IS_FIRSTCALL())
2617         {
2618                 funcctx = SRF_FIRSTCALL_INIT();
2619                 st = setup_firstcall(funcctx, PG_GETARG_HS(0), NULL, fcinfo);
2620         }
2621
2622         funcctx = SRF_PERCALL_SETUP();
2623         st = (SetReturningState *) funcctx->user_fctx;
2624
2625         while(st->it && (r = HStoreIteratorGetCtx(st, &v, true)) != 0)
2626         {
2627                 Datum           res,
2628                                         dvalues[2] = {0, 0};
2629                 bool            nulls[2] = {false, false};
2630                 text       *item;
2631                 HeapTuple       tuple;
2632
2633                 if (r == WHS_ELEM)
2634                 {
2635                         nulls[0] = true;
2636
2637                         item = HStoreValueToText(&v);
2638                         if (item == NULL)
2639                                 nulls[1] = true;
2640                         else
2641                                 dvalues[1] = PointerGetDatum(item);
2642                 }
2643                 else if (r == WHS_KEY)
2644                 {
2645                         item = HStoreValueToText(&v);
2646                         dvalues[0] = PointerGetDatum(item);
2647
2648                         r = HStoreIteratorGetCtx(st, &v, true);
2649                         Assert(r == WHS_VALUE);
2650                         item = HStoreValueToText(&v);
2651                         if (item == NULL)
2652                                 nulls[1] = true;
2653                         else
2654                                 dvalues[1] = PointerGetDatum(item);
2655                 }
2656                 else
2657                 {
2658                         continue;
2659                 }
2660
2661                 tuple = heap_form_tuple(funcctx->tuple_desc, dvalues, nulls);
2662                 res = HeapTupleGetDatum(tuple);
2663
2664                 SRF_RETURN_NEXT(funcctx, PointerGetDatum(res));
2665         }
2666
2667         SRF_RETURN_DONE(funcctx);
2668 }
2669
2670 PG_FUNCTION_INFO_V1(hstore_each_hstore);
2671 Datum           hstore_each_hstore(PG_FUNCTION_ARGS);
2672 Datum
2673 hstore_each_hstore(PG_FUNCTION_ARGS)
2674 {
2675         FuncCallContext         *funcctx;
2676         SetReturningState       *st;
2677         int                                     r;
2678         HStoreValue                     v;
2679
2680         if (SRF_IS_FIRSTCALL())
2681         {
2682                 funcctx = SRF_FIRSTCALL_INIT();
2683                 st = setup_firstcall(funcctx, PG_GETARG_HS(0), NULL, fcinfo);
2684         }
2685
2686         funcctx = SRF_PERCALL_SETUP();
2687         st = (SetReturningState *) funcctx->user_fctx;
2688
2689         while(st->it && (r = HStoreIteratorGetCtx(st, &v, true)) != 0)
2690         {
2691                 Datum           res,
2692                                         dvalues[2] = {0, 0};
2693                 bool            nulls[2] = {false, false};
2694                 text       *item;
2695                 HStore          *hitem;
2696                 HeapTuple       tuple;
2697
2698                 if (r == WHS_ELEM)
2699                 {
2700                         nulls[0] = true;
2701
2702                         hitem = HStoreValueToHStore(&v);
2703                         if (hitem == NULL)
2704                                 nulls[1] = true;
2705                         else
2706                                 dvalues[1] = PointerGetDatum(hitem);
2707                 }
2708                 else if (r == WHS_KEY)
2709                 {
2710                         item = HStoreValueToText(&v);
2711                         dvalues[0] = PointerGetDatum(item);
2712
2713                         r = HStoreIteratorGetCtx(st, &v, true);
2714                         Assert(r == WHS_VALUE);
2715                         hitem = HStoreValueToHStore(&v);
2716                         if (hitem == NULL)
2717                                 nulls[1] = true;
2718                         else
2719                                 dvalues[1] = PointerGetDatum(hitem);
2720                 }
2721                 else
2722                 {
2723                         continue;
2724                 }
2725
2726                 tuple = heap_form_tuple(funcctx->tuple_desc, dvalues, nulls);
2727                 res = HeapTupleGetDatum(tuple);
2728
2729                 SRF_RETURN_NEXT(funcctx, PointerGetDatum(res));
2730         }
2731
2732         SRF_RETURN_DONE(funcctx);
2733 }
2734
2735 static bool
2736 deepContains(HStoreIterator **it1, HStoreIterator **it2)
2737 {
2738         uint32                  r1, r2;
2739         HStoreValue             v1, v2;
2740         bool                    res = true;
2741
2742         r1 = HStoreIteratorGet(it1, &v1, false);
2743         r2 = HStoreIteratorGet(it2, &v2, false);
2744
2745         if (r1 != r2)
2746         {
2747                 res = false;
2748         }
2749         else if (r1 == WHS_BEGIN_HASH)
2750         {
2751                 uint32          lowbound = 0;
2752                 HStoreValue     *v;
2753
2754                 for(;;) {
2755                         r2 = HStoreIteratorGet(it2, &v2, false);
2756                         if (r2 == WHS_END_HASH)
2757                                 break;
2758
2759                         Assert(r2 == WHS_KEY);
2760
2761                         v = findUncompressedHStoreValueByValue((*it1)->buffer,
2762                                                                                                    HS_FLAG_HSTORE,
2763                                                                                                    &lowbound, &v2);
2764
2765                         if (v == NULL)
2766                         {
2767                                 res = false;
2768                                 break;
2769                         }
2770
2771                         r2 = HStoreIteratorGet(it2, &v2, true);
2772                         Assert(r2 == WHS_VALUE);
2773
2774                         if (v->type != v2.type)
2775                         {
2776                                 res = false;
2777                                 break;
2778                         }
2779                         else if (v->type == hsvString || v->type == hsvNull ||
2780                                          v->type == hsvBool || v->type == hsvNumeric)
2781                         {
2782                                 if (compareHStoreValue(v, &v2) != 0)
2783                                 {
2784                                         res = false;
2785                                         break;
2786                                 }
2787                         }
2788                         else
2789                         {
2790                                 HStoreIterator  *it1a, *it2a;
2791
2792                                 Assert(v2.type == hsvBinary);
2793                                 Assert(v->type == hsvBinary);
2794
2795                                 it1a = HStoreIteratorInit(v->binary.data);
2796                                 it2a = HStoreIteratorInit(v2.binary.data);
2797
2798                                 if ((res = deepContains(&it1a, &it2a)) == false)
2799                                         break;
2800                         }
2801                 }
2802         }
2803         else if (r1 == WHS_BEGIN_ARRAY)
2804         {
2805                 HStoreValue             *v;
2806                 HStoreValue             *av = NULL;
2807                 uint32                  nelems = v1.array.nelems;
2808
2809                 for(;;) {
2810                         r2 = HStoreIteratorGet(it2, &v2, true);
2811                         if (r2 == WHS_END_ARRAY)
2812                                 break;
2813
2814                         Assert(r2 == WHS_ELEM);
2815
2816                         if (v2.type == hsvString || v2.type == hsvNull ||
2817                                 v2.type == hsvBool || v2.type == hsvNumeric)
2818                         {
2819                                 v = findUncompressedHStoreValueByValue((*it1)->buffer,
2820                                                                                                            HS_FLAG_ARRAY, NULL,
2821                                                                                                            &v2);
2822                                 if (v == NULL)
2823                                 {
2824                                         res = false;
2825                                         break;
2826                                 }
2827                         }
2828                         else
2829                         {
2830                                 uint32                  i;
2831
2832                                 if (av == NULL)
2833                                 {
2834                                         uint32                  j = 0;
2835
2836                                         av = palloc(sizeof(*av) * nelems);
2837
2838                                         for(i=0; i<nelems; i++)
2839                                         {
2840                                                 r2 = HStoreIteratorGet(it1, &v1, true);
2841                                                 Assert(r2 == WHS_ELEM);
2842
2843                                                 if (v1.type == hsvBinary)
2844                                                         av[j++] = v1;
2845                                         }
2846
2847                                         if (j == 0)
2848                                         {
2849                                                 res = false;
2850                                                 break;
2851                                         }
2852
2853                                         nelems = j;
2854                                 }
2855
2856                                 res = false;
2857                                 for(i = 0; res == false && i<nelems; i++)
2858                                 {
2859                                         HStoreIterator  *it1a, *it2a;
2860
2861                                         it1a = HStoreIteratorInit(av[i].binary.data);
2862                                         it2a = HStoreIteratorInit(v2.binary.data);
2863
2864                                         res = deepContains(&it1a, &it2a);
2865                                 }
2866
2867                                 if (res == false)
2868                                         break;
2869                         }
2870                 }
2871         }
2872         else
2873         {
2874                 elog(PANIC, "impossible state");
2875         }
2876
2877         return res;
2878 }
2879
2880 PG_FUNCTION_INFO_V1(hstore_contains);
2881 Datum           hstore_contains(PG_FUNCTION_ARGS);
2882 Datum
2883 hstore_contains(PG_FUNCTION_ARGS)
2884 {
2885         HStore                  *val = PG_GETARG_HS(0);
2886         HStore                  *tmpl = PG_GETARG_HS(1);
2887         bool                    res = true;
2888         HStoreIterator  *it1, *it2;
2889
2890         if (HS_ROOT_COUNT(val) < HS_ROOT_COUNT(tmpl) ||
2891                 HS_ROOT_IS_HASH(val) != HS_ROOT_IS_HASH(tmpl))
2892                 PG_RETURN_BOOL(false);
2893
2894         it1 = HStoreIteratorInit(VARDATA(val));
2895         it2 = HStoreIteratorInit(VARDATA(tmpl));
2896         res = deepContains(&it1, &it2);
2897
2898         PG_RETURN_BOOL(res);
2899 }
2900
2901
2902 PG_FUNCTION_INFO_V1(hstore_contained);
2903 Datum           hstore_contained(PG_FUNCTION_ARGS);
2904 Datum
2905 hstore_contained(PG_FUNCTION_ARGS)
2906 {
2907         PG_RETURN_DATUM(DirectFunctionCall2(hstore_contains,
2908                                                                                 PG_GETARG_DATUM(1),
2909                                                                                 PG_GETARG_DATUM(0)
2910                                                                                 ));
2911 }
2912
2913 /*
2914  * btree sort order for hstores isn't intended to be useful; we really only
2915  * care about equality versus non-equality.  we compare the entire string
2916  * buffer first, then the entry pos array.
2917  */
2918
2919 PG_FUNCTION_INFO_V1(hstore_cmp);
2920 Datum           hstore_cmp(PG_FUNCTION_ARGS);
2921 Datum
2922 hstore_cmp(PG_FUNCTION_ARGS)
2923 {
2924         HStore                  *hs1 = PG_GETARG_HS(0);
2925         HStore                  *hs2 = PG_GETARG_HS(1);
2926         int                             res;
2927
2928         if (HS_ISEMPTY(hs1) || HS_ISEMPTY(hs2))
2929         {
2930                 if (HS_ISEMPTY(hs1))
2931                 {
2932                         if (HS_ISEMPTY(hs2))
2933                                 res = 0;
2934                         else
2935                                 res = -1;
2936                 }
2937                 else
2938                 {
2939                         res = 1;
2940                 }
2941         }
2942         else
2943         {
2944                 res = compareHStoreBinaryValue(VARDATA(hs1), VARDATA(hs2));
2945         }
2946
2947         /*
2948          * this is a btree support function; this is one of the few places where
2949          * memory needs to be explicitly freed.
2950          */
2951         PG_FREE_IF_COPY(hs1, 0);
2952         PG_FREE_IF_COPY(hs2, 1);
2953         PG_RETURN_INT32(res);
2954 }
2955
2956
2957 PG_FUNCTION_INFO_V1(hstore_eq);
2958 Datum           hstore_eq(PG_FUNCTION_ARGS);
2959 Datum
2960 hstore_eq(PG_FUNCTION_ARGS)
2961 {
2962         int                     res = DatumGetInt32(DirectFunctionCall2(hstore_cmp,
2963                                                                                                                 PG_GETARG_DATUM(0),
2964                                                                                                                 PG_GETARG_DATUM(1)));
2965
2966         PG_RETURN_BOOL(res == 0);
2967 }
2968
2969 PG_FUNCTION_INFO_V1(hstore_ne);
2970 Datum           hstore_ne(PG_FUNCTION_ARGS);
2971 Datum
2972 hstore_ne(PG_FUNCTION_ARGS)
2973 {
2974         int                     res = DatumGetInt32(DirectFunctionCall2(hstore_cmp,
2975                                                                                                                 PG_GETARG_DATUM(0),
2976                                                                                                                 PG_GETARG_DATUM(1)));
2977
2978         PG_RETURN_BOOL(res != 0);
2979 }
2980
2981 PG_FUNCTION_INFO_V1(hstore_gt);
2982 Datum           hstore_gt(PG_FUNCTION_ARGS);
2983 Datum
2984 hstore_gt(PG_FUNCTION_ARGS)
2985 {
2986         int                     res = DatumGetInt32(DirectFunctionCall2(hstore_cmp,
2987                                                                                                                 PG_GETARG_DATUM(0),
2988                                                                                                                 PG_GETARG_DATUM(1)));
2989
2990         PG_RETURN_BOOL(res > 0);
2991 }
2992
2993 PG_FUNCTION_INFO_V1(hstore_ge);
2994 Datum           hstore_ge(PG_FUNCTION_ARGS);
2995 Datum
2996 hstore_ge(PG_FUNCTION_ARGS)
2997 {
2998         int                     res = DatumGetInt32(DirectFunctionCall2(hstore_cmp,
2999                                                                                                                 PG_GETARG_DATUM(0),
3000                                                                                                                 PG_GETARG_DATUM(1)));
3001
3002         PG_RETURN_BOOL(res >= 0);
3003 }
3004
3005 PG_FUNCTION_INFO_V1(hstore_lt);
3006 Datum           hstore_lt(PG_FUNCTION_ARGS);
3007 Datum
3008 hstore_lt(PG_FUNCTION_ARGS)
3009 {
3010         int                     res = DatumGetInt32(DirectFunctionCall2(hstore_cmp,
3011                                                                                                                 PG_GETARG_DATUM(0),
3012                                                                                                                 PG_GETARG_DATUM(1)));
3013
3014         PG_RETURN_BOOL(res < 0);
3015 }
3016
3017 PG_FUNCTION_INFO_V1(hstore_le);
3018 Datum           hstore_le(PG_FUNCTION_ARGS);
3019 Datum
3020 hstore_le(PG_FUNCTION_ARGS)
3021 {
3022         int                     res = DatumGetInt32(DirectFunctionCall2(hstore_cmp,
3023                                                                                                                 PG_GETARG_DATUM(0),
3024                                                                                                                 PG_GETARG_DATUM(1)));
3025
3026         PG_RETURN_BOOL(res <= 0);
3027 }
3028
3029
3030 PG_FUNCTION_INFO_V1(hstore_hash);
3031 Datum           hstore_hash(PG_FUNCTION_ARGS);
3032 Datum
3033 hstore_hash(PG_FUNCTION_ARGS)
3034 {
3035         HStore                  *hs = PG_GETARG_HS(0);
3036         HStoreIterator  *it;
3037         int32                   r;
3038         HStoreValue             v;
3039         int                             crc;
3040
3041         if (HS_ROOT_COUNT(hs) == 0)
3042                 PG_RETURN_INT32(0x1EEE);
3043
3044         it = HStoreIteratorInit(VARDATA(hs));
3045         INIT_CRC32(crc);
3046
3047         while((r = HStoreIteratorGet(&it, &v, false)) != 0)
3048         {
3049                 switch(r)
3050                 {
3051                         case WHS_BEGIN_ARRAY:
3052                                 COMP_CRC32(crc, "ab", 3);
3053                                 COMP_CRC32(crc, &v.array.nelems, sizeof(v.array.nelems));
3054                                 COMP_CRC32(crc, &v.array.scalar, sizeof(v.array.scalar));
3055                                 break;
3056                         case WHS_BEGIN_HASH:
3057                                 COMP_CRC32(crc, "hb", 3);
3058                                 COMP_CRC32(crc, &v.hash.npairs, sizeof(v.hash.npairs));
3059                                 break;
3060                         case WHS_KEY:
3061                                 COMP_CRC32(crc, "k", 2);
3062                         case WHS_VALUE:
3063                         case WHS_ELEM:
3064                                 switch(v.type)
3065                                 {
3066                                         case hsvString:
3067                                                 COMP_CRC32(crc, v.string.val, v.string.len);
3068                                                 break;
3069                                         case hsvNull:
3070                                                 COMP_CRC32(crc, "N", 2);
3071                                                 break;
3072                                         case hsvBool:
3073                                                 COMP_CRC32(crc, &v.boolean, sizeof(v.boolean));
3074                                                 break;
3075                                         case hsvNumeric:
3076                                                 crc ^= DatumGetInt32(DirectFunctionCall1(hash_numeric,
3077                                                                                          NumericGetDatum(v.numeric)));
3078                                                 break;
3079                                         default:
3080                                                 elog(ERROR, "unexpected state of hstore iterator");
3081                                 }
3082
3083                                 break;
3084                         case WHS_END_ARRAY:
3085                                 COMP_CRC32(crc, "ae", 3);
3086                                 break;
3087                         case WHS_END_HASH:
3088                                 COMP_CRC32(crc, "he", 3);
3089                                 break;
3090                         default:
3091                                 elog(ERROR, "unexpected state of hstore iterator");
3092                 }
3093         }
3094
3095         FIN_CRC32(crc);
3096
3097         PG_FREE_IF_COPY(hs, 0);
3098         PG_RETURN_INT32(crc);
3099 }
3100
3101 PG_FUNCTION_INFO_V1(hstore_typeof);
3102 Datum           hstore_typeof(PG_FUNCTION_ARGS);
3103 Datum
3104 hstore_typeof(PG_FUNCTION_ARGS)
3105 {
3106         HStore                  *hs = PG_GETARG_HS(0);
3107         HStoreIterator  *it;
3108         HStoreValue             v;
3109         uint32                  r;
3110
3111         if (HS_ISEMPTY(hs))
3112                 PG_RETURN_NULL();
3113
3114         it = HStoreIteratorInit(VARDATA(hs));
3115         r = HStoreIteratorGet(&it, &v, false);
3116
3117         switch(r)
3118         {
3119                 case WHS_BEGIN_ARRAY:
3120                         if (v.array.scalar)
3121                         {
3122                                 Assert(v.array.nelems == 1);
3123                                 r = HStoreIteratorGet(&it, &v, false);
3124                                 Assert(r == WHS_ELEM);
3125
3126                                 switch(v.type)
3127                                 {
3128                                         case hsvNull:
3129                                                 PG_RETURN_TEXT_P(cstring_to_text("null"));
3130                                         case hsvBool:
3131                                                 PG_RETURN_TEXT_P(cstring_to_text("bool"));
3132                                         case hsvNumeric:
3133                                                 PG_RETURN_TEXT_P(cstring_to_text("numeric"));
3134                                         case hsvString:
3135                                                 PG_RETURN_TEXT_P(cstring_to_text("string"));
3136                                         default:
3137                                                 elog(ERROR, "bogus hstore");
3138                                 }
3139                         }
3140                         else
3141                         {
3142                                 PG_RETURN_TEXT_P(cstring_to_text("array"));
3143                         }
3144                 case WHS_BEGIN_HASH:
3145                         PG_RETURN_TEXT_P(cstring_to_text("hash"));
3146                 case 0:
3147                         PG_RETURN_NULL();
3148                 default:
3149                         elog(ERROR, "bogus hstore");
3150         }
3151
3152         PG_RETURN_NULL();
3153 }
3154