This is an automated email from the ASF dual-hosted git repository.

blerer pushed a commit to branch trunk
in repository https://gitbox.apache.org/repos/asf/cassandra.git


The following commit(s) were added to refs/heads/trunk by this push:
     new 53fabf1f02 Add support for the BETWEEN operator in WHERE clauses
53fabf1f02 is described below

commit 53fabf1f02a7e920db9437bda83c871093ce8f88
Author: xvade <si...@vantuyl.com>
AuthorDate: Fri May 10 12:20:19 2024 -0700

    Add support for the BETWEEN operator in WHERE clauses
    
    patch by Simon Chess; reviewed by Benjamin Lerer and Ekaterina Dimitrova 
for CASSANDRA-19604
---
 CHANGES.txt                                        |   1 +
 .../cassandra/pages/developing/cql/changes.adoc    |   4 +
 pylib/cqlshlib/cql3handling.py                     |   1 +
 pylib/cqlshlib/test/test_cqlsh_completion.py       |   4 +-
 src/antlr/Lexer.g                                  |   1 +
 src/antlr/Parser.g                                 |  25 ++-
 src/java/org/apache/cassandra/cql3/Operator.java   |  79 +++++++
 src/java/org/apache/cassandra/cql3/Relation.java   |  27 ++-
 .../cql3/restrictions/ClusteringElements.java      |  27 +++
 .../cql3/restrictions/MergedRestriction.java       |   8 +-
 .../cql3/restrictions/SimpleRestriction.java       |  14 +-
 .../org/apache/cassandra/cql3/terms/Terms.java     |  42 +++-
 .../org/apache/cassandra/db/filter/RowFilter.java  |  12 +
 .../sai/plan/StorageAttachedIndexQueryPlan.java    |   4 +-
 .../org/apache/cassandra/cql3/RelationTest.java    |   1 +
 .../cql3/restrictions/ClusteringElementsTest.java  |  59 +++++
 .../cql3/validation/operations/DeleteTest.java     |  57 ++++-
 .../operations/SelectMultiColumnRelationTest.java  | 250 +++++++++++++++++++++
 .../operations/SelectOrderedPartitionerTest.java   |  20 +-
 .../operations/SelectSingleColumnRelationTest.java |  57 +++++
 .../cql3/validation/operations/UpdateTest.java     |   3 +
 .../db/AbstractReadQueryToCQLStringTest.java       |   6 +
 22 files changed, 679 insertions(+), 23 deletions(-)

diff --git a/CHANGES.txt b/CHANGES.txt
index dcb65b604b..3a95f15519 100644
--- a/CHANGES.txt
+++ b/CHANGES.txt
@@ -1,4 +1,5 @@
 5.1
+ * Add support for the BETWEEN operator in WHERE clauses (CASSANDRA-19604)
  * Replace Stream iteration with for-loop for 
SimpleRestriction::bindAndGetClusteringElements (CASSANDRA-19679)
  * Consolidate logging on trace level (CASSANDRA-19632)
  * Expand DDL statements on coordinator before submission to the CMS 
(CASSANDRA-19592)
diff --git a/doc/modules/cassandra/pages/developing/cql/changes.adoc 
b/doc/modules/cassandra/pages/developing/cql/changes.adoc
index 7bf6bd6d03..d7d04e66e4 100644
--- a/doc/modules/cassandra/pages/developing/cql/changes.adoc
+++ b/doc/modules/cassandra/pages/developing/cql/changes.adoc
@@ -2,6 +2,10 @@
 
 The following describes the changes in each version of CQL.
 
+== 3.4.8
+
+* Add support for the BETWEEN operator in WHERE clauses (`19604`)
+
 == 3.4.7
 
 * Add vector similarity functions (`18640`)
diff --git a/pylib/cqlshlib/cql3handling.py b/pylib/cqlshlib/cql3handling.py
index c774da3c78..51af7c266f 100644
--- a/pylib/cqlshlib/cql3handling.py
+++ b/pylib/cqlshlib/cql3handling.py
@@ -743,6 +743,7 @@ syntax_rules += r'''
                                  ( "," [rel_tokname]=<cident> )*
                              ")" ("=" | "<" | ">" | "<=" | ">=") 
<tokenDefinition>
              | [rel_lhs]=<cident> "IN" "(" <term> ( "," <term> )* ")"
+             | [rel_lhs]=<cident> "BETWEEN" <term> "AND" <term>
              ;
 <selectClause> ::= "DISTINCT"? <selector> ("AS" <cident>)? ("," <selector> 
("AS" <cident>)?)*
                  | "*"
diff --git a/pylib/cqlshlib/test/test_cqlsh_completion.py 
b/pylib/cqlshlib/test/test_cqlsh_completion.py
index 0630980f26..242db0aae2 100644
--- a/pylib/cqlshlib/test/test_cqlsh_completion.py
+++ b/pylib/cqlshlib/test/test_cqlsh_completion.py
@@ -381,7 +381,7 @@ class TestCqlshCompletion(CqlshCompletionCase):
         self.trycompletions("UPDATE empty_table SET lonelycol = 'eggs' WHERE 
lonel",
                             immediate='ykey ')
         self.trycompletions("UPDATE empty_table SET lonelycol = 'eggs' WHERE 
lonelykey ",
-                            choices=['=', '<=', '>=', '>', '<', 'CONTAINS', 
'IN', '['])
+                            choices=['=', '<=', '>=', '>', '<', 'BETWEEN', 
'CONTAINS', 'IN', '['])
         self.trycompletions("UPDATE empty_table SET lonelycol = 'eggs' WHERE 
lonelykey = 0.0 ",
                             choices=['AND', 'IF', ';'])
         self.trycompletions("UPDATE empty_table SET lonelycol = 'eggs' WHERE 
lonelykey = 0.0 AND ",
@@ -464,7 +464,7 @@ class TestCqlshCompletion(CqlshCompletionCase):
                             choices=['a', 'b', 'TOKEN('])
 
         self.trycompletions('DELETE FROM twenty_rows_composite_table USING 
TIMESTAMP 0 WHERE a ',
-                            choices=['<=', '>=', 'CONTAINS', 'IN', '[', '=', 
'<', '>'])
+                            choices=['<=', '>=', 'BETWEEN', 'CONTAINS', 'IN', 
'[', '=', '<', '>'])
 
         self.trycompletions('DELETE FROM twenty_rows_composite_table USING 
TIMESTAMP 0 WHERE TOKEN(',
                             immediate='a ')
diff --git a/src/antlr/Lexer.g b/src/antlr/Lexer.g
index f259307311..65b22ab653 100644
--- a/src/antlr/Lexer.g
+++ b/src/antlr/Lexer.g
@@ -124,6 +124,7 @@ K_FILTERING:   F I L T E R I N G;
 K_IF:          I F;
 K_IS:          I S;
 K_CONTAINS:    C O N T A I N S;
+K_BETWEEN:     B E T W E E N;
 K_GROUP:       G R O U P;
 K_CLUSTER:     C L U S T E R;
 K_INTERNALS:   I N T E R N A L S;
diff --git a/src/antlr/Parser.g b/src/antlr/Parser.g
index 3482bf4e95..c1e25bb153 100644
--- a/src/antlr/Parser.g
+++ b/src/antlr/Parser.g
@@ -1757,6 +1757,18 @@ propertyValue returns [String str]
     | u=unreserved_keyword { $str = u; }
     ;
 
+singleColumnBetweenValues returns [Terms.Raw terms]
+    @init { List<Term.Raw> list = new ArrayList<>(); }
+    @after { $terms = Terms.Raw.of(list); }
+    : t1=term { list.add(t1); } K_AND t2=term { list.add(t2); }
+    ;
+
+betweenLiterals returns [Terms.Raw literals]
+    @init { List<Term.Raw> list = new ArrayList<>(); }
+    @after { $literals = Terms.Raw.of(list); }
+    : t1=tupleLiteral { list.add(t1); } K_AND t2=tupleLiteral { list.add(t2); }
+    ;
+
 relationType returns [Operator op]
     : '='  { $op = Operator.EQ; }
     | '<'  { $op = Operator.LT; }
@@ -1768,10 +1780,12 @@ relationType returns [Operator op]
 
 relation[WhereClause.Builder clauses]
     : name=cident type=relationType t=term { 
$clauses.add(Relation.singleColumn(name, type, t)); }
+    | name=cident K_BETWEEN betweenValues=singleColumnBetweenValues
+            { $clauses.add(Relation.singleColumn($name.id, Operator.BETWEEN, 
betweenValues)); }
     | name=cident K_LIKE t=term { $clauses.add(Relation.singleColumn(name, 
Operator.LIKE, t)); }
     | name=cident K_IS K_NOT K_NULL { $clauses.add(Relation.singleColumn(name, 
Operator.IS_NOT, Constants.NULL_LITERAL)); }
-    | K_TOKEN l=tupleOfIdentifiers type=relationType t=term
-        { $clauses.add(Relation.token(l, type, t)); }
+    | K_TOKEN l=tupleOfIdentifiers type=relationType t=term { 
$clauses.add(Relation.token(l, type, t)); }
+    | K_TOKEN l=tupleOfIdentifiers K_BETWEEN 
betweenValues=singleColumnBetweenValues { $clauses.add(Relation.token(l, 
Operator.BETWEEN, betweenValues)); }
     | name=cident K_IN marker=inMarker
         { $clauses.add(Relation.singleColumn(name, Operator.IN, marker)); }
     | name=cident K_IN inValues=singleColumnInValues
@@ -1797,6 +1811,12 @@ relation[WhereClause.Builder clauses]
           }
       | type=relationType tupleMarker=markerForTuple /* (a, b, c) >= ? */
           { $clauses.add(Relation.multiColumn(ids, type, tupleMarker)); }
+      | K_BETWEEN
+            ( t1=tupleLiteral K_AND t2=tupleLiteral
+                    { $clauses.add(Relation.multiColumn(ids, Operator.BETWEEN, 
Terms.Raw.of(List.of(t1, t2)))); }
+            | m1=markerForTuple K_AND m2=markerForTuple
+                    { $clauses.add(Relation.multiColumn(ids, Operator.BETWEEN, 
Terms.Raw.of(List.of(m1, m2)))); }
+            )
       )
     | '(' relation[$clauses] ')'
     ;
@@ -2016,5 +2036,6 @@ basic_unreserved_keyword returns [String str]
         | K_SELECT_MASKED
         | K_VECTOR
         | K_ANN
+        | K_BETWEEN
         ) { $str = $k.text; }
     ;
diff --git a/src/java/org/apache/cassandra/cql3/Operator.java 
b/src/java/org/apache/cassandra/cql3/Operator.java
index de372795a1..e180a9981b 100644
--- a/src/java/org/apache/cassandra/cql3/Operator.java
+++ b/src/java/org/apache/cassandra/cql3/Operator.java
@@ -268,6 +268,11 @@ public enum Operator
     },
     IN(7)
     {
+        @Override
+        public Kind kind() {
+            return Kind.MULTI_VALUE;
+        }
+
         public boolean isSatisfiedBy(AbstractType<?> type, ByteBuffer 
leftOperand, ByteBuffer rightOperand)
         {
             ListSerializer<?> serializer = ListType.getInstance(type, 
false).getSerializer();
@@ -507,6 +512,62 @@ public enum Operator
         {
             return true;
         }
+    },
+    BETWEEN(19)
+    {
+        @Override
+        public Kind kind() {
+            return Kind.TERNARY;
+        }
+
+        @Override
+        public String toString()
+        {
+            return "BETWEEN";
+        }
+
+        @Override
+        public boolean isSatisfiedBy(AbstractType<?> type, ByteBuffer 
leftOperand, ByteBuffer rightOperand)
+        {
+            List<ByteBuffer> buffers = ListType.getInstance(type, 
false).unpack(rightOperand);
+            buffers.sort(type);
+            return type.compareForCQL(leftOperand, buffers.get(0)) >= 0 && 
type.compareForCQL(leftOperand, buffers.get(1)) <= 0;
+        }
+
+        @Override
+        public boolean requiresFilteringOrIndexingFor(ColumnMetadata.Kind 
columnKind)
+        {
+            return columnKind != ColumnMetadata.Kind.CLUSTERING;
+        }
+
+        @Override
+        public void restrict(RangeSet<ClusteringElements> rangeSet, 
List<ClusteringElements> args)
+        {
+            assert args.size() == 2 : this + " accepts exactly two values";
+            args.sort(ClusteringElements.CQL_COMPARATOR);
+            rangeSet.removeAll(ClusteringElements.lessThan(args.get(0)));
+            rangeSet.removeAll(ClusteringElements.greaterThan(args.get(1)));
+        }
+
+        @Override
+        public boolean isSlice()
+        {
+            return true;
+        }
+
+        @Override
+        public boolean canBeUsedWith(ColumnsExpression.Kind kind)
+        {
+            return kind != ColumnsExpression.Kind.MAP_ELEMENT;
+        }
+    };
+
+    /**
+     * The different kinds of operators
+     */
+    public enum Kind
+    {
+        BINARY, TERNARY, MULTI_VALUE;
     };
 
     /**
@@ -539,6 +600,24 @@ public enum Operator
         return b;
     }
 
+    /**
+     * Returns the kind of this operator.
+     * @return the kind of this operator
+     */
+    public Kind kind()
+    {
+        return Kind.BINARY;
+    }
+
+    /**
+     * Checks if this operator is a ternary operator.
+     * @return {@code true} if this operator is a ternary operator, {@code 
false} otherwise.
+     */
+    public boolean isTernary()
+    {
+        return kind() == Kind.TERNARY;
+    }
+
     /**
      * Deserializes a <code>Operator</code> instance from the specified input.
      *
diff --git a/src/java/org/apache/cassandra/cql3/Relation.java 
b/src/java/org/apache/cassandra/cql3/Relation.java
index 8054639e8c..40350ab3bb 100644
--- a/src/java/org/apache/cassandra/cql3/Relation.java
+++ b/src/java/org/apache/cassandra/cql3/Relation.java
@@ -72,7 +72,7 @@ public final class Relation
      */
     public static Relation singleColumn(ColumnIdentifier identifier, Operator 
operator, Term.Raw rawTerm)
     {
-        assert operator != Operator.IN;
+        assert operator.kind() == Operator.Kind.BINARY;
         return new Relation(ColumnsExpression.Raw.singleColumn(identifier), 
operator, Terms.Raw.of(rawTerm));
     }
 
@@ -86,6 +86,7 @@ public final class Relation
      */
     public static Relation singleColumn(ColumnIdentifier identifier, Operator 
operator, Terms.Raw rawTerms)
     {
+        assert operator.kind() != Operator.Kind.BINARY;
         return new Relation(ColumnsExpression.Raw.singleColumn(identifier), 
operator, rawTerms);
     }
 
@@ -100,6 +101,7 @@ public final class Relation
      */
     public static Relation mapElement(ColumnIdentifier identifier, Term.Raw 
rawKey, Operator operator, Term.Raw rawTerm)
     {
+        assert operator.kind() == Operator.Kind.BINARY;
         return new Relation(ColumnsExpression.Raw.mapElement(identifier, 
rawKey), operator, Terms.Raw.of(rawTerm));
     }
 
@@ -113,7 +115,7 @@ public final class Relation
      */
     public static Relation multiColumn(List<ColumnIdentifier> identifiers, 
Operator operator, Term.Raw rawTerm)
     {
-        assert operator != Operator.IN;
+        assert operator.kind() == Operator.Kind.BINARY;
         return new Relation(ColumnsExpression.Raw.multiColumn(identifiers), 
operator, Terms.Raw.of(rawTerm));
     }
 
@@ -127,6 +129,7 @@ public final class Relation
      */
     public static Relation multiColumn(List<ColumnIdentifier> identifiers, 
Operator operator, Terms.Raw rawTerms)
     {
+        assert operator.kind() != Operator.Kind.BINARY;
         return new Relation(ColumnsExpression.Raw.multiColumn(identifiers), 
operator, rawTerms);
     }
 
@@ -140,9 +143,24 @@ public final class Relation
      */
     public static Relation token(List<ColumnIdentifier> identifiers, Operator 
operator, Term.Raw rawTerm)
     {
+        assert operator.kind() == Operator.Kind.BINARY;
         return new Relation(ColumnsExpression.Raw.token(identifiers), 
operator, Terms.Raw.of(rawTerm));
     }
 
+    /**
+     * Creates a relation for token expression (e.g. {@code token(columnA, 
columnB) = ?} ).
+     *
+     * @param identifiers the column identifiers for the partition columns
+     * @param operator the relation operator
+     * @param rawTerms the terms to which the token value must be compared
+     * @return a relation for a token expression.
+     */
+    public static Relation token(List<ColumnIdentifier> identifiers, Operator 
operator, Terms.Raw rawTerms)
+    {
+        assert operator.kind() == Operator.Kind.TERNARY;
+        return new Relation(ColumnsExpression.Raw.token(identifiers), 
operator, rawTerms);
+    }
+
     /**
      * Checks if this relation is a token relation (e.g. <pre>token(a) = 
token(1)</pre>).
      *
@@ -225,6 +243,11 @@ public final class Relation
      */
     public String toCQLString()
     {
+        if (operator.isTernary())
+        {
+            List<? extends Term.Raw> terms = rawTerms.asList();
+            return String.format("%s %s %s AND %s", 
rawExpressions.toCQLString(), operator, terms.get(0), terms.get(1));
+        }
         return String.format("%s %s %s", rawExpressions.toCQLString(), 
operator, rawTerms.getText());
     }
 
diff --git 
a/src/java/org/apache/cassandra/cql3/restrictions/ClusteringElements.java 
b/src/java/org/apache/cassandra/cql3/restrictions/ClusteringElements.java
index 1467835752..a7ce83ffb2 100644
--- a/src/java/org/apache/cassandra/cql3/restrictions/ClusteringElements.java
+++ b/src/java/org/apache/cassandra/cql3/restrictions/ClusteringElements.java
@@ -19,6 +19,7 @@
 package org.apache.cassandra.cql3.restrictions;
 
 import java.nio.ByteBuffer;
+import java.util.Comparator;
 import java.util.List;
 import java.util.Objects;
 
@@ -64,6 +65,32 @@ import org.apache.cassandra.schema.ColumnMetadata;
  */
 public class ClusteringElements extends ForwardingList<ByteBuffer> implements 
Comparable<ClusteringElements>
 {
+    /**
+     * A comparator for {@code ClusteringElements} that is used to compare 
elements from a CQL point of view.
+     * <p>The Comparator will ignore reverse type as well as the number of 
elements (e.g. elements with different length but same prefix value are 
considered equals)</p>
+     */
+    public static  final Comparator<ClusteringElements> CQL_COMPARATOR = new 
Comparator<ClusteringElements>()
+    {
+        @Override
+        public int compare(ClusteringElements a, ClusteringElements b)
+        {
+            if (a == null || b == null)
+                throw new NullPointerException();
+
+            a.isComparableWith(b);
+
+            for (int i = 0, m = Math.min(a.size(), b.size()); i < m; i++)
+            {
+                int comparison = 
a.columnType(i).compareForCQL(a.values.get(i), b.values.get(i));
+
+                if (comparison != 0)
+                    return comparison;
+            }
+
+            return 0;
+        }
+    };
+
     /**
      * The empty {@code ClusteringElements} instance used to avoid creating 
unecessary empty instances.
      */
diff --git 
a/src/java/org/apache/cassandra/cql3/restrictions/MergedRestriction.java 
b/src/java/org/apache/cassandra/cql3/restrictions/MergedRestriction.java
index debbf1f89a..69b9a840a0 100644
--- a/src/java/org/apache/cassandra/cql3/restrictions/MergedRestriction.java
+++ b/src/java/org/apache/cassandra/cql3/restrictions/MergedRestriction.java
@@ -141,15 +141,15 @@ public final class MergedRestriction implements 
SingleRestriction
                                      column.name);
             }
 
-            if ((restriction.operator() == Operator.GT || 
restriction.operator() == Operator.GTE) &&
-                    (other.operator() == Operator.GT || other.operator() == 
Operator.GTE))
+            if ((restriction.operator() == Operator.GT || 
restriction.operator() == Operator.GTE || restriction.operator() == 
Operator.BETWEEN) &&
+                    (other.operator() == Operator.GT || other.operator() == 
Operator.GTE || other.operator() == Operator.BETWEEN))
             {
                 throw invalidRequest("More than one restriction was found for 
the start bound on %s",
                                      
toCQLString(getColumnsInCommons(restriction, other)));
             }
 
-            if ((restriction.operator() == Operator.LT || 
restriction.operator() == Operator.LTE) &&
-                    (other.operator() == Operator.LT || other.operator() == 
Operator.LTE))
+            if ((restriction.operator() == Operator.LT || 
restriction.operator() == Operator.LTE || restriction.operator() == 
Operator.BETWEEN) &&
+                    (other.operator() == Operator.LT || other.operator() == 
Operator.LTE || other.operator() == Operator.BETWEEN))
             {
                 throw invalidRequest("More than one restriction was found for 
the end bound on %s",
                                      
toCQLString(getColumnsInCommons(restriction, other)));
diff --git 
a/src/java/org/apache/cassandra/cql3/restrictions/SimpleRestriction.java 
b/src/java/org/apache/cassandra/cql3/restrictions/SimpleRestriction.java
index 0898cacf43..c58eb70985 100644
--- a/src/java/org/apache/cassandra/cql3/restrictions/SimpleRestriction.java
+++ b/src/java/org/apache/cassandra/cql3/restrictions/SimpleRestriction.java
@@ -325,9 +325,9 @@ public final class SimpleRestriction implements 
SingleRestriction
                 List<ByteBuffer> buffers = bindAndGet(options);
 
                 ColumnMetadata column = firstColumn();
-                if (operator == Operator.IN)
+                if (operator == Operator.IN || operator == Operator.BETWEEN)
                 {
-                    filter.add(column, operator, inValues(column, buffers));
+                    filter.add(column, operator, 
multiInputOperatorValues(column, buffers));
                 }
                 else if (operator == Operator.LIKE)
                 {
@@ -366,7 +366,7 @@ public final class SimpleRestriction implements 
SingleRestriction
                                                                              
.map(elements -> elements.get(0))
                                                                              
.collect(Collectors.toList());
 
-                        filter.add(firstColumn(), Operator.IN, 
inValues(firstColumn(), values));
+                        filter.add(firstColumn(), Operator.IN, 
multiInputOperatorValues(firstColumn(), values));
                     }
                     else
                     {
@@ -383,14 +383,20 @@ public final class SimpleRestriction implements 
SingleRestriction
         }
     }
 
-    private static ByteBuffer inValues(ColumnMetadata column, List<ByteBuffer> 
values)
+    private static ByteBuffer multiInputOperatorValues(ColumnMetadata column, 
List<ByteBuffer> values)
     {
+
         return ListType.getInstance(column.type, false).pack(values);
     }
 
     @Override
     public String toString()
     {
+        if (operator.isTernary())
+        {
+            List<? extends Term> terms = values.asList();
+            return String.format("%s %s %s AND %s", 
columnsExpression.toCQLString(), operator, terms.get(0), terms.get(1));
+        }
         return String.format("%s %s %s", columnsExpression.toCQLString(), 
operator, values);
     }
 }
diff --git a/src/java/org/apache/cassandra/cql3/terms/Terms.java 
b/src/java/org/apache/cassandra/cql3/terms/Terms.java
index 8a78ef9bfd..519f94c505 100644
--- a/src/java/org/apache/cassandra/cql3/terms/Terms.java
+++ b/src/java/org/apache/cassandra/cql3/terms/Terms.java
@@ -58,7 +58,7 @@ public interface Terms
         @Override
         public List<Terminal> asList()
         {
-            throw new UnsupportedOperationException();
+            throw new UnsupportedOperationException("Cannot convert 
UNSET_TERMINALS to a list of terminals");
         }
 
         @Override
@@ -158,6 +158,16 @@ public interface Terms
         return NonTerminals.of(terms);
     }
 
+    /**
+     * Converts these {@code Terms} into a {@code List} of {@code Term}.
+     * @return a {@code List} of {@code Term}.
+     * @throws UnsupportedOperationException if the conversion is not 
supported.
+     */
+    default List<? extends Term> asList()
+    {
+        throw new UnsupportedOperationException(this.getClass() + " cannot be 
converted in a list of Term");
+    }
+
     /**
      * Checks if these {@code terms} knows that it contains a single {@code 
term}.
      * <p>
@@ -199,6 +209,16 @@ public interface Terms
          */
         public abstract String getText();
 
+        /**
+         * Converts these {@code Terms.Raw} into a {@code List} of {@code 
Term.Raw} if supported.
+         * @return a {@code List} of {@code Term.Raw}.
+         * @throws UnsupportedOperationException if the conversion is not 
supported.
+         */
+        public List<? extends Term.Raw> asList()
+        {
+            throw new UnsupportedOperationException(this.getClass() + " cannot 
be converted in a list of Term.Raw");
+        }
+
         @Override
         public int hashCode()
         {
@@ -256,6 +276,11 @@ public interface Terms
                                         .collect(Collectors.joining(", ", "(", 
")"));
                 }
 
+                @Override
+                public List<? extends Term.Raw> asList() {
+                    return raws;
+                }
+
                 @Override
                 public AbstractType<?> getExactTypeIfKnown(String keyspace)
                 {
@@ -280,6 +305,11 @@ public interface Terms
                     return Terms.of(raw.prepare(keyspace, receiver));
                 }
 
+                @Override
+                public List<? extends Term.Raw> asList() {
+                    return Collections.singletonList(raw);
+                }
+
                 @Override
                 public String getText()
                 {
@@ -489,6 +519,11 @@ public interface Terms
                     return 
Collections.singletonList(term.bindAndGetElements(options));
                 }
 
+                @Override
+                public List<? extends Term> asList() {
+                    return Collections.singletonList(term);
+                }
+
                 @Override
                 public boolean containsSingleTerm()
                 {
@@ -562,6 +597,11 @@ public interface Terms
                     return buffers;
                 }
 
+                @Override
+                public List<? extends Term> asList() {
+                    return terms;
+                }
+
                 @Override
                 public boolean containsSingleTerm()
                 {
diff --git a/src/java/org/apache/cassandra/db/filter/RowFilter.java 
b/src/java/org/apache/cassandra/db/filter/RowFilter.java
index 60b019d077..6c427873ba 100644
--- a/src/java/org/apache/cassandra/db/filter/RowFilter.java
+++ b/src/java/org/apache/cassandra/db/filter/RowFilter.java
@@ -743,11 +743,23 @@ public class RowFilter implements 
Iterable<RowFilter.Expression>
                     type = ((MapType<?, ?>)type).nameComparator();
                     break;
                 case IN:
+                case BETWEEN:
                     type = ListType.getInstance(type, false);
                     break;
                 default:
                     break;
             }
+
+            if (operator.isTernary())
+            {
+                ListType<?> listType = (ListType<?>) type;
+                List<? extends ByteBuffer> buffers = listType.unpack(value);
+                AbstractType<?> elementType = listType.getElementsType();
+                return cql
+                    ? String.format("%s %s %s AND %s", 
column.name.toCQLString(), operator, elementType.toCQLString(buffers.get(0)), 
elementType.toCQLString(buffers.get(1)))
+                    : String.format("%s %s %s AND %s", column.name.toString(), 
operator, elementType.getString(buffers.get(0)), 
elementType.getString(buffers.get(1)));
+            }
+
             return cql
                  ? String.format("%s %s %s", column.name.toCQLString(), 
operator, type.toCQLString(value) )
                  : String.format("%s %s %s", column.name.toString(), operator, 
type.getString(value));
diff --git 
a/src/java/org/apache/cassandra/index/sai/plan/StorageAttachedIndexQueryPlan.java
 
b/src/java/org/apache/cassandra/index/sai/plan/StorageAttachedIndexQueryPlan.java
index e352f7fcdd..8f2926314e 100644
--- 
a/src/java/org/apache/cassandra/index/sai/plan/StorageAttachedIndexQueryPlan.java
+++ 
b/src/java/org/apache/cassandra/index/sai/plan/StorageAttachedIndexQueryPlan.java
@@ -75,7 +75,7 @@ public class StorageAttachedIndexQueryPlan implements 
Index.QueryPlan
 
         for (RowFilter.Expression expression : filter)
         {
-            // We ignore any expressions here (currently IN and user-defined 
expressions) where we don't have a way to
+            // We ignore any expressions here (user-defined expressions and 
SAI unsupported operators) where we don't have a way to
             // translate their #isSatifiedBy method, they will be included in 
the filter returned by 
             // QueryPlan#postIndexQueryFilter(). If strict filtering is not 
allowed, we must reject the query until the
             // expression(s) in question are compatible with #isSatifiedBy.
@@ -83,7 +83,7 @@ public class StorageAttachedIndexQueryPlan implements 
Index.QueryPlan
             // Note: For both the pre- and post-filters we need to check that 
the expression exists before removing it
             // because the without method assert if the expression doesn't 
exist. This can be the case if we are given
             // a duplicate expression - a = 1 and a = 1. The without method 
removes all instances of the expression.
-            if (expression.operator().isIN() || expression.isUserDefined())
+            if (!Expression.supportsOperator(expression.operator()) || 
expression.isUserDefined())
             {
                 if (!filter.isStrict())
                     throw new 
InvalidRequestException(String.format(UNSUPPORTED_NON_STRICT_OPERATOR, 
expression.operator()));
diff --git a/test/unit/org/apache/cassandra/cql3/RelationTest.java 
b/test/unit/org/apache/cassandra/cql3/RelationTest.java
index 4d59b1c884..46927e1569 100644
--- a/test/unit/org/apache/cassandra/cql3/RelationTest.java
+++ b/test/unit/org/apache/cassandra/cql3/RelationTest.java
@@ -57,6 +57,7 @@ public class RelationTest
         assertEquals("col = 2", singleColumn(col, Operator.EQ, 
two).toCQLString());
         assertEquals("col = 'text'", singleColumn(col, Operator.EQ, 
text).toCQLString());
         assertEquals("col >= ?", singleColumn(col, Operator.GTE, 
marker).toCQLString());
+        assertEquals("col BETWEEN 1 AND 2", singleColumn(col, 
Operator.BETWEEN, oneTwo).toCQLString());
         assertEquals("col IN ?", singleColumn(col, Operator.IN, 
inMarker).toCQLString());
         assertEquals("col IN (1, 2)", singleColumn(col, Operator.IN, 
oneTwo).toCQLString());
         assertEquals("col IN (1)", singleColumn(col, Operator.IN, 
Terms.Raw.of(List.of(one))).toCQLString());
diff --git 
a/test/unit/org/apache/cassandra/cql3/restrictions/ClusteringElementsTest.java 
b/test/unit/org/apache/cassandra/cql3/restrictions/ClusteringElementsTest.java
index 61c740fc3b..60e2508be2 100644
--- 
a/test/unit/org/apache/cassandra/cql3/restrictions/ClusteringElementsTest.java
+++ 
b/test/unit/org/apache/cassandra/cql3/restrictions/ClusteringElementsTest.java
@@ -20,6 +20,7 @@ package org.apache.cassandra.cql3.restrictions;
 
 import java.util.ArrayList;
 import java.util.Arrays;
+import java.util.Comparator;
 import java.util.List;
 import java.util.stream.Collectors;
 
@@ -588,6 +589,64 @@ public class ClusteringElementsTest
         assertUnsupported("Range endpoints cannot be extended", () -> 
bottom.extend(third));
     }
 
+    @Test
+    public void testForCQLComparator()
+    {
+        for (List<ColumnMetadata> columns : asList(newClusteringColumns(ASC, 
ASC, ASC),
+                                                   newClusteringColumns(ASC, 
ASC, DESC),
+                                                   newClusteringColumns(DESC, 
DESC, ASC),
+                                                   newClusteringColumns(DESC, 
DESC, DESC),
+                                                   newClusteringColumns(ASC, 
DESC, ASC),
+                                                   newClusteringColumns(ASC, 
DESC, DESC),
+                                                   newClusteringColumns(DESC, 
ASC, ASC),
+                                                   newClusteringColumns(DESC, 
ASC, DESC)))
+        {
+            ClusteringElements zeroZeroZero = elements(columns, 0, 0, 0);
+            ClusteringElements oneZeroOne = elements(columns, 1, 0, 1);
+            ClusteringElements oneThreeZero = elements(columns, 1, 3, 0);
+            ClusteringElements oneThreeOne = elements(columns, 1, 3, 1);
+            ClusteringElements oneThreeFive = elements(columns, 1, 3, 5);
+            ClusteringElements oneFiveOne = elements(columns, 1, 5, 1);
+            ClusteringElements twoFiveFive = elements(columns, 2, 5, 5);
+
+            List<ClusteringElements> elementsList = new ArrayList<>();
+            elementsList.add(zeroZeroZero);
+            elementsList.add(oneFiveOne);
+            elementsList.add(oneThreeZero);
+            elementsList.add(oneThreeOne);
+            elementsList.add(twoFiveFive);
+            elementsList.add(oneThreeFive);
+            elementsList.add(oneZeroOne);
+
+            Comparator<ClusteringElements> comparator = 
ClusteringElements.CQL_COMPARATOR;
+            elementsList.sort(comparator);
+
+            assertEquals(zeroZeroZero, elementsList.get(0));
+            assertEquals(oneZeroOne, elementsList.get(1));
+            assertEquals(oneThreeZero, elementsList.get(2));
+            assertEquals(oneThreeOne, elementsList.get(3));
+            assertEquals(oneThreeFive, elementsList.get(4));
+            assertEquals(oneFiveOne, elementsList.get(5));
+            assertEquals(twoFiveFive, elementsList.get(6));
+        }
+    }
+
+    @Test
+    public void testForCQLComparatorWithDifferentLength()
+    {
+        ClusteringElements one = elements(newClusteringColumns(ASC), 1);
+        ClusteringElements oneZero = elements(newClusteringColumns(ASC, ASC), 
1, 0);
+        ClusteringElements oneZeroOne = elements(newClusteringColumns(ASC, 
ASC, ASC), 1, 0, 1);
+
+        assertEquals(0, ClusteringElements.CQL_COMPARATOR.compare(one, one));
+        assertEquals(0, ClusteringElements.CQL_COMPARATOR.compare(one, 
oneZero));
+        assertEquals(0, ClusteringElements.CQL_COMPARATOR.compare(oneZero, 
one));
+        assertEquals(0, ClusteringElements.CQL_COMPARATOR.compare(one, 
oneZeroOne));
+        assertEquals(0, ClusteringElements.CQL_COMPARATOR.compare(oneZeroOne, 
one));
+        assertEquals(0, ClusteringElements.CQL_COMPARATOR.compare(oneZero, 
oneZeroOne));
+        assertEquals(0, ClusteringElements.CQL_COMPARATOR.compare(oneZeroOne, 
oneZero));
+    }
+
     private void assertUnsupported(String expectedMsg, Runnable r)
     {
         try
diff --git 
a/test/unit/org/apache/cassandra/cql3/validation/operations/DeleteTest.java 
b/test/unit/org/apache/cassandra/cql3/validation/operations/DeleteTest.java
index 645becd94c..43a036446a 100644
--- a/test/unit/org/apache/cassandra/cql3/validation/operations/DeleteTest.java
+++ b/test/unit/org/apache/cassandra/cql3/validation/operations/DeleteTest.java
@@ -75,7 +75,15 @@ public class DeleteTest extends CQLTester
         execute("INSERT INTO %s (a, b, c) VALUES (?, ?, ?)", 2, 3, 4);
         flush(flushData);
 
-        execute("DELETE FROM %s WHERE a = ? AND b >= ?", 2, 2);
+        execute("DELETE FROM %s WHERE a = ? AND b >= ?", 2, 3);
+        flush(flushTombstone);
+
+        assertRowsIgnoringOrder(execute("SELECT * FROM %s"),
+                                row(1, 1, 1),
+                                row(2, 1, 2),
+                                row(2, 2, 3));
+
+        execute("DELETE FROM %s WHERE a = ? AND b BETWEEN ? AND ?", 2, 2, 2);
         flush(flushTombstone);
 
         assertRowsIgnoringOrder(execute("SELECT * FROM %s"),
@@ -484,10 +492,13 @@ public class DeleteTest extends CQLTester
         assertInvalidMessage("Only EQ and IN relation are supported on the 
partition key (unless you use the token() function)",
                              "DELETE FROM %s WHERE partitionKey > ? ", 0);
 
+        assertInvalidMessage("Only EQ and IN relation are supported on the 
partition key (unless you use the token() function)",
+                             "DELETE FROM %s WHERE partitionKey BETWEEN ? AND 
?", 0, 3);
+
         assertInvalidMessage("Cannot use DELETE with CONTAINS",
                              "DELETE FROM %s WHERE partitionKey CONTAINS ?", 
0);
 
-        // Non primary key in the where clause
+        // Non-primary key in the where clause
         assertInvalidMessage("Non PRIMARY KEY columns found in where clause: 
value",
                              "DELETE FROM %s WHERE partitionKey = ? AND value 
= ?", 0, 1);
     }
@@ -698,6 +709,8 @@ public class DeleteTest extends CQLTester
         execute("INSERT INTO %s (a, b, c) VALUES(1, 1, '1')");
         execute("INSERT INTO %s (a, b, c) VALUES(1, 3, '3')");
         execute("DELETE FROM %s where a=1 and b >= 2 and b <= 3");
+        execute("INSERT INTO %s (a, b, c) VALUES(1, 3, '3')");
+        execute("DELETE FROM %s where a=1 and b BETWEEN 2 AND 3");
         execute("INSERT INTO %s (a, b, c) VALUES(1, 2, '2')");
         flush();
 
@@ -798,6 +811,11 @@ public class DeleteTest extends CQLTester
         assertRows(execute("SELECT * FROM %s WHERE partitionKey = ?", 3),
                    row(3, 2, 17));
 
+        execute("DELETE FROM %s WHERE partitionKey = ? AND (clustering) 
BETWEEN (?) AND (?)", 3, 0, 1);
+        flush(forceFlush);
+        assertRows(execute("SELECT * FROM %s WHERE partitionKey = ?", 3),
+                   row(3, 2, 17));
+
         // Test invalid queries
         assertInvalidMessage("Range deletions are not supported for specific 
columns",
                              "DELETE value FROM %s WHERE partitionKey = ? AND 
clustering >= ?", 2, 1);
@@ -866,6 +884,12 @@ public class DeleteTest extends CQLTester
                    row(0, 2, 1, 11),
                    row(0, 2, 2, 12));
 
+        execute("DELETE FROM %s WHERE partitionKey = ? AND  clustering_1 = ? 
AND clustering_2 BETWEEN ? AND ? ", 0, 2, 2, 5);
+        flush(forceFlush);
+        assertRows(execute("SELECT * FROM %s WHERE partitionKey = ? AND  
clustering_1 = ?", 0, 2),
+                   row(0, 2, 0, 10),
+                   row(0, 2, 1, 11));
+
         execute("DELETE FROM %s WHERE partitionKey = ? AND  clustering_1 = ? 
AND clustering_2 >= ? ", 0, 2, 1);
         flush(forceFlush);
         assertRows(execute("SELECT * FROM %s WHERE partitionKey = ? AND  
clustering_1 = ?", 0, 2),
@@ -887,6 +911,13 @@ public class DeleteTest extends CQLTester
                    row(0, 3, 1, 16),
                    row(0, 3, 4, 19));
 
+        execute("DELETE FROM %s WHERE partitionKey = ? AND  clustering_1 = ? 
AND clustering_2 BETWEEN ? AND ? ",
+                0, 3, 2, 4);
+        flush(forceFlush);
+        assertRows(execute("SELECT * FROM %s WHERE partitionKey = ? AND  
clustering_1 = ?", 0, 3),
+                   row(0, 3, 0, 15),
+                   row(0, 3, 1, 16));
+
         execute("DELETE FROM %s WHERE partitionKey = ? AND  clustering_1 = ? 
AND clustering_2 >= ? AND clustering_2 <= ? ",
                 0, 3, 1, 4);
         flush(forceFlush);
@@ -962,6 +993,12 @@ public class DeleteTest extends CQLTester
                    row(2, 3, 2, 67),
                    row(2, 3, 3, 68));
 
+        execute("DELETE FROM %s WHERE partitionKey = ? AND (clustering_1, 
clustering_2) BETWEEN (?, ?) AND (?, ?)", 2, 3, 3, 4, 4);
+        flush(forceFlush);
+        assertRows(execute("SELECT * FROM %s WHERE partitionKey = ?", 2),
+                   row(2, 3, 1, 66),
+                   row(2, 3, 2, 67));
+
         execute("DELETE FROM %s WHERE partitionKey = ? AND (clustering_1, 
clustering_2) >= (?, ?) AND (clustering_1) <= (?)", 2, 3, 2, 4);
         flush(forceFlush);
         assertRows(execute("SELECT * FROM %s WHERE partitionKey = ?", 2),
@@ -1152,10 +1189,18 @@ public class DeleteTest extends CQLTester
 
         flush();
 
-        execute("DELETE FROM %s WHERE k = ? AND i >= ? AND i <= ?", "a", 2, 7);
+        execute("DELETE FROM %s WHERE k = ? AND i >= ? AND i <= ?", "a", 2, 5);
 
         assertRows(execute("SELECT i FROM %s WHERE k = ? ORDER BY i DESC", 
"a"),
-            row(9), row(8), row(1), row(0)
+                   row(9), row(8), row(7), row(6), row(1), row(0)
+        );
+
+        flush();
+
+        execute("DELETE FROM %s WHERE k = ? AND i BETWEEN ? AND ?", "a", 2, 7);
+
+        assertRows(execute("SELECT i FROM %s WHERE k = ? ORDER BY i DESC", 
"a"),
+                   row(9), row(8), row(1), row(0)
         );
 
         flush();
@@ -1397,7 +1442,9 @@ public class DeleteTest extends CQLTester
         for (int i = 0; i < 10; i++)
             execute("INSERT INTO %s(k, i, v) VALUES (?, ?, ?) USING TIMESTAMP 
3", "a", i*2, longText);
 
-        execute("DELETE FROM %s USING TIMESTAMP 1 WHERE k = ? AND i >= ? AND i 
<= ?", "a", 12, 16);
+        execute("DELETE FROM %s USING TIMESTAMP 1 WHERE k = ? AND i >= ? AND i 
<= ?", "a", 14, 16);
+
+        execute("DELETE FROM %s USING TIMESTAMP 1 WHERE k = ? AND i BETWEEN ? 
AND ?", "a", 12, 13);
 
         flush();
 
diff --git 
a/test/unit/org/apache/cassandra/cql3/validation/operations/SelectMultiColumnRelationTest.java
 
b/test/unit/org/apache/cassandra/cql3/validation/operations/SelectMultiColumnRelationTest.java
index ad93719460..cc8d589c67 100644
--- 
a/test/unit/org/apache/cassandra/cql3/validation/operations/SelectMultiColumnRelationTest.java
+++ 
b/test/unit/org/apache/cassandra/cql3/validation/operations/SelectMultiColumnRelationTest.java
@@ -38,10 +38,16 @@ public class SelectMultiColumnRelationTest extends CQLTester
         assertInvalidSyntax("SELECT * FROM %s WHERE () = (?, ?)", 1, 2);
         assertInvalidMessage("b cannot be restricted by more than one relation 
if it includes an Equal",
                              "SELECT * FROM %s WHERE a = 0 AND (b) = (?) AND 
(b) > (?)", 0, 0);
+        assertInvalidMessage("b cannot be restricted by more than one relation 
if it includes an Equal",
+                             "SELECT * FROM %s WHERE a = 0 AND (b) = (?) AND 
(b) BETWEEN (?) AND (?)", 0, 0, 0);
         assertInvalidMessage("More than one restriction was found for the 
start bound on b",
                              "SELECT * FROM %s WHERE a = 0 AND (b) > (?) AND 
(b) > (?)", 0, 1);
         assertInvalidMessage("More than one restriction was found for the 
start bound on b",
                              "SELECT * FROM %s WHERE a = 0 AND (b) > (?) AND b 
> ?", 0, 1);
+        assertInvalidMessage("More than one restriction was found for the 
start bound on b",
+                             "SELECT * FROM %s WHERE a = 0 AND (b) > (?) AND b 
BETWEEN ? AND ?", 0, 1, 1);
+        assertInvalidMessage("More than one restriction was found for the 
start bound on b",
+                             "SELECT * FROM %s WHERE a = 0 AND (b) > (?) AND b 
BETWEEN (?) AND (?)", 0, 1, 0);
         assertInvalidMessage("Multi-column relations can only be applied to 
clustering columns but was applied to: a",
                              "SELECT * FROM %s WHERE (a, b) = (?, ?)", 0, 0);
     }
@@ -82,6 +88,8 @@ public class SelectMultiColumnRelationTest extends CQLTester
                              "SELECT * FROM %s WHERE a = 0 AND (b, c, d) IN 
((?, ?, ?))", 1, 2, null);
         assertInvalidMessage("Invalid null value for d in tuple (b, c, d)",
                              "SELECT * FROM %s WHERE a = 0 AND (b, c, d) IN 
((?, ?, ?), (?, ?, ?))", 1, 2, null, 2, 1, 4);
+        assertInvalidMessage("Invalid null value for column b",
+                             "SELECT * FROM %s WHERE a = 0 AND b BETWEEN ? AND 
?", 1, null);
 
         // Wrong type for 'd'
         assertInvalid("SELECT * FROM %s WHERE a = 0 AND (b, c, d) = (?, ?, 
?)", 1, 2, "foobar");
@@ -103,6 +111,8 @@ public class SelectMultiColumnRelationTest extends CQLTester
 
         assertInvalidMessage("Clustering column \"c\" cannot be restricted 
(preceding column \"b\" is restricted by a non-EQ relation)",
                              "SELECT * FROM %s WHERE a = ? AND b > ?  AND (c, 
d) > (?, ?)", 0, 0, 0, 0);
+        assertInvalidMessage("Clustering column \"c\" cannot be restricted 
(preceding column \"b\" is restricted by a non-EQ relation)",
+                             "SELECT * FROM %s WHERE a = ? AND b BETWEEN ? AND 
?  AND (c, d) > (?, ?)", 0, 0, 0, 0, 0);
         assertInvalidMessage("PRIMARY KEY column \"c\" cannot be restricted 
(preceding column \"b\" is restricted by a non-EQ relation)",
                              "SELECT * FROM %s WHERE a = ? AND (c, d) > (?, ?) 
AND b > ?  ", 0, 0, 0, 0);
 
@@ -110,6 +120,8 @@ public class SelectMultiColumnRelationTest extends CQLTester
                              "SELECT * FROM %s WHERE a = ? AND (b, c) > (?, ?) 
AND (b) < (?) AND (c) < (?)", 0, 0, 0, 0, 0);
         assertInvalidMessage("Column \"c\" cannot be restricted by two 
inequalities not starting with the same column",
                              "SELECT * FROM %s WHERE a = ? AND (c) < (?) AND 
(b, c) > (?, ?) AND (b) < (?)", 0, 0, 0, 0, 0);
+        assertInvalidMessage("Column \"c\" cannot be restricted by two 
inequalities not starting with the same column",
+                             "SELECT * FROM %s WHERE a = ? AND (c) < (?) AND 
(b, c) > (?, ?) AND (b) BETWEEN (?) AND (?)", 0, 0, 0, 0, 0, 0);
         assertInvalidMessage("Clustering column \"c\" cannot be restricted 
(preceding column \"b\" is restricted by a non-EQ relation)",
                              "SELECT * FROM %s WHERE a = ? AND (b) < (?) AND 
(c) < (?) AND (b, c) > (?, ?)", 0, 0, 0, 0, 0);
         assertInvalidMessage("Clustering column \"c\" cannot be restricted 
(preceding column \"b\" is restricted by a non-EQ relation)",
@@ -212,6 +224,14 @@ public class SelectMultiColumnRelationTest extends 
CQLTester
         assertRows(execute("SELECT * FROM %s WHERE a = ? and d < 1 and (b, c) 
IN ((?, ?), (?, ?)) and d >= ?", 0, 0, 1, 0, 0, 0),
                    row(0, 0, 0, 0),
                    row(0, 0, 1, 0));
+
+        assertRows(execute("SELECT * FROM %s WHERE a = ? and d BETWEEN 0 AND 0 
and (b, c) IN ((?, ?), (?, ?))", 0, 0, 1, 0, 0),
+                   row(0, 0, 0, 0),
+                   row(0, 0, 1, 0));
+
+        assertRows(execute("SELECT * FROM %s WHERE a = ? AND (b, c, d) BETWEEN 
(?, ?, ?) AND (?, ?, ?)", 0, 0, 1, 0, 0, 0, 0),
+                   row(0, 0, 0, 0),
+                   row(0, 0, 1, 0));
     }
 
     @Test
@@ -363,6 +383,10 @@ public class SelectMultiColumnRelationTest extends 
CQLTester
         assertRows(execute("SELECT * FROM %s WHERE a = ? AND b > ? AND (b) < 
(?)", 0, 0, 2),
                    row(0, 1, 0)
         );
+
+        assertRows(execute("SELECT * FROM %s WHERE a = ? AND b BETWEEN (?) AND 
(?)", 0, 1, 1),
+                   row(0, 1, 0)
+        );
     }
 
     @Test
@@ -441,6 +465,15 @@ public class SelectMultiColumnRelationTest extends 
CQLTester
                    row(0, 1, 1, 1)
         );
 
+        assertRows(execute("SELECT * FROM %s WHERE a = ? AND (b) BETWEEN (?) 
AND (?)", 0, 0, 1),
+                   row(0, 0, 0, 0),
+                   row(0, 0, 1, 0),
+                   row(0, 0, 1, 1),
+                   row(0, 1, 0, 0),
+                   row(0, 1, 1, 0),
+                   row(0, 1, 1, 1)
+        );
+
         assertRows(execute("SELECT * FROM %s WHERE a = ? AND (b, c) > (?, ?)", 
0, 1, 0),
                    row(0, 1, 1, 0),
                    row(0, 1, 1, 1)
@@ -452,6 +485,12 @@ public class SelectMultiColumnRelationTest extends 
CQLTester
                    row(0, 1, 1, 1)
         );
 
+        assertRows(execute("SELECT * FROM %s WHERE a = ? AND (b, c) BETWEEN 
(?, ?) AND (?, ?)", 0, 1, 0, 1, 1),
+                   row(0, 1, 0, 0),
+                   row(0, 1, 1, 0),
+                   row(0, 1, 1, 1)
+        );
+
         assertRows(execute("SELECT * FROM %s WHERE a = ? AND (b, c, d) > (?, 
?, ?)", 0, 1, 1, 0),
                    row(0, 1, 1, 1)
         );
@@ -461,6 +500,11 @@ public class SelectMultiColumnRelationTest extends 
CQLTester
                    row(0, 1, 1, 1)
         );
 
+        assertRows(execute("SELECT * FROM %s WHERE a = ? AND (b, c, d) BETWEEN 
(?, ?, ?) AND (?, ?, ?)", 0, 1, 1, 0, 1, 1, 1),
+                   row(0, 1, 1, 0),
+                   row(0, 1, 1, 1)
+        );
+
         assertRows(execute("SELECT * FROM %s WHERE a = ? AND (b) < (?)", 0, 1),
                    row(0, 0, 0, 0),
                    row(0, 0, 1, 0),
@@ -534,6 +578,15 @@ public class SelectMultiColumnRelationTest extends 
CQLTester
                    row(0, 0, 0, 0)
         );
 
+        assertRows(execute("SELECT * FROM %s WHERE a = ? AND (b) BETWEEN (?) 
AND (?) ORDER BY b DESC, c DESC, d DESC", 0, 0, 1),
+                   row(0, 1, 1, 1),
+                   row(0, 1, 1, 0),
+                   row(0, 1, 0, 0),
+                   row(0, 0, 1, 1),
+                   row(0, 0, 1, 0),
+                   row(0, 0, 0, 0)
+        );
+
         assertRows(execute("SELECT * FROM %s WHERE a = ? AND (b, c) > (?, ?) 
ORDER BY b DESC, c DESC, d DESC", 0, 1, 0),
                    row(0, 1, 1, 1),
                    row(0, 1, 1, 0)
@@ -545,6 +598,12 @@ public class SelectMultiColumnRelationTest extends 
CQLTester
                    row(0, 1, 0, 0)
         );
 
+        assertRows(execute("SELECT * FROM %s WHERE a = ? AND (b, c) BETWEEN 
(?, ?) AND (?, ?) ORDER BY b DESC, c DESC, d DESC", 0, 1, 0, 1, 1),
+                   row(0, 1, 1, 1),
+                   row(0, 1, 1, 0),
+                   row(0, 1, 0, 0)
+        );
+
         assertRows(execute("SELECT * FROM %s WHERE a = ? AND (b, c, d) > (?, 
?, ?) ORDER BY b DESC, c DESC, d DESC", 0, 1, 1, 0),
                    row(0, 1, 1, 1)
         );
@@ -554,6 +613,11 @@ public class SelectMultiColumnRelationTest extends 
CQLTester
                    row(0, 1, 1, 0)
         );
 
+        assertRows(execute("SELECT * FROM %s WHERE a = ? AND (b, c, d) BETWEEN 
(?, ?, ?) AND (?, ?, ?) ORDER BY b DESC, c DESC, d DESC", 0, 1, 1, 0, 1, 1, 1),
+                   row(0, 1, 1, 1),
+                   row(0, 1, 1, 0)
+        );
+
         assertRows(execute("SELECT * FROM %s WHERE a = ? AND (b) < (?) ORDER 
BY b DESC, c DESC, d DESC", 0, 1),
                    row(0, 0, 1, 1),
                    row(0, 0, 1, 0),
@@ -579,6 +643,12 @@ public class SelectMultiColumnRelationTest extends 
CQLTester
                    row(0, 0, 0, 0)
         );
 
+        assertRows(execute("SELECT * FROM %s WHERE a = ? AND (b, c) BETWEEN 
(?, ?) AND (?, ?) ORDER BY b DESC, c DESC, d DESC", 0, 0, 1, 0, 0),
+                  row(0, 0, 1, 1),
+                  row(0, 0, 1, 0),
+                  row(0, 0, 0, 0)
+        );
+
         assertRows(execute("SELECT * FROM %s WHERE a = ? AND (b, c, d) < (?, 
?, ?) ORDER BY b DESC, c DESC, d DESC", 0, 0, 1, 1),
                    row(0, 0, 1, 0),
                    row(0, 0, 0, 0)
@@ -590,6 +660,12 @@ public class SelectMultiColumnRelationTest extends 
CQLTester
                    row(0, 0, 0, 0)
         );
 
+        assertRows(execute("SELECT * FROM %s WHERE a = ? AND (b, c, d) BETWEEN 
(?, ?, ?) AND (?, ?, ?) ORDER BY b DESC, c DESC, d DESC", 0, 0, 1, 1, 0, 0, 0),
+                   row(0, 0, 1, 1),
+                   row(0, 0, 1, 0),
+                   row(0, 0, 0, 0)
+        );
+
         assertRows(execute("SELECT * FROM %s WHERE a = ? AND (b, c, d) > (?, 
?, ?) AND (b) < (?) ORDER BY b DESC, c DESC, d DESC", 0, 0, 1, 0, 1),
                    row(0, 0, 1, 1)
         );
@@ -726,6 +802,15 @@ public class SelectMultiColumnRelationTest extends 
CQLTester
                    row(0, 0, 1, 0)
         );
 
+        assertRows(execute("SELECT * FROM %s WHERE a = ? AND (b) BETWEEN (?) 
AND (?)", 0, 0, 1),
+                   row(0, 1, 0, 0),
+                   row(0, 1, 1, 1),
+                   row(0, 1, 1, 0),
+                   row(0, 0, 0, 0),
+                   row(0, 0, 1, 1),
+                   row(0, 0, 1, 0)
+        );
+
         assertRows(execute("SELECT * FROM %s WHERE a = ? AND (b) < (?)", 0, 1),
                    row(0, 0, 0, 0),
                    row(0, 0, 1, 1),
@@ -851,11 +936,21 @@ public class SelectMultiColumnRelationTest extends 
CQLTester
         assertRows(execute("SELECT * FROM %s WHERE (b) >= (?) AND e = ? ALLOW 
FILTERING", 1, 2),
                    row(0, 1, 1, 1, 2));
 
+        
assertInvalidMessage(StatementRestrictions.REQUIRES_ALLOW_FILTERING_MESSAGE,
+                             "SELECT * FROM %s WHERE (b) BETWEEN (?) AND (?) 
AND e = ?", 1, 10, 2);
+        assertRows(execute("SELECT * FROM %s WHERE (b) BETWEEN (?) AND (?) AND 
e = ? ALLOW FILTERING", 1, 10, 2),
+                   row(0, 1, 1, 1, 2));
+
         
assertInvalidMessage(StatementRestrictions.REQUIRES_ALLOW_FILTERING_MESSAGE,
                              "SELECT * FROM %s WHERE (b, c) >= (?, ?) AND e = 
?", 1, 1, 2);
         assertRows(execute("SELECT * FROM %s WHERE (b, c) >= (?, ?) AND e = ? 
ALLOW FILTERING", 1, 1, 2),
                    row(0, 1, 1, 1, 2));
 
+        
assertInvalidMessage(StatementRestrictions.REQUIRES_ALLOW_FILTERING_MESSAGE,
+                            "SELECT * FROM %s WHERE (b, c) BETWEEN (?, ?) AND 
(?, ?) AND e = ?", 1, 1, 1, 2, 2);
+        assertRows(execute("SELECT * FROM %s WHERE (b, c) BETWEEN (?, ?) AND 
(?, ?) AND e = ? ALLOW FILTERING", 1, 1, 2, 2, 2),
+                   row(0, 1, 1, 1, 2));
+
         assertInvalidMessage("Invalid null value for column e",
                              "SELECT * FROM %s WHERE (b, c) >= (?, ?) AND e = 
?  ALLOW FILTERING", 1, 1, null);
 
@@ -902,6 +997,8 @@ public class SelectMultiColumnRelationTest extends CQLTester
                              "SELECT * FROM %s WHERE a = 0 AND b > 1 AND (d,e) 
< (2,2) AND v = 0 ALLOW FILTERING");
         assertInvalidMessage(errorMsg,
                              "SELECT * FROM %s WHERE a = 0 AND (b,c) > (1,0) 
AND (d,e) < (2,2) AND v = 0 ALLOW FILTERING");
+        assertInvalidMessage(errorMsg,
+                             "SELECT * FROM %s WHERE a = 0 AND (b,c) > (1,0) 
AND (d,e) BETWEEN (1,0) AND (2,2) AND v = 0 ALLOW FILTERING");
     }
 
     @Test
@@ -944,6 +1041,11 @@ public class SelectMultiColumnRelationTest extends 
CQLTester
                 row(0, 0, 1, 1, 1, 5),
                 row(0, 0, 2, 0, 0, 5));
 
+        assertRows(execute("SELECT * FROM %s WHERE a = ? AND (c, d) BETWEEN 
(?, ?) AND (?, ?) ALLOW FILTERING", 0, 1, 1, 4, 3),
+                   row(0, 0, 1, 1, 0, 4),
+                   row(0, 0, 1, 1, 1, 5),
+                   row(0, 0, 2, 0, 0, 5));
+
         assertRows(execute("SELECT * FROM %s WHERE a = ? AND b = ? AND (c) IN 
((?)) AND f = ?", 0, 0, 1, 5),
                    row(0, 0, 1, 1, 1, 5));
 
@@ -973,14 +1075,36 @@ public class SelectMultiColumnRelationTest extends 
CQLTester
         
assertInvalidMessage(StatementRestrictions.REQUIRES_ALLOW_FILTERING_MESSAGE,
                              "SELECT * FROM %s WHERE a = ? AND (c) >= (?) AND 
f = ?", 0, 1, 5);
 
+        
assertInvalidMessage(StatementRestrictions.REQUIRES_ALLOW_FILTERING_MESSAGE,
+                             "SELECT * FROM %s WHERE a = ? AND (c) BETWEEN (?) 
AND (?) AND f = ?", 0, 1, 1, 5);
+
+        
assertInvalidMessage(StatementRestrictions.REQUIRES_ALLOW_FILTERING_MESSAGE,
+                             "SELECT * FROM %s WHERE a = ? AND (c) BETWEEN (?) 
AND (?) AND f = ?", 0, 1, -5, 5);
+
         assertRows(execute("SELECT * FROM %s WHERE a = ? AND b = ? AND (c) >= 
(?) AND f = ?", 0, 0, 1, 5),
                    row(0, 0, 1, 1, 1, 5),
                    row(0, 0, 2, 0, 0, 5));
 
+        assertRows(execute("SELECT * FROM %s WHERE a = ? AND b = ? AND (c) 
BETWEEN (?) AND (?) AND f = ?", 0, 0, 1, 2, 5),
+                   row(0, 0, 1, 1, 1, 5),
+                   row(0, 0, 2, 0, 0, 5));
+
+        assertRows(execute("SELECT * FROM %s WHERE a = ? AND b = ? AND (c) 
BETWEEN (?) AND (?) AND f = ?", 0, 0, 0, 4, 5),
+                   row(0, 0, 1, 1, 1, 5),
+                   row(0, 0, 2, 0, 0, 5));
+
         assertRows(execute("SELECT * FROM %s WHERE a = ? AND (c) >= (?) AND f 
= ? ALLOW FILTERING", 0, 1, 5),
                    row(0, 0, 1, 1, 1, 5),
                    row(0, 0, 2, 0, 0, 5));
 
+        assertRows(execute("SELECT * FROM %s WHERE a = ? AND (c) BETWEEN (?) 
AND (?) AND f = ? ALLOW FILTERING", 0, 1, 2, 5),
+                   row(0, 0, 1, 1, 1, 5),
+                   row(0, 0, 2, 0, 0, 5));
+
+        assertRows(execute("SELECT * FROM %s WHERE a = ? AND (c) BETWEEN (?) 
AND (?) AND f = ? ALLOW FILTERING", 0, 1, 4, 5),
+                   row(0, 0, 1, 1, 1, 5),
+                   row(0, 0, 2, 0, 0, 5));
+
         assertRows(execute("SELECT * FROM %s WHERE a = ? AND b = ? AND (c, d) 
>= (?, ?) AND f = ?", 0, 0, 1, 1, 5),
                    row(0, 0, 1, 1, 1, 5),
                    row(0, 0, 2, 0, 0, 5));
@@ -989,6 +1113,24 @@ public class SelectMultiColumnRelationTest extends 
CQLTester
         assertRows(execute("SELECT * FROM %s WHERE a = ? AND (c, d) >= (?, ?) 
AND f = ? ALLOW FILTERING", 0, 1, 1, 5),
                    row(0, 0, 1, 1, 1, 5),
                    row(0, 0, 2, 0, 0, 5));
+
+        assertRows(execute("SELECT * FROM %s WHERE a = ? AND b = ? AND (c, d) 
BETWEEN (?, ?) AND (?, ?) AND f = ?", 0, 0, 1, 1, 2, 0, 5),
+                   row(0, 0, 1, 1, 1, 5),
+                   row(0, 0, 2, 0, 0, 5));
+        
assertInvalidMessage(StatementRestrictions.REQUIRES_ALLOW_FILTERING_MESSAGE,
+                             "SELECT * FROM %s WHERE a = ? AND (c, d) BETWEEN 
(?, ?) AND (?, ?) AND f = ?", 0, 1, 1, 2, 0, 5);
+        assertRows(execute("SELECT * FROM %s WHERE a = ? AND (c, d) >= (?, ?) 
AND f = ? ALLOW FILTERING", 0, 1, 1, 5),
+                   row(0, 0, 1, 1, 1, 5),
+                   row(0, 0, 2, 0, 0, 5));
+
+        assertRows(execute("SELECT * FROM %s WHERE a = ? AND b = ? AND (c, d) 
BETWEEN (?, ?) AND (?, ?) AND f = ?", 0, 0, 1, 1, 2, 0, 5),
+                   row(0, 0, 1, 1, 1, 5),
+                   row(0, 0, 2, 0, 0, 5));
+        
assertInvalidMessage(StatementRestrictions.REQUIRES_ALLOW_FILTERING_MESSAGE,
+                             "SELECT * FROM %s WHERE a = ? AND (c, d) BETWEEN 
(?, ?) AND (?, ?) AND f = ?", 0, 1, 1, 5, -6, 3);
+        assertRows(execute("SELECT * FROM %s WHERE a = ? AND (c, d) BETWEEN 
(?, ?) AND (?, ?) AND f = ? ALLOW FILTERING", 0, 1, 1, 2, 0, 5),
+                   row(0, 0, 1, 1, 1, 5),
+                   row(0, 0, 2, 0, 0, 5));
     }
 
     @Test
@@ -1105,6 +1247,33 @@ public class SelectMultiColumnRelationTest extends 
CQLTester
                    row(0, -1, 0, -1, 0)
         );
 
+        assertRows(execute(
+                   "SELECT * FROM %s" +
+                   " WHERE a = ? " +
+                   "AND (b,c,d,e) BETWEEN (?,?,?,?) " +
+                   "AND (?,?,?,?)", 0, 2, 0, 1, 1, -1, -1, -1, -1),
+
+                   row(0, 2, 0, 1, 1),
+                   row(0, 2, 0, -1, 0),
+                   row(0, 2, 0, -1, 1),
+                   row(0, 1, -1, 1, 0),
+                   row(0, 1, -1, 1, 1),
+                   row(0, 1, -1, 0, 0),
+                   row(0, 1, 0, 1, -1),
+                   row(0, 1, 0, 1, 1),
+                   row(0, 1, 0, 0, -1),
+                   row(0, 1, 0, 0, 0),
+                   row(0, 1, 0, 0, 1),
+                   row(0, 1, 0, -1, -1),
+                   row(0, 1, 1, 0, -1),
+                   row(0, 1, 1, 0, 0),
+                   row(0, 1, 1, 0, 1),
+                   row(0, 1, 1, -1, 0),
+                   row(0, 0, 0, 0, 0),
+                   row(0, -1, 0, 0, 0),
+                   row(0, -1, 0, -1, 0)
+        );
+
         assertRows(execute(
         "SELECT * FROM %s" +
         " WHERE a = ? " +
@@ -1413,6 +1582,18 @@ public class SelectMultiColumnRelationTest extends 
CQLTester
                    row(0, -1, 0, -1, 0)
         );
 
+        assertRows(execute("SELECT * FROM %s WHERE a = ? AND (b,c,d,e) BETWEEN 
(?,?,?,?) AND (?,?,?,?)", 0, 1, 0, 0, 0, -10, -1, -4, -1),
+                   row(0, 1, -1, 1, 0),
+                   row(0, 1, -1, 1, 1),
+                   row(0, 1, -1, 0, 0),
+                   row(0, 1, 0, 0, -1),
+                   row(0, 1, 0, 0, 0),
+                   row(0, 1, 0, -1, -1),
+                   row(0, 0, 0, 0, 0),
+                   row(0, -1, 0, 0, 0),
+                   row(0, -1, 0, -1, 0)
+        );
+
         assertRows(execute("SELECT * FROM %s WHERE a = ? AND (b,c,d,e) > 
(?,?,?,?)", 0, 1, 0, 0, 0),
                    row(0, 2, 0, 1, 1),
                    row(0, 2, 0, -1, 0),
@@ -1527,6 +1708,19 @@ public class SelectMultiColumnRelationTest extends 
CQLTester
                    row(0, 1, 1, 0, 0),
                    row(0, 1, 1, 0, 1)
         );
+
+        assertRows(execute("SELECT * FROM %s WHERE a = ? AND (b,c,d,e) BETWEEN 
(?,?,?,?) AND (?,?,?,?)", 0, 1, 0, 0, 0, 2, 2, 2, 2),
+                   row(0, 2, 0, -1, 0),
+                   row(0, 2, 0, -1, 1),
+                   row(0, 1, 0, 0, 0),
+                   row(0, 1, 0, 0, 1),
+                   row(0, 1, 0, 1, -1),
+                   row(0, 1, 0, 1, 1),
+                   row(0, 1, 1, -1, 0),
+                   row(0, 1, 1, 0, -1),
+                   row(0, 1, 1, 0, 0),
+                   row(0, 1, 1, 0, 1)
+        );
     }
 
     @Test
@@ -1549,6 +1743,9 @@ public class SelectMultiColumnRelationTest extends 
CQLTester
         assertRows(execute("SELECT * FROM %s WHERE a = ? AND (b,c)>=(?,?) AND 
(b,c)<=(?,?) ALLOW FILTERING", 0, 2, 3, 4, 5),
                    row(0, 4, 4), row(0, 4, 5), row(0, 3, 4), row(0, 2, 3), 
row(0, 2, 4)
         );
+        assertRows(execute("SELECT * FROM %s WHERE a = ? AND (b,c) BETWEEN 
(?,?) AND (?,?) ALLOW FILTERING", 0, 2, 3, 4, 5),
+                   row(0, 4, 4), row(0, 4, 5), row(0, 3, 4), row(0, 2, 3), 
row(0, 2, 4)
+        );
         assertRows(execute("SELECT * FROM %s WHERE a = ? AND (b,c)<(?,?) ALLOW 
FILTERING", 0, 4, 5),
                    row(0, 4, 4), row(0, 3, 4), row(0, 2, 3), row(0, 2, 4)
         );
@@ -1752,6 +1949,35 @@ public class SelectMultiColumnRelationTest extends 
CQLTester
                    row(0, 2, -3, 1, 1)
         );
 
+        assertRows(execute(
+                   "SELECT * FROM %s" +
+                   " WHERE a = ? " +
+                   "AND (b,c,d,e) BETWEEN (?,?,?,?) " +
+                   "AND (?,?,?,?)", 0, 2, 0, 1, 1, -1, -10, -10, -10),
+
+                   row(0, -1, 0, 0, 0),
+                   row(0, -1, 0, -1, 0),
+                   row(0, 0, 0, 0, 0),
+                   row(0, 1, 1, 0, -1),
+                   row(0, 1, 1, 0, 0),
+                   row(0, 1, 1, 0, 1),
+                   row(0, 1, 1, -1, 0),
+                   row(0, 1, 0, 1, -1),
+                   row(0, 1, 0, 1, 1),
+                   row(0, 1, 0, 0, -1),
+                   row(0, 1, 0, 0, 0),
+                   row(0, 1, 0, 0, 1),
+                   row(0, 1, 0, -1, -1),
+                   row(0, 1, -1, 1, 0),
+                   row(0, 1, -1, 1, 1),
+                   row(0, 1, -1, 0, 0),
+                   row(0, 2, 0, 1, 1),
+                   row(0, 2, 0, -1, 0),
+                   row(0, 2, 0, -1, 1),
+                   row(0, 2, -1, 1, 1),
+                   row(0, 2, -3, 1, 1)
+        );
+
         assertRows(execute(
         "SELECT * FROM %s" +
         " WHERE a = ? " +
@@ -1840,6 +2066,23 @@ public class SelectMultiColumnRelationTest extends 
CQLTester
                    row(0, 2, -3, 1, 1)
         );
 
+        assertRows(execute("SELECT * FROM %s WHERE a = ? AND (b,c,d) BETWEEN 
(?,?,?) AND (?,?,?)", 0, 1, 0, 0, 2, 2, 2),
+                   row(0, 1, 1, 0, -1),
+                   row(0, 1, 1, 0, 0),
+                   row(0, 1, 1, 0, 1),
+                   row(0, 1, 1, -1, 0),
+                   row(0, 1, 0, 1, -1),
+                   row(0, 1, 0, 1, 1),
+                   row(0, 1, 0, 0, -1),
+                   row(0, 1, 0, 0, 0),
+                   row(0, 1, 0, 0, 1),
+                   row(0, 2, 0, 1, 1),
+                   row(0, 2, 0, -1, 0),
+                   row(0, 2, 0, -1, 1),
+                   row(0, 2, -1, 1, 1),
+                   row(0, 2, -3, 1, 1)
+        );
+
         assertRows(execute("SELECT * FROM %s WHERE a = ? AND (b,c,d) > 
(?,?,?)", 0, 1, 0, 0),
                    row(0, 1, 1, 0, -1),
                    row(0, 1, 1, 0, 0),
@@ -1899,6 +2142,13 @@ public class SelectMultiColumnRelationTest extends 
CQLTester
                    row(3, 3),
                    row(2, 2),
                    row(2, 3));
+
+        assertRows(execute("SELECT b, c FROM %s WHERE a = 0 AND (b, c) BETWEEN 
(2, 2) AND (3, 3) ORDER BY b DESC, c ASC;"),
+                   row(3, 1),
+                   row(3, 2),
+                   row(3, 3),
+                   row(2, 2),
+                   row(2, 3));
     }
 
     /**
diff --git 
a/test/unit/org/apache/cassandra/cql3/validation/operations/SelectOrderedPartitionerTest.java
 
b/test/unit/org/apache/cassandra/cql3/validation/operations/SelectOrderedPartitionerTest.java
index 6e74f4d2c2..d20e3905cf 100644
--- 
a/test/unit/org/apache/cassandra/cql3/validation/operations/SelectOrderedPartitionerTest.java
+++ 
b/test/unit/org/apache/cassandra/cql3/validation/operations/SelectOrderedPartitionerTest.java
@@ -147,7 +147,6 @@ public class SelectOrderedPartitionerTest extends CQLTester
         });
     }
 
-
     @Test
     public void testTokenFunctionWithSingleColumnPartitionKey() throws 
Throwable
     {
@@ -156,6 +155,7 @@ public class SelectOrderedPartitionerTest extends CQLTester
 
         assertRows(execute("SELECT * FROM %s WHERE token(a) >= token(?)", 0), 
row(0, "a"));
         assertRows(execute("SELECT * FROM %s WHERE token(a) >= token(?) and 
token(a) < token(?)", 0, 1), row(0, "a"));
+        assertRows(execute("SELECT * FROM %s WHERE token(a) BETWEEN token(?) 
and token(?)", 0, 1), row(0, "a"));
         assertInvalid("SELECT * FROM %s WHERE token(a) > token(?)", "a");
         assertInvalidMessage("The token() function must contains only 
partition key components",
                              "SELECT * FROM %s WHERE token(a, b) >= token(?, 
?)", "b", 0);
@@ -167,12 +167,18 @@ public class SelectOrderedPartitionerTest extends 
CQLTester
 
         assertInvalidMessage("More than one restriction was found for the 
start bound on a",
                              "SELECT * FROM %s WHERE token(a) > token(?) AND 
token(a) > token(?)", 1, 2);
+        assertInvalidMessage("More than one restriction was found for the 
start bound on a",
+                             "SELECT * FROM %s WHERE token(a) > token(?) AND 
token(a) BETWEEN token(?) AND token(?)", 1, 2, 3);
         assertInvalidMessage("More than one restriction was found for the end 
bound on a",
                              "SELECT * FROM %s WHERE token(a) <= token(?) AND 
token(a) < token(?)", 1, 2);
+        assertInvalidMessage("More than one restriction was found for the end 
bound on a",
+                             "SELECT * FROM %s WHERE token(a) <= token(?) AND 
token(a) BETWEEN token(?) AND token(?)", 1, 2, 3);
         assertInvalidMessage("a cannot be restricted by more than one relation 
if it includes an Equal",
                              "SELECT * FROM %s WHERE token(a) > token(?) AND 
token(a) = token(?)", 1, 2);
         assertInvalidMessage("a cannot be restricted by more than one relation 
if it includes an Equal",
                              "SELECT * FROM %s WHERE  token(a) = token(?) AND 
token(a) > token(?)", 1, 2);
+        assertInvalidMessage("a cannot be restricted by more than one relation 
if it includes an Equal",
+                             "SELECT * FROM %s WHERE  token(a) = token(?) AND 
token(a) BETWEEN token(?) AND token(?)", 1, 2, 3);
     }
 
     @Test
@@ -197,17 +203,27 @@ public class SelectOrderedPartitionerTest extends 
CQLTester
         assertRows(execute("SELECT * FROM %s WHERE token(a, b) > token(?, ?) 
and token(a, b) < token(?, ?)", 0, "a", 0, "d"),
                    row(0, "b"),
                    row(0, "c"));
+        assertRows(execute("SELECT * FROM %s WHERE token(a, b) between 
token(?, ?) and token(?, ?)", 0, "b", 0, "d"),
+                   row(0, "b"),
+                   row(0, "c"));
         assertInvalidMessage("The token() function must be applied to all 
partition key components or none of them",
                              "SELECT * FROM %s WHERE token(a) > token(?) and 
token(b) > token(?)", 0, "a");
         assertInvalidMessage("The token() function must be applied to all 
partition key components or none of them",
                              "SELECT * FROM %s WHERE token(a) > token(?, ?) 
and token(a) < token(?, ?) and token(b) > token(?, ?) ",
                              0, "a", 0, "d", 0, "a");
+        assertInvalidMessage("The token() function must be applied to all 
partition key components or none of them",
+                             "SELECT * FROM %s WHERE token(a) BETWEEN token(?, 
?) AND token(?, ?) and token(b) > token(?, ?) ",
+                             0, "a", 0, "d", 0, "a");
         assertInvalidMessage("The token function arguments must be in the 
partition key order: a, b",
                              "SELECT * FROM %s WHERE token(b, a) > token(0, 
'c')");
+        assertInvalidMessage("The token function arguments must be in the 
partition key order: a, b",
+                             "SELECT * FROM %s WHERE token(b, a) BETWEEN 
token(0, 'c') AND token(0, 'f')");
         assertInvalidMessage("The token() function must be applied to all 
partition key components or none of them",
                              "SELECT * FROM %s WHERE token(a, b) > token(?, ?) 
and token(b) < token(?, ?)", 0, "a", 0, "a");
         assertInvalidMessage("The token() function must be applied to all 
partition key components or none of them",
                              "SELECT * FROM %s WHERE token(a) > token(?, ?) 
and token(b) > token(?, ?)", 0, "a", 0, "a");
+        assertInvalidMessage("The token() function must be applied to all 
partition key components or none of them",
+                             "SELECT * FROM %s WHERE token(a) > token(?, ?) 
and token(b) > token(?, ?)", 0, "a", 0, "a");
     }
 
     @Test
@@ -273,6 +289,8 @@ public class SelectOrderedPartitionerTest extends CQLTester
         assertEmpty(execute("SELECT * FROM %s WHERE token(a) = token(?) AND a 
= ?;", 2, 3));
         assertRows(execute("SELECT * FROM %s WHERE token(a) >= token(?) AND 
token(a) <= token(?) AND a = ?;", 2, 2, 2),
                    row(2, 2));
+        assertRows(execute("SELECT * FROM %s WHERE token(a) BETWEEN token(?) 
AND token(?) AND a = ?;", 2, 2, 2),
+                   row(2, 2));
         assertEmpty(execute("SELECT * FROM %s WHERE token(a) >= token(?) AND 
token(a) < token(?) AND a = ?;", 2, 2, 2));
         assertEmpty(execute("SELECT * FROM %s WHERE token(a) > token(?) AND 
token(a) <= token(?) AND a = ?;", 2, 2, 2));
         assertEmpty(execute("SELECT * FROM %s WHERE token(a) > token(?) AND 
token(a) < token(?) AND a = ?;", 2, 2, 2));
diff --git 
a/test/unit/org/apache/cassandra/cql3/validation/operations/SelectSingleColumnRelationTest.java
 
b/test/unit/org/apache/cassandra/cql3/validation/operations/SelectSingleColumnRelationTest.java
index 2b663808b2..8ecae0d48a 100644
--- 
a/test/unit/org/apache/cassandra/cql3/validation/operations/SelectSingleColumnRelationTest.java
+++ 
b/test/unit/org/apache/cassandra/cql3/validation/operations/SelectSingleColumnRelationTest.java
@@ -63,6 +63,8 @@ public class SelectSingleColumnRelationTest extends CQLTester
                              "SELECT * FROM %s WHERE c = 0 AND b <= ?", 
set(0));
         assertInvalidMessage("Collection column 'b' (set<int>) cannot be 
restricted by a 'IN' relation",
                              "SELECT * FROM %s WHERE c = 0 AND b IN (?)", 
set(0));
+        assertInvalidMessage("Collection column 'b' (set<int>) cannot be 
restricted by a 'BETWEEN' relation",
+                             "SELECT * FROM %s WHERE c = 0 AND b BETWEEN ? AND 
?", set(0), set(0));
         assertInvalidMessage("Unsupported '!=' relation: b != 5",
                 "SELECT * FROM %s WHERE c = 0 AND b != 5");
         assertInvalidMessage("Unsupported restriction: b IS NOT NULL",
@@ -140,6 +142,9 @@ public class SelectSingleColumnRelationTest extends 
CQLTester
 
         assertEmpty(execute("select * from %s where a = ? and c > ? and c < ? 
and b in (?, ?)", "first", 6, 7, 3, 2));
 
+        assertRows(execute("SELECT * FROM %s WHERE a = ? AND b = ? AND c 
BETWEEN ? AND ?", "first", 1, 4, 5),
+                   row("first", 1, 5, 1));
+
         assertInvalidMessage("c cannot be restricted by more than one relation 
if it includes an Equal",
                              "select * from %s where a = ? and c > ? and c = ? 
and b in (?, ?)", "first", 6, 7, 3, 2);
 
@@ -156,6 +161,12 @@ public class SelectSingleColumnRelationTest extends 
CQLTester
 
         assertInvalidMessage("More than one restriction was found for the end 
bound on b",
                              "select * from %s where a = ? and b < ? and b <= 
?", "first", 6, 3, 2);
+
+        assertInvalidMessage("More than one restriction was found for the 
start bound on b",
+                             "SELECT * FROM %s WHERE a = ? AND b > ? AND b 
BETWEEN ? AND ?", "first", 6, 3, 2);
+
+        assertInvalidMessage("More than one restriction was found for the end 
bound on b",
+                             "SELECT * FROM %s WHERE a = ? AND b < ? AND b 
BETWEEN ? AND ?", "first", 6, 3, 2);
     }
 
     @Test
@@ -222,6 +233,16 @@ public class SelectSingleColumnRelationTest extends 
CQLTester
                            "first", 7, 6, 3, 2),
                    row("first", 2, 6, 2),
                    row("first", 3, 7, 3));
+
+        assertRows(execute("select * from %s where a = ? and b between ? and ? 
order by b ASC",
+                           "first", 1, 2),
+                   row("first", 1, 5, 1),
+                   row("first", 2, 6, 2));
+
+        assertRows(execute("select * from %s where a = ? and b between ? and ? 
order by b DESC",
+                           "first", 1, 2),
+                   row("first", 2, 6, 2),
+                   row("first", 1, 5, 1));
     }
 
     @Test
@@ -255,12 +276,19 @@ public class SelectSingleColumnRelationTest extends 
CQLTester
                              "SELECT * FROM %s WHERE c = ?", 2);
         
assertInvalidMessage(StatementRestrictions.REQUIRES_ALLOW_FILTERING_MESSAGE,
                              "SELECT * FROM %s WHERE c > ? AND c <= ?", 2, 4);
+        
assertInvalidMessage(StatementRestrictions.REQUIRES_ALLOW_FILTERING_MESSAGE,
+                             "SELECT * FROM %s WHERE c BETWEEN ? AND ?", 2, 4);
 
         assertRows(execute("SELECT * FROM %s WHERE c = ? ALLOW FILTERING", 2),
                    row(1, 2, 1),
                    row(2, 2, 3));
 
         assertRows(execute("SELECT * FROM %s WHERE c > ? AND c <= ? ALLOW 
FILTERING", 2, 4), row(1, 3, 2));
+
+        assertRows(execute("SELECT * FROM %s WHERE c BETWEEN ? AND ? ALLOW 
FILTERING", 2, 3),
+                   row(1, 2, 1),
+                   row(1, 3, 2),
+                   row(2, 2, 3));
     }
 
     @Test
@@ -298,6 +326,14 @@ public class SelectSingleColumnRelationTest extends 
CQLTester
         assertRows(execute("SELECT * FROM %s WHERE c = ? AND s > ? ALLOW 
FILTERING", 1, 1),
                    row(2, 1, 2, 1));
 
+        assertRows(execute("SELECT * FROM %s WHERE c = ? AND s >= ? AND s <= ? 
ALLOW FILTERING", 1, 1, 1),
+                   row(1, 1, 1, 1),
+                   row(1, 2, 1, 1));
+
+        assertRows(execute("SELECT * FROM %s WHERE c = ? AND s BETWEEN ? AND ? 
ALLOW FILTERING", 1, 1, 1),
+                   row(1, 1, 1, 1),
+                   row(1, 2, 1, 1));
+
         assertRows(execute("SELECT * FROM %s WHERE c = ? AND s < ? ALLOW 
FILTERING", 1, 2),
                    row(1, 1, 1, 1),
                    row(1, 2, 1, 1));
@@ -391,6 +427,7 @@ public class SelectSingleColumnRelationTest extends 
CQLTester
                    row(2, "baz"));
         assertEmpty(execute("SELECT content FROM %s WHERE time1 = 1 AND time2 
= 1 AND author='foo' ALLOW FILTERING"));
         assertEmpty(execute("SELECT content FROM %s WHERE time1 = 1 AND time2 
> 0 AND author='foo' ALLOW FILTERING"));
+        assertEmpty(execute("SELECT content FROM %s WHERE time1 = 1 AND time2 
BETWEEN 1 AND 2 AND author='foo' ALLOW FILTERING"));
 
         
assertInvalidMessage(StatementRestrictions.REQUIRES_ALLOW_FILTERING_MESSAGE,
                              "SELECT content FROM %s WHERE time2 >= 0 AND 
author='foo'");
@@ -535,6 +572,15 @@ public class SelectSingleColumnRelationTest extends 
CQLTester
 
         assertRows(execute("SELECT * FROM %s WHERE a = ? AND c = ? AND d >= ? 
AND f = ? ALLOW FILTERING", 0, 1, 1, 5),
                    row(0, 0, 1, 1, 1, 5));
+
+        
assertInvalidMessage(StatementRestrictions.REQUIRES_ALLOW_FILTERING_MESSAGE,
+                             "SELECT * FROM %s WHERE a = ? AND c = ? AND d 
BETWEEN ? AND ? AND f = ?", 0, 1, 1, 5, 0);
+
+        assertRows(execute("SELECT * FROM %s WHERE a = ? AND b = ? AND c = ? 
AND d BETWEEN ? AND ? AND f = ?", 0, 0, 1, 1, 0, 5),
+                   row(0, 0, 1, 1, 1, 5));
+
+        assertRows(execute("SELECT * FROM %s WHERE a = ? AND c = ? AND d 
BETWEEN ? AND ? AND f = ? ALLOW FILTERING", 0, 1, 1, 0, 5),
+                   row(0, 0, 1, 1, 1, 5));
     }
 
     @Test
@@ -580,6 +626,7 @@ public class SelectSingleColumnRelationTest extends 
CQLTester
         assertInvalidMessage("Invalid unset value for column s", "SELECT * 
from %s WHERE s = ?", unset());
         // range
         assertInvalidMessage("Invalid unset value for column i", "SELECT * 
from %s WHERE k = 1 AND i > ?", unset());
+        assertInvalidMessage("Invalid unset value for column i", "SELECT * 
from %s WHERE k = 1 AND i BETWEEN ? AND ?", 1, unset());
     }
 
     @Test
@@ -590,6 +637,8 @@ public class SelectSingleColumnRelationTest extends 
CQLTester
                              "SELECT * FROM %s WHERE a >= 1 and a < 4");
         assertInvalidMessage("Multi-column relations can only be applied to 
clustering columns but was applied to: a",
                              "SELECT * FROM %s WHERE (a) >= (1) and (a) < 
(4)");
+        assertInvalidMessage("Multi-column relations can only be applied to 
clustering columns but was applied to: a",
+                             "SELECT * FROM %s WHERE (a) BETWEEN (1) AND (4)");
     }
 
     @Test
@@ -608,6 +657,8 @@ public class SelectSingleColumnRelationTest extends 
CQLTester
                              "SELECT * FROM %s WHERE (b) < (4) and (a, b) >= 
(1, 1)");
         assertInvalidMessage("Multi-column relations can only be applied to 
clustering columns but was applied to: a",
                              "SELECT * FROM %s WHERE (a, b) >= (1, 1) and a = 
1");
+        assertInvalidMessage("Multi-column relations can only be applied to 
clustering columns but was applied to: a",
+                             "SELECT * FROM %s WHERE (a, b) BETWEEN (1, 1) AND 
(4, 5) and a = 1");
     }
 
     @Test
@@ -625,6 +676,7 @@ public class SelectSingleColumnRelationTest extends 
CQLTester
         assertInvalidMessage("Undefined column name d", "SELECT c AS d FROM %s 
WHERE d CONTAINS 0");
         assertInvalidMessage("Undefined column name d", "SELECT c AS d FROM %s 
WHERE d CONTAINS KEY 0");
         assertInvalidMessage("Undefined column name d", "SELECT d FROM %s 
WHERE a = 0");
+        assertInvalidMessage("Undefined column name d", "SELECT d FROM %s 
WHERE b BETWEEN 0 AND 0");
     }
 
     @Test
@@ -641,6 +693,7 @@ public class SelectSingleColumnRelationTest extends 
CQLTester
         assertInvalidMessage(msg, "SELECT * FROM %s WHERE b < ?", udt);
         assertInvalidMessage(msg, "SELECT * FROM %s WHERE b >= ?", udt);
         assertInvalidMessage(msg, "SELECT * FROM %s WHERE b <= ?", udt);
+        assertInvalidMessage(msg, "SELECT * FROM %s WHERE b BETWEEN ? AND ?", 
udt, udt);
         assertInvalidMessage(msg, "SELECT * FROM %s WHERE b IN (?)", udt);
         assertInvalidMessage(msg, "SELECT * FROM %s WHERE b LIKE ?", udt);
         assertInvalidMessage("Unsupported '!=' relation: b != {a: 0}",
@@ -803,5 +856,9 @@ public class SelectSingleColumnRelationTest extends 
CQLTester
         assertRows(execute("SELECT * from %s WHERE pk = ? AND c > ? AND c <= 
?", 1, -4, -1),
                    row(1, -2, -2),
                    row(1, -1, -1));
+
+        assertRows(execute("SELECT * from %s WHERE pk = ? AND c BETWEEN ? AND 
?", 1, -4, -1),
+                   row(1, -2, -2),
+                   row(1, -1, -1));
     }
 }
diff --git 
a/test/unit/org/apache/cassandra/cql3/validation/operations/UpdateTest.java 
b/test/unit/org/apache/cassandra/cql3/validation/operations/UpdateTest.java
index 59a0616106..3331230eac 100644
--- a/test/unit/org/apache/cassandra/cql3/validation/operations/UpdateTest.java
+++ b/test/unit/org/apache/cassandra/cql3/validation/operations/UpdateTest.java
@@ -179,6 +179,9 @@ public class UpdateTest extends CQLTester
 
         assertInvalidMessage("Slice restrictions are not supported on the 
clustering columns in UPDATE statements",
                              "UPDATE %s SET value = ? WHERE partitionKey = ? 
AND clustering_1 > ?", 7, 0, 1);
+
+        assertInvalidMessage("Slice restrictions are not supported on the 
clustering columns in UPDATE statements",
+                             "UPDATE %s SET value = ? WHERE partitionKey = ? 
AND clustering_1 BETWEEN ? AND ?", 7, 0, 1, 2);
     }
 
     @Test
diff --git 
a/test/unit/org/apache/cassandra/db/AbstractReadQueryToCQLStringTest.java 
b/test/unit/org/apache/cassandra/db/AbstractReadQueryToCQLStringTest.java
index 11582b95a1..7d3162413b 100644
--- a/test/unit/org/apache/cassandra/db/AbstractReadQueryToCQLStringTest.java
+++ b/test/unit/org/apache/cassandra/db/AbstractReadQueryToCQLStringTest.java
@@ -86,6 +86,7 @@ public class AbstractReadQueryToCQLStringTest extends 
CQLTester
         test("SELECT * FROM %s WHERE v1 > 1 ALLOW FILTERING");
         test("SELECT * FROM %s WHERE v1 <= 1 ALLOW FILTERING");
         test("SELECT * FROM %s WHERE v1 >= 1 ALLOW FILTERING");
+        test("SELECT * FROM %s WHERE v1 BETWEEN 1 AND 2 ALLOW FILTERING");
         test("SELECT * FROM %s WHERE v1 = 1 AND v2 = 2 ALLOW FILTERING");
         test("SELECT * FROM %s WHERE k = 0 AND v1 = 1 ALLOW FILTERING");
         test("SELECT * FROM %s WHERE token(k) > 0 AND v1 = 1 ALLOW FILTERING");
@@ -717,6 +718,8 @@ public class AbstractReadQueryToCQLStringTest extends 
CQLTester
         test("SELECT * FROM vk.vt WHERE k = 0 AND c >= 1");
         test("SELECT * FROM vk.vt WHERE k = 0 AND c > 1 AND c <= 2");
         test("SELECT * FROM vk.vt WHERE k = 0 AND c >= 1 AND c < 2");
+        test("SELECT * FROM vk.vt WHERE k = 0 AND c BETWEEN 1 AND 2",
+             "SELECT * FROM vk.vt WHERE k = 0 AND c >= 1 AND c <= 2");
 
         // token restrictions
         test("SELECT * FROM vk.vt WHERE token(k) > 0");
@@ -725,12 +728,15 @@ public class AbstractReadQueryToCQLStringTest extends 
CQLTester
         test("SELECT * FROM vk.vt WHERE token(k) <= 0");
         test("SELECT * FROM vk.vt WHERE token(k) = 0",
              "SELECT * FROM vk.vt WHERE token(k) >= 0 AND token(k) <= 0");
+        test("SELECT * FROM vk.vt WHERE token(k) BETWEEN 0 AND 0",
+             "SELECT * FROM vk.vt WHERE token(k) >= 0 AND token(k) <= 0");
 
         // row filters
         test("SELECT * FROM vk.vt WHERE c = 1 ALLOW FILTERING");
         test("SELECT * FROM vk.vt WHERE s = 1 ALLOW FILTERING");
         test("SELECT * FROM vk.vt WHERE v = 1 ALLOW FILTERING");
         test("SELECT * FROM vk.vt WHERE k = 0 AND v = 1 ALLOW FILTERING");
+        test("SELECT * FROM vk.vt WHERE k = 0 AND v BETWEEN 1 AND 2 ALLOW 
FILTERING");
         test("SELECT * FROM vk.vt WHERE k = 0 AND c = 1 AND v = 1 ALLOW 
FILTERING");
         test("SELECT * FROM vk.vt WHERE token(k) > 0 AND v = 1 ALLOW 
FILTERING");
         test("SELECT * FROM vk.vt WHERE token(k) > 0 AND c = 1 AND v = 1 ALLOW 
FILTERING");


---------------------------------------------------------------------
To unsubscribe, e-mail: commits-unsubscr...@cassandra.apache.org
For additional commands, e-mail: commits-h...@cassandra.apache.org

Reply via email to