1 /*
2  * contrib/btree_gist/btree_utils_num.c
3  */
4 #include "postgres.h"
5 
6 #include "btree_gist.h"
7 #include "btree_utils_num.h"
8 #include "utils/cash.h"
9 #include "utils/date.h"
10 #include "utils/timestamp.h"
11 
12 
13 GISTENTRY *
gbt_num_compress(GISTENTRY * entry,const gbtree_ninfo * tinfo)14 gbt_num_compress(GISTENTRY *entry, const gbtree_ninfo *tinfo)
15 {
16 	GISTENTRY  *retval;
17 
18 	if (entry->leafkey)
19 	{
20 		union
21 		{
22 			int16		i2;
23 			int32		i4;
24 			int64		i8;
25 			float4		f4;
26 			float8		f8;
27 			DateADT		dt;
28 			TimeADT		tm;
29 			Timestamp	ts;
30 			Cash		ch;
31 		}			v;
32 
33 		GBT_NUMKEY *r = (GBT_NUMKEY *) palloc0(tinfo->indexsize);
34 		void	   *leaf = NULL;
35 
36 		switch (tinfo->t)
37 		{
38 			case gbt_t_int2:
39 				v.i2 = DatumGetInt16(entry->key);
40 				leaf = &v.i2;
41 				break;
42 			case gbt_t_int4:
43 				v.i4 = DatumGetInt32(entry->key);
44 				leaf = &v.i4;
45 				break;
46 			case gbt_t_int8:
47 				v.i8 = DatumGetInt64(entry->key);
48 				leaf = &v.i8;
49 				break;
50 			case gbt_t_oid:
51 			case gbt_t_enum:
52 				v.i4 = DatumGetObjectId(entry->key);
53 				leaf = &v.i4;
54 				break;
55 			case gbt_t_float4:
56 				v.f4 = DatumGetFloat4(entry->key);
57 				leaf = &v.f4;
58 				break;
59 			case gbt_t_float8:
60 				v.f8 = DatumGetFloat8(entry->key);
61 				leaf = &v.f8;
62 				break;
63 			case gbt_t_date:
64 				v.dt = DatumGetDateADT(entry->key);
65 				leaf = &v.dt;
66 				break;
67 			case gbt_t_time:
68 				v.tm = DatumGetTimeADT(entry->key);
69 				leaf = &v.tm;
70 				break;
71 			case gbt_t_ts:
72 				v.ts = DatumGetTimestamp(entry->key);
73 				leaf = &v.ts;
74 				break;
75 			case gbt_t_cash:
76 				v.ch = DatumGetCash(entry->key);
77 				leaf = &v.ch;
78 				break;
79 			default:
80 				leaf = DatumGetPointer(entry->key);
81 		}
82 
83 		Assert(tinfo->indexsize >= 2 * tinfo->size);
84 
85 		memcpy((void *) &r[0], leaf, tinfo->size);
86 		memcpy((void *) &r[tinfo->size], leaf, tinfo->size);
87 		retval = palloc(sizeof(GISTENTRY));
88 		gistentryinit(*retval, PointerGetDatum(r), entry->rel, entry->page,
89 					  entry->offset, false);
90 	}
91 	else
92 		retval = entry;
93 
94 	return retval;
95 }
96 
97 /*
98  * Convert a compressed leaf item back to the original type, for index-only
99  * scans.
100  */
101 GISTENTRY *
gbt_num_fetch(GISTENTRY * entry,const gbtree_ninfo * tinfo)102 gbt_num_fetch(GISTENTRY *entry, const gbtree_ninfo *tinfo)
103 {
104 	GISTENTRY  *retval;
105 	Datum		datum;
106 
107 	Assert(tinfo->indexsize >= 2 * tinfo->size);
108 
109 	/*
110 	 * Get the original Datum from the stored datum. On leaf entries, the
111 	 * lower and upper bound are the same. We just grab the lower bound and
112 	 * return it.
113 	 */
114 	switch (tinfo->t)
115 	{
116 		case gbt_t_int2:
117 			datum = Int16GetDatum(*(int16 *) entry->key);
118 			break;
119 		case gbt_t_int4:
120 			datum = Int32GetDatum(*(int32 *) entry->key);
121 			break;
122 		case gbt_t_int8:
123 			datum = Int64GetDatum(*(int64 *) entry->key);
124 			break;
125 		case gbt_t_oid:
126 		case gbt_t_enum:
127 			datum = ObjectIdGetDatum(*(Oid *) entry->key);
128 			break;
129 		case gbt_t_float4:
130 			datum = Float4GetDatum(*(float4 *) entry->key);
131 			break;
132 		case gbt_t_float8:
133 			datum = Float8GetDatum(*(float8 *) entry->key);
134 			break;
135 		case gbt_t_date:
136 			datum = DateADTGetDatum(*(DateADT *) entry->key);
137 			break;
138 		case gbt_t_time:
139 			datum = TimeADTGetDatum(*(TimeADT *) entry->key);
140 			break;
141 		case gbt_t_ts:
142 			datum = TimestampGetDatum(*(Timestamp *) entry->key);
143 			break;
144 		case gbt_t_cash:
145 			datum = CashGetDatum(*(Cash *) entry->key);
146 			break;
147 		default:
148 			datum = PointerGetDatum(entry->key);
149 	}
150 
151 	retval = palloc(sizeof(GISTENTRY));
152 	gistentryinit(*retval, datum, entry->rel, entry->page, entry->offset,
153 				  false);
154 	return retval;
155 }
156 
157 
158 
159 /*
160 ** The GiST union method for numerical values
161 */
162 
163 void *
gbt_num_union(GBT_NUMKEY * out,const GistEntryVector * entryvec,const gbtree_ninfo * tinfo,FmgrInfo * flinfo)164 gbt_num_union(GBT_NUMKEY *out, const GistEntryVector *entryvec, const gbtree_ninfo *tinfo, FmgrInfo *flinfo)
165 {
166 	int			i,
167 				numranges;
168 	GBT_NUMKEY *cur;
169 	GBT_NUMKEY_R o,
170 				c;
171 
172 	numranges = entryvec->n;
173 	cur = (GBT_NUMKEY *) DatumGetPointer((entryvec->vector[0].key));
174 
175 
176 	o.lower = &((GBT_NUMKEY *) out)[0];
177 	o.upper = &((GBT_NUMKEY *) out)[tinfo->size];
178 
179 	memcpy((void *) out, (void *) cur, 2 * tinfo->size);
180 
181 	for (i = 1; i < numranges; i++)
182 	{
183 		cur = (GBT_NUMKEY *) DatumGetPointer((entryvec->vector[i].key));
184 		c.lower = &cur[0];
185 		c.upper = &cur[tinfo->size];
186 		/* if out->lower > cur->lower, adopt cur as lower */
187 		if (tinfo->f_gt(o.lower, c.lower, flinfo))
188 			memcpy((void *) o.lower, (void *) c.lower, tinfo->size);
189 		/* if out->upper < cur->upper, adopt cur as upper */
190 		if (tinfo->f_lt(o.upper, c.upper, flinfo))
191 			memcpy((void *) o.upper, (void *) c.upper, tinfo->size);
192 	}
193 
194 	return out;
195 }
196 
197 
198 
199 /*
200 ** The GiST same method for numerical values
201 */
202 
203 bool
gbt_num_same(const GBT_NUMKEY * a,const GBT_NUMKEY * b,const gbtree_ninfo * tinfo,FmgrInfo * flinfo)204 gbt_num_same(const GBT_NUMKEY *a, const GBT_NUMKEY *b, const gbtree_ninfo *tinfo, FmgrInfo *flinfo)
205 {
206 	GBT_NUMKEY_R b1,
207 				b2;
208 
209 	b1.lower = &(((GBT_NUMKEY *) a)[0]);
210 	b1.upper = &(((GBT_NUMKEY *) a)[tinfo->size]);
211 	b2.lower = &(((GBT_NUMKEY *) b)[0]);
212 	b2.upper = &(((GBT_NUMKEY *) b)[tinfo->size]);
213 
214 	return (tinfo->f_eq(b1.lower, b2.lower, flinfo) &&
215 			tinfo->f_eq(b1.upper, b2.upper, flinfo));
216 }
217 
218 
219 void
gbt_num_bin_union(Datum * u,GBT_NUMKEY * e,const gbtree_ninfo * tinfo,FmgrInfo * flinfo)220 gbt_num_bin_union(Datum *u, GBT_NUMKEY *e, const gbtree_ninfo *tinfo, FmgrInfo *flinfo)
221 {
222 	GBT_NUMKEY_R rd;
223 
224 	rd.lower = &e[0];
225 	rd.upper = &e[tinfo->size];
226 
227 	if (!DatumGetPointer(*u))
228 	{
229 		*u = PointerGetDatum(palloc0(tinfo->indexsize));
230 		memcpy((void *) &(((GBT_NUMKEY *) DatumGetPointer(*u))[0]), (void *) rd.lower, tinfo->size);
231 		memcpy((void *) &(((GBT_NUMKEY *) DatumGetPointer(*u))[tinfo->size]), (void *) rd.upper, tinfo->size);
232 	}
233 	else
234 	{
235 		GBT_NUMKEY_R ur;
236 
237 		ur.lower = &(((GBT_NUMKEY *) DatumGetPointer(*u))[0]);
238 		ur.upper = &(((GBT_NUMKEY *) DatumGetPointer(*u))[tinfo->size]);
239 		if (tinfo->f_gt((void *) ur.lower, (void *) rd.lower, flinfo))
240 			memcpy((void *) ur.lower, (void *) rd.lower, tinfo->size);
241 		if (tinfo->f_lt((void *) ur.upper, (void *) rd.upper, flinfo))
242 			memcpy((void *) ur.upper, (void *) rd.upper, tinfo->size);
243 	}
244 }
245 
246 
247 
248 /*
249  * The GiST consistent method
250  *
251  * Note: we currently assume that no datatypes that use this routine are
252  * collation-aware; so we don't bother passing collation through.
253  */
254 bool
gbt_num_consistent(const GBT_NUMKEY_R * key,const void * query,const StrategyNumber * strategy,bool is_leaf,const gbtree_ninfo * tinfo,FmgrInfo * flinfo)255 gbt_num_consistent(const GBT_NUMKEY_R *key,
256 				   const void *query,
257 				   const StrategyNumber *strategy,
258 				   bool is_leaf,
259 				   const gbtree_ninfo *tinfo,
260 				   FmgrInfo *flinfo)
261 {
262 	bool		retval;
263 
264 	switch (*strategy)
265 	{
266 		case BTLessEqualStrategyNumber:
267 			retval = tinfo->f_ge(query, key->lower, flinfo);
268 			break;
269 		case BTLessStrategyNumber:
270 			if (is_leaf)
271 				retval = tinfo->f_gt(query, key->lower, flinfo);
272 			else
273 				retval = tinfo->f_ge(query, key->lower, flinfo);
274 			break;
275 		case BTEqualStrategyNumber:
276 			if (is_leaf)
277 				retval = tinfo->f_eq(query, key->lower, flinfo);
278 			else
279 				retval = (tinfo->f_le(key->lower, query, flinfo) &&
280 						  tinfo->f_le(query, key->upper, flinfo));
281 			break;
282 		case BTGreaterStrategyNumber:
283 			if (is_leaf)
284 				retval = tinfo->f_lt(query, key->upper, flinfo);
285 			else
286 				retval = tinfo->f_le(query, key->upper, flinfo);
287 			break;
288 		case BTGreaterEqualStrategyNumber:
289 			retval = tinfo->f_le(query, key->upper, flinfo);
290 			break;
291 		case BtreeGistNotEqualStrategyNumber:
292 			retval = (!(tinfo->f_eq(query, key->lower, flinfo) &&
293 						tinfo->f_eq(query, key->upper, flinfo)));
294 			break;
295 		default:
296 			retval = false;
297 	}
298 
299 	return retval;
300 }
301 
302 
303 /*
304 ** The GiST distance method (for KNN-Gist)
305 */
306 
307 float8
gbt_num_distance(const GBT_NUMKEY_R * key,const void * query,bool is_leaf,const gbtree_ninfo * tinfo,FmgrInfo * flinfo)308 gbt_num_distance(const GBT_NUMKEY_R *key,
309 				 const void *query,
310 				 bool is_leaf,
311 				 const gbtree_ninfo *tinfo,
312 				 FmgrInfo *flinfo)
313 {
314 	float8		retval;
315 
316 	if (tinfo->f_dist == NULL)
317 		elog(ERROR, "KNN search is not supported for btree_gist type %d",
318 			 (int) tinfo->t);
319 	if (tinfo->f_le(query, key->lower, flinfo))
320 		retval = tinfo->f_dist(query, key->lower, flinfo);
321 	else if (tinfo->f_ge(query, key->upper, flinfo))
322 		retval = tinfo->f_dist(query, key->upper, flinfo);
323 	else
324 		retval = 0.0;
325 
326 	return retval;
327 }
328 
329 
330 GIST_SPLITVEC *
gbt_num_picksplit(const GistEntryVector * entryvec,GIST_SPLITVEC * v,const gbtree_ninfo * tinfo,FmgrInfo * flinfo)331 gbt_num_picksplit(const GistEntryVector *entryvec, GIST_SPLITVEC *v,
332 				  const gbtree_ninfo *tinfo, FmgrInfo *flinfo)
333 {
334 	OffsetNumber i,
335 				maxoff = entryvec->n - 1;
336 	Nsrt	   *arr;
337 	int			nbytes;
338 
339 	arr = (Nsrt *) palloc((maxoff + 1) * sizeof(Nsrt));
340 	nbytes = (maxoff + 2) * sizeof(OffsetNumber);
341 	v->spl_left = (OffsetNumber *) palloc(nbytes);
342 	v->spl_right = (OffsetNumber *) palloc(nbytes);
343 	v->spl_ldatum = PointerGetDatum(0);
344 	v->spl_rdatum = PointerGetDatum(0);
345 	v->spl_nleft = 0;
346 	v->spl_nright = 0;
347 
348 	/* Sort entries */
349 
350 	for (i = FirstOffsetNumber; i <= maxoff; i = OffsetNumberNext(i))
351 	{
352 		arr[i].t = (GBT_NUMKEY *) DatumGetPointer((entryvec->vector[i].key));
353 		arr[i].i = i;
354 	}
355 	qsort_arg((void *) &arr[FirstOffsetNumber], maxoff - FirstOffsetNumber + 1, sizeof(Nsrt), (qsort_arg_comparator) tinfo->f_cmp, (void *) flinfo);
356 
357 	/* We do simply create two parts */
358 
359 	for (i = FirstOffsetNumber; i <= maxoff; i = OffsetNumberNext(i))
360 	{
361 		if (i <= (maxoff - FirstOffsetNumber + 1) / 2)
362 		{
363 			gbt_num_bin_union(&v->spl_ldatum, arr[i].t, tinfo, flinfo);
364 			v->spl_left[v->spl_nleft] = arr[i].i;
365 			v->spl_nleft++;
366 		}
367 		else
368 		{
369 			gbt_num_bin_union(&v->spl_rdatum, arr[i].t, tinfo, flinfo);
370 			v->spl_right[v->spl_nright] = arr[i].i;
371 			v->spl_nright++;
372 		}
373 	}
374 
375 	return v;
376 }
377