17
17
#include "access/gist.h"
18
18
#include "access/skey.h"
19
19
#include "utils/builtins.h"
20
+ #include "utils/datum.h"
20
21
#include "utils/rangetypes.h"
21
22
22
23
32
33
#define RANGESTRAT_CONTAINED_BY 8
33
34
#define RANGESTRAT_CONTAINS_ELEM 16
34
35
#define RANGESTRAT_EQ 18
35
- #define RANGESTRAT_NE 19
36
+
37
+ /* Copy a RangeType datum (hardwires typbyval and typlen for ranges...) */
38
+ #define rangeCopy (r ) \
39
+ ((RangeType *) DatumGetPointer(datumCopy(PointerGetDatum(r), \
40
+ false, -1)))
36
41
37
42
/*
38
43
* Auxiliary structure for picksplit method.
@@ -145,6 +150,16 @@ range_gist_penalty(PG_FUNCTION_ARGS)
145
150
146
151
subtype_diff = & typcache -> rng_subdiff_finfo ;
147
152
153
+ /*
154
+ * If new is or contains empty, and orig doesn't, apply infinite penalty.
155
+ * We really don't want to pollute an empty-free subtree with empties.
156
+ */
157
+ if (RangeIsOrContainsEmpty (new ) && !RangeIsOrContainsEmpty (orig ))
158
+ {
159
+ * penalty = get_float4_infinity ();
160
+ PG_RETURN_POINTER (penalty );
161
+ }
162
+
148
163
/*
149
164
* We want to compare the size of "orig" to size of "orig union new".
150
165
* The penalty will be the sum of the reduction in the lower bound plus
@@ -163,30 +178,9 @@ range_gist_penalty(PG_FUNCTION_ARGS)
163
178
}
164
179
else if (empty1 )
165
180
{
166
- if (lower2 .infinite || upper2 .infinite )
167
- {
168
- /* from empty to infinite */
169
- * penalty = get_float4_infinity ();
170
- PG_RETURN_POINTER (penalty );
171
- }
172
- else if (OidIsValid (subtype_diff -> fn_oid ))
173
- {
174
- /* from empty to upper2-lower2 */
175
- * penalty = DatumGetFloat8 (FunctionCall2Coll (subtype_diff ,
176
- typcache -> rng_collation ,
177
- upper2 .val ,
178
- lower2 .val ));
179
- /* upper2 must be >= lower2 */
180
- if (* penalty < 0 )
181
- * penalty = 0 ; /* subtype_diff is broken */
182
- PG_RETURN_POINTER (penalty );
183
- }
184
- else
185
- {
186
- /* wild guess */
187
- * penalty = 1.0 ;
188
- PG_RETURN_POINTER (penalty );
189
- }
181
+ /* infinite penalty for pushing non-empty into all-empty subtree */
182
+ * penalty = get_float4_infinity ();
183
+ PG_RETURN_POINTER (penalty );
190
184
}
191
185
192
186
/* if orig isn't empty, s_union can't be either */
@@ -334,15 +328,27 @@ range_gist_picksplit(PG_FUNCTION_ARGS)
334
328
Datum
335
329
range_gist_same (PG_FUNCTION_ARGS )
336
330
{
337
- /* Datum r1 = PG_GETARG_DATUM (0); */
338
- /* Datum r2 = PG_GETARG_DATUM (1); */
331
+ RangeType * r1 = PG_GETARG_RANGE (0 );
332
+ RangeType * r2 = PG_GETARG_RANGE (1 );
339
333
bool * result = (bool * ) PG_GETARG_POINTER (2 );
340
334
341
335
/*
342
- * We can safely call range_eq using our fcinfo directly; it won't notice
343
- * the third argument. This allows it to use fn_extra for caching.
336
+ * range_eq will ignore the RANGE_CONTAIN_EMPTY flag, so we have to
337
+ * check that for ourselves. More generally, if the entries have been
338
+ * properly normalized, then unequal flags bytes must mean unequal ranges
339
+ * ... so let's just test all the flag bits at once.
344
340
*/
345
- * result = DatumGetBool (range_eq (fcinfo ));
341
+ if (range_get_flags (r1 ) != range_get_flags (r2 ))
342
+ * result = false;
343
+ else
344
+ {
345
+ /*
346
+ * We can safely call range_eq using our fcinfo directly; it won't
347
+ * notice the third argument. This allows it to use fn_extra for
348
+ * caching.
349
+ */
350
+ * result = DatumGetBool (range_eq (fcinfo ));
351
+ }
346
352
347
353
PG_RETURN_POINTER (result );
348
354
}
@@ -356,27 +362,53 @@ range_gist_same(PG_FUNCTION_ARGS)
356
362
/*
357
363
* Return the smallest range that contains r1 and r2
358
364
*
359
- * XXX would it be better to redefine range_union as working this way?
365
+ * This differs from regular range_union in two critical ways:
366
+ * 1. It won't throw an error for non-adjacent r1 and r2, but just absorb
367
+ * the intervening values into the result range.
368
+ * 2. We track whether any empty range has been union'd into the result,
369
+ * so that contained_by searches can be indexed. Note that this means
370
+ * that *all* unions formed within the GiST index must go through here.
360
371
*/
361
372
static RangeType *
362
373
range_super_union (TypeCacheEntry * typcache , RangeType * r1 , RangeType * r2 )
363
374
{
375
+ RangeType * result ;
364
376
RangeBound lower1 ,
365
377
lower2 ;
366
378
RangeBound upper1 ,
367
379
upper2 ;
368
380
bool empty1 ,
369
381
empty2 ;
382
+ char flags1 ,
383
+ flags2 ;
370
384
RangeBound * result_lower ;
371
385
RangeBound * result_upper ;
372
386
373
387
range_deserialize (typcache , r1 , & lower1 , & upper1 , & empty1 );
374
388
range_deserialize (typcache , r2 , & lower2 , & upper2 , & empty2 );
389
+ flags1 = range_get_flags (r1 );
390
+ flags2 = range_get_flags (r2 );
375
391
376
392
if (empty1 )
393
+ {
394
+ /* We can return r2 as-is if it already is or contains empty */
395
+ if (flags2 & (RANGE_EMPTY | RANGE_CONTAIN_EMPTY ))
396
+ return r2 ;
397
+ /* Else we'd better copy it (modify-in-place isn't safe) */
398
+ r2 = rangeCopy (r2 );
399
+ range_set_contain_empty (r2 );
377
400
return r2 ;
401
+ }
378
402
if (empty2 )
403
+ {
404
+ /* We can return r1 as-is if it already is or contains empty */
405
+ if (flags1 & (RANGE_EMPTY | RANGE_CONTAIN_EMPTY ))
406
+ return r1 ;
407
+ /* Else we'd better copy it (modify-in-place isn't safe) */
408
+ r1 = rangeCopy (r1 );
409
+ range_set_contain_empty (r1 );
379
410
return r1 ;
411
+ }
380
412
381
413
if (range_cmp_bounds (typcache , & lower1 , & lower2 ) <= 0 )
382
414
result_lower = & lower1 ;
@@ -389,12 +421,19 @@ range_super_union(TypeCacheEntry *typcache, RangeType * r1, RangeType * r2)
389
421
result_upper = & upper2 ;
390
422
391
423
/* optimization to avoid constructing a new range */
392
- if (result_lower == & lower1 && result_upper == & upper1 )
424
+ if (result_lower == & lower1 && result_upper == & upper1 &&
425
+ ((flags1 & RANGE_CONTAIN_EMPTY ) || !(flags2 & RANGE_CONTAIN_EMPTY )))
393
426
return r1 ;
394
- if (result_lower == & lower2 && result_upper == & upper2 )
427
+ if (result_lower == & lower2 && result_upper == & upper2 &&
428
+ ((flags2 & RANGE_CONTAIN_EMPTY ) || !(flags1 & RANGE_CONTAIN_EMPTY )))
395
429
return r2 ;
396
430
397
- return make_range (typcache , result_lower , result_upper , false);
431
+ result = make_range (typcache , result_lower , result_upper , false);
432
+
433
+ if ((flags1 & RANGE_CONTAIN_EMPTY ) || (flags2 & RANGE_CONTAIN_EMPTY ))
434
+ range_set_contain_empty (result );
435
+
436
+ return result ;
398
437
}
399
438
400
439
/*
@@ -484,21 +523,26 @@ range_gist_consistent_int(FmgrInfo *flinfo, StrategyNumber strategy,
484
523
break ;
485
524
case RANGESTRAT_CONTAINED_BY :
486
525
/*
487
- * Ideally we'd apply range_overlaps here, but at present it
488
- * might fail to find empty ranges in the index, which should
489
- * be reported as being contained by anything. This needs work .
526
+ * Empty ranges are contained by anything, so if key is or
527
+ * contains any empty ranges, we must descend into it. Otherwise,
528
+ * descend only if key overlaps the query .
490
529
*/
491
- return true;
530
+ if (RangeIsOrContainsEmpty (key ))
531
+ return true;
532
+ proc = range_overlaps ;
492
533
break ;
493
534
case RANGESTRAT_CONTAINS_ELEM :
494
535
proc = range_contains_elem ;
495
536
break ;
496
537
case RANGESTRAT_EQ :
538
+ /*
539
+ * If query is empty, descend only if the key is or contains any
540
+ * empty ranges. Otherwise, descend if key contains query.
541
+ */
542
+ if (RangeIsEmpty (DatumGetRangeType (query )))
543
+ return RangeIsOrContainsEmpty (key );
497
544
proc = range_contains ;
498
545
break ;
499
- case RANGESTRAT_NE :
500
- return true;
501
- break ;
502
546
default :
503
547
elog (ERROR , "unrecognized range strategy: %d" , strategy );
504
548
proc = NULL ; /* keep compiler quiet */
@@ -555,9 +599,6 @@ range_gist_consistent_leaf(FmgrInfo *flinfo, StrategyNumber strategy,
555
599
case RANGESTRAT_EQ :
556
600
proc = range_eq ;
557
601
break ;
558
- case RANGESTRAT_NE :
559
- proc = range_ne ;
560
- break ;
561
602
default :
562
603
elog (ERROR , "unrecognized range strategy: %d" , strategy );
563
604
proc = NULL ; /* keep compiler quiet */
0 commit comments