Skip to content

Commit ba39896

Browse files
committed
Update contrib/hstore for new GIN extractQuery API.
In particular, make hstore @> '' succeed for all hstores, likewise hstore ?& '{}'. Previously the results were inconsistent and could depend on whether you were using a GiST index, GIN index, or seqscan.
1 parent 327b257 commit ba39896

File tree

3 files changed

+75
-53
lines changed

3 files changed

+75
-53
lines changed

contrib/hstore/expected/hstore.out

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -435,7 +435,7 @@ select hstore 'a=>NULL, b=>qq' ?& ARRAY['c','d'];
435435
select hstore 'a=>NULL, b=>qq' ?& '{}'::text[];
436436
?column?
437437
----------
438-
f
438+
t
439439
(1 row)
440440

441441
-- delete

contrib/hstore/hstore_gin.c

Lines changed: 65 additions & 47 deletions
Original file line numberDiff line numberDiff line change
@@ -10,21 +10,31 @@
1010
#include "hstore.h"
1111

1212

13+
/*
14+
* When using a GIN index for hstore, we choose to index both keys and values.
15+
* The storage format is "text" values, with K, V, or N prepended to the string
16+
* to indicate key, value, or null values. (As of 9.1 it might be better to
17+
* store null values as nulls, but we'll keep it this way for on-disk
18+
* compatibility.)
19+
*/
1320
#define KEYFLAG 'K'
1421
#define VALFLAG 'V'
1522
#define NULLFLAG 'N'
1623

1724
PG_FUNCTION_INFO_V1(gin_extract_hstore);
1825
Datum gin_extract_hstore(PG_FUNCTION_ARGS);
1926

27+
/* Build an indexable text value */
2028
static text *
21-
makeitem(char *str, int len)
29+
makeitem(char *str, int len, char flag)
2230
{
2331
text *item;
2432

2533
item = (text *) palloc(VARHDRSZ + len + 1);
2634
SET_VARSIZE(item, VARHDRSZ + len + 1);
2735

36+
*VARDATA(item) = flag;
37+
2838
if (str && len > 0)
2939
memcpy(VARDATA(item) + 1, str, len);
3040

@@ -50,20 +60,15 @@ gin_extract_hstore(PG_FUNCTION_ARGS)
5060
{
5161
text *item;
5262

53-
item = makeitem(HS_KEY(hsent, ptr, i), HS_KEYLEN(hsent, i));
54-
*VARDATA(item) = KEYFLAG;
63+
item = makeitem(HS_KEY(hsent, ptr, i), HS_KEYLEN(hsent, i),
64+
KEYFLAG);
5565
entries[2 * i] = PointerGetDatum(item);
5666

5767
if (HS_VALISNULL(hsent, i))
58-
{
59-
item = makeitem(NULL, 0);
60-
*VARDATA(item) = NULLFLAG;
61-
}
68+
item = makeitem(NULL, 0, NULLFLAG);
6269
else
63-
{
64-
item = makeitem(HS_VAL(hsent, ptr, i), HS_VALLEN(hsent, i));
65-
*VARDATA(item) = VALFLAG;
66-
}
70+
item = makeitem(HS_VAL(hsent, ptr, i), HS_VALLEN(hsent, i),
71+
VALFLAG);
6772
entries[2 * i + 1] = PointerGetDatum(item);
6873
}
6974

@@ -76,30 +81,31 @@ Datum gin_extract_hstore_query(PG_FUNCTION_ARGS);
7681
Datum
7782
gin_extract_hstore_query(PG_FUNCTION_ARGS)
7883
{
84+
int32 *nentries = (int32 *) PG_GETARG_POINTER(1);
7985
StrategyNumber strategy = PG_GETARG_UINT16(2);
86+
int32 *searchMode = (int32 *) PG_GETARG_POINTER(6);
87+
Datum *entries;
8088

8189
if (strategy == HStoreContainsStrategyNumber)
8290
{
83-
PG_RETURN_DATUM(DirectFunctionCall2(gin_extract_hstore,
84-
PG_GETARG_DATUM(0),
85-
PG_GETARG_DATUM(1)
86-
));
91+
/* Query is an hstore, so just apply gin_extract_hstore... */
92+
entries = (Datum *)
93+
DatumGetPointer(DirectFunctionCall2(gin_extract_hstore,
94+
PG_GETARG_DATUM(0),
95+
PointerGetDatum(nentries)));
96+
/* ... except that "contains {}" requires a full index scan */
97+
if (entries == NULL)
98+
*searchMode = GIN_SEARCH_MODE_ALL;
8799
}
88100
else if (strategy == HStoreExistsStrategyNumber)
89101
{
90-
text *item,
91-
*query = PG_GETARG_TEXT_PP(0);
92-
int32 *nentries = (int32 *) PG_GETARG_POINTER(1);
93-
Datum *entries = NULL;
102+
text *query = PG_GETARG_TEXT_PP(0);
103+
text *item;
94104

95105
*nentries = 1;
96106
entries = (Datum *) palloc(sizeof(Datum));
97-
98-
item = makeitem(VARDATA_ANY(query), VARSIZE_ANY_EXHDR(query));
99-
*VARDATA(item) = KEYFLAG;
107+
item = makeitem(VARDATA_ANY(query), VARSIZE_ANY_EXHDR(query), KEYFLAG);
100108
entries[0] = PointerGetDatum(item);
101-
102-
PG_RETURN_POINTER(entries);
103109
}
104110
else if (strategy == HStoreExistsAnyStrategyNumber ||
105111
strategy == HStoreExistsAllStrategyNumber)
@@ -110,8 +116,6 @@ gin_extract_hstore_query(PG_FUNCTION_ARGS)
110116
int key_count;
111117
int i,
112118
j;
113-
int32 *nentries = (int32 *) PG_GETARG_POINTER(1);
114-
Datum *entries = NULL;
115119
text *item;
116120

117121
deconstruct_array(query,
@@ -122,21 +126,25 @@ gin_extract_hstore_query(PG_FUNCTION_ARGS)
122126

123127
for (i = 0, j = 0; i < key_count; ++i)
124128
{
129+
/* Nulls in the array are ignored, cf hstoreArrayToPairs */
125130
if (key_nulls[i])
126131
continue;
127-
item = makeitem(VARDATA(key_datums[i]), VARSIZE(key_datums[i]) - VARHDRSZ);
128-
*VARDATA(item) = KEYFLAG;
132+
item = makeitem(VARDATA(key_datums[i]), VARSIZE(key_datums[i]) - VARHDRSZ, KEYFLAG);
129133
entries[j++] = PointerGetDatum(item);
130134
}
131135

132-
*nentries = j ? j : -1;
133-
134-
PG_RETURN_POINTER(entries);
136+
*nentries = j;
137+
/* ExistsAll with no keys should match everything */
138+
if (j == 0 && strategy == HStoreExistsAllStrategyNumber)
139+
*searchMode = GIN_SEARCH_MODE_ALL;
135140
}
136141
else
137-
elog(ERROR, "Unsupported strategy number: %d", strategy);
142+
{
143+
elog(ERROR, "unrecognized strategy number: %d", strategy);
144+
entries = NULL; /* keep compiler quiet */
145+
}
138146

139-
PG_RETURN_POINTER(NULL);
147+
PG_RETURN_POINTER(entries);
140148
}
141149

142150
PG_FUNCTION_INFO_V1(gin_consistent_hstore);
@@ -154,42 +162,52 @@ gin_consistent_hstore(PG_FUNCTION_ARGS)
154162
/* Pointer *extra_data = (Pointer *) PG_GETARG_POINTER(4); */
155163
bool *recheck = (bool *) PG_GETARG_POINTER(5);
156164
bool res = true;
157-
158-
*recheck = false;
165+
int32 i;
159166

160167
if (strategy == HStoreContainsStrategyNumber)
161168
{
162-
int i;
163-
164169
/*
165-
* Index lost information about correspondence of keys and values, so
166-
* we need recheck (pre-8.4 this is handled at SQL level)
170+
* Index doesn't have information about correspondence of keys and
171+
* values, so we need recheck. However, if not all the keys are
172+
* present, we can fail at once.
167173
*/
168174
*recheck = true;
169-
for (i = 0; res && i < nkeys; i++)
170-
if (check[i] == false)
175+
for (i = 0; i < nkeys; i++)
176+
{
177+
if (!check[i])
178+
{
171179
res = false;
180+
break;
181+
}
182+
}
172183
}
173184
else if (strategy == HStoreExistsStrategyNumber)
174185
{
175-
/* Existence of key is guaranteed */
186+
/* Existence of key is guaranteed in default search mode */
187+
*recheck = false;
176188
res = true;
177189
}
178190
else if (strategy == HStoreExistsAnyStrategyNumber)
179191
{
180-
/* Existence of key is guaranteed */
192+
/* Existence of key is guaranteed in default search mode */
193+
*recheck = false;
181194
res = true;
182195
}
183196
else if (strategy == HStoreExistsAllStrategyNumber)
184197
{
185-
int i;
186-
187-
for (i = 0; res && i < nkeys; ++i)
198+
/* Testing for all the keys being present gives an exact result */
199+
*recheck = false;
200+
for (i = 0; i < nkeys; i++)
201+
{
188202
if (!check[i])
203+
{
189204
res = false;
205+
break;
206+
}
207+
}
190208
}
191209
else
192-
elog(ERROR, "Unsupported strategy number: %d", strategy);
210+
elog(ERROR, "unrecognized strategy number: %d", strategy);
193211

194212
PG_RETURN_BOOL(res);
195213
}

contrib/hstore/hstore_op.c

Lines changed: 9 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -168,14 +168,16 @@ hstore_exists_any(PG_FUNCTION_ARGS)
168168
* start one entry past the previous "found" entry, or at the lower bound
169169
* of the last search.
170170
*/
171-
172-
for (i = 0; !res && i < nkeys; ++i)
171+
for (i = 0; i < nkeys; i++)
173172
{
174173
int idx = hstoreFindKey(hs, &lowbound,
175174
key_pairs[i].key, key_pairs[i].keylen);
176175

177176
if (idx >= 0)
177+
{
178178
res = true;
179+
break;
180+
}
179181
}
180182

181183
PG_RETURN_BOOL(res);
@@ -193,22 +195,24 @@ hstore_exists_all(PG_FUNCTION_ARGS)
193195
Pairs *key_pairs = hstoreArrayToPairs(keys, &nkeys);
194196
int i;
195197
int lowbound = 0;
196-
bool res = nkeys ? true : false;
198+
bool res = true;
197199

198200
/*
199201
* we exploit the fact that the pairs list is already sorted into strictly
200202
* increasing order to narrow the hstoreFindKey search; each search can
201203
* start one entry past the previous "found" entry, or at the lower bound
202204
* of the last search.
203205
*/
204-
205-
for (i = 0; res && i < nkeys; ++i)
206+
for (i = 0; i < nkeys; i++)
206207
{
207208
int idx = hstoreFindKey(hs, &lowbound,
208209
key_pairs[i].key, key_pairs[i].keylen);
209210

210211
if (idx < 0)
212+
{
211213
res = false;
214+
break;
215+
}
212216
}
213217

214218
PG_RETURN_BOOL(res);

0 commit comments

Comments
 (0)
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy