[CALCITE-5288] '(a > 5 and a < 15) or (a > 10 and a < 20)' should be simplified to 'SEARCH(a, Sarg[(5..20)])'

This closes #2912
diff --git a/core/src/main/java/org/apache/calcite/rex/RexSimplify.java b/core/src/main/java/org/apache/calcite/rex/RexSimplify.java
index 006ce25..0bc462d 100644
--- a/core/src/main/java/org/apache/calcite/rex/RexSimplify.java
+++ b/core/src/main/java/org/apache/calcite/rex/RexSimplify.java
@@ -2902,14 +2902,16 @@
     boolean needToFix() {
       // Fix and converts to SEARCH if:
       // 1. A Sarg has complexity greater than 1;
-      // 2. The terms are reduced as simpler Sarg points;
-      // 3. The terms are reduced as simpler Sarg comparison.
+      // 2. A Sarg was merged with another Sarg or range;
+      // 3. The terms are reduced as simpler Sarg points;
+      // 4. The terms are reduced as simpler Sarg comparison.
 
       // Ignore 'negate' just to be compatible with previous versions of this
       // method. "build().complexity()" would be a better estimate, if we could
       // switch to it breaking lots of plans.
       final Collection<RexSargBuilder> builders = map.values();
-      return builders.stream().anyMatch(b -> b.build(false).complexity() > 1)
+      return builders.stream()
+          .anyMatch(b -> b.build(false).complexity() > 1 || b.mergedSarg)
           || newTermsCount == 1
           && builders.stream().allMatch(b -> simpleSarg(b.build()));
     }
@@ -2972,6 +2974,8 @@
     final boolean negate;
     final List<RelDataType> types = new ArrayList<>();
     final RangeSet<Comparable> rangeSet = TreeRangeSet.create();
+    boolean hasSarg;
+    boolean mergedSarg;
     RexUnknownAs nullAs = FALSE;
 
     RexSargBuilder(RexNode ref, RexBuilder rexBuilder, boolean negate) {
@@ -3033,6 +3037,7 @@
     void addRange(Range<Comparable> range, RelDataType type) {
       types.add(type);
       rangeSet.add(range);
+      mergedSarg |= hasSarg;
       nullAs = nullAs.or(UNKNOWN);
     }
 
@@ -3049,6 +3054,8 @@
       }
       types.add(type);
       rangeSet.addAll(r);
+      mergedSarg |= !rangeSet.isEmpty();
+      hasSarg = true;
       switch (nullAs) {
       case TRUE:
         this.nullAs = this.nullAs.or(TRUE);
diff --git a/core/src/test/java/org/apache/calcite/rex/RexProgramTest.java b/core/src/test/java/org/apache/calcite/rex/RexProgramTest.java
index bd548eb..637f83a 100644
--- a/core/src/test/java/org/apache/calcite/rex/RexProgramTest.java
+++ b/core/src/test/java/org/apache/calcite/rex/RexProgramTest.java
@@ -1243,6 +1243,20 @@
         RelOptPredicateList.of(rexBuilder,
             ImmutableList.of(le(aRef, literal(5)), le(bRef, literal(5)))),
         "false");
+
+    // condition "(a >= 1 and a <= 3) or (a >= 2 and a <= 4)"
+    // yelds "a >= 1 and a <= 4"
+    checkSimplifyFilter(
+        or(and(ge(aRef, literal(1)), le(aRef, literal(3))),
+            and(ge(aRef, literal(2)), le(aRef, literal(4)))),
+        "SEARCH(?0.a, Sarg[[1..4]])");
+
+    // condition "(a >= 1 and a <= 3) or (a >= 0 and a <= 2)"
+    // yelds "a >= 0 and a <= 3"
+    checkSimplifyFilter(
+        or(and(ge(aRef, literal(1)), le(aRef, literal(3))),
+            and(ge(aRef, literal(0)), le(aRef, literal(2)))),
+        "SEARCH(?0.a, Sarg[[0..3]])");
   }
 
   /** Test case for