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
1724PG_FUNCTION_INFO_V1 (gin_extract_hstore );
1825Datum gin_extract_hstore (PG_FUNCTION_ARGS );
1926
27+ /* Build an indexable text value */
2028static text *
21- makeitem (char * str ,int len )
29+ makeitem (char * str ,int len , char flag )
2230{
2331text * item ;
2432
2533item = (text * )palloc (VARHDRSZ + len + 1 );
2634SET_VARSIZE (item ,VARHDRSZ + len + 1 );
2735
36+ * VARDATA (item )= flag ;
37+
2838if (str && len > 0 )
2939memcpy (VARDATA (item )+ 1 ,str ,len );
3040
@@ -50,20 +60,15 @@ gin_extract_hstore(PG_FUNCTION_ARGS)
5060{
5161text * 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 ) ;
5565entries [2 * i ]= PointerGetDatum (item );
5666
5767if (HS_VALISNULL (hsent ,i ))
58- {
59- item = makeitem (NULL ,0 );
60- * VARDATA (item )= NULLFLAG ;
61- }
68+ item = makeitem (NULL ,0 ,NULLFLAG );
6269else
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 );
6772entries [2 * i + 1 ]= PointerGetDatum (item );
6873}
6974
@@ -76,30 +81,31 @@ Datumgin_extract_hstore_query(PG_FUNCTION_ARGS);
7681Datum
7782gin_extract_hstore_query (PG_FUNCTION_ARGS )
7883{
84+ int32 * nentries = (int32 * )PG_GETARG_POINTER (1 );
7985StrategyNumber strategy = PG_GETARG_UINT16 (2 );
86+ int32 * searchMode = (int32 * )PG_GETARG_POINTER (6 );
87+ Datum * entries ;
8088
8189if (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}
88100else 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 ;
96106entries = (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 );
100108entries [0 ]= PointerGetDatum (item );
101-
102- PG_RETURN_POINTER (entries );
103109}
104110else if (strategy == HStoreExistsAnyStrategyNumber ||
105111strategy == HStoreExistsAllStrategyNumber )
@@ -110,8 +116,6 @@ gin_extract_hstore_query(PG_FUNCTION_ARGS)
110116int key_count ;
111117int i ,
112118j ;
113- int32 * nentries = (int32 * )PG_GETARG_POINTER (1 );
114- Datum * entries = NULL ;
115119text * item ;
116120
117121deconstruct_array (query ,
@@ -122,21 +126,25 @@ gin_extract_hstore_query(PG_FUNCTION_ARGS)
122126
123127for (i = 0 ,j = 0 ;i < key_count ;++ i )
124128{
129+ /* Nulls in the array are ignored, cf hstoreArrayToPairs */
125130if (key_nulls [i ])
126131continue ;
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 );
129133entries [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}
136141else
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
142150PG_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); */
155163bool * recheck = (bool * )PG_GETARG_POINTER (5 );
156164bool res = true;
157-
158- * recheck = false;
165+ int32 i ;
159166
160167if (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+ {
171179res = false;
180+ break ;
181+ }
182+ }
172183}
173184else if (strategy == HStoreExistsStrategyNumber )
174185{
175- /* Existence of key is guaranteed */
186+ /* Existence of key is guaranteed in default search mode */
187+ * recheck = false;
176188res = true;
177189}
178190else if (strategy == HStoreExistsAnyStrategyNumber )
179191{
180- /* Existence of key is guaranteed */
192+ /* Existence of key is guaranteed in default search mode */
193+ * recheck = false;
181194res = true;
182195}
183196else 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+ {
188202if (!check [i ])
203+ {
189204res = false;
205+ break ;
206+ }
207+ }
190208}
191209else
192- elog (ERROR ,"Unsupported strategy number: %d" ,strategy );
210+ elog (ERROR ,"unrecognized strategy number: %d" ,strategy );
193211
194212PG_RETURN_BOOL (res );
195213}