@@ -305,6 +305,16 @@ spg_range_quad_inner_consistent(PG_FUNCTION_ARGS)
305305 int which ;
306306 int i ;
307307
308+ /*
309+ * For adjacent search we need also previous centroid (if any) to improve
310+ * the precision of the consistent check. In this case needPrevious flag is
311+ * set and centroid is passed into reconstructedValues. This is not the
312+ * intended purpose of reconstructedValues (because we already have the
313+ * full value available at the leaf), but it's a convenient place to store
314+ * state while traversing the tree.
315+ */
316+ bool needPrevious = false;
317+
308318 if (in -> allTheSame )
309319 {
310320 /* Report that all nodes should be visited */
@@ -351,6 +361,7 @@ spg_range_quad_inner_consistent(PG_FUNCTION_ARGS)
351361 case RANGESTRAT_OVERLAPS :
352362 case RANGESTRAT_OVERRIGHT :
353363 case RANGESTRAT_AFTER :
364+ case RANGESTRAT_ADJACENT :
354365 /* These strategies return false if any argument is empty */
355366 if (empty )
356367 which = 0 ;
@@ -435,6 +446,9 @@ spg_range_quad_inner_consistent(PG_FUNCTION_ARGS)
435446 /* Are the restrictions on range bounds inclusive? */
436447 bool inclusive = true;
437448 bool strictEmpty = true;
449+ int cmp ,
450+ which1 ,
451+ which2 ;
438452
439453 strategy = in -> scankeys [i ].sk_strategy ;
440454
@@ -522,6 +536,118 @@ spg_range_quad_inner_consistent(PG_FUNCTION_ARGS)
522536 inclusive = false;
523537 break ;
524538
539+ case RANGESTRAT_ADJACENT :
540+ if (empty )
541+ break ; /* Skip to strictEmpty check. */
542+
543+ /*
544+ * which1 is bitmask for possibility to be adjacent with
545+ * lower bound of argument. which2 is bitmask for
546+ * possibility to be adjacent with upper bound of argument.
547+ */
548+ which1 = which2 = (1 << 1 ) | (1 << 2 ) | (1 << 3 ) | (1 << 4 );
549+
550+ /*
551+ * Previously selected quadrant could exclude possibility
552+ * for lower or upper bounds to be adjacent. Deserialize
553+ * previous centroid range if present for checking this.
554+ */
555+ if (in -> reconstructedValue != (Datum ) 0 )
556+ {
557+ RangeType * prevCentroid ;
558+ RangeBound prevLower ,
559+ prevUpper ;
560+ bool prevEmpty ;
561+ int cmp1 ,
562+ cmp2 ;
563+
564+ prevCentroid = DatumGetRangeType (in -> reconstructedValue );
565+ range_deserialize (typcache , prevCentroid ,
566+ & prevLower , & prevUpper , & prevEmpty );
567+
568+ /*
569+ * Check if lower bound of argument is not in a
570+ * quadrant we visited in the previous step.
571+ */
572+ cmp1 = range_cmp_bounds (typcache , & lower , & prevUpper );
573+ cmp2 = range_cmp_bounds (typcache , & centroidUpper ,
574+ & prevUpper );
575+ if ((cmp2 < 0 && cmp1 > 0 ) || (cmp2 > 0 && cmp1 < 0 ))
576+ which1 = 0 ;
577+
578+ /*
579+ * Check if upper bound of argument is not in a
580+ * quadrant we visited in the previous step.
581+ */
582+ cmp1 = range_cmp_bounds (typcache , & upper , & prevLower );
583+ cmp2 = range_cmp_bounds (typcache , & centroidLower ,
584+ & prevLower );
585+ if ((cmp2 < 0 && cmp1 > 0 ) || (cmp2 > 0 && cmp1 < 0 ))
586+ which2 = 0 ;
587+ }
588+
589+ if (which1 )
590+ {
591+ /*
592+ * For a range's upper bound to be adjacent to the
593+ * argument's lower bound, it will be found along the
594+ * line adjacent to (and just below) Y=lower.
595+ * Therefore, if the argument's lower bound is less
596+ * than the centroid's upper bound, the line falls in
597+ * quadrants 2 and 3; if greater, the line falls in
598+ * quadrants 1 and 4.
599+ *
600+ * The above is true even when the argument's lower
601+ * bound is greater and adjacent to the centroid's
602+ * upper bound. If the argument's lower bound is
603+ * greater than the centroid's upper bound, then the
604+ * lowest value that an adjacent range could have is
605+ * that of the centroid's upper bound, which still
606+ * falls in quadrants 1 and 4.
607+ *
608+ * In the edge case, where the argument's lower bound
609+ * is equal to the cetroid's upper bound, there may be
610+ * adjacent ranges in any quadrant.
611+ */
612+ cmp = range_cmp_bounds (typcache , & lower ,
613+ & centroidUpper );
614+ if (cmp < 0 )
615+ which1 &= (1 << 2 ) | (1 << 3 );
616+ else if (cmp > 0 )
617+ which1 &= (1 << 1 ) | (1 << 4 );
618+ }
619+
620+ if (which2 )
621+ {
622+ /*
623+ * For a range's lower bound to be adjacent to the
624+ * argument's upper bound, it will be found along the
625+ * line adjacent to (and just right of)
626+ * X=upper. Therefore, if the argument's upper bound is
627+ * less than (and not adjacent to) the centroid's upper
628+ * bound, the line falls in quadrants 3 and 4; if
629+ * greater or equal to, the line falls in quadrants 1
630+ * and 2.
631+ *
632+ * The edge case is when the argument's upper bound is
633+ * less than and adjacent to the centroid's lower
634+ * bound. In that case, adjacent ranges may be in any
635+ * quadrant.
636+ */
637+ cmp = range_cmp_bounds (typcache , & lower ,
638+ & centroidUpper );
639+ if (cmp < 0 &&
640+ !bounds_adjacent (typcache , upper , centroidLower ))
641+ which1 &= (1 << 3 ) | (1 << 4 );
642+ else if (cmp > 0 )
643+ which1 &= (1 << 1 ) | (1 << 2 );
644+ }
645+
646+ which &= which1 | which2 ;
647+
648+ needPrevious = true;
649+ break ;
650+
525651 case RANGESTRAT_CONTAINS :
526652 /*
527653 * Non-empty range A contains non-empty range B if lower
@@ -652,11 +778,18 @@ spg_range_quad_inner_consistent(PG_FUNCTION_ARGS)
652778
653779 /* We must descend into the quadrant(s) identified by 'which' */
654780 out -> nodeNumbers = (int * ) palloc (sizeof (int ) * in -> nNodes );
781+ if (needPrevious )
782+ out -> reconstructedValues = (Datum * ) palloc (sizeof (Datum ) * in -> nNodes );
655783 out -> nNodes = 0 ;
656784 for (i = 1 ; i <= in -> nNodes ; i ++ )
657785 {
658786 if (which & (1 << i ))
787+ {
788+ /* Save previous prefix if needed */
789+ if (needPrevious )
790+ out -> reconstructedValues [out -> nNodes ] = in -> prefixDatum ;
659791 out -> nodeNumbers [out -> nNodes ++ ] = i - 1 ;
792+ }
660793 }
661794
662795 PG_RETURN_VOID ();
@@ -713,6 +846,10 @@ spg_range_quad_leaf_consistent(PG_FUNCTION_ARGS)
713846 res = range_after_internal (typcache , leafRange ,
714847 DatumGetRangeType (keyDatum ));
715848 break ;
849+ case RANGESTRAT_ADJACENT :
850+ res = range_adjacent_internal (typcache , leafRange ,
851+ DatumGetRangeType (keyDatum ));
852+ break ;
716853 case RANGESTRAT_CONTAINS :
717854 res = range_contains_internal (typcache , leafRange ,
718855 DatumGetRangeType (keyDatum ));
0 commit comments