@@ -19,14 +19,18 @@ PG_MODULE_MAGIC_EXT(
1919 .version = PG_VERSION
2020);
2121
22+ /* extra data passed from gin_btree_extract_query to gin_btree_compare_prefix */
2223typedef struct QueryInfo
2324{
24- StrategyNumber strategy ;
25- Datum datum ;
26- bool is_varlena ;
27- Datum ( * typecmp ) ( FunctionCallInfo );
25+ StrategyNumber strategy ; /* operator strategy number */
26+ Datum orig_datum ; /* original query (comparison) datum */
27+ Datum entry_datum ; /* datum we reported as the entry value */
28+ PGFunction typecmp ; /* appropriate btree comparison function */
2829} QueryInfo ;
2930
31+ typedef Datum (* btree_gin_leftmost_function ) (void );
32+
33+
3034/*** GIN support functions shared by all datatypes ***/
3135
3236static Datum
@@ -36,6 +40,7 @@ gin_btree_extract_value(FunctionCallInfo fcinfo, bool is_varlena)
3640 int32 * nentries = (int32 * ) PG_GETARG_POINTER (1 );
3741 Datum * entries = (Datum * ) palloc (sizeof (Datum ));
3842
43+ /* Ensure that values stored in the index are not toasted */
3944 if (is_varlena )
4045 datum = PointerGetDatum (PG_DETOAST_DATUM (datum ));
4146 entries [0 ] = datum ;
@@ -44,19 +49,11 @@ gin_btree_extract_value(FunctionCallInfo fcinfo, bool is_varlena)
4449 PG_RETURN_POINTER (entries );
4550}
4651
47- /*
48- * For BTGreaterEqualStrategyNumber, BTGreaterStrategyNumber, and
49- * BTEqualStrategyNumber we want to start the index scan at the
50- * supplied query datum, and work forward. For BTLessStrategyNumber
51- * and BTLessEqualStrategyNumber, we need to start at the leftmost
52- * key, and work forward until the supplied query datum (which must be
53- * sent along inside the QueryInfo structure).
54- */
5552static Datum
5653gin_btree_extract_query (FunctionCallInfo fcinfo ,
5754 bool is_varlena ,
58- Datum ( * leftmostvalue ) ( void ) ,
59- Datum ( * typecmp ) ( FunctionCallInfo ) )
55+ btree_gin_leftmost_function leftmostvalue ,
56+ PGFunction typecmp )
6057{
6158 Datum datum = PG_GETARG_DATUM (0 );
6259 int32 * nentries = (int32 * ) PG_GETARG_POINTER (1 );
@@ -65,20 +62,29 @@ gin_btree_extract_query(FunctionCallInfo fcinfo,
6562 Pointer * * extra_data = (Pointer * * ) PG_GETARG_POINTER (4 );
6663 Datum * entries = (Datum * ) palloc (sizeof (Datum ));
6764 QueryInfo * data = (QueryInfo * ) palloc (sizeof (QueryInfo ));
68- bool * ptr_partialmatch ;
65+ bool * ptr_partialmatch = ( bool * ) palloc ( sizeof ( bool )) ;
6966
70- * nentries = 1 ;
71- ptr_partialmatch = * partialmatch = (bool * ) palloc (sizeof (bool ));
72- * ptr_partialmatch = false;
67+ /*
68+ * Detoast the comparison datum. This isn't necessary for correctness,
69+ * but it can save repeat detoastings within the comparison function.
70+ */
7371 if (is_varlena )
7472 datum = PointerGetDatum (PG_DETOAST_DATUM (datum ));
75- data -> strategy = strategy ;
76- data -> datum = datum ;
77- data -> is_varlena = is_varlena ;
78- data -> typecmp = typecmp ;
79- * extra_data = (Pointer * ) palloc (sizeof (Pointer ));
80- * * extra_data = (Pointer ) data ;
8173
74+ /* Prep single comparison key with possible partial-match flag */
75+ * nentries = 1 ;
76+ * partialmatch = ptr_partialmatch ;
77+ * ptr_partialmatch = false;
78+
79+ /*
80+ * For BTGreaterEqualStrategyNumber, BTGreaterStrategyNumber, and
81+ * BTEqualStrategyNumber we want to start the index scan at the supplied
82+ * query datum, and work forward. For BTLessStrategyNumber and
83+ * BTLessEqualStrategyNumber, we need to start at the leftmost key, and
84+ * work forward until the supplied query datum (which we'll send along
85+ * inside the QueryInfo structure). Use partial match rules except for
86+ * BTEqualStrategyNumber.
87+ */
8288 switch (strategy )
8389 {
8490 case BTLessStrategyNumber :
@@ -97,30 +103,42 @@ gin_btree_extract_query(FunctionCallInfo fcinfo,
97103 elog (ERROR , "unrecognized strategy number: %d" , strategy );
98104 }
99105
106+ /* Fill "extra" data */
107+ data -> strategy = strategy ;
108+ data -> orig_datum = datum ;
109+ data -> entry_datum = entries [0 ];
110+ data -> typecmp = typecmp ;
111+ * extra_data = (Pointer * ) palloc (sizeof (Pointer ));
112+ * * extra_data = (Pointer ) data ;
113+
100114 PG_RETURN_POINTER (entries );
101115}
102116
103- /*
104- * Datum a is a value from extract_query method and for BTLess*
105- * strategy it is a left-most value. So, use original datum from QueryInfo
106- * to decide to stop scanning or not. Datum b is always from index.
107- */
108117static Datum
109118gin_btree_compare_prefix (FunctionCallInfo fcinfo )
110119{
111- Datum a = PG_GETARG_DATUM (0 );
112- Datum b = PG_GETARG_DATUM (1 );
120+ Datum partial_key PG_USED_FOR_ASSERTS_ONLY = PG_GETARG_DATUM (0 );
121+ Datum key = PG_GETARG_DATUM (1 );
113122 QueryInfo * data = (QueryInfo * ) PG_GETARG_POINTER (3 );
114123 int32 res ,
115124 cmp ;
116125
126+ /*
127+ * partial_key is only an approximation to the real comparison value,
128+ * especially if it's a leftmost value. We can get an accurate answer by
129+ * doing a possibly-cross-type comparison to the real comparison value.
130+ * But just to be sure that things are what we expect, let's assert that
131+ * partial_key is indeed what gin_btree_extract_query reported, so that
132+ * we'll notice if anyone ever changes the core code in a way that breaks
133+ * our assumptions.
134+ */
135+ Assert (partial_key == data -> entry_datum );
136+
117137 cmp = DatumGetInt32 (CallerFInfoFunctionCall2 (data -> typecmp ,
118138 fcinfo -> flinfo ,
119139 PG_GET_COLLATION (),
120- (data -> strategy == BTLessStrategyNumber ||
121- data -> strategy == BTLessEqualStrategyNumber )
122- ? data -> datum : a ,
123- b ));
140+ data -> orig_datum ,
141+ key ));
124142
125143 switch (data -> strategy )
126144 {
@@ -152,7 +170,7 @@ gin_btree_compare_prefix(FunctionCallInfo fcinfo)
152170 res = 1 ;
153171 break ;
154172 case BTGreaterStrategyNumber :
155- /* If original datum <= indexed one then return match */
173+ /* If original datum < indexed one then return match */
156174 /* If original datum == indexed one then continue scan */
157175 if (cmp < 0 )
158176 res = 0 ;
0 commit comments