You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@cassandra.apache.org by bl...@apache.org on 2016/02/11 10:49:52 UTC

[1/6] cassandra git commit: Fix SELECT on tuple relations for mixed ASC/DESC clustering order

Repository: cassandra
Updated Branches:
  refs/heads/trunk ebd6c23df -> b0448e66b


http://git-wip-us.apache.org/repos/asf/cassandra/blob/90fc8969/test/unit/org/apache/cassandra/cql3/validation/operations/SelectMultiColumnRelationTest.java
----------------------------------------------------------------------
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 b3232d5..b8a1d50 100644
--- a/test/unit/org/apache/cassandra/cql3/validation/operations/SelectMultiColumnRelationTest.java
+++ b/test/unit/org/apache/cassandra/cql3/validation/operations/SelectMultiColumnRelationTest.java
@@ -792,8 +792,10 @@ public class SelectMultiColumnRelationTest extends CQLTester
                     row(0, 0, 1, 0)
             );
 
-            // preserve pre-6875 behavior (even though the query result is technically incorrect)
-            assertEmpty(execute("SELECT * FROM %s WHERE a = ? AND (b, c) > (?, ?)", 0, 1, 0));
+            assertRows(execute("SELECT * FROM %s WHERE a = ? AND (b, c) > (?, ?)", 0, 1, 0),
+                    row(0,1, 1, 1),
+                    row(0, 1, 1, 0)
+                    );
         }
     }
 
@@ -990,6 +992,859 @@ public class SelectMultiColumnRelationTest extends CQLTester
                              "SELECT * from %s WHERE (i, j) IN ? ALLOW FILTERING", unset());
     }
 
+    @Test
+    public void testMixedOrderColumns1() throws Throwable
+    {
+        createTable("CREATE TABLE %s (a int, b int, c int, d int, e int, PRIMARY KEY (a, b, c, d, e)) " +
+                "WITH CLUSTERING ORDER BY (b DESC, c ASC, d DESC, e ASC)");
+
+        execute("INSERT INTO %s (a, b, c, d, e) VALUES (?, ?, ?, ?, ?)", 0, 2, 0, -1, 0);
+        execute("INSERT INTO %s (a, b, c, d, e) VALUES (?, ?, ?, ?, ?)", 0, 2, 0, -1, 1);
+        execute("INSERT INTO %s (a, b, c, d, e) VALUES (?, ?, ?, ?, ?)", 0, 2, 0, 1, 1);
+        execute("INSERT INTO %s (a, b, c, d, e) VALUES (?, ?, ?, ?, ?)", 0, 1, -1, 0, 0);
+        execute("INSERT INTO %s (a, b, c, d, e) VALUES (?, ?, ?, ?, ?)", 0, 1, -1, 1, 1);
+        execute("INSERT INTO %s (a, b, c, d, e) VALUES (?, ?, ?, ?, ?)", 0, 1, -1, 1, 0);
+        execute("INSERT INTO %s (a, b, c, d, e) VALUES (?, ?, ?, ?, ?)", 0, 1, 0, 1, -1);
+        execute("INSERT INTO %s (a, b, c, d, e) VALUES (?, ?, ?, ?, ?)", 0, 1, 0, 1, 1);
+        execute("INSERT INTO %s (a, b, c, d, e) VALUES (?, ?, ?, ?, ?)", 0, 1, 0, 0, -1);
+        execute("INSERT INTO %s (a, b, c, d, e) VALUES (?, ?, ?, ?, ?)", 0, 1, 0, 0, 0);
+        execute("INSERT INTO %s (a, b, c, d, e) VALUES (?, ?, ?, ?, ?)", 0, 1, 0, 0, 1);
+        execute("INSERT INTO %s (a, b, c, d, e) VALUES (?, ?, ?, ?, ?)", 0, 1, 0, -1, -1);
+        execute("INSERT INTO %s (a, b, c, d, e) VALUES (?, ?, ?, ?, ?)", 0, 1, 1, 0, -1);
+        execute("INSERT INTO %s (a, b, c, d, e) VALUES (?, ?, ?, ?, ?)", 0, 1, 1, 0, 0);
+        execute("INSERT INTO %s (a, b, c, d, e) VALUES (?, ?, ?, ?, ?)", 0, 1, 1, 0, -1);
+        execute("INSERT INTO %s (a, b, c, d, e) VALUES (?, ?, ?, ?, ?)", 0, 1, 1, 0, 0);
+        execute("INSERT INTO %s (a, b, c, d, e) VALUES (?, ?, ?, ?, ?)", 0, 1, 1, 0, 1);
+        execute("INSERT INTO %s (a, b, c, d, e) VALUES (?, ?, ?, ?, ?)", 0, 1, 1, -1, 0);
+        execute("INSERT INTO %s (a, b, c, d, e) VALUES (?, ?, ?, ?, ?)", 0, 0, 0, 0, 0);
+        execute("INSERT INTO %s (a, b, c, d, e) VALUES (?, ?, ?, ?, ?)", 0, -1, 0, -1, 0);
+        execute("INSERT INTO %s (a, b, c, d, e) VALUES (?, ?, ?, ?, ?)", 0, -1, 0, 0, 0);
+        assertRows(execute(
+                        "SELECT * FROM %s" +
+                                " WHERE a = ? " +
+                                "AND (b,c,d,e)<=(?,?,?,?) " +
+                                "AND (b)>(?)", 0, 2, 0, 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)
+        );
+
+
+
+        assertRows(execute(
+                        "SELECT * FROM %s" +
+                                " WHERE a = ? " +
+                                "AND (b,c,d,e)<=(?,?,?,?) " +
+                                "AND (b)>=(?)", 0, 2, 0, 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 = ? " +
+                                "AND (b,c,d)>=(?,?,?)"+
+                                "AND (b,c,d,e)<(?,?,?,?) ", 0, 1, 1,0,1,  1, 0,1),
+                row( 0, 1, 1, 0, -1),
+                row( 0, 1, 1, 0, 0)
+
+        );
+
+        assertRows(execute(
+                        "SELECT * FROM %s" +
+                                " WHERE a = ? " +
+                                "AND (b,c,d,e)>(?,?,?,?)"+
+                                "AND (b,c,d)<=(?,?,?) ", 0, -1, 0,-1,-1,  2, 0,-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 = ? " +
+                                "AND (b,c,d,e) < (?,?,?,?) " +
+                                "AND (b,c,d,e)>(?,?,?,?)", 0, 1, 0, 0, 0, 1, 0,-1,-1),
+                row(0, 1, 0, 0, -1)
+        );
+
+        assertRows(execute(
+                        "SELECT * FROM %s" +
+                                " WHERE a = ? " +
+                                "AND (b,c,d,e) <= (?,?,?,?) " +
+                                "AND (b,c,d,e)>(?,?,?,?)", 0, 1, 0, 0, 0, 1, 0,-1,-1),
+                row(0, 1, 0, 0, -1),
+                row(0, 1, 0, 0, 0)
+        );
+
+        assertRows(execute(
+                        "SELECT * FROM %s" +
+                                " WHERE a = ? " +
+                                "AND (b)<(?) " +
+                                "AND (b,c,d,e)>(?,?,?,?)", 0, 2, -1, 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 = ? " +
+                                "AND (b)<(?) " +
+                                "AND (b)>(?)", 0, 2, -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)
+
+        );
+
+        assertRows(execute(
+                        "SELECT * FROM %s" +
+                                " WHERE a = ? " +
+                                "AND (b)<(?) " +
+                                "AND (b)>=(?)", 0, 2, -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 = ? " +
+                                "AND (b,c,d,e)<=(?,?,?,?) " +
+                                "AND (b,c,d,e)>(?,?,?,?)", 0, 2, 0, 1, 1, -1, 0,-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 = ? " +
+                                "AND (b,c)<=(?,?) " +
+                                "AND (b,c,d,e)>(?,?,?,?)", 0, 2, 0, -1, 0,-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 = ? " +
+                                "AND (b,c,d)<=(?,?,?) " +
+                                "AND (b,c,d,e)>(?,?,?,?)", 0, 2, 0,-1, -1, 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 = ? " +
+                                "AND (b,c,d,e)>(?,?,?,?)"+
+                                "AND (b,c,d)<=(?,?,?) ", 0, -1, 0,-1,-1,  2, 0,-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 = ? " +
+                                "AND (b,c,d)>=(?,?,?)"+
+                                "AND (b,c,d,e)<(?,?,?,?) ", 0, 1, 1,0,1,  1, 0,1),
+                row( 0, 1, 1, 0, -1),
+                row( 0, 1, 1, 0, 0)
+        );
+        assertRows(execute(
+                        "SELECT * FROM %s" +
+                                " WHERE a = ? " +
+                                "AND (b,c,d,e)<(?,?,?,?) "+
+                                "AND (b,c,d)>=(?,?,?)", 0, 1, 1,0,1,1,  1, 0),
+                row( 0, 1, 1, 0, -1),
+                row( 0, 1, 1, 0, 0)
+
+        );
+
+        assertRows(execute(
+                        "SELECT * FROM %s" +
+                                " WHERE a = ? " +
+                                "AND (b,c)<(?,?) " +
+                                "AND (b,c,d,e)>(?,?,?,?)", 0, 2, 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, 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 = ? " +
+                                "AND (b,c)<(?,?) " +
+                                "AND (b,c,d,e)>(?,?,?,?)", 0, 2, 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, 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 = ? AND (b,c,d,e) <= (?,?,?,?)", 0, 1, 0, 0, 0),
+                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),
+                row(0, 2, 0, -1, 1),
+                row(0, 1, 0,  1, -1),
+                row(0, 1, 0,  1, 1),
+                row(0, 1, 0,  0, 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)
+        );
+
+        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),
+                row(0, 2, 0, -1, 1),
+                row(0, 1, 0,  1, -1),
+                row(0, 1, 0,  1, 1),
+                row(0, 1, 0,  0, 0),
+                row(0, 1, 0,  0, 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)
+        );
+
+        assertRows(execute("SELECT * FROM %s WHERE a = ? AND (b,c,d) >= (?,?,?)", 0, 1, 0, 0),
+                row( 0, 2, 0, 1, 1),
+                row(0, 2, 0, -1, 0),
+                row(0, 2, 0, -1, 1),
+                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, 1,  0, -1),
+                row(0, 1, 1,  0, 0),
+                row(0, 1, 1,  0, 1),
+                row(0, 1, 1, -1, 0)
+        );
+
+        assertRows(execute("SELECT * FROM %s WHERE a = ? AND (b,c,d) > (?,?,?)", 0, 1, 0, 0),
+                row(0, 2, 0, 1, 1),
+                row(0, 2, 0, -1, 0),
+                row(0, 2, 0, -1, 1),
+                row(0, 1, 0,  1, -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)
+        );
+
+    }
+
+    @Test
+    public void testMixedOrderColumns2() throws Throwable
+    {
+        createTable("CREATE TABLE %s (a int, b int, c int, d int, e int, PRIMARY KEY (a, b, c, d, e)) " +
+                "WITH CLUSTERING ORDER BY (b DESC, c ASC, d ASC, e ASC)");
+
+        // b and d are reversed in the clustering order
+        execute("INSERT INTO %s (a, b, c, d, e) VALUES (?, ?, ?, ?, ?)", 0, 2, 0, -1, 0);
+        execute("INSERT INTO %s (a, b, c, d, e) VALUES (?, ?, ?, ?, ?)", 0, 2, 0, -1, 1);
+        execute("INSERT INTO %s (a, b, c, d, e) VALUES (?, ?, ?, ?, ?)", 0, 1, -1, 0, 0);
+        execute("INSERT INTO %s (a, b, c, d, e) VALUES (?, ?, ?, ?, ?)", 0, 1, -1, 1, 0);
+        execute("INSERT INTO %s (a, b, c, d, e) VALUES (?, ?, ?, ?, ?)", 0, 1, -1, 1, 1);
+        execute("INSERT INTO %s (a, b, c, d, e) VALUES (?, ?, ?, ?, ?)", 0, 1, 0, 1, -1);
+        execute("INSERT INTO %s (a, b, c, d, e) VALUES (?, ?, ?, ?, ?)", 0, 1, 0, 1, 1);
+        execute("INSERT INTO %s (a, b, c, d, e) VALUES (?, ?, ?, ?, ?)", 0, 1, 0, 0, -1);
+        execute("INSERT INTO %s (a, b, c, d, e) VALUES (?, ?, ?, ?, ?)", 0, 1, 0, 0, 0);
+        execute("INSERT INTO %s (a, b, c, d, e) VALUES (?, ?, ?, ?, ?)", 0, 1, 0, 0, 1);
+        execute("INSERT INTO %s (a, b, c, d, e) VALUES (?, ?, ?, ?, ?)", 0, 1, 0, -1, -1);
+        execute("INSERT INTO %s (a, b, c, d, e) VALUES (?, ?, ?, ?, ?)", 0, 1, 1, 0, -1);
+        execute("INSERT INTO %s (a, b, c, d, e) VALUES (?, ?, ?, ?, ?)", 0, 1, 1, 0, 0);
+        execute("INSERT INTO %s (a, b, c, d, e) VALUES (?, ?, ?, ?, ?)", 0, 1, 1, 0, -1);
+        execute("INSERT INTO %s (a, b, c, d, e) VALUES (?, ?, ?, ?, ?)", 0, 1, 1, 0, 0);
+        execute("INSERT INTO %s (a, b, c, d, e) VALUES (?, ?, ?, ?, ?)", 0, 1, 1, 0, 1);
+        execute("INSERT INTO %s (a, b, c, d, e) VALUES (?, ?, ?, ?, ?)", 0, 1, 1, -1, 0);
+        execute("INSERT INTO %s (a, b, c, d, e) VALUES (?, ?, ?, ?, ?)", 0, 0, 0, 0, 0);
+
+        assertRows(execute("SELECT * FROM %s WHERE a = ? AND (b,c,d,e) <= (?,?,?,?)", 0, 1, 0, 0, 0),
+                row(0, 1, -1, 0, 0),
+                row(0, 1, -1, 1, 0),
+                row(0, 1, -1, 1, 1),
+                row(0, 1, 0, -1, -1),
+                row(0, 1, 0, 0, -1),
+                row(0, 1, 0, 0, 0),
+                row(0, 0, 0, 0, 0)
+        );
+
+        assertRows(execute("SELECT * FROM %s WHERE a = ? AND (b,c,d,e) > (?,?,?,?)", 0, 1, 0, 0, 0),
+                row(0, 2, 0, -1, 0),
+                row(0, 2, 0, -1, 1),
+                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)
+        );
+        assertRows(execute("SELECT * FROM %s WHERE a = ? AND (b,c,d,e) >= (?,?,?,?)", 0, 1, 0, 0, 0),
+                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
+    public void testMixedOrderColumns3() throws Throwable
+    {
+        createTable("create table %s (a int, b int, c int, PRIMARY KEY (a, b, c)) WITH CLUSTERING ORDER BY (b DESC, c ASC)");
+
+        execute("INSERT INTO %s (a, b, c) VALUES (?,?,?);", 0, 2, 3);
+        execute("INSERT INTO %s (a, b, c) VALUES (?,?,?);", 0, 2, 4);
+        execute("INSERT INTO %s (a, b, c) VALUES (?,?,?);", 0, 4, 4);
+        execute("INSERT INTO %s (a, b, c) VALUES (?,?,?);", 0, 3, 4);
+        execute("INSERT INTO %s (a, b, c) VALUES (?,?,?);", 0, 4, 5);
+        execute("INSERT INTO %s (a, b, c) VALUES (?,?,?);", 0, 4, 6);
+
+
+        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, 3, 4), row(0, 2, 3), row(0, 2, 4)
+        );
+        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)<(?,?) ALLOW FILTERING", 0, 4, 5),
+                row(0, 4, 4),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, 6)
+        );
+
+        assertRows(execute("SELECT * FROM %s WHERE a = ? AND (b)<(?) and (b)>(?) ALLOW FILTERING", 0, 4, 2),
+                row(0, 3, 4)
+        );
+    }
+
+    @Test
+    public void testMixedOrderColumns4() throws Throwable
+    {
+        createTable("CREATE TABLE %s (a int, b int, c int, d int, e int, PRIMARY KEY (a, b, c, d, e)) " +
+                "WITH CLUSTERING ORDER BY (b ASC, c DESC, d DESC, e ASC)");
+
+        execute("INSERT INTO %s (a, b, c, d, e) VALUES (?, ?, ?, ?, ?)", 0, 2, 0, -1, 0);
+        execute("INSERT INTO %s (a, b, c, d, e) VALUES (?, ?, ?, ?, ?)", 0, 2, 0, -1, 1);
+        execute("INSERT INTO %s (a, b, c, d, e) VALUES (?, ?, ?, ?, ?)", 0, 2, 0, 1, 1);
+        execute("INSERT INTO %s (a, b, c, d, e) VALUES (?, ?, ?, ?, ?)", 0, 2, -1, 1, 1);
+        execute("INSERT INTO %s (a, b, c, d, e) VALUES (?, ?, ?, ?, ?)", 0, 2, -3, 1, 1);
+        execute("INSERT INTO %s (a, b, c, d, e) VALUES (?, ?, ?, ?, ?)", 0, 1, -1, 0, 0);
+        execute("INSERT INTO %s (a, b, c, d, e) VALUES (?, ?, ?, ?, ?)", 0, 1, -1, 1, 1);
+        execute("INSERT INTO %s (a, b, c, d, e) VALUES (?, ?, ?, ?, ?)", 0, 1, -1, 1, 0);
+        execute("INSERT INTO %s (a, b, c, d, e) VALUES (?, ?, ?, ?, ?)", 0, 1, 0, 1, -1);
+        execute("INSERT INTO %s (a, b, c, d, e) VALUES (?, ?, ?, ?, ?)", 0, 1, 0, 1, 1);
+        execute("INSERT INTO %s (a, b, c, d, e) VALUES (?, ?, ?, ?, ?)", 0, 1, 0, 0, -1);
+        execute("INSERT INTO %s (a, b, c, d, e) VALUES (?, ?, ?, ?, ?)", 0, 1, 0, 0, 0);
+        execute("INSERT INTO %s (a, b, c, d, e) VALUES (?, ?, ?, ?, ?)", 0, 1, 0, 0, 1);
+        execute("INSERT INTO %s (a, b, c, d, e) VALUES (?, ?, ?, ?, ?)", 0, 1, 0, -1, -1);
+        execute("INSERT INTO %s (a, b, c, d, e) VALUES (?, ?, ?, ?, ?)", 0, 1, 1, 0, -1);
+        execute("INSERT INTO %s (a, b, c, d, e) VALUES (?, ?, ?, ?, ?)", 0, 1, 1, 0, 0);
+        execute("INSERT INTO %s (a, b, c, d, e) VALUES (?, ?, ?, ?, ?)", 0, 1, 1, 0, -1);
+        execute("INSERT INTO %s (a, b, c, d, e) VALUES (?, ?, ?, ?, ?)", 0, 1, 1, 0, 0);
+        execute("INSERT INTO %s (a, b, c, d, e) VALUES (?, ?, ?, ?, ?)", 0, 1, 1, 0, 1);
+        execute("INSERT INTO %s (a, b, c, d, e) VALUES (?, ?, ?, ?, ?)", 0, 1, 1, -1, 0);
+        execute("INSERT INTO %s (a, b, c, d, e) VALUES (?, ?, ?, ?, ?)", 0, 0, 0, 0, 0);
+        execute("INSERT INTO %s (a, b, c, d, e) VALUES (?, ?, ?, ?, ?)", 0, -1, 0, -1, 0);
+        execute("INSERT INTO %s (a, b, c, d, e) VALUES (?, ?, ?, ?, ?)", 0, -1, 0, 0, 0);
+
+        assertRows(execute(
+                        "SELECT * FROM %s" +
+                                " WHERE a = ? " +
+                                "AND (b,c,d,e)<(?,?,?,?) " +
+                                "AND (b,c,d,e)>(?,?,?,?)", 0, 2, 0, 1, 1, -1, 0, -1, -1),
+
+                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, 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,e) < (?,?,?,?) " +
+                                "AND (b,c,d,e)>(?,?,?,?)", 0, 1, 0, 0, 0, 1, 0, -1, -1),
+                row(0, 1, 0, 0, -1)
+        );
+
+        assertRows(execute(
+                        "SELECT * FROM %s" +
+                                " WHERE a = ? " +
+                                "AND (b,c,d,e) <= (?,?,?,?) " +
+                                "AND (b,c,d,e)>(?,?,?,?)", 0, 1, 0, 0, 0, 1, 0,-1,-1),
+                row(0, 1, 0, 0, -1),
+                row(0, 1, 0, 0, 0)
+        );
+
+
+        assertRows(execute(
+                        "SELECT * FROM %s" +
+                                " WHERE a = ? " +
+                                "AND (b,c,d,e)<=(?,?,?,?) " +
+                                "AND (b,c,d,e)>(?,?,?,?)", 0, 2, 0, 1, 1, -1, 0,-1,-1),
+
+                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 = ? " +
+                                "AND (b,c)<=(?,?) " +
+                                "AND (b,c,d,e)>(?,?,?,?)", 0, 2, 0, -1, 0,-1,-1),
+
+                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 = ? " +
+                                "AND (b,c)<(?,?) " +
+                                "AND (b,c,d,e)>(?,?,?,?)", 0, 2, 0, -1, 0, -1, -1),
+                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, -1, 1, 1),
+                row(0, 2, -3, 1, 1)
+        );
+
+        assertRows(execute(
+                        "SELECT * FROM %s" +
+                                " WHERE a = ? " +
+                                "AND (b,c,d,e)<=(?,?,?,?) " +
+                                "AND (b)>=(?)", 0, 2, 0, 1, 1, -1),
+
+                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 = ? " +
+                                "AND (b,c,d,e)<=(?,?,?,?) " +
+                                "AND (b)>(?)", 0, 2, 0, 1, 1, -1),
+
+                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 = ? AND (b,c,d,e) <= (?,?,?,?)", 0, 1, 0, 0, 0),
+                row(0, -1, 0, 0, 0),
+                row(0, -1, 0, -1, 0),
+                row(0, 0, 0, 0, 0),
+                row(0, 1, 0, 0, -1),
+                row(0, 1, 0, 0, 0),
+                row(0, 1, 0, -1, -1),
+                row(0, 1, -1, 1, 0),
+                row(0, 1, -1, 1, 1),
+                row(0, 1, -1, 0, 0)
+        );
+
+        assertRows(execute("SELECT * FROM %s WHERE a = ? AND (b,c,d,e) > (?,?,?,?)", 0, 1, 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, 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,e) >= (?,?,?,?)", 0, 1, 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, 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),
+                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),
+                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, 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) < (?) ", 0, 0),
+                row(0, -1, 0, 0, 0), row(0, -1, 0, -1, 0)
+        );
+        assertRows(execute(
+                        "SELECT * FROM %s" +
+                                " WHERE a = ? " +
+                                "AND (b) <= (?) ", 0,-1),
+                row(0, -1, 0, 0, 0), row(0, -1, 0, -1, 0)
+        );
+        assertRows(execute(
+                        "SELECT * FROM %s" +
+                                " WHERE a = ? " +
+                                "AND (b,c,d,e) < (?,?,?,?) and (b,c,d,e) > (?,?,?,?) ", 0, 2, 0, 0, 0, 2, -2, 0, 0),
+                row(0, 2, 0, -1, 0),
+                row(0, 2, 0, -1, 1),
+                row(0, 2, -1, 1, 1)
+        );
+    }
+
     /**
      * Check select on tuple relations, see CASSANDRA-8613
      * migrated from cql_tests.py:TestCQL.simple_tuple_query_test()


[6/6] cassandra git commit: Merge branch cassandra-3.0 into trunk

Posted by bl...@apache.org.
Merge branch cassandra-3.0 into trunk


Project: http://git-wip-us.apache.org/repos/asf/cassandra/repo
Commit: http://git-wip-us.apache.org/repos/asf/cassandra/commit/b0448e66
Tree: http://git-wip-us.apache.org/repos/asf/cassandra/tree/b0448e66
Diff: http://git-wip-us.apache.org/repos/asf/cassandra/diff/b0448e66

Branch: refs/heads/trunk
Commit: b0448e66b567bb8dc80acb1cb9c66bbf8b5f461c
Parents: ebd6c23 1aa97e3
Author: Benjamin Lerer <b....@gmail.com>
Authored: Thu Feb 11 10:44:30 2016 +0100
Committer: Benjamin Lerer <b....@gmail.com>
Committed: Thu Feb 11 10:48:28 2016 +0100

----------------------------------------------------------------------
 CHANGES.txt                                     |    1 +
 .../cql3/restrictions/AbstractRestriction.java  |   14 +
 .../ForwardingPrimaryKeyRestrictions.java       |    3 +-
 .../restrictions/MultiColumnRestriction.java    |   76 +-
 .../restrictions/PrimaryKeyRestrictionSet.java  |   34 +-
 .../cql3/restrictions/Restriction.java          |    4 +-
 .../cql3/restrictions/RestrictionSet.java       |    4 +-
 .../restrictions/SingleColumnRestriction.java   |    9 +-
 .../cql3/restrictions/TokenRestriction.java     |    2 +-
 .../org/apache/cassandra/db/MultiCBuilder.java  |   90 +-
 .../PrimaryKeyRestrictionSetTest.java           | 1174 +++++++++++++++---
 .../SelectMultiColumnRelationTest.java          |  859 ++++++++++++-
 12 files changed, 2059 insertions(+), 211 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/cassandra/blob/b0448e66/CHANGES.txt
----------------------------------------------------------------------

http://git-wip-us.apache.org/repos/asf/cassandra/blob/b0448e66/src/java/org/apache/cassandra/cql3/restrictions/AbstractRestriction.java
----------------------------------------------------------------------

http://git-wip-us.apache.org/repos/asf/cassandra/blob/b0448e66/src/java/org/apache/cassandra/cql3/restrictions/ForwardingPrimaryKeyRestrictions.java
----------------------------------------------------------------------

http://git-wip-us.apache.org/repos/asf/cassandra/blob/b0448e66/src/java/org/apache/cassandra/cql3/restrictions/PrimaryKeyRestrictionSet.java
----------------------------------------------------------------------
diff --cc src/java/org/apache/cassandra/cql3/restrictions/PrimaryKeyRestrictionSet.java
index d24799a,8121858..978ebbc
--- a/src/java/org/apache/cassandra/cql3/restrictions/PrimaryKeyRestrictionSet.java
+++ b/src/java/org/apache/cassandra/cql3/restrictions/PrimaryKeyRestrictionSet.java
@@@ -163,20 -175,6 +163,30 @@@ final class PrimaryKeyRestrictionSet ex
          return new PrimaryKeyRestrictionSet(this, restriction);
      }
  
 +    // Whether any of the underlying restriction is an IN
 +    private boolean hasIN()
 +    {
 +        if (isIN())
 +            return true;
 +
 +        for (Restriction restriction : restrictions)
 +        {
 +            if (restriction.isIN())
 +                return true;
 +        }
 +        return false;
 +    }
 +
++    private boolean hasMultiColumnSlice()
++    {
++        for (Restriction restriction : restrictions)
++        {
++            if (restriction.isMultiColumn() && restriction.isSlice())
++                return true;
++        }
++        return false;
++    }
++
      @Override
      public NavigableSet<Clustering> valuesAsClustering(QueryOptions options) throws InvalidRequestException
      {
@@@ -204,7 -202,7 +214,7 @@@
      @Override
      public NavigableSet<Slice.Bound> boundsAsClustering(Bound bound, QueryOptions options) throws InvalidRequestException
      {
-         MultiCBuilder builder = MultiCBuilder.create(comparator, hasIN());
 -        MultiCBuilder builder = MultiCBuilder.create(comparator);
++        MultiCBuilder builder = MultiCBuilder.create(comparator, hasIN() || hasMultiColumnSlice());
          int keyPosition = 0;
          for (Restriction r : restrictions)
          {

http://git-wip-us.apache.org/repos/asf/cassandra/blob/b0448e66/src/java/org/apache/cassandra/cql3/restrictions/Restriction.java
----------------------------------------------------------------------

http://git-wip-us.apache.org/repos/asf/cassandra/blob/b0448e66/src/java/org/apache/cassandra/cql3/restrictions/RestrictionSet.java
----------------------------------------------------------------------

http://git-wip-us.apache.org/repos/asf/cassandra/blob/b0448e66/src/java/org/apache/cassandra/cql3/restrictions/SingleColumnRestriction.java
----------------------------------------------------------------------

http://git-wip-us.apache.org/repos/asf/cassandra/blob/b0448e66/src/java/org/apache/cassandra/db/MultiCBuilder.java
----------------------------------------------------------------------
diff --cc src/java/org/apache/cassandra/db/MultiCBuilder.java
index 7a4eef0,7c77ab0..f03e674
--- a/src/java/org/apache/cassandra/db/MultiCBuilder.java
+++ b/src/java/org/apache/cassandra/db/MultiCBuilder.java
@@@ -18,8 -18,11 +18,13 @@@
  package org.apache.cassandra.db;
  
  import java.nio.ByteBuffer;
- import java.util.*;
+ import java.util.ArrayList;
++import java.util.Arrays;
+ import java.util.List;
+ import java.util.NavigableSet;
  
+ import org.apache.cassandra.config.ColumnDefinition;
++import org.apache.cassandra.db.Slice.Bound;
  import org.apache.cassandra.utils.ByteBufferUtil;
  import org.apache.cassandra.utils.btree.BTreeSet;
  
@@@ -159,260 -257,123 +164,333 @@@ public abstract class MultiCBuilde
       *
       * @return the clusterings
       */
 -    public NavigableSet<Clustering> build()
 -    {
 -        built = true;
 -
 -        if (hasMissingElements)
 -            return BTreeSet.empty(comparator);
 -
 -        CBuilder builder = CBuilder.create(comparator);
 -
 -        if (elementsList.isEmpty())
 -            return BTreeSet.of(builder.comparator(), builder.build());
 -
 -        BTreeSet.Builder<Clustering> set = BTreeSet.builder(builder.comparator());
 -        for (int i = 0, m = elementsList.size(); i < m; i++)
 -        {
 -            List<ByteBuffer> elements = elementsList.get(i);
 -            set.add(builder.buildWith(elements));
 -        }
 -        return set.build();
 -    }
 +    public abstract NavigableSet<Clustering> build();
  
      /**
-      * Builds the <code>clusterings</code> with the specified EOC.
+      * Builds the <code>Slice.Bound</code>s for slice restrictions.
       *
-      * @return the clusterings
+      * @param isStart specify if the bound is a start one
+      * @param isInclusive specify if the bound is inclusive or not
+      * @param isOtherBoundInclusive specify if the other bound is inclusive or not
+      * @param columnDefs the columns of the slice restriction
+      * @return the <code>Slice.Bound</code>s
+      */
 -    public NavigableSet<Slice.Bound> buildBoundForSlice(boolean isStart,
 -                                                        boolean isInclusive,
 -                                                        boolean isOtherBoundInclusive,
 -                                                        List<ColumnDefinition> columnDefs)
++    public abstract NavigableSet<Slice.Bound> buildBoundForSlice(boolean isStart,
++                                                                 boolean isInclusive,
++                                                                 boolean isOtherBoundInclusive,
++                                                                 List<ColumnDefinition> columnDefs);
++
++    /**
++     * Builds the <code>Slice.Bound</code>s
++     *
++     * @param isStart specify if the bound is a start one
++     * @param isInclusive specify if the bound is inclusive or not
++     * @return the <code>Slice.Bound</code>s
 +     */
 +    public abstract NavigableSet<Slice.Bound> buildBound(boolean isStart, boolean isInclusive);
 +
 +    /**
 +     * Checks if some elements can still be added to the clusterings.
 +     *
 +     * @return <code>true</code> if it is possible to add more elements to the clusterings, <code>false</code> otherwise.
 +     */
 +    public boolean hasRemaining()
      {
 -        built = true;
 +        return remainingCount() > 0;
 +    }
  
 -        if (hasMissingElements)
 -            return BTreeSet.empty(comparator);
 +    /**
 +     * Specialization of MultiCBuilder when we know only one clustering/bound is created.
 +     */
 +    private static class OneClusteringBuilder extends MultiCBuilder
 +    {
 +        /**
 +         * The elements of the clusterings
 +         */
 +        private final ByteBuffer[] elements;
  
 -        CBuilder builder = CBuilder.create(comparator);
 +        public OneClusteringBuilder(ClusteringComparator comparator)
 +        {
 +            super(comparator);
 +            this.elements = new ByteBuffer[comparator.size()];
 +        }
  
 -        if (elementsList.isEmpty())
 -            return BTreeSet.of(comparator, builder.buildBound(isStart, isInclusive));
 +        public MultiCBuilder addElementToAll(ByteBuffer value)
 +        {
 +            checkUpdateable();
  
 -        // Use a TreeSet to sort and eliminate duplicates
 -        BTreeSet.Builder<Slice.Bound> set = BTreeSet.builder(comparator);
 +            if (value == null)
 +                containsNull = true;
 +            if (value == ByteBufferUtil.UNSET_BYTE_BUFFER)
 +                containsUnset = true;
  
 -        // The first column of the slice might not be the first clustering column (e.g. clustering_0 = ? AND (clustering_1, clustering_2) >= (?, ?)
 -        int offset = columnDefs.get(0).position();
 +            elements[size++] = value;
 +            return this;
 +        }
  
 -        for (int i = 0, m = elementsList.size(); i < m; i++)
 +        public MultiCBuilder addEachElementToAll(List<ByteBuffer> values)
          {
 -            List<ByteBuffer> elements = elementsList.get(i);
 -
 -            // Handle the no bound case
 -            if (elements.size() == offset)
 +            if (values.isEmpty())
              {
 -                set.add(builder.buildBoundWith(elements, isStart, true));
 -                continue;
 +                hasMissingElements = true;
 +                return this;
              }
  
 -            // In the case of mixed order columns, we will have some extra slices where the columns change directions.
 -            // For example: if we have clustering_0 DESC and clustering_1 ASC a slice like (clustering_0, clustering_1) > (1, 2)
 -            // will produce 2 slices: [BOTTOM, 1) and (1.2, 1]
 -            // So, the END bound will return 2 bounds with the same values 1
 -            ColumnDefinition lastColumn = columnDefs.get(columnDefs.size() - 1);
 -            if (elements.size() <= lastColumn.position() && i < m - 1 && elements.equals(elementsList.get(i + 1)))
 +            assert values.size() == 1;
 +
 +            return addElementToAll(values.get(0));
 +        }
 +
 +        public MultiCBuilder addAllElementsToAll(List<List<ByteBuffer>> values)
 +        {
 +            if (values.isEmpty())
              {
 -                set.add(builder.buildBoundWith(elements, isStart, false));
 -                set.add(builder.buildBoundWith(elementsList.get(i++), isStart, true));
 -                continue;
 +                hasMissingElements = true;
 +                return this;
              }
  
 -            // Handle the normal bounds
 -            ColumnDefinition column = columnDefs.get(elements.size() - 1 - offset);
 -            set.add(builder.buildBoundWith(elements, isStart, column.isReversedType() ? isOtherBoundInclusive : isInclusive));
 +            assert values.size() == 1;
 +            return addEachElementToAll(values.get(0));
          }
 -        return set.build();
 -    }
  
 -    public NavigableSet<Slice.Bound> buildBound(boolean isStart, boolean isInclusive)
 -    {
 -        built = true;
 -
 -        if (hasMissingElements)
 -            return BTreeSet.empty(comparator);
 +        public NavigableSet<Clustering> build()
 +        {
 +            built = true;
  
 -        CBuilder builder = CBuilder.create(comparator);
 +            if (hasMissingElements)
 +                return BTreeSet.empty(comparator);
  
 -        if (elementsList.isEmpty())
 -            return BTreeSet.of(comparator, builder.buildBound(isStart, isInclusive));
 +            return BTreeSet.of(comparator, size == 0 ? Clustering.EMPTY : Clustering.make(elements));
 +        }
  
 -        // Use a TreeSet to sort and eliminate duplicates
 -        BTreeSet.Builder<Slice.Bound> set = BTreeSet.builder(comparator);
++        @Override
++        public NavigableSet<Bound> buildBoundForSlice(boolean isStart,
++                                                      boolean isInclusive,
++                                                      boolean isOtherBoundInclusive,
++                                                      List<ColumnDefinition> columnDefs)
++        {
++            return buildBound(isStart, columnDefs.get(0).isReversedType() ? isOtherBoundInclusive : isInclusive);
++        }
+ 
 -        for (int i = 0, m = elementsList.size(); i < m; i++)
 +        public NavigableSet<Slice.Bound> buildBound(boolean isStart, boolean isInclusive)
          {
 -            List<ByteBuffer> elements = elementsList.get(i);
 -            set.add(builder.buildBoundWith(elements, isStart, isInclusive));
 +            built = true;
 +
 +            if (hasMissingElements)
 +                return BTreeSet.empty(comparator);
 +
 +            if (size == 0)
 +                return BTreeSet.of(comparator, isStart ? Slice.Bound.BOTTOM : Slice.Bound.TOP);
 +
 +            ByteBuffer[] newValues = size == elements.length
 +                                   ? elements
 +                                   : Arrays.copyOf(elements, size);
 +
 +            return BTreeSet.of(comparator, Slice.Bound.create(Slice.Bound.boundKind(isStart, isInclusive), newValues));
          }
 -        return set.build();
      }
  
      /**
 -     * Checks if some elements can still be added to the clusterings.
 -     *
 -     * @return <code>true</code> if it is possible to add more elements to the clusterings, <code>false</code> otherwise.
 +     * MultiCBuilder implementation actually supporting the creation of multiple clustering/bound.
       */
 -    public boolean hasRemaining()
 +    private static class MultiClusteringBuilder extends MultiCBuilder
      {
 -        return remainingCount() > 0;
 -    }
 +        /**
 +         * The elements of the clusterings
 +         */
 +        private final List<List<ByteBuffer>> elementsList = new ArrayList<>();
  
 -    private void checkUpdateable()
 -    {
 -        if (!hasRemaining() || built)
 -            throw new IllegalStateException("this builder cannot be updated anymore");
 +        public MultiClusteringBuilder(ClusteringComparator comparator)
 +        {
 +            super(comparator);
 +        }
 +
 +        public MultiCBuilder addElementToAll(ByteBuffer value)
 +        {
 +            checkUpdateable();
 +
 +            if (elementsList.isEmpty())
 +                elementsList.add(new ArrayList<ByteBuffer>());
 +
 +            if (value == null)
 +                containsNull = true;
 +            else if (value == ByteBufferUtil.UNSET_BYTE_BUFFER)
 +                containsUnset = true;
 +
 +            for (int i = 0, m = elementsList.size(); i < m; i++)
 +                elementsList.get(i).add(value);
 +
 +            size++;
 +            return this;
 +        }
 +
 +        public MultiCBuilder addEachElementToAll(List<ByteBuffer> values)
 +        {
 +            checkUpdateable();
 +
 +            if (elementsList.isEmpty())
 +                elementsList.add(new ArrayList<ByteBuffer>());
 +
 +            if (values.isEmpty())
 +            {
 +                hasMissingElements = true;
 +            }
 +            else
 +            {
 +                for (int i = 0, m = elementsList.size(); i < m; i++)
 +                {
 +                    List<ByteBuffer> oldComposite = elementsList.remove(0);
 +
 +                    for (int j = 0, n = values.size(); j < n; j++)
 +                    {
 +                        List<ByteBuffer> newComposite = new ArrayList<>(oldComposite);
 +                        elementsList.add(newComposite);
 +
 +                        ByteBuffer value = values.get(j);
 +
 +                        if (value == null)
 +                            containsNull = true;
 +                        if (value == ByteBufferUtil.UNSET_BYTE_BUFFER)
 +                            containsUnset = true;
 +
 +                        newComposite.add(values.get(j));
 +                    }
 +                }
 +            }
 +            size++;
 +            return this;
 +        }
 +
 +        public MultiCBuilder addAllElementsToAll(List<List<ByteBuffer>> values)
 +        {
 +            checkUpdateable();
 +
 +            if (elementsList.isEmpty())
 +                elementsList.add(new ArrayList<ByteBuffer>());
 +
 +            if (values.isEmpty())
 +            {
 +                hasMissingElements = true;
 +            }
 +            else
 +            {
 +                for (int i = 0, m = elementsList.size(); i < m; i++)
 +                {
 +                    List<ByteBuffer> oldComposite = elementsList.remove(0);
 +
 +                    for (int j = 0, n = values.size(); j < n; j++)
 +                    {
 +                        List<ByteBuffer> newComposite = new ArrayList<>(oldComposite);
 +                        elementsList.add(newComposite);
 +
 +                        List<ByteBuffer> value = values.get(j);
 +
-                         if (value.isEmpty())
-                             hasMissingElements = true;
- 
 +                        if (value.contains(null))
 +                            containsNull = true;
 +                        if (value.contains(ByteBufferUtil.UNSET_BYTE_BUFFER))
 +                            containsUnset = true;
 +
 +                        newComposite.addAll(value);
 +                    }
 +                }
 +                size += values.get(0).size();
 +            }
 +            return this;
 +        }
 +
 +        public NavigableSet<Clustering> build()
 +        {
 +            built = true;
 +
 +            if (hasMissingElements)
 +                return BTreeSet.empty(comparator);
 +
 +            CBuilder builder = CBuilder.create(comparator);
 +
 +            if (elementsList.isEmpty())
 +                return BTreeSet.of(builder.comparator(), builder.build());
 +
 +            BTreeSet.Builder<Clustering> set = BTreeSet.builder(builder.comparator());
 +            for (int i = 0, m = elementsList.size(); i < m; i++)
 +            {
 +                List<ByteBuffer> elements = elementsList.get(i);
 +                set.add(builder.buildWith(elements));
 +            }
 +            return set.build();
 +        }
 +
++        public NavigableSet<Slice.Bound> buildBoundForSlice(boolean isStart,
++                                                            boolean isInclusive,
++                                                            boolean isOtherBoundInclusive,
++                                                            List<ColumnDefinition> columnDefs)
++        {
++            built = true;
++
++            if (hasMissingElements)
++                return BTreeSet.empty(comparator);
++
++            CBuilder builder = CBuilder.create(comparator);
++
++            if (elementsList.isEmpty())
++                return BTreeSet.of(comparator, builder.buildBound(isStart, isInclusive));
++
++            // Use a TreeSet to sort and eliminate duplicates
++            BTreeSet.Builder<Slice.Bound> set = BTreeSet.builder(comparator);
++
++            // The first column of the slice might not be the first clustering column (e.g. clustering_0 = ? AND (clustering_1, clustering_2) >= (?, ?)
++            int offset = columnDefs.get(0).position();
++
++            for (int i = 0, m = elementsList.size(); i < m; i++)
++            {
++                List<ByteBuffer> elements = elementsList.get(i);
++
++                // Handle the no bound case
++                if (elements.size() == offset)
++                {
++                    set.add(builder.buildBoundWith(elements, isStart, true));
++                    continue;
++                }
++
++                // In the case of mixed order columns, we will have some extra slices where the columns change directions.
++                // For example: if we have clustering_0 DESC and clustering_1 ASC a slice like (clustering_0, clustering_1) > (1, 2)
++                // will produce 2 slices: [BOTTOM, 1) and (1.2, 1]
++                // So, the END bound will return 2 bounds with the same values 1
++                ColumnDefinition lastColumn = columnDefs.get(columnDefs.size() - 1);
++                if (elements.size() <= lastColumn.position() && i < m - 1 && elements.equals(elementsList.get(i + 1)))
++                {
++                    set.add(builder.buildBoundWith(elements, isStart, false));
++                    set.add(builder.buildBoundWith(elementsList.get(i++), isStart, true));
++                    continue;
++                }
++
++                // Handle the normal bounds
++                ColumnDefinition column = columnDefs.get(elements.size() - 1 - offset);
++                set.add(builder.buildBoundWith(elements, isStart, column.isReversedType() ? isOtherBoundInclusive : isInclusive));
++            }
++            return set.build();
++        }
++
 +        public NavigableSet<Slice.Bound> buildBound(boolean isStart, boolean isInclusive)
 +        {
 +            built = true;
 +
 +            if (hasMissingElements)
 +                return BTreeSet.empty(comparator);
 +
 +            CBuilder builder = CBuilder.create(comparator);
 +
 +            if (elementsList.isEmpty())
 +                return BTreeSet.of(comparator, builder.buildBound(isStart, isInclusive));
 +
 +            // Use a TreeSet to sort and eliminate duplicates
 +            BTreeSet.Builder<Slice.Bound> set = BTreeSet.builder(comparator);
 +
 +            for (int i = 0, m = elementsList.size(); i < m; i++)
 +            {
 +                List<ByteBuffer> elements = elementsList.get(i);
 +                set.add(builder.buildBoundWith(elements, isStart, isInclusive));
 +            }
 +            return set.build();
 +        }
      }
  }


[2/6] cassandra git commit: Fix SELECT on tuple relations for mixed ASC/DESC clustering order

Posted by bl...@apache.org.
http://git-wip-us.apache.org/repos/asf/cassandra/blob/90fc8969/test/unit/org/apache/cassandra/cql3/restrictions/PrimaryKeyRestrictionSetTest.java
----------------------------------------------------------------------
diff --git a/test/unit/org/apache/cassandra/cql3/restrictions/PrimaryKeyRestrictionSetTest.java b/test/unit/org/apache/cassandra/cql3/restrictions/PrimaryKeyRestrictionSetTest.java
index 05d6e98..1deeb9e 100644
--- a/test/unit/org/apache/cassandra/cql3/restrictions/PrimaryKeyRestrictionSetTest.java
+++ b/test/unit/org/apache/cassandra/cql3/restrictions/PrimaryKeyRestrictionSetTest.java
@@ -31,11 +31,10 @@ import org.apache.cassandra.cql3.statements.Bound;
 import org.apache.cassandra.db.ColumnFamilyType;
 import org.apache.cassandra.db.composites.Composite;
 import org.apache.cassandra.db.composites.Composite.EOC;
-import org.apache.cassandra.db.composites.Composites;
 import org.apache.cassandra.db.composites.CompoundSparseCellNameType;
 import org.apache.cassandra.db.marshal.AbstractType;
 import org.apache.cassandra.db.marshal.Int32Type;
-import org.apache.cassandra.exceptions.InvalidRequestException;
+import org.apache.cassandra.db.marshal.ReversedType;
 import org.apache.cassandra.utils.ByteBufferUtil;
 
 import static java.util.Arrays.asList;
@@ -45,9 +44,9 @@ import static org.junit.Assert.assertTrue;
 public class PrimaryKeyRestrictionSetTest
 {
     @Test
-    public void testBoundsAsCompositesWithNoRestrictions() throws InvalidRequestException
+    public void testBoundsAsCompositesWithNoRestrictions()
     {
-        CFMetaData cfMetaData = newCFMetaData(1);
+        CFMetaData cfMetaData = newCFMetaData(Sort.ASC);
 
         PrimaryKeyRestrictions restrictions = new PrimaryKeyRestrictionSet(cfMetaData.comparator);
 
@@ -64,9 +63,9 @@ public class PrimaryKeyRestrictionSetTest
      * Test 'clustering_0 = 1' with only one clustering column
      */
     @Test
-    public void testBoundsAsCompositesWithOneEqRestrictionsAndOneClusteringColumn() throws InvalidRequestException
+    public void testBoundsAsCompositesWithOneEqRestrictionsAndOneClusteringColumn()
     {
-        CFMetaData cfMetaData = newCFMetaData(1);
+        CFMetaData cfMetaData = newCFMetaData(Sort.ASC);
 
         ByteBuffer clustering_0 = ByteBufferUtil.bytes(1);
         Restriction eq = newSingleEq(cfMetaData, 0, clustering_0);
@@ -87,9 +86,9 @@ public class PrimaryKeyRestrictionSetTest
      * Test 'clustering_1 = 1' with 2 clustering columns
      */
     @Test
-    public void testBoundsAsCompositesWithOneEqRestrictionsAndTwoClusteringColumns() throws InvalidRequestException
+    public void testBoundsAsCompositesWithOneEqRestrictionsAndTwoClusteringColumns()
     {
-        CFMetaData cfMetaData = newCFMetaData(2);
+        CFMetaData cfMetaData = newCFMetaData(Sort.ASC, Sort.ASC);
 
         ByteBuffer clustering_0 = ByteBufferUtil.bytes(1);
         Restriction eq = newSingleEq(cfMetaData, 0, clustering_0);
@@ -110,13 +109,13 @@ public class PrimaryKeyRestrictionSetTest
      * Test 'clustering_0 IN (1, 2, 3)' with only one clustering column
      */
     @Test
-    public void testBoundsAsCompositesWithOneInRestrictionsAndOneClusteringColumn() throws InvalidRequestException
+    public void testBoundsAsCompositesWithOneInRestrictionsAndOneClusteringColumn()
     {
         ByteBuffer value1 = ByteBufferUtil.bytes(1);
         ByteBuffer value2 = ByteBufferUtil.bytes(2);
         ByteBuffer value3 = ByteBufferUtil.bytes(3);
 
-        CFMetaData cfMetaData = newCFMetaData(2);
+        CFMetaData cfMetaData = newCFMetaData(Sort.ASC, Sort.ASC);
 
         Restriction in = newSingleIN(cfMetaData, 0, value1, value2, value3);
 
@@ -140,9 +139,9 @@ public class PrimaryKeyRestrictionSetTest
      * Test slice restriction (e.g 'clustering_0 > 1') with only one clustering column
      */
     @Test
-    public void testBoundsAsCompositesWithSliceRestrictionsAndOneClusteringColumn() throws InvalidRequestException
+    public void testBoundsAsCompositesWithSliceRestrictionsAndOneClusteringColumn()
     {
-        CFMetaData cfMetaData = newCFMetaData(1);
+        CFMetaData cfMetaData = newCFMetaData(Sort.ASC, Sort.ASC);
 
         ByteBuffer value1 = ByteBufferUtil.bytes(1);
         ByteBuffer value2 = ByteBufferUtil.bytes(2);
@@ -223,12 +222,98 @@ public class PrimaryKeyRestrictionSetTest
     }
 
     /**
+     * Test slice restriction (e.g 'clustering_0 > 1') with only one descending clustering column
+     */
+    @Test
+    public void testBoundsAsCompositesWithSliceRestrictionsAndOneDescendingClusteringColumn()
+    {
+        CFMetaData cfMetaData = newCFMetaData(Sort.DESC, Sort.DESC);
+
+        ByteBuffer value1 = ByteBufferUtil.bytes(1);
+        ByteBuffer value2 = ByteBufferUtil.bytes(2);
+
+        Restriction slice = newSingleSlice(cfMetaData, 0, Bound.START, false, value1);
+        PrimaryKeyRestrictions restrictions = new PrimaryKeyRestrictionSet(cfMetaData.comparator);
+        restrictions = restrictions.mergeWith(slice);
+
+        List<Composite> bounds = restrictions.boundsAsComposites(Bound.START, QueryOptions.DEFAULT);
+        assertEquals(1, bounds.size());
+        assertEmptyComposite(bounds.get(0));
+
+        bounds = restrictions.boundsAsComposites(Bound.END, QueryOptions.DEFAULT);
+        assertEquals(1, bounds.size());
+        assertComposite(bounds.get(0), value1, EOC.START);
+
+        slice = newSingleSlice(cfMetaData, 0, Bound.START, true, value1);
+        restrictions = new PrimaryKeyRestrictionSet(cfMetaData.comparator);
+        restrictions = restrictions.mergeWith(slice);
+
+        bounds = restrictions.boundsAsComposites(Bound.START, QueryOptions.DEFAULT);
+        assertEquals(1, bounds.size());
+        assertEmptyComposite(bounds.get(0));
+
+        bounds = restrictions.boundsAsComposites(Bound.END, QueryOptions.DEFAULT);
+        assertEquals(1, bounds.size());
+        assertComposite(bounds.get(0), value1, EOC.END);
+
+        slice = newSingleSlice(cfMetaData, 0, Bound.END, true, value1);
+        restrictions = new PrimaryKeyRestrictionSet(cfMetaData.comparator);
+        restrictions = restrictions.mergeWith(slice);
+
+        bounds = restrictions.boundsAsComposites(Bound.START, QueryOptions.DEFAULT);
+        assertEquals(1, bounds.size());
+        assertComposite(bounds.get(0), value1, EOC.NONE);
+
+        bounds = restrictions.boundsAsComposites(Bound.END, QueryOptions.DEFAULT);
+        assertEquals(1, bounds.size());
+        assertEmptyComposite(bounds.get(0));
+
+        slice = newSingleSlice(cfMetaData, 0, Bound.END, false, value1);
+        restrictions = new PrimaryKeyRestrictionSet(cfMetaData.comparator);
+        restrictions = restrictions.mergeWith(slice);
+
+        bounds = restrictions.boundsAsComposites(Bound.START, QueryOptions.DEFAULT);
+        assertEquals(1, bounds.size());
+        assertComposite(bounds.get(0), value1, EOC.END);
+
+        bounds = restrictions.boundsAsComposites(Bound.END, QueryOptions.DEFAULT);
+        assertEquals(1, bounds.size());
+        assertEmptyComposite(bounds.get(0));
+
+        slice = newSingleSlice(cfMetaData, 0, Bound.START, false, value1);
+        Restriction slice2 = newSingleSlice(cfMetaData, 0, Bound.END, false, value2);
+        restrictions = new PrimaryKeyRestrictionSet(cfMetaData.comparator);
+        restrictions = restrictions.mergeWith(slice).mergeWith(slice2);
+
+        bounds = restrictions.boundsAsComposites(Bound.START, QueryOptions.DEFAULT);
+        assertEquals(1, bounds.size());
+        assertComposite(bounds.get(0), value2, EOC.END);
+
+        bounds = restrictions.boundsAsComposites(Bound.END, QueryOptions.DEFAULT);
+        assertEquals(1, bounds.size());
+        assertComposite(bounds.get(0), value1, EOC.START);
+
+        slice = newSingleSlice(cfMetaData, 0, Bound.START, true, value1);
+        slice2 = newSingleSlice(cfMetaData, 0, Bound.END, true, value2);
+        restrictions = new PrimaryKeyRestrictionSet(cfMetaData.comparator);
+        restrictions = restrictions.mergeWith(slice).mergeWith(slice2);
+
+        bounds = restrictions.boundsAsComposites(Bound.START, QueryOptions.DEFAULT);
+        assertEquals(1, bounds.size());
+        assertComposite(bounds.get(0), value2, EOC.NONE);
+
+        bounds = restrictions.boundsAsComposites(Bound.END, QueryOptions.DEFAULT);
+        assertEquals(1, bounds.size());
+        assertComposite(bounds.get(0), value1, EOC.END);
+    }
+
+    /**
      * Test 'clustering_0 = 1 AND clustering_1 IN (1, 2, 3)'
      */
     @Test
-    public void testBoundsAsCompositesWithEqAndInRestrictions() throws InvalidRequestException
+    public void testBoundsAsCompositesWithEqAndInRestrictions()
     {
-        CFMetaData cfMetaData = newCFMetaData(2);
+        CFMetaData cfMetaData = newCFMetaData(Sort.ASC, Sort.ASC);
 
         ByteBuffer value1 = ByteBufferUtil.bytes(1);
         ByteBuffer value2 = ByteBufferUtil.bytes(2);
@@ -255,9 +340,9 @@ public class PrimaryKeyRestrictionSetTest
      * Test equal and slice restrictions (e.g 'clustering_0 = 0 clustering_1 > 1')
      */
     @Test
-    public void testBoundsAsCompositesWithEqAndSliceRestrictions() throws InvalidRequestException
+    public void testBoundsAsCompositesWithEqAndSliceRestrictions()
     {
-        CFMetaData cfMetaData = newCFMetaData(2);
+        CFMetaData cfMetaData = newCFMetaData(Sort.ASC, Sort.ASC);
 
         ByteBuffer value1 = ByteBufferUtil.bytes(1);
         ByteBuffer value2 = ByteBufferUtil.bytes(2);
@@ -286,218 +371,909 @@ public class PrimaryKeyRestrictionSetTest
         assertComposite(bounds.get(0), value3, value1, EOC.NONE);
 
         bounds = restrictions.boundsAsComposites(Bound.END, QueryOptions.DEFAULT);
-        assertEquals(1, bounds.size());
-        assertComposite(bounds.get(0), value3, EOC.END);
+        assertEquals(1, bounds.size());
+        assertComposite(bounds.get(0), value3, EOC.END);
+
+        slice = newSingleSlice(cfMetaData, 1, Bound.END, true, value1);
+        restrictions = new PrimaryKeyRestrictionSet(cfMetaData.comparator);
+        restrictions = restrictions.mergeWith(eq).mergeWith(slice);
+
+        bounds = restrictions.boundsAsComposites(Bound.START, QueryOptions.DEFAULT);
+        assertEquals(1, bounds.size());
+        assertComposite(bounds.get(0), value3, EOC.START);
+
+        bounds = restrictions.boundsAsComposites(Bound.END, QueryOptions.DEFAULT);
+        assertEquals(1, bounds.size());
+        assertComposite(bounds.get(0), value3, value1, EOC.END);
+
+        slice = newSingleSlice(cfMetaData, 1, Bound.END, false, value1);
+        restrictions = new PrimaryKeyRestrictionSet(cfMetaData.comparator);
+        restrictions = restrictions.mergeWith(eq).mergeWith(slice);
+
+        bounds = restrictions.boundsAsComposites(Bound.START, QueryOptions.DEFAULT);
+        assertEquals(1, bounds.size());
+        assertComposite(bounds.get(0), value3, EOC.START);
+
+        bounds = restrictions.boundsAsComposites(Bound.END, QueryOptions.DEFAULT);
+        assertEquals(1, bounds.size());
+        assertComposite(bounds.get(0), value3, value1, EOC.START);
+
+        slice = newSingleSlice(cfMetaData, 1, Bound.START, false, value1);
+        Restriction slice2 = newSingleSlice(cfMetaData, 1, Bound.END, false, value2);
+        restrictions = new PrimaryKeyRestrictionSet(cfMetaData.comparator);
+        restrictions = restrictions.mergeWith(eq).mergeWith(slice).mergeWith(slice2);
+
+        bounds = restrictions.boundsAsComposites(Bound.START, QueryOptions.DEFAULT);
+        assertEquals(1, bounds.size());
+        assertComposite(bounds.get(0), value3, value1, EOC.END);
+
+        bounds = restrictions.boundsAsComposites(Bound.END, QueryOptions.DEFAULT);
+        assertEquals(1, bounds.size());
+        assertComposite(bounds.get(0), value3, value2, EOC.START);
+
+        slice = newSingleSlice(cfMetaData, 1, Bound.START, true, value1);
+        slice2 = newSingleSlice(cfMetaData, 1, Bound.END, true, value2);
+        restrictions = new PrimaryKeyRestrictionSet(cfMetaData.comparator);
+        restrictions = restrictions.mergeWith(eq).mergeWith(slice).mergeWith(slice2);
+
+        bounds = restrictions.boundsAsComposites(Bound.START, QueryOptions.DEFAULT);
+        assertEquals(1, bounds.size());
+        assertComposite(bounds.get(0), value3, value1, EOC.NONE);
+
+        bounds = restrictions.boundsAsComposites(Bound.END, QueryOptions.DEFAULT);
+        assertEquals(1, bounds.size());
+        assertComposite(bounds.get(0), value3, value2, EOC.END);
+    }
+
+    /**
+     * Test '(clustering_0, clustering_1) = (1, 2)' with two clustering column
+     */
+    @Test
+    public void testBoundsAsCompositesWithMultiEqRestrictions()
+    {
+        CFMetaData cfMetaData = newCFMetaData(Sort.ASC, Sort.ASC);
+
+        ByteBuffer value1 = ByteBufferUtil.bytes(1);
+        ByteBuffer value2 = ByteBufferUtil.bytes(2);
+        Restriction eq = newMultiEq(cfMetaData, 0, value1, value2);
+        PrimaryKeyRestrictions restrictions = new PrimaryKeyRestrictionSet(cfMetaData.comparator);
+        restrictions = restrictions.mergeWith(eq);
+
+        List<Composite> bounds = restrictions.boundsAsComposites(Bound.START, QueryOptions.DEFAULT);
+        assertEquals(1, bounds.size());
+        assertComposite(bounds.get(0), value1, value2, EOC.START);
+
+        bounds = restrictions.boundsAsComposites(Bound.END, QueryOptions.DEFAULT);
+        assertEquals(1, bounds.size());
+        assertComposite(bounds.get(0), value1, value2, EOC.END);
+    }
+
+    /**
+     * Test '(clustering_0, clustering_1) IN ((1, 2), (2, 3))' with two clustering column
+     */
+    @Test
+    public void testBoundsAsCompositesWithMultiInRestrictions()
+    {
+        CFMetaData cfMetaData = newCFMetaData(Sort.ASC, Sort.ASC);
+
+        ByteBuffer value1 = ByteBufferUtil.bytes(1);
+        ByteBuffer value2 = ByteBufferUtil.bytes(2);
+        ByteBuffer value3 = ByteBufferUtil.bytes(3);
+        Restriction in = newMultiIN(cfMetaData, 0, asList(value1, value2), asList(value2, value3));
+        PrimaryKeyRestrictions restrictions = new PrimaryKeyRestrictionSet(cfMetaData.comparator);
+        restrictions = restrictions.mergeWith(in);
+
+        List<Composite> bounds = restrictions.boundsAsComposites(Bound.START, QueryOptions.DEFAULT);
+        assertEquals(2, bounds.size());
+        assertComposite(bounds.get(0), value1, value2, EOC.START);
+        assertComposite(bounds.get(1), value2, value3, EOC.START);
+
+        bounds = restrictions.boundsAsComposites(Bound.END, QueryOptions.DEFAULT);
+        assertEquals(2, bounds.size());
+        assertComposite(bounds.get(0), value1, value2, EOC.END);
+        assertComposite(bounds.get(1), value2, value3, EOC.END);
+    }
+
+    /**
+     * Test multi-column slice restrictions (e.g '(clustering_0) > (1)') with only one clustering column
+     */
+    @Test
+    public void testBoundsAsCompositesWithMultiSliceRestrictionsWithOneClusteringColumn()
+    {
+        CFMetaData cfMetaData = newCFMetaData(Sort.ASC);
+
+        ByteBuffer value1 = ByteBufferUtil.bytes(1);
+        ByteBuffer value2 = ByteBufferUtil.bytes(2);
+
+        Restriction slice = newMultiSlice(cfMetaData, 0, Bound.START, false, value1);
+        PrimaryKeyRestrictions restrictions = new PrimaryKeyRestrictionSet(cfMetaData.comparator);
+        restrictions = restrictions.mergeWith(slice);
+
+        List<Composite> bounds = restrictions.boundsAsComposites(Bound.START, QueryOptions.DEFAULT);
+        assertEquals(1, bounds.size());
+        assertComposite(bounds.get(0), value1, EOC.END);
+
+        bounds = restrictions.boundsAsComposites(Bound.END, QueryOptions.DEFAULT);
+        assertEquals(1, bounds.size());
+        assertEmptyComposite(bounds.get(0));
+
+        slice = newMultiSlice(cfMetaData, 0, Bound.START, true, value1);
+        restrictions = new PrimaryKeyRestrictionSet(cfMetaData.comparator);
+        restrictions = restrictions.mergeWith(slice);
+
+        bounds = restrictions.boundsAsComposites(Bound.START, QueryOptions.DEFAULT);
+        assertEquals(1, bounds.size());
+        assertComposite(bounds.get(0), value1, EOC.NONE);
+
+        bounds = restrictions.boundsAsComposites(Bound.END, QueryOptions.DEFAULT);
+        assertEquals(1, bounds.size());
+        assertEmptyComposite(bounds.get(0));
+
+        slice = newMultiSlice(cfMetaData, 0, Bound.END, true, value1);
+        restrictions = new PrimaryKeyRestrictionSet(cfMetaData.comparator);
+        restrictions = restrictions.mergeWith(slice);
+
+        bounds = restrictions.boundsAsComposites(Bound.START, QueryOptions.DEFAULT);
+        assertEquals(1, bounds.size());
+        assertEmptyComposite(bounds.get(0));
+
+        bounds = restrictions.boundsAsComposites(Bound.END, QueryOptions.DEFAULT);
+        assertEquals(1, bounds.size());
+        assertComposite(bounds.get(0), value1, EOC.END);
+
+        slice = newMultiSlice(cfMetaData, 0, Bound.END, false, value1);
+        restrictions = new PrimaryKeyRestrictionSet(cfMetaData.comparator);
+        restrictions = restrictions.mergeWith(slice);
+
+        bounds = restrictions.boundsAsComposites(Bound.START, QueryOptions.DEFAULT);
+        assertEquals(1, bounds.size());
+        assertEmptyComposite(bounds.get(0));
+
+        bounds = restrictions.boundsAsComposites(Bound.END, QueryOptions.DEFAULT);
+        assertEquals(1, bounds.size());
+        assertComposite(bounds.get(0), value1, EOC.START);
+
+        slice = newMultiSlice(cfMetaData, 0, Bound.START, false, value1);
+        Restriction slice2 = newMultiSlice(cfMetaData, 0, Bound.END, false, value2);
+        restrictions = new PrimaryKeyRestrictionSet(cfMetaData.comparator);
+        restrictions = restrictions.mergeWith(slice).mergeWith(slice2);
+
+        bounds = restrictions.boundsAsComposites(Bound.START, QueryOptions.DEFAULT);
+        assertEquals(1, bounds.size());
+        assertComposite(bounds.get(0), value1, EOC.END);
+
+        bounds = restrictions.boundsAsComposites(Bound.END, QueryOptions.DEFAULT);
+        assertEquals(1, bounds.size());
+        assertComposite(bounds.get(0), value2, EOC.START);
+
+        slice = newMultiSlice(cfMetaData, 0, Bound.START, true, value1);
+        slice2 = newMultiSlice(cfMetaData, 0, Bound.END, true, value2);
+        restrictions = new PrimaryKeyRestrictionSet(cfMetaData.comparator);
+        restrictions = restrictions.mergeWith(slice).mergeWith(slice2);
+
+        bounds = restrictions.boundsAsComposites(Bound.START, QueryOptions.DEFAULT);
+        assertEquals(1, bounds.size());
+        assertComposite(bounds.get(0), value1, EOC.NONE);
+
+        bounds = restrictions.boundsAsComposites(Bound.END, QueryOptions.DEFAULT);
+        assertEquals(1, bounds.size());
+        assertComposite(bounds.get(0), value2, EOC.END);
+    }
+
+    /**
+     * Test multi-column slice restrictions (e.g '(clustering_0) > (1)') with only one clustering column in reverse
+     * order
+     */
+    @Test
+    public void testBoundsAsCompositesWithMultiSliceRestrictionsWithOneDescendingClusteringColumn()
+    {
+        CFMetaData cfMetaData = newCFMetaData(Sort.DESC);
+
+        ByteBuffer value1 = ByteBufferUtil.bytes(1);
+        ByteBuffer value2 = ByteBufferUtil.bytes(2);
+
+        Restriction slice = newMultiSlice(cfMetaData, 0, Bound.START, false, value1);
+        PrimaryKeyRestrictions restrictions = new PrimaryKeyRestrictionSet(cfMetaData.comparator);
+        restrictions = restrictions.mergeWith(slice);
+
+        List<Composite> bounds = restrictions.boundsAsComposites(Bound.START, QueryOptions.DEFAULT);
+        assertEquals(1, bounds.size());
+        assertEmptyComposite(bounds.get(0));
+
+        bounds = restrictions.boundsAsComposites(Bound.END, QueryOptions.DEFAULT);
+        assertEquals(1, bounds.size());
+        assertComposite(bounds.get(0), value1, EOC.START);
+
+        slice = newMultiSlice(cfMetaData, 0, Bound.START, true, value1);
+        restrictions = new PrimaryKeyRestrictionSet(cfMetaData.comparator);
+        restrictions = restrictions.mergeWith(slice);
+
+        bounds = restrictions.boundsAsComposites(Bound.START, QueryOptions.DEFAULT);
+        assertEquals(1, bounds.size());
+        assertEmptyComposite(bounds.get(0));
+
+        bounds = restrictions.boundsAsComposites(Bound.END, QueryOptions.DEFAULT);
+        assertEquals(1, bounds.size());
+        assertComposite(bounds.get(0), value1, EOC.END);
+
+        slice = newMultiSlice(cfMetaData, 0, Bound.END, true, value1);
+        restrictions = new PrimaryKeyRestrictionSet(cfMetaData.comparator);
+        restrictions = restrictions.mergeWith(slice);
+
+        bounds = restrictions.boundsAsComposites(Bound.START, QueryOptions.DEFAULT);
+        assertEquals(1, bounds.size());
+        assertComposite(bounds.get(0), value1, EOC.NONE);
+
+        bounds = restrictions.boundsAsComposites(Bound.END, QueryOptions.DEFAULT);
+        assertEquals(1, bounds.size());
+        assertEmptyComposite(bounds.get(0));
+
+        slice = newMultiSlice(cfMetaData, 0, Bound.END, false, value1);
+        restrictions = new PrimaryKeyRestrictionSet(cfMetaData.comparator);
+        restrictions = restrictions.mergeWith(slice);
+
+        bounds = restrictions.boundsAsComposites(Bound.START, QueryOptions.DEFAULT);
+        assertEquals(1, bounds.size());
+        assertComposite(bounds.get(0), value1, EOC.END);
+
+        bounds = restrictions.boundsAsComposites(Bound.END, QueryOptions.DEFAULT);
+        assertEquals(1, bounds.size());
+        assertEmptyComposite(bounds.get(0));
+
+        slice = newMultiSlice(cfMetaData, 0, Bound.START, false, value1);
+        Restriction slice2 = newMultiSlice(cfMetaData, 0, Bound.END, false, value2);
+        restrictions = new PrimaryKeyRestrictionSet(cfMetaData.comparator);
+        restrictions = restrictions.mergeWith(slice).mergeWith(slice2);
+
+        bounds = restrictions.boundsAsComposites(Bound.START, QueryOptions.DEFAULT);
+        assertEquals(1, bounds.size());
+        assertComposite(bounds.get(0), value2, EOC.END);
+
+        bounds = restrictions.boundsAsComposites(Bound.END, QueryOptions.DEFAULT);
+        assertEquals(1, bounds.size());
+        assertComposite(bounds.get(0), value1, EOC.START);
+
+        slice = newMultiSlice(cfMetaData, 0, Bound.START, true, value1);
+        slice2 = newMultiSlice(cfMetaData, 0, Bound.END, true, value2);
+        restrictions = new PrimaryKeyRestrictionSet(cfMetaData.comparator);
+        restrictions = restrictions.mergeWith(slice).mergeWith(slice2);
+
+        bounds = restrictions.boundsAsComposites(Bound.START, QueryOptions.DEFAULT);
+        assertEquals(1, bounds.size());
+        assertComposite(bounds.get(0), value2, EOC.NONE);
+
+        bounds = restrictions.boundsAsComposites(Bound.END, QueryOptions.DEFAULT);
+        assertEquals(1, bounds.size());
+        assertComposite(bounds.get(0), value1, EOC.END);
+    }
+
+    /**
+     * Test multi-column slice restrictions (e.g '(clustering_0, clustering_1) > (1, 2)')
+     */
+    @Test
+    public void testBoundsAsCompositesWithMultiSliceRestrictionsWithTwoClusteringColumn()
+    {
+        CFMetaData cfMetaData = newCFMetaData(Sort.ASC, Sort.ASC);
+
+        ByteBuffer value1 = ByteBufferUtil.bytes(1);
+        ByteBuffer value2 = ByteBufferUtil.bytes(2);
+
+        // (clustering_0, clustering1) > (1, 2)
+        Restriction slice = newMultiSlice(cfMetaData, 0, Bound.START, false, value1, value2);
+        PrimaryKeyRestrictions restrictions = new PrimaryKeyRestrictionSet(cfMetaData.comparator);
+        restrictions = restrictions.mergeWith(slice);
+
+        List<Composite> bounds = restrictions.boundsAsComposites(Bound.START, QueryOptions.DEFAULT);
+        assertEquals(1, bounds.size());
+        assertComposite(bounds.get(0), value1, value2, EOC.END);
+
+        bounds = restrictions.boundsAsComposites(Bound.END, QueryOptions.DEFAULT);
+        assertEquals(1, bounds.size());
+        assertEmptyComposite(bounds.get(0));
+
+        // (clustering_0, clustering1) >= (1, 2)
+        slice = newMultiSlice(cfMetaData, 0, Bound.START, true, value1, value2);
+        restrictions = new PrimaryKeyRestrictionSet(cfMetaData.comparator);
+        restrictions = restrictions.mergeWith(slice);
+
+        bounds = restrictions.boundsAsComposites(Bound.START, QueryOptions.DEFAULT);
+        assertEquals(1, bounds.size());
+        assertComposite(bounds.get(0), value1, value2, EOC.NONE);
+
+        bounds = restrictions.boundsAsComposites(Bound.END, QueryOptions.DEFAULT);
+        assertEquals(1, bounds.size());
+        assertEmptyComposite(bounds.get(0));
+
+        // (clustering_0, clustering1) <= (1, 2)
+        slice = newMultiSlice(cfMetaData, 0, Bound.END, true, value1, value2);
+        restrictions = new PrimaryKeyRestrictionSet(cfMetaData.comparator);
+        restrictions = restrictions.mergeWith(slice);
+
+        bounds = restrictions.boundsAsComposites(Bound.START, QueryOptions.DEFAULT);
+        assertEquals(1, bounds.size());
+        assertEmptyComposite(bounds.get(0));
+
+        bounds = restrictions.boundsAsComposites(Bound.END, QueryOptions.DEFAULT);
+        assertEquals(1, bounds.size());
+        assertComposite(bounds.get(0), value1, value2, EOC.END);
+
+        // (clustering_0, clustering1) < (1, 2)
+        slice = newMultiSlice(cfMetaData, 0, Bound.END, false, value1, value2);
+        restrictions = new PrimaryKeyRestrictionSet(cfMetaData.comparator);
+        restrictions = restrictions.mergeWith(slice);
+
+        bounds = restrictions.boundsAsComposites(Bound.START, QueryOptions.DEFAULT);
+        assertEquals(1, bounds.size());
+        assertEmptyComposite(bounds.get(0));
+
+        bounds = restrictions.boundsAsComposites(Bound.END, QueryOptions.DEFAULT);
+        assertEquals(1, bounds.size());
+        assertComposite(bounds.get(0), value1, value2, EOC.START);
+
+        // (clustering_0, clustering1) > (1, 2) AND (clustering_0) < (2)
+        slice = newMultiSlice(cfMetaData, 0, Bound.START, false, value1, value2);
+        Restriction slice2 = newMultiSlice(cfMetaData, 0, Bound.END, false, value2);
+        restrictions = new PrimaryKeyRestrictionSet(cfMetaData.comparator);
+        restrictions = restrictions.mergeWith(slice).mergeWith(slice2);
+
+        bounds = restrictions.boundsAsComposites(Bound.START, QueryOptions.DEFAULT);
+        assertEquals(1, bounds.size());
+        assertComposite(bounds.get(0), value1, value2, EOC.END);
+
+        bounds = restrictions.boundsAsComposites(Bound.END, QueryOptions.DEFAULT);
+        assertEquals(1, bounds.size());
+        assertComposite(bounds.get(0), value2, EOC.START);
+
+        // (clustering_0, clustering1) >= (1, 2) AND (clustering_0, clustering1) <= (2, 1)
+        slice = newMultiSlice(cfMetaData, 0, Bound.START, true, value1, value2);
+        slice2 = newMultiSlice(cfMetaData, 0, Bound.END, true, value2, value1);
+        restrictions = new PrimaryKeyRestrictionSet(cfMetaData.comparator);
+        restrictions = restrictions.mergeWith(slice).mergeWith(slice2);
+
+        bounds = restrictions.boundsAsComposites(Bound.START, QueryOptions.DEFAULT);
+        assertEquals(1, bounds.size());
+        assertComposite(bounds.get(0), value1, value2, EOC.NONE);
+
+        bounds = restrictions.boundsAsComposites(Bound.END, QueryOptions.DEFAULT);
+        assertEquals(1, bounds.size());
+        assertComposite(bounds.get(0), value2, value1, EOC.END);
+    }
+
+    /**
+     * Test multi-column slice restrictions with 2 descending clustering columns (e.g '(clustering_0, clustering_1) > (1, 2)')
+     */
+    @Test
+    public void testBoundsAsCompositesWithMultiSliceRestrictionsWithTwoDescendingClusteringColumns()
+    {
+        CFMetaData cfMetaData = newCFMetaData(Sort.DESC, Sort.DESC);
+
+        ByteBuffer value1 = ByteBufferUtil.bytes(1);
+        ByteBuffer value2 = ByteBufferUtil.bytes(2);
+
+        // (clustering_0, clustering1) > (1, 2)
+        Restriction slice = newMultiSlice(cfMetaData, 0, Bound.START, false, value1, value2);
+        PrimaryKeyRestrictions restrictions = new PrimaryKeyRestrictionSet(cfMetaData.comparator);
+        restrictions = restrictions.mergeWith(slice);
+
+        List<Composite> bounds = restrictions.boundsAsComposites(Bound.START, QueryOptions.DEFAULT);
+        assertEquals(1, bounds.size());
+        assertEmptyComposite(bounds.get(0));
+
+        bounds = restrictions.boundsAsComposites(Bound.END, QueryOptions.DEFAULT);
+        assertEquals(1, bounds.size());
+        assertComposite(bounds.get(0), value1, value2, EOC.START);
+
+        // (clustering_0, clustering1) >= (1, 2)
+        slice = newMultiSlice(cfMetaData, 0, Bound.START, true, value1, value2);
+        restrictions = new PrimaryKeyRestrictionSet(cfMetaData.comparator);
+        restrictions = restrictions.mergeWith(slice);
+
+        bounds = restrictions.boundsAsComposites(Bound.START, QueryOptions.DEFAULT);
+        assertEquals(1, bounds.size());
+        assertEmptyComposite(bounds.get(0));
+
+        bounds = restrictions.boundsAsComposites(Bound.END, QueryOptions.DEFAULT);
+        assertEquals(1, bounds.size());
+        assertComposite(bounds.get(0), value1, value2, EOC.END);
+
+        // (clustering_0, clustering1) <= (1, 2)
+        slice = newMultiSlice(cfMetaData, 0, Bound.END, true, value1, value2);
+        restrictions = new PrimaryKeyRestrictionSet(cfMetaData.comparator);
+        restrictions = restrictions.mergeWith(slice);
+
+        bounds = restrictions.boundsAsComposites(Bound.START, QueryOptions.DEFAULT);
+        assertEquals(1, bounds.size());
+        assertComposite(bounds.get(0), value1, value2, EOC.NONE);
+
+        bounds = restrictions.boundsAsComposites(Bound.END, QueryOptions.DEFAULT);
+        assertEquals(1, bounds.size());
+        assertEmptyComposite(bounds.get(0));
+
+        // (clustering_0, clustering1) < (1, 2)
+        slice = newMultiSlice(cfMetaData, 0, Bound.END, false, value1, value2);
+        restrictions = new PrimaryKeyRestrictionSet(cfMetaData.comparator);
+        restrictions = restrictions.mergeWith(slice);
+
+        bounds = restrictions.boundsAsComposites(Bound.START, QueryOptions.DEFAULT);
+        assertEquals(1, bounds.size());
+        assertComposite(bounds.get(0), value1, value2, EOC.END);
+
+        bounds = restrictions.boundsAsComposites(Bound.END, QueryOptions.DEFAULT);
+        assertEquals(1, bounds.size());
+        assertEmptyComposite(bounds.get(0));
+
+
+        // (clustering_0, clustering1) > (1, 2) AND (clustering_0) < (2)
+        slice = newMultiSlice(cfMetaData, 0, Bound.START, false, value1, value2);
+        Restriction slice2 = newMultiSlice(cfMetaData, 0, Bound.END, false, value2);
+        restrictions = new PrimaryKeyRestrictionSet(cfMetaData.comparator);
+        restrictions = restrictions.mergeWith(slice).mergeWith(slice2);
+
+        bounds = restrictions.boundsAsComposites(Bound.START, QueryOptions.DEFAULT);
+        assertEquals(1, bounds.size());
+        assertComposite(bounds.get(0), value2, EOC.END);
+
+        bounds = restrictions.boundsAsComposites(Bound.END, QueryOptions.DEFAULT);
+        assertEquals(1, bounds.size());
+        assertComposite(bounds.get(0), value1, value2, EOC.START);
+
+        // (clustering_0, clustering1) >= (1, 2) AND (clustering_0, clustering1) <= (2, 1)
+        slice = newMultiSlice(cfMetaData, 0, Bound.START, true, value1, value2);
+        slice2 = newMultiSlice(cfMetaData, 0, Bound.END, true, value2, value1);
+        restrictions = new PrimaryKeyRestrictionSet(cfMetaData.comparator);
+        restrictions = restrictions.mergeWith(slice).mergeWith(slice2);
+
+        bounds = restrictions.boundsAsComposites(Bound.START, QueryOptions.DEFAULT);
+        assertEquals(1, bounds.size());
+        assertComposite(bounds.get(0), value2, value1, EOC.NONE);
+
+        bounds = restrictions.boundsAsComposites(Bound.END, QueryOptions.DEFAULT);
+        assertEquals(1, bounds.size());
+        assertComposite(bounds.get(0), value1, value2, EOC.END);
+    }
+
+    /**
+     * Test multi-column slice restrictions with 1 descending clustering column and 1 ascending
+     * (e.g '(clustering_0, clustering_1) > (1, 2)')
+     */
+    @Test
+    public void testBoundsAsCompositesWithMultiSliceRestrictionsWithOneDescendingAndOneAscendingClusteringColumns()
+    {
+        CFMetaData cfMetaData = newCFMetaData(Sort.DESC, Sort.ASC);
+
+        ByteBuffer value1 = ByteBufferUtil.bytes(1);
+        ByteBuffer value2 = ByteBufferUtil.bytes(2);
+
+        // (clustering_0, clustering1) > (1, 2)
+        Restriction slice = newMultiSlice(cfMetaData, 0, Bound.START, false, value1, value2);
+        PrimaryKeyRestrictions restrictions = new PrimaryKeyRestrictionSet(cfMetaData.comparator);
+        restrictions = restrictions.mergeWith(slice);
+
+        List<Composite> bounds = restrictions.boundsAsComposites(Bound.START, QueryOptions.DEFAULT);
+        assertEquals(2, bounds.size());
+        assertEmptyComposite(bounds.get(0));
+        assertComposite(bounds.get(1), value1, value2, EOC.END);
+
+        bounds = restrictions.boundsAsComposites(Bound.END, QueryOptions.DEFAULT);
+        assertEquals(2, bounds.size());
+        assertComposite(bounds.get(0), value1, EOC.START);
+        assertComposite(bounds.get(1), value1, EOC.END);
+
+        // (clustering_0, clustering1) >= (1, 2)
+        slice = newMultiSlice(cfMetaData, 0, Bound.START, true, value1, value2);
+        restrictions = new PrimaryKeyRestrictionSet(cfMetaData.comparator);
+        restrictions = restrictions.mergeWith(slice);
+
+        bounds = restrictions.boundsAsComposites(Bound.START, QueryOptions.DEFAULT);
+        assertEquals(2, bounds.size());
+        assertEmptyComposite(bounds.get(0));
+        assertComposite(bounds.get(1), value1, value2, EOC.NONE);
+
+        bounds = restrictions.boundsAsComposites(Bound.END, QueryOptions.DEFAULT);
+        assertEquals(2, bounds.size());
+        assertComposite(bounds.get(0), value1, EOC.START);
+        assertComposite(bounds.get(1), value1, EOC.END);
+
+        // (clustering_0, clustering1) <= (1, 2)
+        slice = newMultiSlice(cfMetaData, 0, Bound.END, true, value1, value2);
+        restrictions = new PrimaryKeyRestrictionSet(cfMetaData.comparator);
+        restrictions = restrictions.mergeWith(slice);
+
+        bounds = restrictions.boundsAsComposites(Bound.START, QueryOptions.DEFAULT);
+        assertEquals(2, bounds.size());
+        assertComposite(bounds.get(0), value1, EOC.START);
+        assertComposite(bounds.get(1), value1, EOC.END);
+
+        bounds = restrictions.boundsAsComposites(Bound.END, QueryOptions.DEFAULT);
+        assertEquals(2, bounds.size());
+        assertComposite(bounds.get(0), value1, value2, EOC.END);
+        assertEmptyComposite(bounds.get(1));
+
+        // (clustering_0, clustering1) < (1, 2)
+        slice = newMultiSlice(cfMetaData, 0, Bound.END, false, value1, value2);
+        restrictions = new PrimaryKeyRestrictionSet(cfMetaData.comparator);
+        restrictions = restrictions.mergeWith(slice);
+
+        bounds = restrictions.boundsAsComposites(Bound.START, QueryOptions.DEFAULT);
+        assertEquals(2, bounds.size());
+        assertComposite(bounds.get(0), value1, EOC.START);
+        assertComposite(bounds.get(1), value1, EOC.END);
+
+        bounds = restrictions.boundsAsComposites(Bound.END, QueryOptions.DEFAULT);
+        assertEquals(2, bounds.size());
+        assertComposite(bounds.get(0), value1, value2, EOC.START);
+        assertEmptyComposite(bounds.get(1));
+
+        // (clustering_0, clustering1) > (1, 2) AND (clustering_0) < (2)
+        slice = newMultiSlice(cfMetaData, 0, Bound.START, false, value1, value2);
+        Restriction slice2 = newMultiSlice(cfMetaData, 0, Bound.END, false, value2);
+        restrictions = new PrimaryKeyRestrictionSet(cfMetaData.comparator);
+        restrictions = restrictions.mergeWith(slice).mergeWith(slice2);
+
+        bounds = restrictions.boundsAsComposites(Bound.START, QueryOptions.DEFAULT);
+        assertEquals(2, bounds.size());
+        assertComposite(bounds.get(0), value2, EOC.END);
+        assertComposite(bounds.get(1), value1, value2, EOC.END);
+
+        bounds = restrictions.boundsAsComposites(Bound.END, QueryOptions.DEFAULT);
+        assertEquals(2, bounds.size());
+        assertComposite(bounds.get(0), value1, EOC.START);
+        assertComposite(bounds.get(1), value1, EOC.END);
+
+        // (clustering_0) > (1) AND (clustering_0, clustering1) < (2, 1)
+        slice = newMultiSlice(cfMetaData, 0, Bound.START, false, value1);
+        slice2 = newMultiSlice(cfMetaData, 0, Bound.END, false, value2, value1);
+        restrictions = new PrimaryKeyRestrictionSet(cfMetaData.comparator);
+        restrictions = restrictions.mergeWith(slice).mergeWith(slice2);
+
+        bounds = restrictions.boundsAsComposites(Bound.START, QueryOptions.DEFAULT);
+        assertEquals(2, bounds.size());
+        assertComposite(bounds.get(0), value2, EOC.START);
+        assertComposite(bounds.get(1), value2, EOC.END);
+
+        bounds = restrictions.boundsAsComposites(Bound.END, QueryOptions.DEFAULT);
+        assertEquals(2, bounds.size());
+        assertComposite(bounds.get(0), value2, value1, EOC.START);
+        assertComposite(bounds.get(1), value1, EOC.START);
+
+        // (clustering_0, clustering1) >= (1, 2) AND (clustering_0, clustering1) <= (2, 1)
+        slice = newMultiSlice(cfMetaData, 0, Bound.START, true, value1, value2);
+        slice2 = newMultiSlice(cfMetaData, 0, Bound.END, true, value2, value1);
+        restrictions = new PrimaryKeyRestrictionSet(cfMetaData.comparator);
+        restrictions = restrictions.mergeWith(slice).mergeWith(slice2);
+
+        bounds = restrictions.boundsAsComposites(Bound.START, QueryOptions.DEFAULT);
+        assertEquals(3, bounds.size());
+        assertComposite(bounds.get(0), value2, EOC.START);
+        assertComposite(bounds.get(1), value2, EOC.END);
+        assertComposite(bounds.get(2), value1, value2, EOC.NONE);
+
+        bounds = restrictions.boundsAsComposites(Bound.END, QueryOptions.DEFAULT);
+        assertEquals(3, bounds.size());
+        assertComposite(bounds.get(0), value2, value1, EOC.END);
+        assertComposite(bounds.get(1), value1, EOC.START);
+        assertComposite(bounds.get(2), value1, EOC.END);
+    }
+
+    /**
+     * Test multi-column slice restrictions with 1 descending clustering column and 1 ascending
+     * (e.g '(clustering_0, clustering_1) > (1, 2)')
+     */
+    @Test
+    public void testBoundsAsCompositesWithMultiSliceRestrictionsWithOneAscendingAndOneDescendingClusteringColumns()
+    {
+        CFMetaData cfMetaData = newCFMetaData(Sort.ASC, Sort.DESC);
+
+        ByteBuffer value1 = ByteBufferUtil.bytes(1);
+        ByteBuffer value2 = ByteBufferUtil.bytes(2);
+
+        // (clustering_0, clustering1) > (1, 2)
+        Restriction slice = newMultiSlice(cfMetaData, 0, Bound.START, false, value1, value2);
+        PrimaryKeyRestrictions restrictions = new PrimaryKeyRestrictionSet(cfMetaData.comparator);
+        restrictions = restrictions.mergeWith(slice);
+
+        List<Composite> bounds = restrictions.boundsAsComposites(Bound.START, QueryOptions.DEFAULT);
+        assertEquals(2, bounds.size());
+        assertComposite(bounds.get(0), value1, EOC.START);
+        assertComposite(bounds.get(1), value1, EOC.END);
+
+        bounds = restrictions.boundsAsComposites(Bound.END, QueryOptions.DEFAULT);
+        assertEquals(2, bounds.size());
+        assertComposite(bounds.get(0), value1, value2, EOC.START);
+        assertEmptyComposite(bounds.get(1));
+
+        // (clustering_0, clustering1) >= (1, 2)
+        slice = newMultiSlice(cfMetaData, 0, Bound.START, true, value1, value2);
+        restrictions = new PrimaryKeyRestrictionSet(cfMetaData.comparator);
+        restrictions = restrictions.mergeWith(slice);
+
+        bounds = restrictions.boundsAsComposites(Bound.START, QueryOptions.DEFAULT);
+        assertEquals(2, bounds.size());
+        assertComposite(bounds.get(0), value1, EOC.START);
+        assertComposite(bounds.get(1), value1, EOC.END);
+
+        bounds = restrictions.boundsAsComposites(Bound.END, QueryOptions.DEFAULT);
+        assertEquals(2, bounds.size());
+        assertComposite(bounds.get(0), value1, value2, EOC.END);
+        assertEmptyComposite(bounds.get(1));
 
-        slice = newSingleSlice(cfMetaData, 1, Bound.END, true, value1);
+        // (clustering_0, clustering1) <= (1, 2)
+        slice = newMultiSlice(cfMetaData, 0, Bound.END, true, value1, value2);
         restrictions = new PrimaryKeyRestrictionSet(cfMetaData.comparator);
-        restrictions = restrictions.mergeWith(eq).mergeWith(slice);
+        restrictions = restrictions.mergeWith(slice);
 
         bounds = restrictions.boundsAsComposites(Bound.START, QueryOptions.DEFAULT);
-        assertEquals(1, bounds.size());
-        assertComposite(bounds.get(0), value3, EOC.START);
+        assertEquals(2, bounds.size());
+        assertEmptyComposite(bounds.get(0));
+        assertComposite(bounds.get(1), value1, value2, EOC.NONE);
 
         bounds = restrictions.boundsAsComposites(Bound.END, QueryOptions.DEFAULT);
-        assertEquals(1, bounds.size());
-        assertComposite(bounds.get(0), value3, value1, EOC.END);
+        assertEquals(2, bounds.size());
+        assertComposite(bounds.get(0), value1, EOC.START);
+        assertComposite(bounds.get(1), value1, EOC.END);
 
-        slice = newSingleSlice(cfMetaData, 1, Bound.END, false, value1);
+        // (clustering_0, clustering1) < (1, 2)
+        slice = newMultiSlice(cfMetaData, 0, Bound.END, false, value1, value2);
         restrictions = new PrimaryKeyRestrictionSet(cfMetaData.comparator);
-        restrictions = restrictions.mergeWith(eq).mergeWith(slice);
+        restrictions = restrictions.mergeWith(slice);
 
         bounds = restrictions.boundsAsComposites(Bound.START, QueryOptions.DEFAULT);
-        assertEquals(1, bounds.size());
-        assertComposite(bounds.get(0), value3, EOC.START);
+        assertEquals(2, bounds.size());
+        assertEmptyComposite(bounds.get(0));
+        assertComposite(bounds.get(1), value1, value2, EOC.END);
 
         bounds = restrictions.boundsAsComposites(Bound.END, QueryOptions.DEFAULT);
-        assertEquals(1, bounds.size());
-        assertComposite(bounds.get(0), value3, value1, EOC.START);
+        assertEquals(2, bounds.size());
+        assertComposite(bounds.get(0), value1, EOC.START);
+        assertComposite(bounds.get(1), value1, EOC.END);
 
-        slice = newSingleSlice(cfMetaData, 1, Bound.START, false, value1);
-        Restriction slice2 = newSingleSlice(cfMetaData, 1, Bound.END, false, value2);
+        // (clustering_0, clustering1) > (1, 2) AND (clustering_0) < (2)
+        slice = newMultiSlice(cfMetaData, 0, Bound.START, false, value1, value2);
+        Restriction slice2 = newMultiSlice(cfMetaData, 0, Bound.END, false, value2);
         restrictions = new PrimaryKeyRestrictionSet(cfMetaData.comparator);
-        restrictions = restrictions.mergeWith(eq).mergeWith(slice).mergeWith(slice2);
+        restrictions = restrictions.mergeWith(slice).mergeWith(slice2);
 
         bounds = restrictions.boundsAsComposites(Bound.START, QueryOptions.DEFAULT);
-        assertEquals(1, bounds.size());
-        assertComposite(bounds.get(0), value3, value1, EOC.END);
+        assertEquals(2, bounds.size());
+        assertComposite(bounds.get(0), value1, EOC.START);
+        assertComposite(bounds.get(1), value1, EOC.END);
 
         bounds = restrictions.boundsAsComposites(Bound.END, QueryOptions.DEFAULT);
-        assertEquals(1, bounds.size());
-        assertComposite(bounds.get(0), value3, value2, EOC.START);
+        assertEquals(2, bounds.size());
+        assertComposite(bounds.get(0), value1, value2, EOC.START);
+        assertComposite(bounds.get(1), value2, EOC.START);
 
-        slice = newSingleSlice(cfMetaData, 1, Bound.START, true, value1);
-        slice2 = newSingleSlice(cfMetaData, 1, Bound.END, true, value2);
+        // (clustering_0, clustering1) >= (1, 2) AND (clustering_0, clustering1) <= (2, 1)
+        slice = newMultiSlice(cfMetaData, 0, Bound.START, true, value1, value2);
+        slice2 = newMultiSlice(cfMetaData, 0, Bound.END, true, value2, value1);
         restrictions = new PrimaryKeyRestrictionSet(cfMetaData.comparator);
-        restrictions = restrictions.mergeWith(eq).mergeWith(slice).mergeWith(slice2);
+        restrictions = restrictions.mergeWith(slice).mergeWith(slice2);
 
         bounds = restrictions.boundsAsComposites(Bound.START, QueryOptions.DEFAULT);
-        assertEquals(1, bounds.size());
-        assertComposite(bounds.get(0), value3, value1, EOC.NONE);
+        assertEquals(3, bounds.size());
+        assertComposite(bounds.get(0), value1, EOC.START);
+        assertComposite(bounds.get(1), value1, EOC.END);
+        assertComposite(bounds.get(2), value2, value1, EOC.NONE);
 
         bounds = restrictions.boundsAsComposites(Bound.END, QueryOptions.DEFAULT);
-        assertEquals(1, bounds.size());
-        assertComposite(bounds.get(0), value3, value2, EOC.END);
+        assertEquals(3, bounds.size());
+        assertComposite(bounds.get(0), value1, value2, EOC.END);
+        assertComposite(bounds.get(1), value2, EOC.START);
+        assertComposite(bounds.get(2), value2, EOC.END);
     }
 
     /**
-     * Test '(clustering_0, clustering_1) = (1, 2)' with two clustering column
+     * Test multi-column slice restrictions with 2 ascending clustering column and 2 descending
+     * (e.g '(clustering_0, clustering1, clustering_3, clustering4) > (1, 2, 3, 4)')
      */
     @Test
-    public void testBoundsAsCompositesWithMultiEqRestrictions() throws InvalidRequestException
+    public void testBoundsAsCompositesWithMultiSliceRestrictionsWithTwoAscendingAndTwoDescendingClusteringColumns()
     {
-        CFMetaData cfMetaData = newCFMetaData(2);
+        CFMetaData cfMetaData = newCFMetaData(Sort.ASC, Sort.ASC, Sort.DESC, Sort.DESC);
 
         ByteBuffer value1 = ByteBufferUtil.bytes(1);
         ByteBuffer value2 = ByteBufferUtil.bytes(2);
-        Restriction eq = newMultiEq(cfMetaData, 0, value1, value2);
+        ByteBuffer value3 = ByteBufferUtil.bytes(3);
+        ByteBuffer value4 = ByteBufferUtil.bytes(4);
+
+        // (clustering_0, clustering1, clustering_2, clustering_3) > (1, 2, 3, 4)
+        Restriction slice = newMultiSlice(cfMetaData, 0, Bound.START, false, value1, value2, value3, value4);
         PrimaryKeyRestrictions restrictions = new PrimaryKeyRestrictionSet(cfMetaData.comparator);
-        restrictions = restrictions.mergeWith(eq);
+        restrictions = restrictions.mergeWith(slice);
 
         List<Composite> bounds = restrictions.boundsAsComposites(Bound.START, QueryOptions.DEFAULT);
-        assertEquals(1, bounds.size());
+        assertEquals(2, bounds.size());
         assertComposite(bounds.get(0), value1, value2, EOC.START);
+        assertComposite(bounds.get(1), value1, value2, EOC.END);
 
         bounds = restrictions.boundsAsComposites(Bound.END, QueryOptions.DEFAULT);
-        assertEquals(1, bounds.size());
-        assertComposite(bounds.get(0), value1, value2, EOC.END);
-    }
-
-    /**
-     * Test '(clustering_0, clustering_1) IN ((1, 2), (2, 3))' with two clustering column
-     */
-    @Test
-    public void testBoundsAsCompositesWithMultiInRestrictions() throws InvalidRequestException
-    {
-        CFMetaData cfMetaData = newCFMetaData(2);
+        assertEquals(2, bounds.size());
+        assertComposite(bounds.get(0), value1, value2, value3, value4, EOC.START);
+        assertEmptyComposite(bounds.get(1));
 
-        ByteBuffer value1 = ByteBufferUtil.bytes(1);
-        ByteBuffer value2 = ByteBufferUtil.bytes(2);
-        ByteBuffer value3 = ByteBufferUtil.bytes(3);
-        Restriction in = newMultiIN(cfMetaData, 0, asList(value1, value2), asList(value2, value3));
-        PrimaryKeyRestrictions restrictions = new PrimaryKeyRestrictionSet(cfMetaData.comparator);
-        restrictions = restrictions.mergeWith(in);
+        // clustering_0 = 1 AND (clustering_1, clustering_2, clustering_3) > (2, 3, 4)
+        Restriction eq = newSingleEq(cfMetaData, 0, value1);
+        slice = newMultiSlice(cfMetaData, 1, Bound.START, false, value2, value3, value4);
+        restrictions = new PrimaryKeyRestrictionSet(cfMetaData.comparator);
+        restrictions = restrictions.mergeWith(slice);
+        restrictions = restrictions.mergeWith(eq);
 
-        List<Composite> bounds = restrictions.boundsAsComposites(Bound.START, QueryOptions.DEFAULT);
+        bounds = restrictions.boundsAsComposites(Bound.START, QueryOptions.DEFAULT);
         assertEquals(2, bounds.size());
         assertComposite(bounds.get(0), value1, value2, EOC.START);
-        assertComposite(bounds.get(1), value2, value3, EOC.START);
+        assertComposite(bounds.get(1), value1, value2, EOC.END);
 
         bounds = restrictions.boundsAsComposites(Bound.END, QueryOptions.DEFAULT);
         assertEquals(2, bounds.size());
-        assertComposite(bounds.get(0), value1, value2, EOC.END);
-        assertComposite(bounds.get(1), value2, value3, EOC.END);
-    }
+        assertComposite(bounds.get(0), value1, value2, value3, value4, EOC.START);
+        assertComposite(bounds.get(1), value1, EOC.END);
 
-    /**
-     * Test multi-column slice restrictions (e.g '(clustering_0) > (1)') with only one clustering column
-     */
-    @Test
-    public void testBoundsAsCompositesWithMultiSliceRestrictionsWithOneClusteringColumn() throws InvalidRequestException
-    {
-        CFMetaData cfMetaData = newCFMetaData(1);
+        // clustering_0 = 1 AND (clustering_1, clustering_2, clustering_3) > (2, 3, 4)
+        Restriction in = newSingleIN(cfMetaData, 0, value1, value2);
+        slice = newMultiSlice(cfMetaData, 1, Bound.START, false, value2, value3, value4);
+        restrictions = new PrimaryKeyRestrictionSet(cfMetaData.comparator);
+        restrictions = restrictions.mergeWith(slice);
+        restrictions = restrictions.mergeWith(in);
 
-        ByteBuffer value1 = ByteBufferUtil.bytes(1);
-        ByteBuffer value2 = ByteBufferUtil.bytes(2);
+        bounds = restrictions.boundsAsComposites(Bound.START, QueryOptions.DEFAULT);
+        assertEquals(4, bounds.size());
+        assertComposite(bounds.get(0), value1, value2, EOC.START);
+        assertComposite(bounds.get(1), value1, value2, EOC.END);
+        assertComposite(bounds.get(2), value2, value2, EOC.START);
+        assertComposite(bounds.get(3), value2, value2, EOC.END);
 
-        Restriction slice = newMultiSlice(cfMetaData, 0, Bound.START, false, value1);
-        PrimaryKeyRestrictions restrictions = new PrimaryKeyRestrictionSet(cfMetaData.comparator);
+        bounds = restrictions.boundsAsComposites(Bound.END, QueryOptions.DEFAULT);
+        assertEquals(4, bounds.size());
+        assertComposite(bounds.get(0), value1, value2, value3, value4, EOC.START);
+        assertComposite(bounds.get(1), value1, EOC.END);
+        assertComposite(bounds.get(2), value2, value2, value3, value4, EOC.START);
+        assertComposite(bounds.get(3), value2, EOC.END);
+
+        // (clustering_0, clustering1) >= (1, 2)
+        slice = newMultiSlice(cfMetaData, 0, Bound.START, true, value1, value2);
+        restrictions = new PrimaryKeyRestrictionSet(cfMetaData.comparator);
         restrictions = restrictions.mergeWith(slice);
 
-        List<Composite> bounds = restrictions.boundsAsComposites(Bound.START, QueryOptions.DEFAULT);
+        bounds = restrictions.boundsAsComposites(Bound.START, QueryOptions.DEFAULT);
         assertEquals(1, bounds.size());
-        assertComposite(bounds.get(0), value1, EOC.END);
+        assertComposite(bounds.get(0), value1, value2, EOC.NONE);
 
         bounds = restrictions.boundsAsComposites(Bound.END, QueryOptions.DEFAULT);
         assertEquals(1, bounds.size());
         assertEmptyComposite(bounds.get(0));
 
-        slice = newMultiSlice(cfMetaData, 0, Bound.START, true, value1);
+        // (clustering_0, clustering1, clustering_2, clustering_3) >= (1, 2, 3, 4)
+        slice = newMultiSlice(cfMetaData, 0, Bound.START, true, value1, value2, value3, value4);
         restrictions = new PrimaryKeyRestrictionSet(cfMetaData.comparator);
         restrictions = restrictions.mergeWith(slice);
 
         bounds = restrictions.boundsAsComposites(Bound.START, QueryOptions.DEFAULT);
-        assertEquals(1, bounds.size());
-        assertComposite(bounds.get(0), value1, EOC.NONE);
+        assertEquals(2, bounds.size());
+        assertComposite(bounds.get(0), value1, value2, EOC.START);
+        assertComposite(bounds.get(1), value1, value2, EOC.END);
 
         bounds = restrictions.boundsAsComposites(Bound.END, QueryOptions.DEFAULT);
-        assertEquals(1, bounds.size());
-        assertEmptyComposite(bounds.get(0));
+        assertEquals(2, bounds.size());
+        assertComposite(bounds.get(0), value1, value2, value3, value4, EOC.END);
+        assertEmptyComposite(bounds.get(1));
 
-        slice = newMultiSlice(cfMetaData, 0, Bound.END, true, value1);
+        // (clustering_0, clustering1, clustering_2, clustering_3) <= (1, 2, 3, 4)
+        slice = newMultiSlice(cfMetaData, 0, Bound.END, true, value1, value2, value3, value4);
         restrictions = new PrimaryKeyRestrictionSet(cfMetaData.comparator);
         restrictions = restrictions.mergeWith(slice);
 
         bounds = restrictions.boundsAsComposites(Bound.START, QueryOptions.DEFAULT);
-        assertEquals(1, bounds.size());
+        assertEquals(2, bounds.size());
         assertEmptyComposite(bounds.get(0));
+        assertComposite(bounds.get(1), value1, value2, value3, value4, EOC.NONE);
 
         bounds = restrictions.boundsAsComposites(Bound.END, QueryOptions.DEFAULT);
-        assertEquals(1, bounds.size());
-        assertComposite(bounds.get(0), value1, EOC.END);
+        assertEquals(2, bounds.size());
+        assertComposite(bounds.get(0), value1, value2, EOC.START);
+        assertComposite(bounds.get(1), value1, value2, EOC.END);
 
-        slice = newMultiSlice(cfMetaData, 0, Bound.END, false, value1);
+        // (clustering_0, clustering1, clustering_2, clustering_3) < (1, 2, 3, 4)
+        slice = newMultiSlice(cfMetaData, 0, Bound.END, false, value1, value2, value3, value4);
         restrictions = new PrimaryKeyRestrictionSet(cfMetaData.comparator);
         restrictions = restrictions.mergeWith(slice);
 
         bounds = restrictions.boundsAsComposites(Bound.START, QueryOptions.DEFAULT);
-        assertEquals(1, bounds.size());
+        assertEquals(2, bounds.size());
         assertEmptyComposite(bounds.get(0));
+        assertComposite(bounds.get(1), value1, value2, value3, value4, EOC.END);
 
         bounds = restrictions.boundsAsComposites(Bound.END, QueryOptions.DEFAULT);
-        assertEquals(1, bounds.size());
-        assertComposite(bounds.get(0), value1, EOC.START);
+        assertEquals(2, bounds.size());
+        assertComposite(bounds.get(0), value1, value2, EOC.START);
+        assertComposite(bounds.get(1), value1, value2, EOC.END);
 
-        slice = newMultiSlice(cfMetaData, 0, Bound.START, false, value1);
-        Restriction slice2 = newMultiSlice(cfMetaData, 0, Bound.END, false, value2);
+        // (clustering_0, clustering1, clustering_2, clustering_3) > (1, 2, 3, 4) AND (clustering_0, clustering_1) < (2, 3)
+        slice = newMultiSlice(cfMetaData, 0, Bound.START, false, value1, value2, value3, value4);
+        Restriction slice2 = newMultiSlice(cfMetaData, 0, Bound.END, false, value2, value3);
         restrictions = new PrimaryKeyRestrictionSet(cfMetaData.comparator);
         restrictions = restrictions.mergeWith(slice).mergeWith(slice2);
 
         bounds = restrictions.boundsAsComposites(Bound.START, QueryOptions.DEFAULT);
-        assertEquals(1, bounds.size());
-        assertComposite(bounds.get(0), value1, EOC.END);
+        assertEquals(2, bounds.size());
+        assertComposite(bounds.get(0), value1, value2, EOC.START);
+        assertComposite(bounds.get(1), value1, value2, EOC.END);
 
         bounds = restrictions.boundsAsComposites(Bound.END, QueryOptions.DEFAULT);
-        assertEquals(1, bounds.size());
-        assertComposite(bounds.get(0), value2, EOC.START);
+        assertEquals(2, bounds.size());
+        assertComposite(bounds.get(0), value1, value2, value3, value4, EOC.START);
+        assertComposite(bounds.get(1), value2, value3, EOC.START);
 
-        slice = newMultiSlice(cfMetaData, 0, Bound.START, true, value1);
-        slice2 = newMultiSlice(cfMetaData, 0, Bound.END, true, value2);
+        // (clustering_0, clustering1, clustering_2, clustering_3) >= (1, 2, 3, 4) AND (clustering_0, clustering1, clustering_2, clustering_3) <= (4, 3, 2, 1)
+        slice = newMultiSlice(cfMetaData, 0, Bound.START, true, value1, value2, value3, value4);
+        slice2 = newMultiSlice(cfMetaData, 0, Bound.END, true, value4, value3, value2, value1);
         restrictions = new PrimaryKeyRestrictionSet(cfMetaData.comparator);
         restrictions = restrictions.mergeWith(slice).mergeWith(slice2);
 
         bounds = restrictions.boundsAsComposites(Bound.START, QueryOptions.DEFAULT);
-        assertEquals(1, bounds.size());
-        assertComposite(bounds.get(0), value1, EOC.NONE);
+        assertEquals(3, bounds.size());
+        assertComposite(bounds.get(0), value1, value2,  EOC.START);
+        assertComposite(bounds.get(1), value1, value2, EOC.END);
+        assertComposite(bounds.get(2), value4, value3, value2, value1, EOC.NONE);
 
         bounds = restrictions.boundsAsComposites(Bound.END, QueryOptions.DEFAULT);
-        assertEquals(1, bounds.size());
-        assertComposite(bounds.get(0), value2, EOC.END);
+        assertEquals(3, bounds.size());
+        assertComposite(bounds.get(0), value1, value2, value3, value4, EOC.END);
+        assertComposite(bounds.get(1), value4, value3, EOC.START);
+        assertComposite(bounds.get(2), value4, value3, EOC.END);
     }
 
     /**
-     * Test multi-column slice restrictions (e.g '(clustering_0, clustering_1) > (1, 2)')
+     * Test multi-column slice restrictions with ascending, descending, ascending and descending columns
+     * (e.g '(clustering_0, clustering1, clustering_3, clustering4) > (1, 2, 3, 4)')
      */
     @Test
-    public void testBoundsAsCompositesWithMultiSliceRestrictionsWithTwoClusteringColumn() throws InvalidRequestException
+    public void testBoundsAsCompositesWithMultiSliceRestrictionsMixingAscendingDescendingClusteringColumns()
     {
-        CFMetaData cfMetaData = newCFMetaData(2);
+        CFMetaData cfMetaData = newCFMetaData(Sort.ASC, Sort.DESC, Sort.ASC, Sort.DESC);
 
         ByteBuffer value1 = ByteBufferUtil.bytes(1);
         ByteBuffer value2 = ByteBufferUtil.bytes(2);
+        ByteBuffer value3 = ByteBufferUtil.bytes(3);
+        ByteBuffer value4 = ByteBufferUtil.bytes(4);
 
-        // (clustering_0, clustering1) > (1, 2)
-        Restriction slice = newMultiSlice(cfMetaData, 0, Bound.START, false, value1, value2);
+        // (clustering_0, clustering1, clustering_2, clustering_3) > (1, 2, 3, 4)
+        Restriction slice = newMultiSlice(cfMetaData, 0, Bound.START, false, value1, value2, value3, value4);
         PrimaryKeyRestrictions restrictions = new PrimaryKeyRestrictionSet(cfMetaData.comparator);
         restrictions = restrictions.mergeWith(slice);
 
         List<Composite> bounds = restrictions.boundsAsComposites(Bound.START, QueryOptions.DEFAULT);
-        assertEquals(1, bounds.size());
-        assertComposite(bounds.get(0), value1, value2, EOC.END);
+        assertEquals(4, bounds.size());
+        assertComposite(bounds.get(0), value1, EOC.START);
+        assertComposite(bounds.get(1), value1, value2, value3, EOC.START);
+        assertComposite(bounds.get(2), value1, value2, value3, EOC.END);
+        assertComposite(bounds.get(3), value1, EOC.END);
+
 
         bounds = restrictions.boundsAsComposites(Bound.END, QueryOptions.DEFAULT);
-        assertEquals(1, bounds.size());
-        assertEmptyComposite(bounds.get(0));
+        assertEquals(4, bounds.size());
+        assertComposite(bounds.get(0), value1, value2, EOC.START);
+        assertComposite(bounds.get(1), value1, value2, value3, value4, EOC.START);
+        assertComposite(bounds.get(2), value1, value2, EOC.END);
+        assertEmptyComposite(bounds.get(3));
+
+        // clustering_0 = 1 AND (clustering_1, clustering_2, clustering_3) > (2, 3, 4)
+        Restriction eq = newSingleEq(cfMetaData, 0, value1);
+        slice = newMultiSlice(cfMetaData, 1, Bound.START, false, value2, value3, value4);
+        restrictions = new PrimaryKeyRestrictionSet(cfMetaData.comparator);
+        restrictions = restrictions.mergeWith(slice);
+        restrictions = restrictions.mergeWith(eq);
+
+        bounds = restrictions.boundsAsComposites(Bound.START, QueryOptions.DEFAULT);
+        assertEquals(3, bounds.size());
+        assertComposite(bounds.get(0), value1, EOC.START);
+        assertComposite(bounds.get(1), value1, value2, value3, EOC.START);
+        assertComposite(bounds.get(2), value1, value2, value3, EOC.END);
+
+        bounds = restrictions.boundsAsComposites(Bound.END, QueryOptions.DEFAULT);
+        assertEquals(3, bounds.size());
+        assertComposite(bounds.get(0), value1, value2, EOC.START);
+        assertComposite(bounds.get(1), value1, value2, value3, value4, EOC.START);
+        assertComposite(bounds.get(2), value1, value2, EOC.END);
 
         // (clustering_0, clustering1) >= (1, 2)
         slice = newMultiSlice(cfMetaData, 0, Bound.START, true, value1, value2);
@@ -505,75 +1281,129 @@ public class PrimaryKeyRestrictionSetTest
         restrictions = restrictions.mergeWith(slice);
 
         bounds = restrictions.boundsAsComposites(Bound.START, QueryOptions.DEFAULT);
-        assertEquals(1, bounds.size());
-        assertComposite(bounds.get(0), value1, value2, EOC.NONE);
+        assertEquals(2, bounds.size());
+        assertComposite(bounds.get(0), value1, EOC.START);
+        assertComposite(bounds.get(1), value1, EOC.END);
 
         bounds = restrictions.boundsAsComposites(Bound.END, QueryOptions.DEFAULT);
-        assertEquals(1, bounds.size());
-        assertEmptyComposite(bounds.get(0));
+        assertEquals(2, bounds.size());
+        assertComposite(bounds.get(0), value1, value2, EOC.END);
+        assertEmptyComposite(bounds.get(1));
 
-        // (clustering_0, clustering1) <= (1, 2)
-        slice = newMultiSlice(cfMetaData, 0, Bound.END, true, value1, value2);
+        // (clustering_0, clustering1, clustering_2, clustering_3) >= (1, 2, 3, 4)
+        slice = newMultiSlice(cfMetaData, 0, Bound.START, true, value1, value2, value3, value4);
         restrictions = new PrimaryKeyRestrictionSet(cfMetaData.comparator);
         restrictions = restrictions.mergeWith(slice);
 
         bounds = restrictions.boundsAsComposites(Bound.START, QueryOptions.DEFAULT);
-        assertEquals(1, bounds.size());
+        assertEquals(4, bounds.size());
+        assertComposite(bounds.get(0), value1, EOC.START);
+        assertComposite(bounds.get(1), value1, value2, value3, EOC.START);
+        assertComposite(bounds.get(2), value1, value2, value3, EOC.END);
+        assertComposite(bounds.get(3), value1, EOC.END);
+
+        bounds = restrictions.boundsAsComposites(Bound.END, QueryOptions.DEFAULT);
+        assertEquals(4, bounds.size());
+        assertComposite(bounds.get(0), value1, value2, EOC.START);
+        assertComposite(bounds.get(1), value1, value2, value3, value4, EOC.END);
+        assertComposite(bounds.get(2), value1, value2, EOC.END);
+        assertEmptyComposite(bounds.get(3));
+
+
+        // (clustering_0, clustering1, clustering_2, clustering_3) <= (1, 2, 3, 4)
+        slice = newMultiSlice(cfMetaData, 0, Bound.END, true, value1, value2, value3, value4);
+        restrictions = new PrimaryKeyRestrictionSet(cfMetaData.comparator);
+        restrictions = restrictions.mergeWith(slice);
+
+        bounds = restrictions.boundsAsComposites(Bound.START, QueryOptions.DEFAULT);
+        assertEquals(4, bounds.size());
         assertEmptyComposite(bounds.get(0));
+        assertComposite(bounds.get(1), value1, value2, EOC.START);
+        assertComposite(bounds.get(2), value1, value2, value3, value4, EOC.NONE);
+        assertComposite(bounds.get(3), value1, value2, EOC.END);
 
         bounds = restrictions.boundsAsComposites(Bound.END, QueryOptions.DEFAULT);
-        assertEquals(1, bounds.size());
-        assertComposite(bounds.get(0), value1, value2, EOC.END);
+        assertEquals(4, bounds.size());
+        assertComposite(bounds.get(0), value1, EOC.START);
+        assertComposite(bounds.get(1), value1, value2, value3, EOC.START);
+        assertComposite(bounds.get(2), value1, value2, value3, EOC.END);
+        assertComposite(bounds.get(3), value1, EOC.END);
 
-        // (clustering_0, clustering1) < (1, 2)
-        slice = newMultiSlice(cfMetaData, 0, Bound.END, false, value1, value2);
+        // (clustering_0, clustering1, clustering_2, clustering_3) < (1, 2, 3, 4)
+        slice = newMultiSlice(cfMetaData, 0, Bound.END, false, value1, value2, value3, value4);
         restrictions = new PrimaryKeyRestrictionSet(cfMetaData.comparator);
         restrictions = restrictions.mergeWith(slice);
 
         bounds = restrictions.boundsAsComposites(Bound.START, QueryOptions.DEFAULT);
-        assertEquals(1, bounds.size());
+        assertEquals(4, bounds.size());
         assertEmptyComposite(bounds.get(0));
+        assertComposite(bounds.get(1), value1, value2, EOC.START);
+        assertComposite(bounds.get(2), value1, value2, value3, value4, EOC.END);
+        assertComposite(bounds.get(3), value1, value2, EOC.END);
 
         bounds = restrictions.boundsAsComposites(Bound.END, QueryOptions.DEFAULT);
-        assertEquals(1, bounds.size());
-        assertComposite(bounds.get(0), value1, value2, EOC.START);
+        assertEquals(4, bounds.size());
+        assertComposite(bounds.get(0), value1, EOC.START);
+        assertComposite(bounds.get(1), value1, value2, value3, EOC.START);
+        assertComposite(bounds.get(2), value1, value2, value3, EOC.END);
+        assertComposite(bounds.get(3), value1, EOC.END);
 
-        // (clustering_0, clustering1) > (1, 2) AND (clustering_0) < (2)
-        slice = newMultiSlice(cfMetaData, 0, Bound.START, false, value1, value2);
-        Restriction slice2 = newMultiSlice(cfMetaData, 0, Bound.END, false, value2);
+        // (clustering_0, clustering1, clustering_2, clustering_3) > (1, 2, 3, 4) AND (clustering_0, clustering_1) < (2, 3)
+        slice = newMultiSlice(cfMetaData, 0, Bound.START, false, value1, value2, value3, value4);
+        Restriction slice2 = newMultiSlice(cfMetaData, 0, Bound.END, false, value2, value3);
         restrictions = new PrimaryKeyRestrictionSet(cfMetaData.comparator);
         restrictions = restrictions.mergeWith(slice).mergeWith(slice2);
 
         bounds = restrictions.boundsAsComposites(Bound.START, QueryOptions.DEFAULT);
-        assertEquals(1, bounds.size());
-        assertComposite(bounds.get(0), value1, value2, EOC.END);
+        assertEquals(5, bounds.size());
+        assertComposite(bounds.get(0), value1, EOC.START);
+        assertComposite(bounds.get(1), value1, value2, value3, EOC.START);
+        assertComposite(bounds.get(2), value1, value2, value3, EOC.END);
+        assertComposite(bounds.get(3), value1, EOC.END);
+        assertComposite(bounds.get(4), value2, value3, EOC.END);
 
         bounds = restrictions.boundsAsComposites(Bound.END, QueryOptions.DEFAULT);
-        assertEquals(1, bounds.size());
-        assertComposite(bounds.get(0), value2, EOC.START);
-
-        // (clustering_0, clustering1) >= (1, 2) AND (clustering_0, clustering1) <= (2, 1)
-        slice = newMultiSlice(cfMetaData, 0, Bound.START, true, value1, value2);
-        slice2 = newMultiSlice(cfMetaData, 0, Bound.END, true, value2, value1);
+        assertEquals(5, bounds.size());
+        assertComposite(bounds.get(0), value1, value2, EOC.START);
+        assertComposite(bounds.get(1), value1, value2, value3, value4, EOC.START);
+        assertComposite(bounds.get(2), value1, value2, EOC.END);
+        assertComposite(bounds.get(3), value2, EOC.START);
+        assertComposite(bounds.get(4), value2, EOC.END);
+
+        // (clustering_0, clustering1, clustering_2, clustering_3) >= (1, 2, 3, 4) AND (clustering_0, clustering1, clustering_2, clustering_3) <= (4, 3, 2, 1)
+        slice = newMultiSlice(cfMetaData, 0, Bound.START, true, value1, value2, value3, value4);
+        slice2 = newMultiSlice(cfMetaData, 0, Bound.END, true, value4, value3, value2, value1);
         restrictions = new PrimaryKeyRestrictionSet(cfMetaData.comparator);
         restrictions = restrictions.mergeWith(slice).mergeWith(slice2);
 
         bounds = restrictions.boundsAsComposites(Bound.START, QueryOptions.DEFAULT);
-        assertEquals(1, bounds.size());
-        assertComposite(bounds.get(0), value1, value2, EOC.NONE);
+        assertEquals(7, bounds.size());
+        assertComposite(bounds.get(0), value1, EOC.START);
+        assertComposite(bounds.get(1), value1, value2, value3, EOC.START);
+        assertComposite(bounds.get(2), value1, value2, value3, EOC.END);
+        assertComposite(bounds.get(3), value1, EOC.END);
+        assertComposite(bounds.get(4), value4, value3, EOC.START);
+        assertComposite(bounds.get(5), value4, value3, value2, value1, EOC.NONE);
+        assertComposite(bounds.get(6), value4, value3, EOC.END);
 
         bounds = restrictions.boundsAsComposites(Bound.END, QueryOptions.DEFAULT);
-        assertEquals(1, bounds.size());
-        assertComposite(bounds.get(0), value2, value1, EOC.END);
+        assertEquals(7, bounds.size());
+        assertComposite(bounds.get(0), value1, value2, EOC.START);
+        assertComposite(bounds.get(1), value1, value2, value3, value4, EOC.END);
+        assertComposite(bounds.get(2), value1, value2, EOC.END);
+        assertComposite(bounds.get(3), value4, EOC.START);
+        assertComposite(bounds.get(4), value4, value3, value2, EOC.START);
+        assertComposite(bounds.get(5), value4, value3, value2, EOC.END);
+        assertComposite(bounds.get(6), value4, EOC.END);
     }
 
     /**
      * Test mixing single and multi equals restrictions (e.g. clustering_0 = 1 AND (clustering_1, clustering_2) = (2, 3))
      */
     @Test
-    public void testBoundsAsCompositesWithSingleEqAndMultiEqRestrictions() throws InvalidRequestException
+    public void testBoundsAsCompositesWithSingleEqAndMultiEqRestrictions()
     {
-        CFMetaData cfMetaData = newCFMetaData(4);
+        CFMetaData cfMetaData = newCFMetaData(Sort.ASC, Sort.ASC, Sort.ASC, Sort.ASC);
 
         ByteBuffer value1 = ByteBufferUtil.bytes(1);
         ByteBuffer value2 = ByteBufferUtil.bytes(2);
@@ -643,9 +1473,9 @@ public class PrimaryKeyRestrictionSetTest
      * Test clustering_0 = 1 AND (clustering_1, clustering_2) IN ((2, 3), (4, 5))
      */
     @Test
-    public void testBoundsAsCompositesWithSingleEqAndMultiINRestrictions() throws InvalidRequestException
+    public void testBoundsAsCompositesWithSingleEqAndMultiINRestrictions()
     {
-        CFMetaData cfMetaData = newCFMetaData(4);
+        CFMetaData cfMetaData = newCFMetaData(Sort.ASC, Sort.ASC, Sort.ASC, Sort.ASC);
 
         ByteBuffer value1 = ByteBufferUtil.bytes(1);
         ByteBuffer value2 = ByteBufferUtil.bytes(2);
@@ -706,9 +1536,9 @@ public class PrimaryKeyRestrictionSetTest
      * (e.g. clustering_0 = 1 AND (clustering_1, clustering_2) > (2, 3))
      */
     @Test
-    public void testBoundsAsCompositesWithSingleEqAndSliceRestrictions() throws InvalidRequestException
+    public void testBoundsAsCompositesWithSingleEqAndSliceRestrictions()
     {
-        CFMetaData cfMetaData = newCFMetaData(3);
+        CFMetaData cfMetaData = newCFMetaData(Sort.ASC, Sort.ASC, Sort.ASC);
 
         ByteBuffer value1 = ByteBufferUtil.bytes(1);
         ByteBuffer value2 = ByteBufferUtil.bytes(2);
@@ -766,9 +1596,9 @@ public class PrimaryKeyRestrictionSetTest
      * (e.g. clustering_0 = 1 AND (clustering_1, clustering_2) > (2, 3))
      */
     @Test
-    public void testBoundsAsCompositesWithMultiEqAndSingleSliceRestrictions() throws InvalidRequestException
+    public void testBoundsAsCompositesWithMultiEqAndSingleSliceRestrictions()
     {
-        CFMetaData cfMetaData = newCFMetaData(3);
+        CFMetaData cfMetaData = newCFMetaData(Sort.ASC, Sort.ASC, Sort.ASC);
 
         ByteBuffer value1 = ByteBufferUtil.bytes(1);
         ByteBuffer value2 = ByteBufferUtil.bytes(2);
@@ -790,9 +1620,9 @@ public class PrimaryKeyRestrictionSetTest
     }
 
     @Test
-    public void testBoundsAsCompositesWithSeveralMultiColumnRestrictions() throws InvalidRequestException
+    public void testBoundsAsCompositesWithSeveralMultiColumnRestrictions()
     {
-        CFMetaData cfMetaData = newCFMetaData(4);
+        CFMetaData cfMetaData = newCFMetaData(Sort.ASC, Sort.ASC, Sort.ASC, Sort.ASC);
 
         ByteBuffer value1 = ByteBufferUtil.bytes(1);
         ByteBuffer value2 = ByteBufferUtil.bytes(2);
@@ -852,7 +1682,7 @@ public class PrimaryKeyRestrictionSetTest
      */
     private static void assertEmptyComposite(Composite composite)
     {
-        assertEquals(Composites.EMPTY, composite);
+        assertTrue(composite.isEmpty());
     }
 
     /**
@@ -947,20 +1777,20 @@ public class PrimaryKeyRestrictionSetTest
      * @param numberOfClusteringColumns the number of clustering column
      * @return a new <code>CFMetaData</code> instance
      */
-    private static CFMetaData newCFMetaData(int numberOfClusteringColumns)
+    private static CFMetaData newCFMetaData(Sort... sorts)
     {
         List<AbstractType<?>> types = new ArrayList<>();
 
-        for (int i = 0; i < numberOfClusteringColumns; i++)
-            types.add(Int32Type.instance);
+        for (Sort sort : sorts)
+            types.add(sort == Sort.ASC ? Int32Type.instance : ReversedType.getInstance(Int32Type.instance));
 
         CompoundSparseCellNameType cType = new CompoundSparseCellNameType(types);
         CFMetaData cfMetaData = new CFMetaData("keyspace", "test", ColumnFamilyType.Standard, cType);
 
-        for (int i = 0; i < numberOfClusteringColumns; i++)
+        for (int i = 0; i < sorts.length; i++)
         {
             ByteBuffer name = ByteBufferUtil.bytes("clustering_" + i);
-            ColumnDefinition columnDef = ColumnDefinition.clusteringKeyDef(cfMetaData, name, Int32Type.instance, i);
+            ColumnDefinition columnDef = ColumnDefinition.clusteringKeyDef(cfMetaData, name, types.get(i), i);
             cfMetaData.addColumnDefinition(columnDef);
         }
         cfMetaData.rebuild();
@@ -1117,4 +1947,10 @@ public class PrimaryKeyRestrictionSetTest
             terms.add(toTerm(value));
         return terms;
     }
+
+    private static enum Sort
+    {
+        ASC,
+        DESC;
+    }
 }


[5/6] cassandra git commit: Merge branch cassandra-2.2 into cassandra-3.0

Posted by bl...@apache.org.
Merge branch cassandra-2.2 into cassandra-3.0


Project: http://git-wip-us.apache.org/repos/asf/cassandra/repo
Commit: http://git-wip-us.apache.org/repos/asf/cassandra/commit/1aa97e30
Tree: http://git-wip-us.apache.org/repos/asf/cassandra/tree/1aa97e30
Diff: http://git-wip-us.apache.org/repos/asf/cassandra/diff/1aa97e30

Branch: refs/heads/trunk
Commit: 1aa97e30ca075ad3c350d1b49aee94dffc62db19
Parents: 62f94b7 90fc896
Author: Benjamin Lerer <b....@gmail.com>
Authored: Thu Feb 11 10:40:50 2016 +0100
Committer: Benjamin Lerer <b....@gmail.com>
Committed: Thu Feb 11 10:42:17 2016 +0100

----------------------------------------------------------------------
 CHANGES.txt                                     |    1 +
 .../cql3/restrictions/AbstractRestriction.java  |   14 +
 .../ForwardingPrimaryKeyRestrictions.java       |    3 +-
 .../restrictions/MultiColumnRestriction.java    |   76 +-
 .../restrictions/PrimaryKeyRestrictionSet.java  |   22 +-
 .../cql3/restrictions/Restriction.java          |    4 +-
 .../cql3/restrictions/RestrictionSet.java       |    4 +-
 .../restrictions/SingleColumnRestriction.java   |    9 +-
 .../cql3/restrictions/TokenRestriction.java     |    2 +-
 .../org/apache/cassandra/db/MultiCBuilder.java  |   67 +-
 .../PrimaryKeyRestrictionSetTest.java           | 1174 +++++++++++++++---
 .../SelectMultiColumnRelationTest.java          |  859 ++++++++++++-
 12 files changed, 2025 insertions(+), 210 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/cassandra/blob/1aa97e30/CHANGES.txt
----------------------------------------------------------------------
diff --cc CHANGES.txt
index 188cdd6,5674b9d..bca5fcd
--- a/CHANGES.txt
+++ b/CHANGES.txt
@@@ -1,12 -1,5 +1,13 @@@
 -2.2.6
 +3.0.4
 + * AssertionError when listing sstable files on inconsistent disk state (CASSANDRA-11156)
 + * Fix wrong rack counting and invalid conditions check for TokenAllocation
 +   (CASSANDRA-11139)
 + * Avoid creating empty hint files (CASSANDRA-11090)
 + * Fix leak detection strong reference loop using weak reference (CASSANDRA-11120)
 + * Configurie BatchlogManager to stop delayed tasks on shutdown (CASSANDRA-11062)
 + * Hadoop integration is incompatible with Cassandra Driver 3.0.0 (CASSANDRA-11001)
 +Merged from 2.2.6:
+  * Fix SELECT on tuple relations for mixed ASC/DESC clustering order (CASSANDRA-7281)
   * (cqlsh) Support utf-8/cp65001 encoding on Windows (CASSANDRA-11030)
   * Fix paging on DISTINCT queries repeats result when first row in partition changes (CASSANDRA-10010)
  Merged from 2.1:

http://git-wip-us.apache.org/repos/asf/cassandra/blob/1aa97e30/src/java/org/apache/cassandra/cql3/restrictions/AbstractRestriction.java
----------------------------------------------------------------------
diff --cc src/java/org/apache/cassandra/cql3/restrictions/AbstractRestriction.java
index 023c2ac,dac7203..df04331
--- a/src/java/org/apache/cassandra/cql3/restrictions/AbstractRestriction.java
+++ b/src/java/org/apache/cassandra/cql3/restrictions/AbstractRestriction.java
@@@ -17,10 -17,19 +17,12 @@@
   */
  package org.apache.cassandra.cql3.restrictions;
  
 -import java.nio.ByteBuffer;
 -
 -import org.apache.cassandra.config.ColumnDefinition;
 -
 -import org.apache.cassandra.cql3.ColumnSpecification;
  import org.apache.cassandra.cql3.QueryOptions;
  import org.apache.cassandra.cql3.statements.Bound;
 -import org.apache.cassandra.db.composites.CompositesBuilder;
 -import org.apache.cassandra.exceptions.InvalidRequestException;
 -import static org.apache.cassandra.cql3.statements.RequestValidations.checkBindValueSet;
 -import static org.apache.cassandra.cql3.statements.RequestValidations.checkFalse;
 -import static org.apache.cassandra.cql3.statements.RequestValidations.checkNotNull;
 +import org.apache.cassandra.db.MultiCBuilder;
 +
++import org.apache.cassandra.config.ColumnDefinition;
+ 
  /**
   * Base class for <code>Restriction</code>s
   */
@@@ -85,4 -88,26 +87,16 @@@ abstract class AbstractRestriction  imp
      {
          return true;
      }
+ 
 -    protected static ByteBuffer validateIndexedValue(ColumnSpecification columnSpec,
 -                                                     ByteBuffer value)
 -                                                     throws InvalidRequestException
 -    {
 -        checkNotNull(value, "Unsupported null value for indexed column %s", columnSpec.name);
 -        checkBindValueSet(value, "Unsupported unset value for indexed column %s", columnSpec.name);
 -        checkFalse(value.remaining() > 0xFFFF, "Index expression values may not be larger than 64K");
 -        return value;
 -    }
 -
+     /**
+      * Reverses the specified bound if the column type is a reversed one.
+      *
+      * @param columnDefinition the column definition
+      * @param bound the bound
+      * @return the bound reversed if the column type was a reversed one or the original bound
+      */
+     protected static Bound reverseBoundIfNeeded(ColumnDefinition columnDefinition, Bound bound)
+     {
+         return columnDefinition.isReversedType() ? bound.reverse() : bound;
+     }
  }

http://git-wip-us.apache.org/repos/asf/cassandra/blob/1aa97e30/src/java/org/apache/cassandra/cql3/restrictions/ForwardingPrimaryKeyRestrictions.java
----------------------------------------------------------------------
diff --cc src/java/org/apache/cassandra/cql3/restrictions/ForwardingPrimaryKeyRestrictions.java
index 18e7105,71855a0..723ebc3
--- a/src/java/org/apache/cassandra/cql3/restrictions/ForwardingPrimaryKeyRestrictions.java
+++ b/src/java/org/apache/cassandra/cql3/restrictions/ForwardingPrimaryKeyRestrictions.java
@@@ -18,9 -18,7 +18,8 @@@
  package org.apache.cassandra.cql3.restrictions;
  
  import java.nio.ByteBuffer;
- import java.util.Collection;
  import java.util.List;
 +import java.util.NavigableSet;
  
  import org.apache.cassandra.config.ColumnDefinition;
  import org.apache.cassandra.cql3.QueryOptions;

http://git-wip-us.apache.org/repos/asf/cassandra/blob/1aa97e30/src/java/org/apache/cassandra/cql3/restrictions/MultiColumnRestriction.java
----------------------------------------------------------------------
diff --cc src/java/org/apache/cassandra/cql3/restrictions/MultiColumnRestriction.java
index 069a01b,96e6f2b..ec02d06
--- a/src/java/org/apache/cassandra/cql3/restrictions/MultiColumnRestriction.java
+++ b/src/java/org/apache/cassandra/cql3/restrictions/MultiColumnRestriction.java
@@@ -357,16 -357,64 +357,64 @@@ public abstract class MultiColumnRestri
          }
  
          @Override
 -        public CompositesBuilder appendBoundTo(CompositesBuilder builder, Bound bound, QueryOptions options)
 +        public MultiCBuilder appendBoundTo(MultiCBuilder builder, Bound bound, QueryOptions options)
          {
-             List<ByteBuffer> vals = componentBounds(bound, options);
+             boolean reversed = getFirstColumn().isReversedType();
  
-             for (int i = 0, m = vals.size(); i < m; i++)
+             EnumMap<Bound, List<ByteBuffer>> componentBounds = new EnumMap<Bound, List<ByteBuffer>>(Bound.class);
+             componentBounds.put(Bound.START, componentBounds(Bound.START, options));
+             componentBounds.put(Bound.END, componentBounds(Bound.END, options));
+ 
+             List<List<ByteBuffer>> toAdd = new ArrayList<>();
+             List<ByteBuffer> values = new ArrayList<>();
+ 
+             for (int i = 0, m = columnDefs.size(); i < m; i++)
              {
-                 ByteBuffer v = checkNotNull(vals.get(i), "Invalid null value in condition for column %s", columnDefs.get(i).name);
-                 builder.addElementToAll(v);
+                 ColumnDefinition column = columnDefs.get(i);
+                 Bound b = reverseBoundIfNeeded(column, bound);
+ 
+                 // For mixed order columns, we need to create additional slices when 2 columns are in reverse order
+                 if (reversed != column.isReversedType())
+                 {
+                     reversed = column.isReversedType();
+                     // As we are switching direction we need to add the current composite
+                     toAdd.add(values);
+ 
+                     // The new bound side has no value for this component.  just stop
+                     if (!hasComponent(b, i, componentBounds))
+                         continue;
+ 
+                     // The other side has still some components. We need to end the slice that we have just open.
+                     if (hasComponent(b.reverse(), i, componentBounds))
+                         toAdd.add(values);
+ 
+                     // We need to rebuild where we are in this bound side
+                     values = new ArrayList<ByteBuffer>();
+ 
+                     List<ByteBuffer> vals = componentBounds.get(b);
+ 
+                     int n = Math.min(i, vals.size());
+                     for (int j = 0; j < n; j++)
+                     {
+                         ByteBuffer v = checkNotNull(vals.get(j),
+                                                     "Invalid null value in condition for column %s",
+                                                     columnDefs.get(j).name);
+                         values.add(v);
+                     }
+                 }
+ 
+                 if (!hasComponent(b, i, componentBounds))
+                     continue;
+ 
+                 ByteBuffer v = checkNotNull(componentBounds.get(b).get(i), "Invalid null value in condition for column %s", columnDefs.get(i).name);
+                 values.add(v);
              }
-             return builder;
+             toAdd.add(values);
+ 
+             if (bound.isEnd())
+                 Collections.reverse(toAdd);
+ 
+             return builder.addAllElementsToAll(toAdd);
          }
  
          @Override
@@@ -458,60 -506,10 +509,65 @@@
  
              return Collections.singletonList(terminal.get(options.getProtocolVersion()));
          }
+ 
+         private boolean hasComponent(Bound b, int index, EnumMap<Bound, List<ByteBuffer>> componentBounds)
+         {
+             return componentBounds.get(b).size() > index;
+         }
      }
 +
 +    public static class NotNullRestriction extends MultiColumnRestriction
 +    {
 +        public NotNullRestriction(List<ColumnDefinition> columnDefs)
 +        {
 +            super(columnDefs);
 +            assert columnDefs.size() == 1;
 +        }
 +
 +        @Override
 +        public Iterable<Function> getFunctions()
 +        {
 +            return Collections.emptyList();
 +        }
 +
 +        @Override
 +        public boolean isNotNull()
 +        {
 +            return true;
 +        }
 +
 +        @Override
 +        public String toString()
 +        {
 +            return "IS NOT NULL";
 +        }
 +
 +        @Override
 +        public Restriction doMergeWith(Restriction otherRestriction) throws InvalidRequestException
 +        {
 +            throw invalidRequest("%s cannot be restricted by a relation if it includes an IS NOT NULL clause",
 +                                 getColumnsInCommons(otherRestriction));
 +        }
 +
 +        @Override
 +        protected boolean isSupportedBy(Index index)
 +        {
 +            for(ColumnDefinition column : columnDefs)
 +                if (index.supportsExpression(column, Operator.IS_NOT))
 +                    return true;
 +            return false;
 +        }
 +
 +        @Override
 +        public MultiCBuilder appendTo(MultiCBuilder builder, QueryOptions options)
 +        {
 +            throw new UnsupportedOperationException("Cannot use IS NOT NULL restriction for slicing");
 +        }
 +
 +        @Override
 +        public final void addRowFilterTo(RowFilter filter, SecondaryIndexManager indexMananger, QueryOptions options) throws InvalidRequestException
 +        {
 +            throw new UnsupportedOperationException("Secondary indexes do not support IS NOT NULL restrictions");
 +        }
 +    }
  }

http://git-wip-us.apache.org/repos/asf/cassandra/blob/1aa97e30/src/java/org/apache/cassandra/cql3/restrictions/PrimaryKeyRestrictionSet.java
----------------------------------------------------------------------
diff --cc src/java/org/apache/cassandra/cql3/restrictions/PrimaryKeyRestrictionSet.java
index 107cbd1,936dbd6..8121858
--- a/src/java/org/apache/cassandra/cql3/restrictions/PrimaryKeyRestrictionSet.java
+++ b/src/java/org/apache/cassandra/cql3/restrictions/PrimaryKeyRestrictionSet.java
@@@ -216,22 -200,14 +213,17 @@@ final class PrimaryKeyRestrictionSet ex
  
              if (r.isSlice())
              {
-                 if (!r.hasBound(b))
-                 {
-                     // There wasn't any non EQ relation on that key, we select all records having the preceding component as prefix.
-                     // For composites, if there was preceding component and we're computing the end, we must change the last component
-                     // End-Of-Component, otherwise we would be selecting only one record.
-                     return builder.buildBound(bound.isStart(), true);
-                 }
- 
-                 r.appendBoundTo(builder, b, options);
-                 return builder.buildBound(bound.isStart(), r.isInclusive(b));
+                 r.appendBoundTo(builder, bound, options);
 -                return filterAndSort(setEocs(r, bound, builder.build()));
++                return builder.buildBoundForSlice(bound.isStart(),
++                                                  r.isInclusive(bound),
++                                                  r.isInclusive(bound.reverse()),
++                                                  r.getColumnDefs());
              }
  
-             r.appendBoundTo(builder, b, options);
+             r.appendBoundTo(builder, bound, options);
  
              if (builder.hasMissingElements())
 -                return Collections.emptyList();
 +                return BTreeSet.empty(comparator);
  
              keyPosition = r.getLastColumn().position() + 1;
          }

http://git-wip-us.apache.org/repos/asf/cassandra/blob/1aa97e30/src/java/org/apache/cassandra/cql3/restrictions/Restriction.java
----------------------------------------------------------------------

http://git-wip-us.apache.org/repos/asf/cassandra/blob/1aa97e30/src/java/org/apache/cassandra/cql3/restrictions/RestrictionSet.java
----------------------------------------------------------------------

http://git-wip-us.apache.org/repos/asf/cassandra/blob/1aa97e30/src/java/org/apache/cassandra/cql3/restrictions/SingleColumnRestriction.java
----------------------------------------------------------------------
diff --cc src/java/org/apache/cassandra/cql3/restrictions/SingleColumnRestriction.java
index d851253,735a2e2..3a61616
--- a/src/java/org/apache/cassandra/cql3/restrictions/SingleColumnRestriction.java
+++ b/src/java/org/apache/cassandra/cql3/restrictions/SingleColumnRestriction.java
@@@ -350,9 -349,14 +350,14 @@@ public abstract class SingleColumnRestr
          }
  
          @Override
 -        public CompositesBuilder appendBoundTo(CompositesBuilder builder, Bound bound, QueryOptions options)
 +        public MultiCBuilder appendBoundTo(MultiCBuilder builder, Bound bound, QueryOptions options)
          {
-             ByteBuffer value = slice.bound(bound).bindAndGet(options);
+             Bound b = reverseBoundIfNeeded(getFirstColumn(), bound);
+ 
+             if (!hasBound(b))
+                 return builder;
+ 
+             ByteBuffer value = slice.bound(b).bindAndGet(options);
              checkBindValueSet(value, "Invalid unset value for column %s", columnDef.name);
              return builder.addElementToAll(value);
  

http://git-wip-us.apache.org/repos/asf/cassandra/blob/1aa97e30/src/java/org/apache/cassandra/cql3/restrictions/TokenRestriction.java
----------------------------------------------------------------------

http://git-wip-us.apache.org/repos/asf/cassandra/blob/1aa97e30/src/java/org/apache/cassandra/db/MultiCBuilder.java
----------------------------------------------------------------------
diff --cc src/java/org/apache/cassandra/db/MultiCBuilder.java
index be654fa,0000000..7c77ab0
mode 100644,000000..100644
--- a/src/java/org/apache/cassandra/db/MultiCBuilder.java
+++ b/src/java/org/apache/cassandra/db/MultiCBuilder.java
@@@ -1,324 -1,0 +1,379 @@@
 +/*
 + * Licensed to the Apache Software Foundation (ASF) under one
 + * or more contributor license agreements.  See the NOTICE file
 + * distributed with this work for additional information
 + * regarding copyright ownership.  The ASF licenses this file
 + * to you under the Apache License, Version 2.0 (the
 + * "License"); you may not use this file except in compliance
 + * with the License.  You may obtain a copy of the License at
 + *
 + *     http://www.apache.org/licenses/LICENSE-2.0
 + *
 + * Unless required by applicable law or agreed to in writing, software
 + * distributed under the License is distributed on an "AS IS" BASIS,
 + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 + * See the License for the specific language governing permissions and
 + * limitations under the License.
 + */
 +package org.apache.cassandra.db;
 +
 +import java.nio.ByteBuffer;
- import java.util.*;
++import java.util.ArrayList;
++import java.util.List;
++import java.util.NavigableSet;
 +
++import org.apache.cassandra.config.ColumnDefinition;
 +import org.apache.cassandra.utils.ByteBufferUtil;
 +import org.apache.cassandra.utils.btree.BTreeSet;
 +
 +/**
 + * Builder that allow to build multiple Clustering/Slice.Bound at the same time.
 + */
 +public class MultiCBuilder
 +{
 +    /**
 +     * The table comparator.
 +     */
 +    private final ClusteringComparator comparator;
 +
 +    /**
 +     * The elements of the clusterings
 +     */
 +    private final List<List<ByteBuffer>> elementsList = new ArrayList<>();
 +
 +    /**
 +     * The number of elements that have been added.
 +     */
 +    private int size;
 +
 +    /**
 +     * <code>true</code> if the clusterings have been build, <code>false</code> otherwise.
 +     */
 +    private boolean built;
 +
 +    /**
 +     * <code>true</code> if the clusterings contains some <code>null</code> elements.
 +     */
 +    private boolean containsNull;
 +
 +    /**
 +     * <code>true</code> if the composites contains some <code>unset</code> elements.
 +     */
 +    private boolean containsUnset;
 +
 +    /**
 +     * <code>true</code> if some empty collection have been added.
 +     */
 +    private boolean hasMissingElements;
 +
 +    private MultiCBuilder(ClusteringComparator comparator)
 +    {
 +        this.comparator = comparator;
 +    }
 +
 +    /**
 +     * Creates a new empty {@code MultiCBuilder}.
 +     */
 +    public static MultiCBuilder create(ClusteringComparator comparator)
 +    {
 +        return new MultiCBuilder(comparator);
 +    }
 +
 +    /**
 +     * Checks if this builder is empty.
 +     *
 +     * @return <code>true</code> if this builder is empty, <code>false</code> otherwise.
 +     */
 +    private boolean isEmpty()
 +    {
 +        return elementsList.isEmpty();
 +    }
 +
 +    /**
 +     * Adds the specified element to all the clusterings.
 +     * <p>
 +     * If this builder contains 2 clustering: A-B and A-C a call to this method to add D will result in the clusterings:
 +     * A-B-D and A-C-D.
 +     * </p>
 +     *
 +     * @param value the value of the next element
 +     * @return this <code>MulitCBuilder</code>
 +     */
 +    public MultiCBuilder addElementToAll(ByteBuffer value)
 +    {
 +        checkUpdateable();
 +
 +        if (isEmpty())
 +            elementsList.add(new ArrayList<ByteBuffer>());
 +
 +        for (int i = 0, m = elementsList.size(); i < m; i++)
 +        {
 +            if (value == null)
 +                containsNull = true;
 +            if (value == ByteBufferUtil.UNSET_BYTE_BUFFER)
 +                containsUnset = true;
 +
 +            elementsList.get(i).add(value);
 +        }
 +        size++;
 +        return this;
 +    }
 +
 +    /**
 +     * Adds individually each of the specified elements to the end of all of the existing clusterings.
 +     * <p>
 +     * If this builder contains 2 clusterings: A-B and A-C a call to this method to add D and E will result in the 4
 +     * clusterings: A-B-D, A-B-E, A-C-D and A-C-E.
 +     * </p>
 +     *
 +     * @param values the elements to add
 +     * @return this <code>CompositeBuilder</code>
 +     */
 +    public MultiCBuilder addEachElementToAll(List<ByteBuffer> values)
 +    {
 +        checkUpdateable();
 +
 +        if (isEmpty())
 +            elementsList.add(new ArrayList<ByteBuffer>());
 +
 +        if (values.isEmpty())
 +        {
 +            hasMissingElements = true;
 +        }
 +        else
 +        {
 +            for (int i = 0, m = elementsList.size(); i < m; i++)
 +            {
 +                List<ByteBuffer> oldComposite = elementsList.remove(0);
 +
 +                for (int j = 0, n = values.size(); j < n; j++)
 +                {
 +                    List<ByteBuffer> newComposite = new ArrayList<>(oldComposite);
 +                    elementsList.add(newComposite);
 +
 +                    ByteBuffer value = values.get(j);
 +
 +                    if (value == null)
 +                        containsNull = true;
 +                    if (value == ByteBufferUtil.UNSET_BYTE_BUFFER)
 +                        containsUnset = true;
 +
 +                    newComposite.add(values.get(j));
 +                }
 +            }
 +        }
 +        size++;
 +        return this;
 +    }
 +
 +    /**
 +     * Adds individually each of the specified list of elements to the end of all of the existing composites.
 +     * <p>
 +     * If this builder contains 2 composites: A-B and A-C a call to this method to add [[D, E], [F, G]] will result in the 4
 +     * composites: A-B-D-E, A-B-F-G, A-C-D-E and A-C-F-G.
 +     * </p>
 +     *
 +     * @param values the elements to add
 +     * @return this <code>CompositeBuilder</code>
 +     */
 +    public MultiCBuilder addAllElementsToAll(List<List<ByteBuffer>> values)
 +    {
 +        checkUpdateable();
 +
 +        if (isEmpty())
 +            elementsList.add(new ArrayList<ByteBuffer>());
 +
 +        if (values.isEmpty())
 +        {
 +            hasMissingElements = true;
 +        }
 +        else
 +        {
 +            for (int i = 0, m = elementsList.size(); i < m; i++)
 +            {
 +                List<ByteBuffer> oldComposite = elementsList.remove(0);
 +
 +                for (int j = 0, n = values.size(); j < n; j++)
 +                {
 +                    List<ByteBuffer> newComposite = new ArrayList<>(oldComposite);
 +                    elementsList.add(newComposite);
 +
 +                    List<ByteBuffer> value = values.get(j);
 +
-                     if (value.isEmpty())
-                         hasMissingElements = true;
- 
 +                    if (value.contains(null))
 +                        containsNull = true;
 +                    if (value.contains(ByteBufferUtil.UNSET_BYTE_BUFFER))
 +                        containsUnset = true;
 +
 +                    newComposite.addAll(value);
 +                }
 +            }
 +            size += values.get(0).size();
 +        }
 +        return this;
 +    }
 +
 +    /**
 +     * Returns the number of elements that can be added to the clusterings.
 +     *
 +     * @return the number of elements that can be added to the clusterings.
 +     */
 +    public int remainingCount()
 +    {
 +        return comparator.size() - size;
 +    }
 +
 +    /**
 +     * Checks if the clusterings contains null elements.
 +     *
 +     * @return <code>true</code> if the clusterings contains <code>null</code> elements, <code>false</code> otherwise.
 +     */
 +    public boolean containsNull()
 +    {
 +        return containsNull;
 +    }
 +
 +    /**
 +     * Checks if the clusterings contains unset elements.
 +     *
 +     * @return <code>true</code> if the clusterings contains <code>unset</code> elements, <code>false</code> otherwise.
 +     */
 +    public boolean containsUnset()
 +    {
 +        return containsUnset;
 +    }
 +
 +    /**
 +     * Checks if some empty list of values have been added
 +     * @return <code>true</code> if the clusterings have some missing elements, <code>false</code> otherwise.
 +     */
 +    public boolean hasMissingElements()
 +    {
 +        return hasMissingElements;
 +    }
 +
 +    /**
 +     * Builds the <code>clusterings</code>.
 +     *
 +     * @return the clusterings
 +     */
 +    public NavigableSet<Clustering> build()
 +    {
 +        built = true;
 +
 +        if (hasMissingElements)
 +            return BTreeSet.empty(comparator);
 +
 +        CBuilder builder = CBuilder.create(comparator);
 +
 +        if (elementsList.isEmpty())
 +            return BTreeSet.of(builder.comparator(), builder.build());
 +
 +        BTreeSet.Builder<Clustering> set = BTreeSet.builder(builder.comparator());
 +        for (int i = 0, m = elementsList.size(); i < m; i++)
 +        {
 +            List<ByteBuffer> elements = elementsList.get(i);
 +            set.add(builder.buildWith(elements));
 +        }
 +        return set.build();
 +    }
 +
 +    /**
-      * Builds the <code>clusterings</code> with the specified EOC.
++     * Builds the <code>Slice.Bound</code>s for slice restrictions.
 +     *
-      * @return the clusterings
++     * @param isStart specify if the bound is a start one
++     * @param isInclusive specify if the bound is inclusive or not
++     * @param isOtherBoundInclusive specify if the other bound is inclusive or not
++     * @param columnDefs the columns of the slice restriction
++     * @return the <code>Slice.Bound</code>s
 +     */
++    public NavigableSet<Slice.Bound> buildBoundForSlice(boolean isStart,
++                                                        boolean isInclusive,
++                                                        boolean isOtherBoundInclusive,
++                                                        List<ColumnDefinition> columnDefs)
++    {
++        built = true;
++
++        if (hasMissingElements)
++            return BTreeSet.empty(comparator);
++
++        CBuilder builder = CBuilder.create(comparator);
++
++        if (elementsList.isEmpty())
++            return BTreeSet.of(comparator, builder.buildBound(isStart, isInclusive));
++
++        // Use a TreeSet to sort and eliminate duplicates
++        BTreeSet.Builder<Slice.Bound> set = BTreeSet.builder(comparator);
++
++        // The first column of the slice might not be the first clustering column (e.g. clustering_0 = ? AND (clustering_1, clustering_2) >= (?, ?)
++        int offset = columnDefs.get(0).position();
++
++        for (int i = 0, m = elementsList.size(); i < m; i++)
++        {
++            List<ByteBuffer> elements = elementsList.get(i);
++
++            // Handle the no bound case
++            if (elements.size() == offset)
++            {
++                set.add(builder.buildBoundWith(elements, isStart, true));
++                continue;
++            }
++
++            // In the case of mixed order columns, we will have some extra slices where the columns change directions.
++            // For example: if we have clustering_0 DESC and clustering_1 ASC a slice like (clustering_0, clustering_1) > (1, 2)
++            // will produce 2 slices: [BOTTOM, 1) and (1.2, 1]
++            // So, the END bound will return 2 bounds with the same values 1
++            ColumnDefinition lastColumn = columnDefs.get(columnDefs.size() - 1);
++            if (elements.size() <= lastColumn.position() && i < m - 1 && elements.equals(elementsList.get(i + 1)))
++            {
++                set.add(builder.buildBoundWith(elements, isStart, false));
++                set.add(builder.buildBoundWith(elementsList.get(i++), isStart, true));
++                continue;
++            }
++
++            // Handle the normal bounds
++            ColumnDefinition column = columnDefs.get(elements.size() - 1 - offset);
++            set.add(builder.buildBoundWith(elements, isStart, column.isReversedType() ? isOtherBoundInclusive : isInclusive));
++        }
++        return set.build();
++    }
++
 +    public NavigableSet<Slice.Bound> buildBound(boolean isStart, boolean isInclusive)
 +    {
 +        built = true;
 +
 +        if (hasMissingElements)
 +            return BTreeSet.empty(comparator);
 +
 +        CBuilder builder = CBuilder.create(comparator);
 +
 +        if (elementsList.isEmpty())
 +            return BTreeSet.of(comparator, builder.buildBound(isStart, isInclusive));
 +
 +        // Use a TreeSet to sort and eliminate duplicates
 +        BTreeSet.Builder<Slice.Bound> set = BTreeSet.builder(comparator);
 +
 +        for (int i = 0, m = elementsList.size(); i < m; i++)
 +        {
 +            List<ByteBuffer> elements = elementsList.get(i);
 +            set.add(builder.buildBoundWith(elements, isStart, isInclusive));
 +        }
 +        return set.build();
 +    }
 +
 +    /**
 +     * Checks if some elements can still be added to the clusterings.
 +     *
 +     * @return <code>true</code> if it is possible to add more elements to the clusterings, <code>false</code> otherwise.
 +     */
 +    public boolean hasRemaining()
 +    {
 +        return remainingCount() > 0;
 +    }
 +
 +    private void checkUpdateable()
 +    {
 +        if (!hasRemaining() || built)
 +            throw new IllegalStateException("this builder cannot be updated anymore");
 +    }
 +}


[4/6] cassandra git commit: Merge branch cassandra-2.2 into cassandra-3.0

Posted by bl...@apache.org.
http://git-wip-us.apache.org/repos/asf/cassandra/blob/1aa97e30/test/unit/org/apache/cassandra/cql3/restrictions/PrimaryKeyRestrictionSetTest.java
----------------------------------------------------------------------
diff --cc test/unit/org/apache/cassandra/cql3/restrictions/PrimaryKeyRestrictionSetTest.java
index d738c46,1deeb9e..abbd36b
--- a/test/unit/org/apache/cassandra/cql3/restrictions/PrimaryKeyRestrictionSetTest.java
+++ b/test/unit/org/apache/cassandra/cql3/restrictions/PrimaryKeyRestrictionSetTest.java
@@@ -28,10 -28,13 +28,11 @@@ import org.apache.cassandra.config.Colu
  import org.apache.cassandra.cql3.*;
  import org.apache.cassandra.cql3.Term.MultiItemTerminal;
  import org.apache.cassandra.cql3.statements.Bound;
 -import org.apache.cassandra.db.ColumnFamilyType;
 -import org.apache.cassandra.db.composites.Composite;
 -import org.apache.cassandra.db.composites.Composite.EOC;
 -import org.apache.cassandra.db.composites.CompoundSparseCellNameType;
++
 +import org.apache.cassandra.db.*;
  import org.apache.cassandra.db.marshal.AbstractType;
  import org.apache.cassandra.db.marshal.Int32Type;
- import org.apache.cassandra.exceptions.InvalidRequestException;
+ import org.apache.cassandra.db.marshal.ReversedType;
  import org.apache.cassandra.utils.ByteBufferUtil;
  
  import static java.util.Arrays.asList;
@@@ -41,28 -44,28 +42,28 @@@ import static org.junit.Assert.assertTr
  public class PrimaryKeyRestrictionSetTest
  {
      @Test
-     public void testboundsAsClusteringWithNoRestrictions() throws InvalidRequestException
 -    public void testBoundsAsCompositesWithNoRestrictions()
++    public void testBoundsAsClusteringWithNoRestrictions()
      {
-         CFMetaData cfMetaData = newCFMetaData(1);
+         CFMetaData cfMetaData = newCFMetaData(Sort.ASC);
  
 -        PrimaryKeyRestrictions restrictions = new PrimaryKeyRestrictionSet(cfMetaData.comparator);
 +        PrimaryKeyRestrictions restrictions = new PrimaryKeyRestrictionSet(cfMetaData.comparator, false);
  
 -        List<Composite> bounds = restrictions.boundsAsComposites(Bound.START, QueryOptions.DEFAULT);
 +        SortedSet<Slice.Bound> bounds = restrictions.boundsAsClustering(Bound.START, QueryOptions.DEFAULT);
          assertEquals(1, bounds.size());
 -        assertEmptyComposite(bounds.get(0));
 +        assertEmptyStart(get(bounds, 0));
  
 -        bounds = restrictions.boundsAsComposites(Bound.END, QueryOptions.DEFAULT);
 +        bounds = restrictions.boundsAsClustering(Bound.END, QueryOptions.DEFAULT);
          assertEquals(1, bounds.size());
 -        assertEmptyComposite(bounds.get(0));
 +        assertEmptyEnd(get(bounds, 0));
      }
  
      /**
       * Test 'clustering_0 = 1' with only one clustering column
       */
      @Test
-     public void testboundsAsClusteringWithOneEqRestrictionsAndOneClusteringColumn() throws InvalidRequestException
 -    public void testBoundsAsCompositesWithOneEqRestrictionsAndOneClusteringColumn()
++    public void testBoundsAsClusteringWithOneEqRestrictionsAndOneClusteringColumn()
      {
-         CFMetaData cfMetaData = newCFMetaData(1);
+         CFMetaData cfMetaData = newCFMetaData(Sort.ASC);
  
          ByteBuffer clustering_0 = ByteBufferUtil.bytes(1);
          Restriction eq = newSingleEq(cfMetaData, 0, clustering_0);
@@@ -83,9 -86,9 +84,9 @@@
       * Test 'clustering_1 = 1' with 2 clustering columns
       */
      @Test
-     public void testboundsAsClusteringWithOneEqRestrictionsAndTwoClusteringColumns() throws InvalidRequestException
 -    public void testBoundsAsCompositesWithOneEqRestrictionsAndTwoClusteringColumns()
++    public void testBoundsAsClusteringWithOneEqRestrictionsAndTwoClusteringColumns()
      {
-         CFMetaData cfMetaData = newCFMetaData(2);
+         CFMetaData cfMetaData = newCFMetaData(Sort.ASC, Sort.ASC);
  
          ByteBuffer clustering_0 = ByteBufferUtil.bytes(1);
          Restriction eq = newSingleEq(cfMetaData, 0, clustering_0);
@@@ -106,7 -109,7 +107,7 @@@
       * Test 'clustering_0 IN (1, 2, 3)' with only one clustering column
       */
      @Test
-     public void testboundsAsClusteringWithOneInRestrictionsAndOneClusteringColumn() throws InvalidRequestException
 -    public void testBoundsAsCompositesWithOneInRestrictionsAndOneClusteringColumn()
++    public void testBoundsAsClusteringWithOneInRestrictionsAndOneClusteringColumn()
      {
          ByteBuffer value1 = ByteBufferUtil.bytes(1);
          ByteBuffer value2 = ByteBufferUtil.bytes(2);
@@@ -136,9 -139,9 +137,9 @@@
       * Test slice restriction (e.g 'clustering_0 > 1') with only one clustering column
       */
      @Test
-     public void testboundsAsClusteringWithSliceRestrictionsAndOneClusteringColumn() throws InvalidRequestException
 -    public void testBoundsAsCompositesWithSliceRestrictionsAndOneClusteringColumn()
++    public void testBoundsAsClusteringWithSliceRestrictionsAndOneClusteringColumn()
      {
-         CFMetaData cfMetaData = newCFMetaData(1);
+         CFMetaData cfMetaData = newCFMetaData(Sort.ASC, Sort.ASC);
  
          ByteBuffer value1 = ByteBufferUtil.bytes(1);
          ByteBuffer value2 = ByteBufferUtil.bytes(2);
@@@ -219,12 -222,98 +220,98 @@@
      }
  
      /**
+      * Test slice restriction (e.g 'clustering_0 > 1') with only one descending clustering column
+      */
+     @Test
 -    public void testBoundsAsCompositesWithSliceRestrictionsAndOneDescendingClusteringColumn()
++    public void testBoundsAsClusteringWithSliceRestrictionsAndOneDescendingClusteringColumn()
+     {
+         CFMetaData cfMetaData = newCFMetaData(Sort.DESC, Sort.DESC);
+ 
+         ByteBuffer value1 = ByteBufferUtil.bytes(1);
+         ByteBuffer value2 = ByteBufferUtil.bytes(2);
+ 
+         Restriction slice = newSingleSlice(cfMetaData, 0, Bound.START, false, value1);
 -        PrimaryKeyRestrictions restrictions = new PrimaryKeyRestrictionSet(cfMetaData.comparator);
++        PrimaryKeyRestrictions restrictions = new PrimaryKeyRestrictionSet(cfMetaData.comparator, false);
+         restrictions = restrictions.mergeWith(slice);
+ 
 -        List<Composite> bounds = restrictions.boundsAsComposites(Bound.START, QueryOptions.DEFAULT);
++        SortedSet<Slice.Bound> bounds = restrictions.boundsAsClustering(Bound.START, QueryOptions.DEFAULT);
+         assertEquals(1, bounds.size());
 -        assertEmptyComposite(bounds.get(0));
++        assertEmptyStart(get(bounds, 0));
+ 
 -        bounds = restrictions.boundsAsComposites(Bound.END, QueryOptions.DEFAULT);
++        bounds = restrictions.boundsAsClustering(Bound.END, QueryOptions.DEFAULT);
+         assertEquals(1, bounds.size());
 -        assertComposite(bounds.get(0), value1, EOC.START);
++        assertEndBound(get(bounds, 0), false, value1);
+ 
+         slice = newSingleSlice(cfMetaData, 0, Bound.START, true, value1);
 -        restrictions = new PrimaryKeyRestrictionSet(cfMetaData.comparator);
++        restrictions = new PrimaryKeyRestrictionSet(cfMetaData.comparator, false);
+         restrictions = restrictions.mergeWith(slice);
+ 
 -        bounds = restrictions.boundsAsComposites(Bound.START, QueryOptions.DEFAULT);
++        bounds = restrictions.boundsAsClustering(Bound.START, QueryOptions.DEFAULT);
+         assertEquals(1, bounds.size());
 -        assertEmptyComposite(bounds.get(0));
++        assertEmptyStart(get(bounds, 0));
+ 
 -        bounds = restrictions.boundsAsComposites(Bound.END, QueryOptions.DEFAULT);
++        bounds = restrictions.boundsAsClustering(Bound.END, QueryOptions.DEFAULT);
+         assertEquals(1, bounds.size());
 -        assertComposite(bounds.get(0), value1, EOC.END);
++        assertEndBound(get(bounds, 0), true, value1);
+ 
+         slice = newSingleSlice(cfMetaData, 0, Bound.END, true, value1);
 -        restrictions = new PrimaryKeyRestrictionSet(cfMetaData.comparator);
++        restrictions = new PrimaryKeyRestrictionSet(cfMetaData.comparator, false);
+         restrictions = restrictions.mergeWith(slice);
+ 
 -        bounds = restrictions.boundsAsComposites(Bound.START, QueryOptions.DEFAULT);
++        bounds = restrictions.boundsAsClustering(Bound.START, QueryOptions.DEFAULT);
+         assertEquals(1, bounds.size());
 -        assertComposite(bounds.get(0), value1, EOC.NONE);
++        assertStartBound(get(bounds, 0), true, value1);
+ 
 -        bounds = restrictions.boundsAsComposites(Bound.END, QueryOptions.DEFAULT);
++        bounds = restrictions.boundsAsClustering(Bound.END, QueryOptions.DEFAULT);
+         assertEquals(1, bounds.size());
 -        assertEmptyComposite(bounds.get(0));
++        assertEmptyEnd(get(bounds, 0));
+ 
+         slice = newSingleSlice(cfMetaData, 0, Bound.END, false, value1);
 -        restrictions = new PrimaryKeyRestrictionSet(cfMetaData.comparator);
++        restrictions = new PrimaryKeyRestrictionSet(cfMetaData.comparator, false);
+         restrictions = restrictions.mergeWith(slice);
+ 
 -        bounds = restrictions.boundsAsComposites(Bound.START, QueryOptions.DEFAULT);
++        bounds = restrictions.boundsAsClustering(Bound.START, QueryOptions.DEFAULT);
+         assertEquals(1, bounds.size());
 -        assertComposite(bounds.get(0), value1, EOC.END);
++        assertStartBound(get(bounds, 0), false, value1);
+ 
 -        bounds = restrictions.boundsAsComposites(Bound.END, QueryOptions.DEFAULT);
++        bounds = restrictions.boundsAsClustering(Bound.END, QueryOptions.DEFAULT);
+         assertEquals(1, bounds.size());
 -        assertEmptyComposite(bounds.get(0));
++        assertEmptyEnd(get(bounds, 0));
+ 
+         slice = newSingleSlice(cfMetaData, 0, Bound.START, false, value1);
+         Restriction slice2 = newSingleSlice(cfMetaData, 0, Bound.END, false, value2);
 -        restrictions = new PrimaryKeyRestrictionSet(cfMetaData.comparator);
++        restrictions = new PrimaryKeyRestrictionSet(cfMetaData.comparator, false);
+         restrictions = restrictions.mergeWith(slice).mergeWith(slice2);
+ 
 -        bounds = restrictions.boundsAsComposites(Bound.START, QueryOptions.DEFAULT);
++        bounds = restrictions.boundsAsClustering(Bound.START, QueryOptions.DEFAULT);
+         assertEquals(1, bounds.size());
 -        assertComposite(bounds.get(0), value2, EOC.END);
++        assertStartBound(get(bounds, 0), false, value2);
+ 
 -        bounds = restrictions.boundsAsComposites(Bound.END, QueryOptions.DEFAULT);
++        bounds = restrictions.boundsAsClustering(Bound.END, QueryOptions.DEFAULT);
+         assertEquals(1, bounds.size());
 -        assertComposite(bounds.get(0), value1, EOC.START);
++        assertEndBound(get(bounds, 0), false, value1);
+ 
+         slice = newSingleSlice(cfMetaData, 0, Bound.START, true, value1);
+         slice2 = newSingleSlice(cfMetaData, 0, Bound.END, true, value2);
 -        restrictions = new PrimaryKeyRestrictionSet(cfMetaData.comparator);
++        restrictions = new PrimaryKeyRestrictionSet(cfMetaData.comparator, false);
+         restrictions = restrictions.mergeWith(slice).mergeWith(slice2);
+ 
 -        bounds = restrictions.boundsAsComposites(Bound.START, QueryOptions.DEFAULT);
++        bounds = restrictions.boundsAsClustering(Bound.START, QueryOptions.DEFAULT);
+         assertEquals(1, bounds.size());
 -        assertComposite(bounds.get(0), value2, EOC.NONE);
++        assertStartBound(get(bounds, 0), true, value2);
+ 
 -        bounds = restrictions.boundsAsComposites(Bound.END, QueryOptions.DEFAULT);
++        bounds = restrictions.boundsAsClustering(Bound.END, QueryOptions.DEFAULT);
+         assertEquals(1, bounds.size());
 -        assertComposite(bounds.get(0), value1, EOC.END);
++        assertEndBound(get(bounds, 0), true, value1);
+     }
+ 
+     /**
       * Test 'clustering_0 = 1 AND clustering_1 IN (1, 2, 3)'
       */
      @Test
-     public void testboundsAsClusteringWithEqAndInRestrictions() throws InvalidRequestException
 -    public void testBoundsAsCompositesWithEqAndInRestrictions()
++    public void testBoundsAsClusteringWithEqAndInRestrictions()
      {
-         CFMetaData cfMetaData = newCFMetaData(2);
+         CFMetaData cfMetaData = newCFMetaData(Sort.ASC, Sort.ASC);
  
          ByteBuffer value1 = ByteBufferUtil.bytes(1);
          ByteBuffer value2 = ByteBufferUtil.bytes(2);
@@@ -251,9 -340,9 +338,9 @@@
       * Test equal and slice restrictions (e.g 'clustering_0 = 0 clustering_1 > 1')
       */
      @Test
-     public void testboundsAsClusteringWithEqAndSliceRestrictions() throws InvalidRequestException
 -    public void testBoundsAsCompositesWithEqAndSliceRestrictions()
++    public void testBoundsAsClusteringWithEqAndSliceRestrictions()
      {
-         CFMetaData cfMetaData = newCFMetaData(2);
+         CFMetaData cfMetaData = newCFMetaData(Sort.ASC, Sort.ASC);
  
          ByteBuffer value1 = ByteBufferUtil.bytes(1);
          ByteBuffer value2 = ByteBufferUtil.bytes(2);
@@@ -262,314 -351,1059 +349,1059 @@@
          Restriction eq = newSingleEq(cfMetaData, 0, value3);
  
          Restriction slice = newSingleSlice(cfMetaData, 1, Bound.START, false, value1);
 -        PrimaryKeyRestrictions restrictions = new PrimaryKeyRestrictionSet(cfMetaData.comparator);
 +        PrimaryKeyRestrictions restrictions = new PrimaryKeyRestrictionSet(cfMetaData.comparator, false);
          restrictions = restrictions.mergeWith(eq).mergeWith(slice);
  
 -        List<Composite> bounds = restrictions.boundsAsComposites(Bound.START, QueryOptions.DEFAULT);
 +        SortedSet<Slice.Bound> bounds = restrictions.boundsAsClustering(Bound.START, QueryOptions.DEFAULT);
          assertEquals(1, bounds.size());
 -        assertComposite(bounds.get(0), value3, value1, EOC.END);
 +        assertStartBound(get(bounds, 0), false, value3, value1);
  
 -        bounds = restrictions.boundsAsComposites(Bound.END, QueryOptions.DEFAULT);
 +        bounds = restrictions.boundsAsClustering(Bound.END, QueryOptions.DEFAULT);
-         assertEquals(1, bounds.size());
-         assertEndBound(get(bounds, 0), true, value3);
+         assertEquals(1, bounds.size());
 -        assertComposite(bounds.get(0), value3, EOC.END);
++        assertEndBound(get(bounds, 0), true, value3);
+ 
+         slice = newSingleSlice(cfMetaData, 1, Bound.START, true, value1);
 -        restrictions = new PrimaryKeyRestrictionSet(cfMetaData.comparator);
++        restrictions = new PrimaryKeyRestrictionSet(cfMetaData.comparator, false);
+         restrictions = restrictions.mergeWith(eq).mergeWith(slice);
+ 
 -        bounds = restrictions.boundsAsComposites(Bound.START, QueryOptions.DEFAULT);
++        bounds = restrictions.boundsAsClustering(Bound.START, QueryOptions.DEFAULT);
+         assertEquals(1, bounds.size());
 -        assertComposite(bounds.get(0), value3, value1, EOC.NONE);
++        assertStartBound(get(bounds, 0), true, value3, value1);
+ 
 -        bounds = restrictions.boundsAsComposites(Bound.END, QueryOptions.DEFAULT);
++        bounds = restrictions.boundsAsClustering(Bound.END, QueryOptions.DEFAULT);
+         assertEquals(1, bounds.size());
 -        assertComposite(bounds.get(0), value3, EOC.END);
++        assertEndBound(get(bounds, 0), true, value3);
+ 
+         slice = newSingleSlice(cfMetaData, 1, Bound.END, true, value1);
 -        restrictions = new PrimaryKeyRestrictionSet(cfMetaData.comparator);
++        restrictions = new PrimaryKeyRestrictionSet(cfMetaData.comparator, false);
+         restrictions = restrictions.mergeWith(eq).mergeWith(slice);
+ 
 -        bounds = restrictions.boundsAsComposites(Bound.START, QueryOptions.DEFAULT);
++        bounds = restrictions.boundsAsClustering(Bound.START, QueryOptions.DEFAULT);
+         assertEquals(1, bounds.size());
 -        assertComposite(bounds.get(0), value3, EOC.START);
++        assertStartBound(get(bounds, 0), true, value3);
+ 
 -        bounds = restrictions.boundsAsComposites(Bound.END, QueryOptions.DEFAULT);
++        bounds = restrictions.boundsAsClustering(Bound.END, QueryOptions.DEFAULT);
+         assertEquals(1, bounds.size());
 -        assertComposite(bounds.get(0), value3, value1, EOC.END);
++        assertEndBound(get(bounds, 0), true, value3, value1);
+ 
+         slice = newSingleSlice(cfMetaData, 1, Bound.END, false, value1);
 -        restrictions = new PrimaryKeyRestrictionSet(cfMetaData.comparator);
++        restrictions = new PrimaryKeyRestrictionSet(cfMetaData.comparator, false);
+         restrictions = restrictions.mergeWith(eq).mergeWith(slice);
+ 
 -        bounds = restrictions.boundsAsComposites(Bound.START, QueryOptions.DEFAULT);
++        bounds = restrictions.boundsAsClustering(Bound.START, QueryOptions.DEFAULT);
+         assertEquals(1, bounds.size());
 -        assertComposite(bounds.get(0), value3, EOC.START);
++        assertStartBound(get(bounds, 0), true, value3);
+ 
 -        bounds = restrictions.boundsAsComposites(Bound.END, QueryOptions.DEFAULT);
++        bounds = restrictions.boundsAsClustering(Bound.END, QueryOptions.DEFAULT);
+         assertEquals(1, bounds.size());
 -        assertComposite(bounds.get(0), value3, value1, EOC.START);
++        assertEndBound(get(bounds, 0), false, value3, value1);
+ 
+         slice = newSingleSlice(cfMetaData, 1, Bound.START, false, value1);
+         Restriction slice2 = newSingleSlice(cfMetaData, 1, Bound.END, false, value2);
 -        restrictions = new PrimaryKeyRestrictionSet(cfMetaData.comparator);
++        restrictions = new PrimaryKeyRestrictionSet(cfMetaData.comparator, false);
+         restrictions = restrictions.mergeWith(eq).mergeWith(slice).mergeWith(slice2);
+ 
 -        bounds = restrictions.boundsAsComposites(Bound.START, QueryOptions.DEFAULT);
++        bounds = restrictions.boundsAsClustering(Bound.START, QueryOptions.DEFAULT);
+         assertEquals(1, bounds.size());
 -        assertComposite(bounds.get(0), value3, value1, EOC.END);
++        assertStartBound(get(bounds, 0), false, value3, value1);
+ 
 -        bounds = restrictions.boundsAsComposites(Bound.END, QueryOptions.DEFAULT);
++        bounds = restrictions.boundsAsClustering(Bound.END, QueryOptions.DEFAULT);
+         assertEquals(1, bounds.size());
 -        assertComposite(bounds.get(0), value3, value2, EOC.START);
++        assertEndBound(get(bounds, 0), false, value3, value2);
+ 
+         slice = newSingleSlice(cfMetaData, 1, Bound.START, true, value1);
+         slice2 = newSingleSlice(cfMetaData, 1, Bound.END, true, value2);
 -        restrictions = new PrimaryKeyRestrictionSet(cfMetaData.comparator);
++        restrictions = new PrimaryKeyRestrictionSet(cfMetaData.comparator, false);
+         restrictions = restrictions.mergeWith(eq).mergeWith(slice).mergeWith(slice2);
+ 
 -        bounds = restrictions.boundsAsComposites(Bound.START, QueryOptions.DEFAULT);
++        bounds = restrictions.boundsAsClustering(Bound.START, QueryOptions.DEFAULT);
+         assertEquals(1, bounds.size());
 -        assertComposite(bounds.get(0), value3, value1, EOC.NONE);
++        assertStartBound(get(bounds, 0), true, value3, value1);
+ 
 -        bounds = restrictions.boundsAsComposites(Bound.END, QueryOptions.DEFAULT);
++        bounds = restrictions.boundsAsClustering(Bound.END, QueryOptions.DEFAULT);
+         assertEquals(1, bounds.size());
 -        assertComposite(bounds.get(0), value3, value2, EOC.END);
++        assertEndBound(get(bounds, 0), true, value3, value2);
+     }
+ 
+     /**
+      * Test '(clustering_0, clustering_1) = (1, 2)' with two clustering column
+      */
+     @Test
 -    public void testBoundsAsCompositesWithMultiEqRestrictions()
++    public void testBoundsAsClusteringWithMultiEqRestrictions()
+     {
+         CFMetaData cfMetaData = newCFMetaData(Sort.ASC, Sort.ASC);
+ 
+         ByteBuffer value1 = ByteBufferUtil.bytes(1);
+         ByteBuffer value2 = ByteBufferUtil.bytes(2);
+         Restriction eq = newMultiEq(cfMetaData, 0, value1, value2);
 -        PrimaryKeyRestrictions restrictions = new PrimaryKeyRestrictionSet(cfMetaData.comparator);
++        PrimaryKeyRestrictions restrictions = new PrimaryKeyRestrictionSet(cfMetaData.comparator, false);
+         restrictions = restrictions.mergeWith(eq);
+ 
 -        List<Composite> bounds = restrictions.boundsAsComposites(Bound.START, QueryOptions.DEFAULT);
++        SortedSet<Slice.Bound> bounds = restrictions.boundsAsClustering(Bound.START, QueryOptions.DEFAULT);
+         assertEquals(1, bounds.size());
 -        assertComposite(bounds.get(0), value1, value2, EOC.START);
++        assertStartBound(get(bounds, 0), true, value1, value2);
+ 
 -        bounds = restrictions.boundsAsComposites(Bound.END, QueryOptions.DEFAULT);
++        bounds = restrictions.boundsAsClustering(Bound.END, QueryOptions.DEFAULT);
+         assertEquals(1, bounds.size());
 -        assertComposite(bounds.get(0), value1, value2, EOC.END);
++        assertEndBound(get(bounds, 0), true, value1, value2);
+     }
+ 
+     /**
+      * Test '(clustering_0, clustering_1) IN ((1, 2), (2, 3))' with two clustering column
+      */
+     @Test
 -    public void testBoundsAsCompositesWithMultiInRestrictions()
++    public void testBoundsAsClusteringWithMultiInRestrictions()
+     {
+         CFMetaData cfMetaData = newCFMetaData(Sort.ASC, Sort.ASC);
+ 
+         ByteBuffer value1 = ByteBufferUtil.bytes(1);
+         ByteBuffer value2 = ByteBufferUtil.bytes(2);
+         ByteBuffer value3 = ByteBufferUtil.bytes(3);
+         Restriction in = newMultiIN(cfMetaData, 0, asList(value1, value2), asList(value2, value3));
 -        PrimaryKeyRestrictions restrictions = new PrimaryKeyRestrictionSet(cfMetaData.comparator);
++        PrimaryKeyRestrictions restrictions = new PrimaryKeyRestrictionSet(cfMetaData.comparator, false);
+         restrictions = restrictions.mergeWith(in);
+ 
 -        List<Composite> bounds = restrictions.boundsAsComposites(Bound.START, QueryOptions.DEFAULT);
++        SortedSet<Slice.Bound> bounds = restrictions.boundsAsClustering(Bound.START, QueryOptions.DEFAULT);
+         assertEquals(2, bounds.size());
 -        assertComposite(bounds.get(0), value1, value2, EOC.START);
 -        assertComposite(bounds.get(1), value2, value3, EOC.START);
++        assertStartBound(get(bounds, 0), true, value1, value2);
++        assertStartBound(get(bounds, 1), true, value2, value3);
+ 
 -        bounds = restrictions.boundsAsComposites(Bound.END, QueryOptions.DEFAULT);
++        bounds = restrictions.boundsAsClustering(Bound.END, QueryOptions.DEFAULT);
+         assertEquals(2, bounds.size());
 -        assertComposite(bounds.get(0), value1, value2, EOC.END);
 -        assertComposite(bounds.get(1), value2, value3, EOC.END);
++        assertEndBound(get(bounds, 0), true, value1, value2);
++        assertEndBound(get(bounds, 1), true, value2, value3);
+     }
+ 
+     /**
+      * Test multi-column slice restrictions (e.g '(clustering_0) > (1)') with only one clustering column
+      */
+     @Test
 -    public void testBoundsAsCompositesWithMultiSliceRestrictionsWithOneClusteringColumn()
++    public void testBoundsAsClusteringWithMultiSliceRestrictionsWithOneClusteringColumn()
+     {
+         CFMetaData cfMetaData = newCFMetaData(Sort.ASC);
+ 
++
+         ByteBuffer value1 = ByteBufferUtil.bytes(1);
+         ByteBuffer value2 = ByteBufferUtil.bytes(2);
+ 
+         Restriction slice = newMultiSlice(cfMetaData, 0, Bound.START, false, value1);
 -        PrimaryKeyRestrictions restrictions = new PrimaryKeyRestrictionSet(cfMetaData.comparator);
++        PrimaryKeyRestrictions restrictions = new PrimaryKeyRestrictionSet(cfMetaData.comparator, false);
+         restrictions = restrictions.mergeWith(slice);
+ 
 -        List<Composite> bounds = restrictions.boundsAsComposites(Bound.START, QueryOptions.DEFAULT);
++        SortedSet<Slice.Bound> bounds = restrictions.boundsAsClustering(Bound.START, QueryOptions.DEFAULT);
+         assertEquals(1, bounds.size());
 -        assertComposite(bounds.get(0), value1, EOC.END);
++        assertStartBound(get(bounds, 0), false, value1);
+ 
 -        bounds = restrictions.boundsAsComposites(Bound.END, QueryOptions.DEFAULT);
++        bounds = restrictions.boundsAsClustering(Bound.END, QueryOptions.DEFAULT);
+         assertEquals(1, bounds.size());
 -        assertEmptyComposite(bounds.get(0));
++        assertEmptyEnd(get(bounds, 0));
+ 
+         slice = newMultiSlice(cfMetaData, 0, Bound.START, true, value1);
 -        restrictions = new PrimaryKeyRestrictionSet(cfMetaData.comparator);
++        restrictions = new PrimaryKeyRestrictionSet(cfMetaData.comparator, false);
+         restrictions = restrictions.mergeWith(slice);
+ 
 -        bounds = restrictions.boundsAsComposites(Bound.START, QueryOptions.DEFAULT);
++        bounds = restrictions.boundsAsClustering(Bound.START, QueryOptions.DEFAULT);
+         assertEquals(1, bounds.size());
 -        assertComposite(bounds.get(0), value1, EOC.NONE);
++        assertStartBound(get(bounds, 0), true, value1);
+ 
 -        bounds = restrictions.boundsAsComposites(Bound.END, QueryOptions.DEFAULT);
++        bounds = restrictions.boundsAsClustering(Bound.END, QueryOptions.DEFAULT);
+         assertEquals(1, bounds.size());
 -        assertEmptyComposite(bounds.get(0));
++        assertEmptyEnd(get(bounds, 0));
+ 
+         slice = newMultiSlice(cfMetaData, 0, Bound.END, true, value1);
 -        restrictions = new PrimaryKeyRestrictionSet(cfMetaData.comparator);
++        restrictions = new PrimaryKeyRestrictionSet(cfMetaData.comparator, false);
+         restrictions = restrictions.mergeWith(slice);
+ 
 -        bounds = restrictions.boundsAsComposites(Bound.START, QueryOptions.DEFAULT);
++        bounds = restrictions.boundsAsClustering(Bound.START, QueryOptions.DEFAULT);
+         assertEquals(1, bounds.size());
 -        assertEmptyComposite(bounds.get(0));
++        assertEmptyStart(get(bounds, 0));
+ 
 -        bounds = restrictions.boundsAsComposites(Bound.END, QueryOptions.DEFAULT);
++        bounds = restrictions.boundsAsClustering(Bound.END, QueryOptions.DEFAULT);
+         assertEquals(1, bounds.size());
 -        assertComposite(bounds.get(0), value1, EOC.END);
++        assertEndBound(get(bounds, 0), true, value1);
+ 
+         slice = newMultiSlice(cfMetaData, 0, Bound.END, false, value1);
 -        restrictions = new PrimaryKeyRestrictionSet(cfMetaData.comparator);
++        restrictions = new PrimaryKeyRestrictionSet(cfMetaData.comparator, false);
+         restrictions = restrictions.mergeWith(slice);
+ 
 -        bounds = restrictions.boundsAsComposites(Bound.START, QueryOptions.DEFAULT);
++        bounds = restrictions.boundsAsClustering(Bound.START, QueryOptions.DEFAULT);
+         assertEquals(1, bounds.size());
 -        assertEmptyComposite(bounds.get(0));
++        assertEmptyStart(get(bounds, 0));
+ 
 -        bounds = restrictions.boundsAsComposites(Bound.END, QueryOptions.DEFAULT);
++        bounds = restrictions.boundsAsClustering(Bound.END, QueryOptions.DEFAULT);
+         assertEquals(1, bounds.size());
 -        assertComposite(bounds.get(0), value1, EOC.START);
++        assertEndBound(get(bounds, 0), false, value1);
+ 
+         slice = newMultiSlice(cfMetaData, 0, Bound.START, false, value1);
+         Restriction slice2 = newMultiSlice(cfMetaData, 0, Bound.END, false, value2);
 -        restrictions = new PrimaryKeyRestrictionSet(cfMetaData.comparator);
++        restrictions = new PrimaryKeyRestrictionSet(cfMetaData.comparator, false);
+         restrictions = restrictions.mergeWith(slice).mergeWith(slice2);
+ 
 -        bounds = restrictions.boundsAsComposites(Bound.START, QueryOptions.DEFAULT);
++        bounds = restrictions.boundsAsClustering(Bound.START, QueryOptions.DEFAULT);
+         assertEquals(1, bounds.size());
 -        assertComposite(bounds.get(0), value1, EOC.END);
++        assertStartBound(get(bounds, 0), false, value1);
+ 
 -        bounds = restrictions.boundsAsComposites(Bound.END, QueryOptions.DEFAULT);
++        bounds = restrictions.boundsAsClustering(Bound.END, QueryOptions.DEFAULT);
+         assertEquals(1, bounds.size());
 -        assertComposite(bounds.get(0), value2, EOC.START);
++        assertEndBound(get(bounds, 0), false, value2);
+ 
+         slice = newMultiSlice(cfMetaData, 0, Bound.START, true, value1);
+         slice2 = newMultiSlice(cfMetaData, 0, Bound.END, true, value2);
 -        restrictions = new PrimaryKeyRestrictionSet(cfMetaData.comparator);
++        restrictions = new PrimaryKeyRestrictionSet(cfMetaData.comparator, false);
+         restrictions = restrictions.mergeWith(slice).mergeWith(slice2);
+ 
 -        bounds = restrictions.boundsAsComposites(Bound.START, QueryOptions.DEFAULT);
++        bounds = restrictions.boundsAsClustering(Bound.START, QueryOptions.DEFAULT);
+         assertEquals(1, bounds.size());
 -        assertComposite(bounds.get(0), value1, EOC.NONE);
++        assertStartBound(get(bounds, 0), true, value1);
+ 
 -        bounds = restrictions.boundsAsComposites(Bound.END, QueryOptions.DEFAULT);
++        bounds = restrictions.boundsAsClustering(Bound.END, QueryOptions.DEFAULT);
+         assertEquals(1, bounds.size());
 -        assertComposite(bounds.get(0), value2, EOC.END);
++        assertEndBound(get(bounds, 0), true, value2);
+     }
+ 
+     /**
+      * Test multi-column slice restrictions (e.g '(clustering_0) > (1)') with only one clustering column in reverse
+      * order
+      */
+     @Test
 -    public void testBoundsAsCompositesWithMultiSliceRestrictionsWithOneDescendingClusteringColumn()
++    public void testBoundsAsClusteringWithMultiSliceRestrictionsWithOneDescendingClusteringColumn()
+     {
+         CFMetaData cfMetaData = newCFMetaData(Sort.DESC);
+ 
+         ByteBuffer value1 = ByteBufferUtil.bytes(1);
+         ByteBuffer value2 = ByteBufferUtil.bytes(2);
+ 
+         Restriction slice = newMultiSlice(cfMetaData, 0, Bound.START, false, value1);
 -        PrimaryKeyRestrictions restrictions = new PrimaryKeyRestrictionSet(cfMetaData.comparator);
++        PrimaryKeyRestrictions restrictions = new PrimaryKeyRestrictionSet(cfMetaData.comparator, false);
+         restrictions = restrictions.mergeWith(slice);
+ 
 -        List<Composite> bounds = restrictions.boundsAsComposites(Bound.START, QueryOptions.DEFAULT);
++        SortedSet<Slice.Bound> bounds = restrictions.boundsAsClustering(Bound.START, QueryOptions.DEFAULT);
+         assertEquals(1, bounds.size());
 -        assertEmptyComposite(bounds.get(0));
++        assertEmptyStart(get(bounds, 0));
+ 
 -        bounds = restrictions.boundsAsComposites(Bound.END, QueryOptions.DEFAULT);
++        bounds = restrictions.boundsAsClustering(Bound.END, QueryOptions.DEFAULT);
+         assertEquals(1, bounds.size());
 -        assertComposite(bounds.get(0), value1, EOC.START);
++        assertEndBound(get(bounds, 0), false, value1);
+ 
+         slice = newMultiSlice(cfMetaData, 0, Bound.START, true, value1);
 -        restrictions = new PrimaryKeyRestrictionSet(cfMetaData.comparator);
++        restrictions = new PrimaryKeyRestrictionSet(cfMetaData.comparator, false);
+         restrictions = restrictions.mergeWith(slice);
+ 
 -        bounds = restrictions.boundsAsComposites(Bound.START, QueryOptions.DEFAULT);
++        bounds = restrictions.boundsAsClustering(Bound.START, QueryOptions.DEFAULT);
+         assertEquals(1, bounds.size());
 -        assertEmptyComposite(bounds.get(0));
++        assertEmptyStart(get(bounds, 0));
+ 
 -        bounds = restrictions.boundsAsComposites(Bound.END, QueryOptions.DEFAULT);
++        bounds = restrictions.boundsAsClustering(Bound.END, QueryOptions.DEFAULT);
+         assertEquals(1, bounds.size());
 -        assertComposite(bounds.get(0), value1, EOC.END);
++        assertEndBound(get(bounds, 0), true, value1);
+ 
+         slice = newMultiSlice(cfMetaData, 0, Bound.END, true, value1);
 -        restrictions = new PrimaryKeyRestrictionSet(cfMetaData.comparator);
++        restrictions = new PrimaryKeyRestrictionSet(cfMetaData.comparator, false);
+         restrictions = restrictions.mergeWith(slice);
+ 
 -        bounds = restrictions.boundsAsComposites(Bound.START, QueryOptions.DEFAULT);
++        bounds = restrictions.boundsAsClustering(Bound.START, QueryOptions.DEFAULT);
+         assertEquals(1, bounds.size());
 -        assertComposite(bounds.get(0), value1, EOC.NONE);
++        assertStartBound(get(bounds, 0), true, value1);
+ 
 -        bounds = restrictions.boundsAsComposites(Bound.END, QueryOptions.DEFAULT);
++        bounds = restrictions.boundsAsClustering(Bound.END, QueryOptions.DEFAULT);
+         assertEquals(1, bounds.size());
 -        assertEmptyComposite(bounds.get(0));
++        assertEmptyEnd(get(bounds, 0));
+ 
+         slice = newMultiSlice(cfMetaData, 0, Bound.END, false, value1);
 -        restrictions = new PrimaryKeyRestrictionSet(cfMetaData.comparator);
++        restrictions = new PrimaryKeyRestrictionSet(cfMetaData.comparator, false);
+         restrictions = restrictions.mergeWith(slice);
+ 
 -        bounds = restrictions.boundsAsComposites(Bound.START, QueryOptions.DEFAULT);
++        bounds = restrictions.boundsAsClustering(Bound.START, QueryOptions.DEFAULT);
+         assertEquals(1, bounds.size());
 -        assertComposite(bounds.get(0), value1, EOC.END);
++        assertStartBound(get(bounds, 0), false, value1);
+ 
 -        bounds = restrictions.boundsAsComposites(Bound.END, QueryOptions.DEFAULT);
++        bounds = restrictions.boundsAsClustering(Bound.END, QueryOptions.DEFAULT);
+         assertEquals(1, bounds.size());
 -        assertEmptyComposite(bounds.get(0));
++        assertEmptyEnd(get(bounds, 0));
+ 
+         slice = newMultiSlice(cfMetaData, 0, Bound.START, false, value1);
+         Restriction slice2 = newMultiSlice(cfMetaData, 0, Bound.END, false, value2);
 -        restrictions = new PrimaryKeyRestrictionSet(cfMetaData.comparator);
++        restrictions = new PrimaryKeyRestrictionSet(cfMetaData.comparator, false);
+         restrictions = restrictions.mergeWith(slice).mergeWith(slice2);
+ 
 -        bounds = restrictions.boundsAsComposites(Bound.START, QueryOptions.DEFAULT);
++        bounds = restrictions.boundsAsClustering(Bound.START, QueryOptions.DEFAULT);
+         assertEquals(1, bounds.size());
 -        assertComposite(bounds.get(0), value2, EOC.END);
++        assertStartBound(get(bounds, 0), false, value2);
+ 
 -        bounds = restrictions.boundsAsComposites(Bound.END, QueryOptions.DEFAULT);
++        bounds = restrictions.boundsAsClustering(Bound.END, QueryOptions.DEFAULT);
+         assertEquals(1, bounds.size());
 -        assertComposite(bounds.get(0), value1, EOC.START);
++        assertEndBound(get(bounds, 0), false, value1);
+ 
+         slice = newMultiSlice(cfMetaData, 0, Bound.START, true, value1);
+         slice2 = newMultiSlice(cfMetaData, 0, Bound.END, true, value2);
 -        restrictions = new PrimaryKeyRestrictionSet(cfMetaData.comparator);
++        restrictions = new PrimaryKeyRestrictionSet(cfMetaData.comparator, false);
+         restrictions = restrictions.mergeWith(slice).mergeWith(slice2);
+ 
 -        bounds = restrictions.boundsAsComposites(Bound.START, QueryOptions.DEFAULT);
++        bounds = restrictions.boundsAsClustering(Bound.START, QueryOptions.DEFAULT);
+         assertEquals(1, bounds.size());
 -        assertComposite(bounds.get(0), value2, EOC.NONE);
++        assertStartBound(get(bounds, 0), true, value2);
+ 
 -        bounds = restrictions.boundsAsComposites(Bound.END, QueryOptions.DEFAULT);
++        bounds = restrictions.boundsAsClustering(Bound.END, QueryOptions.DEFAULT);
+         assertEquals(1, bounds.size());
 -        assertComposite(bounds.get(0), value1, EOC.END);
++        assertEndBound(get(bounds, 0), true, value1);
+     }
+ 
+     /**
+      * Test multi-column slice restrictions (e.g '(clustering_0, clustering_1) > (1, 2)')
+      */
+     @Test
 -    public void testBoundsAsCompositesWithMultiSliceRestrictionsWithTwoClusteringColumn()
++    public void testBoundsAsClusteringWithMultiSliceRestrictionsWithTwoClusteringColumn()
+     {
+         CFMetaData cfMetaData = newCFMetaData(Sort.ASC, Sort.ASC);
+ 
+         ByteBuffer value1 = ByteBufferUtil.bytes(1);
+         ByteBuffer value2 = ByteBufferUtil.bytes(2);
+ 
+         // (clustering_0, clustering1) > (1, 2)
+         Restriction slice = newMultiSlice(cfMetaData, 0, Bound.START, false, value1, value2);
 -        PrimaryKeyRestrictions restrictions = new PrimaryKeyRestrictionSet(cfMetaData.comparator);
++        PrimaryKeyRestrictions restrictions = new PrimaryKeyRestrictionSet(cfMetaData.comparator, false);
+         restrictions = restrictions.mergeWith(slice);
+ 
 -        List<Composite> bounds = restrictions.boundsAsComposites(Bound.START, QueryOptions.DEFAULT);
++        SortedSet<Slice.Bound> bounds = restrictions.boundsAsClustering(Bound.START, QueryOptions.DEFAULT);
+         assertEquals(1, bounds.size());
 -        assertComposite(bounds.get(0), value1, value2, EOC.END);
++        assertStartBound(get(bounds, 0), false, value1, value2);
+ 
 -        bounds = restrictions.boundsAsComposites(Bound.END, QueryOptions.DEFAULT);
++        bounds = restrictions.boundsAsClustering(Bound.END, QueryOptions.DEFAULT);
+         assertEquals(1, bounds.size());
 -        assertEmptyComposite(bounds.get(0));
++        assertEmptyEnd(get(bounds, 0));
+ 
+         // (clustering_0, clustering1) >= (1, 2)
+         slice = newMultiSlice(cfMetaData, 0, Bound.START, true, value1, value2);
 -        restrictions = new PrimaryKeyRestrictionSet(cfMetaData.comparator);
++        restrictions = new PrimaryKeyRestrictionSet(cfMetaData.comparator, false);
+         restrictions = restrictions.mergeWith(slice);
+ 
 -        bounds = restrictions.boundsAsComposites(Bound.START, QueryOptions.DEFAULT);
++        bounds = restrictions.boundsAsClustering(Bound.START, QueryOptions.DEFAULT);
+         assertEquals(1, bounds.size());
 -        assertComposite(bounds.get(0), value1, value2, EOC.NONE);
++        assertStartBound(get(bounds, 0), true, value1, value2);
+ 
 -        bounds = restrictions.boundsAsComposites(Bound.END, QueryOptions.DEFAULT);
++        bounds = restrictions.boundsAsClustering(Bound.END, QueryOptions.DEFAULT);
+         assertEquals(1, bounds.size());
 -        assertEmptyComposite(bounds.get(0));
++        assertEmptyEnd(get(bounds, 0));
+ 
+         // (clustering_0, clustering1) <= (1, 2)
+         slice = newMultiSlice(cfMetaData, 0, Bound.END, true, value1, value2);
 -        restrictions = new PrimaryKeyRestrictionSet(cfMetaData.comparator);
++        restrictions = new PrimaryKeyRestrictionSet(cfMetaData.comparator, false);
+         restrictions = restrictions.mergeWith(slice);
+ 
 -        bounds = restrictions.boundsAsComposites(Bound.START, QueryOptions.DEFAULT);
++        bounds = restrictions.boundsAsClustering(Bound.START, QueryOptions.DEFAULT);
+         assertEquals(1, bounds.size());
 -        assertEmptyComposite(bounds.get(0));
++        assertEmptyStart(get(bounds, 0));
+ 
 -        bounds = restrictions.boundsAsComposites(Bound.END, QueryOptions.DEFAULT);
++        bounds = restrictions.boundsAsClustering(Bound.END, QueryOptions.DEFAULT);
+         assertEquals(1, bounds.size());
 -        assertComposite(bounds.get(0), value1, value2, EOC.END);
++        assertEndBound(get(bounds, 0), true, value1, value2);
+ 
+         // (clustering_0, clustering1) < (1, 2)
+         slice = newMultiSlice(cfMetaData, 0, Bound.END, false, value1, value2);
 -        restrictions = new PrimaryKeyRestrictionSet(cfMetaData.comparator);
++        restrictions = new PrimaryKeyRestrictionSet(cfMetaData.comparator, false);
+         restrictions = restrictions.mergeWith(slice);
+ 
 -        bounds = restrictions.boundsAsComposites(Bound.START, QueryOptions.DEFAULT);
++        bounds = restrictions.boundsAsClustering(Bound.START, QueryOptions.DEFAULT);
+         assertEquals(1, bounds.size());
 -        assertEmptyComposite(bounds.get(0));
++        assertEmptyStart(get(bounds, 0));
+ 
 -        bounds = restrictions.boundsAsComposites(Bound.END, QueryOptions.DEFAULT);
++        bounds = restrictions.boundsAsClustering(Bound.END, QueryOptions.DEFAULT);
+         assertEquals(1, bounds.size());
 -        assertComposite(bounds.get(0), value1, value2, EOC.START);
++        assertEndBound(get(bounds, 0), false, value1, value2);
+ 
+         // (clustering_0, clustering1) > (1, 2) AND (clustering_0) < (2)
+         slice = newMultiSlice(cfMetaData, 0, Bound.START, false, value1, value2);
+         Restriction slice2 = newMultiSlice(cfMetaData, 0, Bound.END, false, value2);
 -        restrictions = new PrimaryKeyRestrictionSet(cfMetaData.comparator);
++        restrictions = new PrimaryKeyRestrictionSet(cfMetaData.comparator, false);
+         restrictions = restrictions.mergeWith(slice).mergeWith(slice2);
+ 
 -        bounds = restrictions.boundsAsComposites(Bound.START, QueryOptions.DEFAULT);
++        bounds = restrictions.boundsAsClustering(Bound.START, QueryOptions.DEFAULT);
+         assertEquals(1, bounds.size());
 -        assertComposite(bounds.get(0), value1, value2, EOC.END);
++        assertStartBound(get(bounds, 0), false, value1, value2);
+ 
 -        bounds = restrictions.boundsAsComposites(Bound.END, QueryOptions.DEFAULT);
++        bounds = restrictions.boundsAsClustering(Bound.END, QueryOptions.DEFAULT);
+         assertEquals(1, bounds.size());
 -        assertComposite(bounds.get(0), value2, EOC.START);
++        assertEndBound(get(bounds, 0), false, value2);
+ 
+         // (clustering_0, clustering1) >= (1, 2) AND (clustering_0, clustering1) <= (2, 1)
+         slice = newMultiSlice(cfMetaData, 0, Bound.START, true, value1, value2);
+         slice2 = newMultiSlice(cfMetaData, 0, Bound.END, true, value2, value1);
 -        restrictions = new PrimaryKeyRestrictionSet(cfMetaData.comparator);
++        restrictions = new PrimaryKeyRestrictionSet(cfMetaData.comparator, false);
+         restrictions = restrictions.mergeWith(slice).mergeWith(slice2);
+ 
 -        bounds = restrictions.boundsAsComposites(Bound.START, QueryOptions.DEFAULT);
++        bounds = restrictions.boundsAsClustering(Bound.START, QueryOptions.DEFAULT);
+         assertEquals(1, bounds.size());
 -        assertComposite(bounds.get(0), value1, value2, EOC.NONE);
++        assertStartBound(get(bounds, 0), true, value1, value2);
+ 
 -        bounds = restrictions.boundsAsComposites(Bound.END, QueryOptions.DEFAULT);
++        bounds = restrictions.boundsAsClustering(Bound.END, QueryOptions.DEFAULT);
+         assertEquals(1, bounds.size());
 -        assertComposite(bounds.get(0), value2, value1, EOC.END);
++        assertEndBound(get(bounds, 0), true, value2, value1);
+     }
+ 
+     /**
+      * Test multi-column slice restrictions with 2 descending clustering columns (e.g '(clustering_0, clustering_1) > (1, 2)')
+      */
+     @Test
 -    public void testBoundsAsCompositesWithMultiSliceRestrictionsWithTwoDescendingClusteringColumns()
++    public void testBoundsAsClusteringWithMultiSliceRestrictionsWithTwoDescendingClusteringColumns()
+     {
+         CFMetaData cfMetaData = newCFMetaData(Sort.DESC, Sort.DESC);
+ 
+         ByteBuffer value1 = ByteBufferUtil.bytes(1);
+         ByteBuffer value2 = ByteBufferUtil.bytes(2);
+ 
+         // (clustering_0, clustering1) > (1, 2)
+         Restriction slice = newMultiSlice(cfMetaData, 0, Bound.START, false, value1, value2);
 -        PrimaryKeyRestrictions restrictions = new PrimaryKeyRestrictionSet(cfMetaData.comparator);
++        PrimaryKeyRestrictions restrictions = new PrimaryKeyRestrictionSet(cfMetaData.comparator, false);
+         restrictions = restrictions.mergeWith(slice);
+ 
 -        List<Composite> bounds = restrictions.boundsAsComposites(Bound.START, QueryOptions.DEFAULT);
++        SortedSet<Slice.Bound> bounds = restrictions.boundsAsClustering(Bound.START, QueryOptions.DEFAULT);
+         assertEquals(1, bounds.size());
 -        assertEmptyComposite(bounds.get(0));
++        assertEmptyStart(get(bounds, 0));
+ 
 -        bounds = restrictions.boundsAsComposites(Bound.END, QueryOptions.DEFAULT);
++        bounds = restrictions.boundsAsClustering(Bound.END, QueryOptions.DEFAULT);
+         assertEquals(1, bounds.size());
 -        assertComposite(bounds.get(0), value1, value2, EOC.START);
++        assertEndBound(get(bounds, 0), false, value1, value2);
+ 
+         // (clustering_0, clustering1) >= (1, 2)
+         slice = newMultiSlice(cfMetaData, 0, Bound.START, true, value1, value2);
 -        restrictions = new PrimaryKeyRestrictionSet(cfMetaData.comparator);
++        restrictions = new PrimaryKeyRestrictionSet(cfMetaData.comparator, false);
+         restrictions = restrictions.mergeWith(slice);
+ 
 -        bounds = restrictions.boundsAsComposites(Bound.START, QueryOptions.DEFAULT);
++        bounds = restrictions.boundsAsClustering(Bound.START, QueryOptions.DEFAULT);
+         assertEquals(1, bounds.size());
 -        assertEmptyComposite(bounds.get(0));
++        assertEmptyStart(get(bounds, 0));
+ 
 -        bounds = restrictions.boundsAsComposites(Bound.END, QueryOptions.DEFAULT);
++        bounds = restrictions.boundsAsClustering(Bound.END, QueryOptions.DEFAULT);
+         assertEquals(1, bounds.size());
 -        assertComposite(bounds.get(0), value1, value2, EOC.END);
++        assertEndBound(get(bounds, 0), true, value1, value2);
+ 
+         // (clustering_0, clustering1) <= (1, 2)
+         slice = newMultiSlice(cfMetaData, 0, Bound.END, true, value1, value2);
 -        restrictions = new PrimaryKeyRestrictionSet(cfMetaData.comparator);
++        restrictions = new PrimaryKeyRestrictionSet(cfMetaData.comparator, false);
+         restrictions = restrictions.mergeWith(slice);
+ 
 -        bounds = restrictions.boundsAsComposites(Bound.START, QueryOptions.DEFAULT);
++        bounds = restrictions.boundsAsClustering(Bound.START, QueryOptions.DEFAULT);
+         assertEquals(1, bounds.size());
 -        assertComposite(bounds.get(0), value1, value2, EOC.NONE);
++        assertStartBound(get(bounds, 0), true, value1, value2);
+ 
 -        bounds = restrictions.boundsAsComposites(Bound.END, QueryOptions.DEFAULT);
++        bounds = restrictions.boundsAsClustering(Bound.END, QueryOptions.DEFAULT);
+         assertEquals(1, bounds.size());
 -        assertEmptyComposite(bounds.get(0));
++        assertEmptyEnd(get(bounds, 0));
+ 
+         // (clustering_0, clustering1) < (1, 2)
+         slice = newMultiSlice(cfMetaData, 0, Bound.END, false, value1, value2);
 -        restrictions = new PrimaryKeyRestrictionSet(cfMetaData.comparator);
++        restrictions = new PrimaryKeyRestrictionSet(cfMetaData.comparator, false);
+         restrictions = restrictions.mergeWith(slice);
+ 
 -        bounds = restrictions.boundsAsComposites(Bound.START, QueryOptions.DEFAULT);
++        bounds = restrictions.boundsAsClustering(Bound.START, QueryOptions.DEFAULT);
+         assertEquals(1, bounds.size());
 -        assertComposite(bounds.get(0), value1, value2, EOC.END);
++        assertStartBound(get(bounds, 0), false, value1, value2);
+ 
 -        bounds = restrictions.boundsAsComposites(Bound.END, QueryOptions.DEFAULT);
++        bounds = restrictions.boundsAsClustering(Bound.END, QueryOptions.DEFAULT);
+         assertEquals(1, bounds.size());
 -        assertEmptyComposite(bounds.get(0));
++        assertEmptyEnd(get(bounds, 0));
+ 
+ 
+         // (clustering_0, clustering1) > (1, 2) AND (clustering_0) < (2)
+         slice = newMultiSlice(cfMetaData, 0, Bound.START, false, value1, value2);
+         Restriction slice2 = newMultiSlice(cfMetaData, 0, Bound.END, false, value2);
 -        restrictions = new PrimaryKeyRestrictionSet(cfMetaData.comparator);
++        restrictions = new PrimaryKeyRestrictionSet(cfMetaData.comparator, false);
+         restrictions = restrictions.mergeWith(slice).mergeWith(slice2);
+ 
 -        bounds = restrictions.boundsAsComposites(Bound.START, QueryOptions.DEFAULT);
++        bounds = restrictions.boundsAsClustering(Bound.START, QueryOptions.DEFAULT);
+         assertEquals(1, bounds.size());
 -        assertComposite(bounds.get(0), value2, EOC.END);
++        assertStartBound(get(bounds, 0), false, value2);
+ 
 -        bounds = restrictions.boundsAsComposites(Bound.END, QueryOptions.DEFAULT);
++        bounds = restrictions.boundsAsClustering(Bound.END, QueryOptions.DEFAULT);
+         assertEquals(1, bounds.size());
 -        assertComposite(bounds.get(0), value1, value2, EOC.START);
++        assertEndBound(get(bounds, 0), false, value1, value2);
+ 
+         // (clustering_0, clustering1) >= (1, 2) AND (clustering_0, clustering1) <= (2, 1)
+         slice = newMultiSlice(cfMetaData, 0, Bound.START, true, value1, value2);
+         slice2 = newMultiSlice(cfMetaData, 0, Bound.END, true, value2, value1);
 -        restrictions = new PrimaryKeyRestrictionSet(cfMetaData.comparator);
++        restrictions = new PrimaryKeyRestrictionSet(cfMetaData.comparator, false);
+         restrictions = restrictions.mergeWith(slice).mergeWith(slice2);
+ 
 -        bounds = restrictions.boundsAsComposites(Bound.START, QueryOptions.DEFAULT);
++        bounds = restrictions.boundsAsClustering(Bound.START, QueryOptions.DEFAULT);
+         assertEquals(1, bounds.size());
 -        assertComposite(bounds.get(0), value2, value1, EOC.NONE);
++        assertStartBound(get(bounds, 0), true, value2, value1);
+ 
 -        bounds = restrictions.boundsAsComposites(Bound.END, QueryOptions.DEFAULT);
++        bounds = restrictions.boundsAsClustering(Bound.END, QueryOptions.DEFAULT);
+         assertEquals(1, bounds.size());
 -        assertComposite(bounds.get(0), value1, value2, EOC.END);
++        assertEndBound(get(bounds, 0), true, value1, value2);
+     }
+ 
+     /**
+      * Test multi-column slice restrictions with 1 descending clustering column and 1 ascending
+      * (e.g '(clustering_0, clustering_1) > (1, 2)')
+      */
+     @Test
 -    public void testBoundsAsCompositesWithMultiSliceRestrictionsWithOneDescendingAndOneAscendingClusteringColumns()
++    public void testBoundsAsClusteringWithMultiSliceRestrictionsWithOneDescendingAndOneAscendingClusteringColumns()
+     {
+         CFMetaData cfMetaData = newCFMetaData(Sort.DESC, Sort.ASC);
+ 
+         ByteBuffer value1 = ByteBufferUtil.bytes(1);
+         ByteBuffer value2 = ByteBufferUtil.bytes(2);
+ 
+         // (clustering_0, clustering1) > (1, 2)
+         Restriction slice = newMultiSlice(cfMetaData, 0, Bound.START, false, value1, value2);
 -        PrimaryKeyRestrictions restrictions = new PrimaryKeyRestrictionSet(cfMetaData.comparator);
++        PrimaryKeyRestrictions restrictions = new PrimaryKeyRestrictionSet(cfMetaData.comparator, false);
+         restrictions = restrictions.mergeWith(slice);
+ 
 -        List<Composite> bounds = restrictions.boundsAsComposites(Bound.START, QueryOptions.DEFAULT);
++        SortedSet<Slice.Bound> bounds = restrictions.boundsAsClustering(Bound.START, QueryOptions.DEFAULT);
+         assertEquals(2, bounds.size());
 -        assertEmptyComposite(bounds.get(0));
 -        assertComposite(bounds.get(1), value1, value2, EOC.END);
++        assertEmptyStart(get(bounds, 0));
++        assertStartBound(get(bounds, 1), false, value1, value2);
+ 
 -        bounds = restrictions.boundsAsComposites(Bound.END, QueryOptions.DEFAULT);
++        bounds = restrictions.boundsAsClustering(Bound.END, QueryOptions.DEFAULT);
+         assertEquals(2, bounds.size());
 -        assertComposite(bounds.get(0), value1, EOC.START);
 -        assertComposite(bounds.get(1), value1, EOC.END);
++        assertEndBound(get(bounds, 0), false, value1);
++        assertEndBound(get(bounds, 1), true, value1);
+ 
+         // (clustering_0, clustering1) >= (1, 2)
+         slice = newMultiSlice(cfMetaData, 0, Bound.START, true, value1, value2);
 -        restrictions = new PrimaryKeyRestrictionSet(cfMetaData.comparator);
++        restrictions = new PrimaryKeyRestrictionSet(cfMetaData.comparator, false);
+         restrictions = restrictions.mergeWith(slice);
+ 
 -        bounds = restrictions.boundsAsComposites(Bound.START, QueryOptions.DEFAULT);
++        bounds = restrictions.boundsAsClustering(Bound.START, QueryOptions.DEFAULT);
+         assertEquals(2, bounds.size());
 -        assertEmptyComposite(bounds.get(0));
 -        assertComposite(bounds.get(1), value1, value2, EOC.NONE);
++        assertEmptyStart(get(bounds, 0));
++        assertStartBound(get(bounds, 1), true, value1, value2);
+ 
 -        bounds = restrictions.boundsAsComposites(Bound.END, QueryOptions.DEFAULT);
++        bounds = restrictions.boundsAsClustering(Bound.END, QueryOptions.DEFAULT);
+         assertEquals(2, bounds.size());
 -        assertComposite(bounds.get(0), value1, EOC.START);
 -        assertComposite(bounds.get(1), value1, EOC.END);
++        assertEndBound(get(bounds, 0), false, value1);
++        assertEndBound(get(bounds, 1), true, value1);
+ 
+         // (clustering_0, clustering1) <= (1, 2)
+         slice = newMultiSlice(cfMetaData, 0, Bound.END, true, value1, value2);
 -        restrictions = new PrimaryKeyRestrictionSet(cfMetaData.comparator);
++        restrictions = new PrimaryKeyRestrictionSet(cfMetaData.comparator, false);
+         restrictions = restrictions.mergeWith(slice);
+ 
 -        bounds = restrictions.boundsAsComposites(Bound.START, QueryOptions.DEFAULT);
++        bounds = restrictions.boundsAsClustering(Bound.START, QueryOptions.DEFAULT);
+         assertEquals(2, bounds.size());
 -        assertComposite(bounds.get(0), value1, EOC.START);
 -        assertComposite(bounds.get(1), value1, EOC.END);
++        assertStartBound(get(bounds, 0), true, value1);
++        assertStartBound(get(bounds, 1), false, value1);
+ 
 -        bounds = restrictions.boundsAsComposites(Bound.END, QueryOptions.DEFAULT);
++        bounds = restrictions.boundsAsClustering(Bound.END, QueryOptions.DEFAULT);
+         assertEquals(2, bounds.size());
 -        assertComposite(bounds.get(0), value1, value2, EOC.END);
 -        assertEmptyComposite(bounds.get(1));
++        assertEndBound(get(bounds, 0), true, value1, value2);
++        assertEmptyEnd(get(bounds, 1));
+ 
+         // (clustering_0, clustering1) < (1, 2)
+         slice = newMultiSlice(cfMetaData, 0, Bound.END, false, value1, value2);
 -        restrictions = new PrimaryKeyRestrictionSet(cfMetaData.comparator);
++        restrictions = new PrimaryKeyRestrictionSet(cfMetaData.comparator, false);
+         restrictions = restrictions.mergeWith(slice);
+ 
 -        bounds = restrictions.boundsAsComposites(Bound.START, QueryOptions.DEFAULT);
++        bounds = restrictions.boundsAsClustering(Bound.START, QueryOptions.DEFAULT);
+         assertEquals(2, bounds.size());
 -        assertComposite(bounds.get(0), value1, EOC.START);
 -        assertComposite(bounds.get(1), value1, EOC.END);
++        assertStartBound(get(bounds, 0), true, value1);
++        assertStartBound(get(bounds, 1), false, value1);
+ 
 -        bounds = restrictions.boundsAsComposites(Bound.END, QueryOptions.DEFAULT);
++        bounds = restrictions.boundsAsClustering(Bound.END, QueryOptions.DEFAULT);
+         assertEquals(2, bounds.size());
 -        assertComposite(bounds.get(0), value1, value2, EOC.START);
 -        assertEmptyComposite(bounds.get(1));
++        assertEndBound(get(bounds, 0), false, value1, value2);
++        assertEmptyEnd(get(bounds, 1));
+ 
+         // (clustering_0, clustering1) > (1, 2) AND (clustering_0) < (2)
+         slice = newMultiSlice(cfMetaData, 0, Bound.START, false, value1, value2);
+         Restriction slice2 = newMultiSlice(cfMetaData, 0, Bound.END, false, value2);
 -        restrictions = new PrimaryKeyRestrictionSet(cfMetaData.comparator);
++        restrictions = new PrimaryKeyRestrictionSet(cfMetaData.comparator, false);
+         restrictions = restrictions.mergeWith(slice).mergeWith(slice2);
+ 
 -        bounds = restrictions.boundsAsComposites(Bound.START, QueryOptions.DEFAULT);
++        bounds = restrictions.boundsAsClustering(Bound.START, QueryOptions.DEFAULT);
+         assertEquals(2, bounds.size());
 -        assertComposite(bounds.get(0), value2, EOC.END);
 -        assertComposite(bounds.get(1), value1, value2, EOC.END);
++        assertStartBound(get(bounds, 0), false, value2);
++        assertStartBound(get(bounds, 1), false, value1, value2);
+ 
 -        bounds = restrictions.boundsAsComposites(Bound.END, QueryOptions.DEFAULT);
++        bounds = restrictions.boundsAsClustering(Bound.END, QueryOptions.DEFAULT);
+         assertEquals(2, bounds.size());
 -        assertComposite(bounds.get(0), value1, EOC.START);
 -        assertComposite(bounds.get(1), value1, EOC.END);
++        assertEndBound(get(bounds, 0), false, value1);
++        assertEndBound(get(bounds, 1), true, value1);
+ 
+         // (clustering_0) > (1) AND (clustering_0, clustering1) < (2, 1)
+         slice = newMultiSlice(cfMetaData, 0, Bound.START, false, value1);
+         slice2 = newMultiSlice(cfMetaData, 0, Bound.END, false, value2, value1);
 -        restrictions = new PrimaryKeyRestrictionSet(cfMetaData.comparator);
++        restrictions = new PrimaryKeyRestrictionSet(cfMetaData.comparator, false);
+         restrictions = restrictions.mergeWith(slice).mergeWith(slice2);
+ 
 -        bounds = restrictions.boundsAsComposites(Bound.START, QueryOptions.DEFAULT);
++        bounds = restrictions.boundsAsClustering(Bound.START, QueryOptions.DEFAULT);
+         assertEquals(2, bounds.size());
 -        assertComposite(bounds.get(0), value2, EOC.START);
 -        assertComposite(bounds.get(1), value2, EOC.END);
++        assertStartBound(get(bounds, 0), true, value2);
++        assertStartBound(get(bounds, 1), false, value2);
+ 
 -        bounds = restrictions.boundsAsComposites(Bound.END, QueryOptions.DEFAULT);
++        bounds = restrictions.boundsAsClustering(Bound.END, QueryOptions.DEFAULT);
+         assertEquals(2, bounds.size());
 -        assertComposite(bounds.get(0), value2, value1, EOC.START);
 -        assertComposite(bounds.get(1), value1, EOC.START);
++        assertEndBound(get(bounds, 0), false, value2, value1);
++        assertEndBound(get(bounds, 1), false, value1);
+ 
+         // (clustering_0, clustering1) >= (1, 2) AND (clustering_0, clustering1) <= (2, 1)
+         slice = newMultiSlice(cfMetaData, 0, Bound.START, true, value1, value2);
+         slice2 = newMultiSlice(cfMetaData, 0, Bound.END, true, value2, value1);
 -        restrictions = new PrimaryKeyRestrictionSet(cfMetaData.comparator);
++        restrictions = new PrimaryKeyRestrictionSet(cfMetaData.comparator, false);
+         restrictions = restrictions.mergeWith(slice).mergeWith(slice2);
+ 
 -        bounds = restrictions.boundsAsComposites(Bound.START, QueryOptions.DEFAULT);
++        bounds = restrictions.boundsAsClustering(Bound.START, QueryOptions.DEFAULT);
+         assertEquals(3, bounds.size());
 -        assertComposite(bounds.get(0), value2, EOC.START);
 -        assertComposite(bounds.get(1), value2, EOC.END);
 -        assertComposite(bounds.get(2), value1, value2, EOC.NONE);
++        assertStartBound(get(bounds, 0), true, value2);
++        assertStartBound(get(bounds, 1), false, value2);
++        assertStartBound(get(bounds, 2), true, value1, value2);
+ 
 -        bounds = restrictions.boundsAsComposites(Bound.END, QueryOptions.DEFAULT);
++        bounds = restrictions.boundsAsClustering(Bound.END, QueryOptions.DEFAULT);
+         assertEquals(3, bounds.size());
 -        assertComposite(bounds.get(0), value2, value1, EOC.END);
 -        assertComposite(bounds.get(1), value1, EOC.START);
 -        assertComposite(bounds.get(2), value1, EOC.END);
++        assertEndBound(get(bounds, 0), true, value2, value1);
++        assertEndBound(get(bounds, 1), false, value1);
++        assertEndBound(get(bounds, 2), true, value1);
+     }
+ 
+     /**
+      * Test multi-column slice restrictions with 1 descending clustering column and 1 ascending
+      * (e.g '(clustering_0, clustering_1) > (1, 2)')
+      */
+     @Test
 -    public void testBoundsAsCompositesWithMultiSliceRestrictionsWithOneAscendingAndOneDescendingClusteringColumns()
++    public void testBoundsAsClusteringWithMultiSliceRestrictionsWithOneAscendingAndOneDescendingClusteringColumns()
+     {
+         CFMetaData cfMetaData = newCFMetaData(Sort.ASC, Sort.DESC);
+ 
+         ByteBuffer value1 = ByteBufferUtil.bytes(1);
+         ByteBuffer value2 = ByteBufferUtil.bytes(2);
+ 
+         // (clustering_0, clustering1) > (1, 2)
+         Restriction slice = newMultiSlice(cfMetaData, 0, Bound.START, false, value1, value2);
 -        PrimaryKeyRestrictions restrictions = new PrimaryKeyRestrictionSet(cfMetaData.comparator);
++        PrimaryKeyRestrictions restrictions = new PrimaryKeyRestrictionSet(cfMetaData.comparator, false);
+         restrictions = restrictions.mergeWith(slice);
+ 
 -        List<Composite> bounds = restrictions.boundsAsComposites(Bound.START, QueryOptions.DEFAULT);
++        SortedSet<Slice.Bound> bounds = restrictions.boundsAsClustering(Bound.START, QueryOptions.DEFAULT);
+         assertEquals(2, bounds.size());
 -        assertComposite(bounds.get(0), value1, EOC.START);
 -        assertComposite(bounds.get(1), value1, EOC.END);
++        assertStartBound(get(bounds, 0), true, value1);
++        assertStartBound(get(bounds, 1), false, value1);
+ 
 -        bounds = restrictions.boundsAsComposites(Bound.END, QueryOptions.DEFAULT);
++        bounds = restrictions.boundsAsClustering(Bound.END, QueryOptions.DEFAULT);
+         assertEquals(2, bounds.size());
 -        assertComposite(bounds.get(0), value1, value2, EOC.START);
 -        assertEmptyComposite(bounds.get(1));
++        assertEndBound(get(bounds, 0), false, value1, value2);
++        assertEmptyEnd(get(bounds, 1));
  
-         slice = newSingleSlice(cfMetaData, 1, Bound.START, true, value1);
+         // (clustering_0, clustering1) >= (1, 2)
+         slice = newMultiSlice(cfMetaData, 0, Bound.START, true, value1, value2);
 -        restrictions = new PrimaryKeyRestrictionSet(cfMetaData.comparator);
 +        restrictions = new PrimaryKeyRestrictionSet(cfMetaData.comparator, false);
-         restrictions = restrictions.mergeWith(eq).mergeWith(slice);
+         restrictions = restrictions.mergeWith(slice);
  
 -        bounds = restrictions.boundsAsComposites(Bound.START, QueryOptions.DEFAULT);
 +        bounds = restrictions.boundsAsClustering(Bound.START, QueryOptions.DEFAULT);
-         assertEquals(1, bounds.size());
-         assertStartBound(get(bounds, 0), true, value3, value1);
+         assertEquals(2, bounds.size());
 -        assertComposite(bounds.get(0), value1, EOC.START);
 -        assertComposite(bounds.get(1), value1, EOC.END);
++        assertStartBound(get(bounds, 0), true, value1);
++        assertStartBound(get(bounds, 1), false, value1);
  
 -        bounds = restrictions.boundsAsComposites(Bound.END, QueryOptions.DEFAULT);
 +        bounds = restrictions.boundsAsClustering(Bound.END, QueryOptions.DEFAULT);
-         assertEquals(1, bounds.size());
-         assertEndBound(get(bounds, 0), true, value3);
+         assertEquals(2, bounds.size());
 -        assertComposite(bounds.get(0), value1, value2, EOC.END);
 -        assertEmptyComposite(bounds.get(1));
++        assertEndBound(get(bounds, 0), true, value1, value2);
++        assertEmptyEnd(get(bounds, 1));
  
-         slice = newSingleSlice(cfMetaData, 1, Bound.END, true, value1);
+         // (clustering_0, clustering1) <= (1, 2)
+         slice = newMultiSlice(cfMetaData, 0, Bound.END, true, value1, value2);
 -        restrictions = new PrimaryKeyRestrictionSet(cfMetaData.comparator);
 +        restrictions = new PrimaryKeyRestrictionSet(cfMetaData.comparator, false);
-         restrictions = restrictions.mergeWith(eq).mergeWith(slice);
+         restrictions = restrictions.mergeWith(slice);
  
 -        bounds = restrictions.boundsAsComposites(Bound.START, QueryOptions.DEFAULT);
 +        bounds = restrictions.boundsAsClustering(Bound.START, QueryOptions.DEFAULT);
-         assertEquals(1, bounds.size());
-         assertStartBound(get(bounds, 0), true, value3);
+         assertEquals(2, bounds.size());
 -        assertEmptyComposite(bounds.get(0));
 -        assertComposite(bounds.get(1), value1, value2, EOC.NONE);
++        assertEmptyStart(get(bounds, 0));
++        assertStartBound(get(bounds, 1), true, value1, value2);
  
 -        bounds = restrictions.boundsAsComposites(Bound.END, QueryOptions.DEFAULT);
 +        bounds = restrictions.boundsAsClustering(Bound.END, QueryOptions.DEFAULT);
-         assertEquals(1, bounds.size());
-         assertEndBound(get(bounds, 0), true, value3, value1);
+         assertEquals(2, bounds.size());
 -        assertComposite(bounds.get(0), value1, EOC.START);
 -        assertComposite(bounds.get(1), value1, EOC.END);
++        assertEndBound(get(bounds, 0), false, value1);
++        assertEndBound(get(bounds, 1), true, value1);
  
-         slice = newSingleSlice(cfMetaData, 1, Bound.END, false, value1);
+         // (clustering_0, clustering1) < (1, 2)
+         slice = newMultiSlice(cfMetaData, 0, Bound.END, false, value1, value2);
 -        restrictions = new PrimaryKeyRestrictionSet(cfMetaData.comparator);
 +        restrictions = new PrimaryKeyRestrictionSet(cfMetaData.comparator, false);
-         restrictions = restrictions.mergeWith(eq).mergeWith(slice);
+         restrictions = restrictions.mergeWith(slice);
  
 -        bounds = restrictions.boundsAsComposites(Bound.START, QueryOptions.DEFAULT);
 +        bounds = restrictions.boundsAsClustering(Bound.START, QueryOptions.DEFAULT);
-         assertEquals(1, bounds.size());
-         assertStartBound(get(bounds, 0), true, value3);
+         assertEquals(2, bounds.size());
 -        assertEmptyComposite(bounds.get(0));
 -        assertComposite(bounds.get(1), value1, value2, EOC.END);
++        assertEmptyStart(get(bounds, 0));
++        assertStartBound(get(bounds, 1), false, value1, value2);
  
 -        bounds = restrictions.boundsAsComposites(Bound.END, QueryOptions.DEFAULT);
 +        bounds = restrictions.boundsAsClustering(Bound.END, QueryOptions.DEFAULT);
-         assertEquals(1, bounds.size());
-         assertEndBound(get(bounds, 0), false, value3, value1);
+         assertEquals(2, bounds.size());
 -        assertComposite(bounds.get(0), value1, EOC.START);
 -        assertComposite(bounds.get(1), value1, EOC.END);
++        assertEndBound(get(bounds, 0), false, value1);
++        assertEndBound(get(bounds, 1), true, value1);
  
-         slice = newSingleSlice(cfMetaData, 1, Bound.START, false, value1);
-         Restriction slice2 = newSingleSlice(cfMetaData, 1, Bound.END, false, value2);
+         // (clustering_0, clustering1) > (1, 2) AND (clustering_0) < (2)
+         slice = newMultiSlice(cfMetaData, 0, Bound.START, false, value1, value2);
+         Restriction slice2 = newMultiSlice(cfMetaData, 0, Bound.END, false, value2);
 -        restrictions = new PrimaryKeyRestrictionSet(cfMetaData.comparator);
 +        restrictions = new PrimaryKeyRestrictionSet(cfMetaData.comparator, false);
-         restrictions = restrictions.mergeWith(eq).mergeWith(slice).mergeWith(slice2);
+         restrictions = restrictions.mergeWith(slice).mergeWith(slice2);
  
 -        bounds = restrictions.boundsAsComposites(Bound.START, QueryOptions.DEFAULT);
 +        bounds = restrictions.boundsAsClustering(Bound.START, QueryOptions.DEFAULT);
-         assertEquals(1, bounds.size());
-         assertStartBound(get(bounds, 0), false, value3, value1);
+         assertEquals(2, bounds.size());
 -        assertComposite(bounds.get(0), value1, EOC.START);
 -        assertComposite(bounds.get(1), value1, EOC.END);
++        assertStartBound(get(bounds, 0), true, value1);
++        assertStartBound(get(bounds, 1), false, value1);
  
 -        bounds = restrictions.boundsAsComposites(Bound.END, QueryOptions.DEFAULT);
 +        bounds = restrictions.boundsAsClustering(Bound.END, QueryOptions.DEFAULT);
-         assertEquals(1, bounds.size());
-         assertEndBound(get(bounds, 0), false, value3, value2);
+         assertEquals(2, bounds.size());
 -        assertComposite(bounds.get(0), value1, value2, EOC.START);
 -        assertComposite(bounds.get(1), value2, EOC.START);
++        assertEndBound(get(bounds, 0), false, value1, value2);
++        assertEndBound(get(bounds, 1), false, value2);
  
-         slice = newSingleSlice(cfMetaData, 1, Bound.START, true, value1);
-         slice2 = newSingleSlice(cfMetaData, 1, Bound.END, true, value2);
+         // (clustering_0, clustering1) >= (1, 2) AND (clustering_0, clustering1) <= (2, 1)
+         slice = newMultiSlice(cfMetaData, 0, Bound.START, true, value1, value2);
+         slice2 = newMultiSlice(cfMetaData, 0, Bound.END, true, value2, value1);
 -        restrictions = new PrimaryKeyRestrictionSet(cfMetaData.comparator);
 +        restrictions = new PrimaryKeyRestrictionSet(cfMetaData.comparator, false);
-         restrictions = restrictions.mergeWith(eq).mergeWith(slice).mergeWith(slice2);
+         restrictions = restrictions.mergeWith(slice).mergeWith(slice2);
  
 -        bounds = restrictions.boundsAsComposites(Bound.START, QueryOptions.DEFAULT);
 +        bounds = restrictions.boundsAsClustering(Bound.START, QueryOptions.DEFAULT);
-         assertEquals(1, bounds.size());
-         assertStartBound(get(bounds, 0), true, value3, value1);
+         assertEquals(3, bounds.size());
 -        assertComposite(bounds.get(0), value1, EOC.START);
 -        assertComposite(bounds.get(1), value1, EOC.END);
 -        assertComposite(bounds.get(2), value2, value1, EOC.NONE);
++        assertStartBound(get(bounds, 0), true, value1);
++        assertStartBound(get(bounds, 1), false, value1);
++        assertStartBound(get(bounds, 2), true, value2, value1);
  
 -        bounds = restrictions.boundsAsComposites(Bound.END, QueryOptions.DEFAULT);
 +        bounds = restrictions.boundsAsClustering(Bound.END, QueryOptions.DEFAULT);
-         assertEquals(1, bounds.size());
-         assertEndBound(get(bounds, 0), true, value3, value2);
+         assertEquals(3, bounds.size());
 -        assertComposite(bounds.get(0), value1, value2, EOC.END);
 -        assertComposite(bounds.get(1), value2, EOC.START);
 -        assertComposite(bounds.get(2), value2, EOC.END);
++        assertEndBound(get(bounds, 0), true, value1, value2);
++        assertEndBound(get(bounds, 1), false, value2);
++        assertEndBound(get(bounds, 2), true, value2);
      }
  
      /**
-      * Test '(clustering_0, clustering_1) = (1, 2)' with two clustering column
+      * Test multi-column slice restrictions with 2 ascending clustering column and 2 descending
+      * (e.g '(clustering_0, clustering1, clustering_3, clustering4) > (1, 2, 3, 4)')
       */
      @Test
-     public void testboundsAsClusteringWithMultiEqRestrictions() throws InvalidRequestException
 -    public void testBoundsAsCompositesWithMultiSliceRestrictionsWithTwoAscendingAndTwoDescendingClusteringColumns()
++    public void testBoundsAsClusteringWithMultiSliceRestrictionsWithTwoAscendingAndTwoDescendingClusteringColumns()
      {
-         CFMetaData cfMetaData = newCFMetaData(2);
+         CFMetaData cfMetaData = newCFMetaData(Sort.ASC, Sort.ASC, Sort.DESC, Sort.DESC);
  
          ByteBuffer value1 = ByteBufferUtil.bytes(1);
          ByteBuffer value2 = ByteBufferUtil.bytes(2);
-         Restriction eq = newMultiEq(cfMetaData, 0, value1, value2);
+         ByteBuffer value3 = ByteBufferUtil.bytes(3);
+         ByteBuffer value4 = ByteBufferUtil.bytes(4);
+ 
+         // (clustering_0, clustering1, clustering_2, clustering_3) > (1, 2, 3, 4)
+         Restriction slice = newMultiSlice(cfMetaData, 0, Bound.START, false, value1, value2, value3, value4);
 -        PrimaryKeyRestrictions restrictions = new PrimaryKeyRestrictionSet(cfMetaData.comparator);
 +        PrimaryKeyRestrictions restrictions = new PrimaryKeyRestrictionSet(cfMetaData.comparator, false);
-         restrictions = restrictions.mergeWith(eq);
+         restrictions = restrictions.mergeWith(slice);
  
 -        List<Composite> bounds = restrictions.boundsAsComposites(Bound.START, QueryOptions.DEFAULT);
 +        SortedSet<Slice.Bound> bounds = restrictions.boundsAsClustering(Bound.START, QueryOptions.DEFAULT);
-         assertEquals(1, bounds.size());
+         assertEquals(2, bounds.size());
 -        assertComposite(bounds.get(0), value1, value2, EOC.START);
 -        assertComposite(bounds.get(1), value1, value2, EOC.END);
 +        assertStartBound(get(bounds, 0), true, value1, value2);
++        assertStartBound(get(bounds, 1), false, value1, value2);
  
 -        bounds = restrictions.boundsAsComposites(Bound.END, QueryOptions.DEFAULT);
 +        bounds = restrictions.boundsAsClustering(Bound.END, QueryOptions.DEFAULT);
-         assertEquals(1, bounds.size());
-         assertEndBound(get(bounds, 0), true, value1, value2);
-     }
- 
-     /**
-      * Test '(clustering_0, clustering_1) IN ((1, 2), (2, 3))' with two clustering column
-      */
-     @Test
-     public void testboundsAsClusteringWithMultiInRestrictions() throws InvalidRequestException
-     {
-         CFMetaData cfMetaData = newCFMetaData(2);
+         assertEquals(2, bounds.size());
 -        assertComposite(bounds.get(0), value1, value2, value3, value4, EOC.START);
 -        assertEmptyComposite(bounds.get(1));
++        assertEndBound(get(bounds, 0), false, value1, value2, value3, value4);
++        assertEmptyEnd(get(bounds, 1));
  
-         ByteBuffer value1 = ByteBufferUtil.bytes(1);
-         ByteBuffer value2 = ByteBufferUtil.bytes(2);
-         ByteBuffer value3 = ByteBufferUtil.bytes(3);
-         Restriction in = newMultiIN(cfMetaData, 0, asList(value1, value2), asList(value2, value3));
-         PrimaryKeyRestrictions restrictions = new PrimaryKeyRestrictionSet(cfMetaData.comparator, false);
-         restrictions = restrictions.mergeWith(in);
+         // clustering_0 = 1 AND (clustering_1, clustering_2, clustering_3) > (2, 3, 4)
+         Restriction eq = newSingleEq(cfMetaData, 0, value1);
+         slice = newMultiSlice(cfMetaData, 1, Bound.START, false, value2, value3, value4);
 -        restrictions = new PrimaryKeyRestrictionSet(cfMetaData.comparator);
++        restrictions = new PrimaryKeyRestrictionSet(cfMetaData.comparator, false);
+         restrictions = restrictions.mergeWith(slice);
+         restrictions = restrictions.mergeWith(eq);
  
-         SortedSet<Slice.Bound> bounds = restrictions.boundsAsClustering(Bound.START, QueryOptions.DEFAULT);
 -        bounds = restrictions.boundsAsComposites(Bound.START, QueryOptions.DEFAULT);
++        bounds = restrictions.boundsAsClustering(Bound.START, QueryOptions.DEFAULT);
          assertEquals(2, bounds.size());
 -        assertComposite(bounds.get(0), value1, value2, EOC.START);
 -        assertComposite(bounds.get(1), value1, value2, EOC.END);
 +        assertStartBound(get(bounds, 0), true, value1, value2);
-         assertStartBound(get(bounds, 1), true, value2, value3);
++        assertStartBound(get(bounds, 1), false, value1, value2);
  
 -        bounds = restrictions.boundsAsComposites(Bound.END, QueryOptions.DEFAULT);
 +        bounds = restrictions.boundsAsClustering(Bound.END, QueryOptions.DEFAULT);
          assertEquals(2, bounds.size());
-         assertEndBound(get(bounds, 0), true, value1, value2);
-         assertEndBound(get(bounds, 1), true, value2, value3);
-     }
 -        assertComposite(bounds.get(0), value1, value2, value3, value4, EOC.START);
 -        assertComposite(bounds.get(1), value1, EOC.END);
++        assertEndBound(get(bounds, 0), false, value1, value2, value3, value4);
++        assertEndBound(get(bounds, 1), true, value1);
  
-     /**
-      * Test multi-column slice restrictions (e.g '(clustering_0) > (1)') with only one clustering column
-      */
-     @Test
-     public void testboundsAsClusteringWithMultiSliceRestrictionsWithOneClusteringColumn() throws InvalidRequestException
-     {
-         CFMetaData cfMetaData = newCFMetaData(1);
 -        // clustering_0 = 1 AND (clustering_1, clustering_2, clustering_3) > (2, 3, 4)
++        // clustering_0 IN (1, 2) AND (clustering_1, clustering_2, clustering_3) > (2, 3, 4)
+         Restriction in = newSingleIN(cfMetaData, 0, value1, value2);
+         slice = newMultiSlice(cfMetaData, 1, Bound.START, false, value2, value3, value4);
 -        restrictions = new PrimaryKeyRestrictionSet(cfMetaData.comparator);
++        restrictions = new PrimaryKeyRestrictionSet(cfMetaData.comparator, false);
+         restrictions = restrictions.mergeWith(slice);
+         restrictions = restrictions.mergeWith(in);
  
-         ByteBuffer value1 = ByteBufferUtil.bytes(1);
-         ByteBuffer value2 = ByteBufferUtil.bytes(2);
 -        bounds = restrictions.boundsAsComposites(Bound.START, QueryOptions.DEFAULT);
++        bounds = restrictions.boundsAsClustering(Bound.START, QueryOptions.DEFAULT);
+         assertEquals(4, bounds.size());
 -        assertComposite(bounds.get(0), value1, value2, EOC.START);
 -        assertComposite(bounds.get(1), value1, value2, EOC.END);
 -        assertComposite(bounds.get(2), value2, value2, EOC.START);
 -        assertComposite(bounds.get(3), value2, value2, EOC.END);
++        assertStartBound(get(bounds, 0), true, value1, value2);
++        assertStartBound(get(bounds, 1), false, value1, value2);
++        assertStartBound(get(bounds, 2), true, value2, value2);
++        assertStartBound(get(bounds, 3), false, value2, value2);
  
-         Restriction slice = newMultiSlice(cfMetaData, 0, Bound.START, false, value1);
-         PrimaryKeyRestrictions restrictions = new PrimaryKeyRestrictionSet(cfMetaData.comparator, false);
 -        bounds = restrictions.boundsAsComposites(Bound.END, QueryOptions.DEFAULT);
++        bounds = restrictions.boundsAsClustering(Bound.END, QueryOptions.DEFAULT);
+         assertEquals(4, bounds.size());
 -        assertComposite(bounds.get(0), value1, value2, value3, value4, EOC.START);
 -        assertComposite(bounds.get(1), value1, EOC.END);
 -        assertComposite(bounds.get(2), value2, value2, value3, value4, EOC.START);
 -        assertComposite(bounds.get(3), value2, EOC.END);
++        assertEndBound(get(bounds, 0), false, value1, value2, value3, value4);
++        assertEndBound(get(bounds, 1), true, value1);
++        assertEndBound(get(bounds, 2), false, value2, value2, value3, value4);
++        assertEndBound(get(bounds, 3), true, value2);
+ 
+         // (clustering_0, clustering1) >= (1, 2)
+         slice = newMultiSlice(cfMetaData, 0, Bound.START, true, value1, value2);
 -        restrictions = new PrimaryKeyRestrictionSet(cfMetaData.comparator);
++        restrictions = new PrimaryKeyRestrictionSet(cfMetaData.comparator, false);
          restrictions = restrictions.mergeWith(slice);
  
-         SortedSet<Slice.Bound> bounds = restrictions.boundsAsClustering(Bound.START, QueryOptions.DEFAULT);
 -        bounds = restrictions.boundsAsComposites(Bound.START, QueryOptions.DEFAULT);
++        bounds = restrictions.boundsAsClustering(Bound.START, QueryOptions.DEFAULT);
          assertEquals(1, bounds.size());
-         assertStartBound(get(bounds, 0), false, value1);
 -        assertComposite(bounds.get(0), value1, value2, EOC.NONE);
++        assertStartBound(get(bounds, 0), true, value1, value2);
  
 -        bounds = restrictions.boundsAsComposites(Bound.END, QueryOptions.DEFAULT);
 +        bounds = restrictions.boundsAsClustering(Bound.END, QueryOptions.DEFAULT);
          assertEquals(1, bounds.size());
 -        assertEmptyComposite(bounds.get(0));
 +        assertEmptyEnd(get(bounds, 0));
  
-         slice = newMultiSlice(cfMetaData, 0, Bound.START, true, value1);
+         // (clustering_0, clustering1, clustering_2, clustering_3) >= (1, 2, 3, 4)
+         slice = newMultiSlice(cfMetaData, 0, Bound.START, true, value1, value2, value3, value4);
 -        restrictions = new PrimaryKeyRestrictionSet(cfMetaData.comparator);
 +        restrictions = new PrimaryKeyRestrictionSet(cfMetaData.comparator, false);
          restrictions = restrictions.mergeWith(slice);
  
 -        bounds = restrictions.boundsAsComposites(Bound.START, QueryOptions.DEFAULT);
 +        bounds = restrictions.boundsAsClustering(Bound.START, QueryOptions.DEFAULT);
-         assertEquals(1, bounds.size());
-         assertStartBound(get(bounds, 0), true, value1);
+         assertEquals(2, bounds.size());
 -        assertComposite(bounds.get(0), value1, value2, EOC.START);
 -        assertComposite(bounds.get(1), value1, value2, EOC.END);
++        assertStartBound(get(bounds, 0), true, value1, value2);
++        assertStartBound(get(bounds, 1), false, value1, value2);
  
 -        bounds = restrictions.boundsAsComposites(Bound.END, QueryOptions.DEFAULT);
 +        bounds = restrictions.boundsAsClustering(Bound.END, QueryOptions.DEFAULT);
-         assertEquals(1, bounds.size());
-         assertEmptyEnd(get(bounds, 0));
+         assertEquals(2, bounds.size());
 -        assertComposite(bounds.get(0), value1, value2, value3, value4, EOC.END);
 -        assertEmptyComposite(bounds.get(1));
++        assertEndBound(get(bounds, 0), true, value1, value2, value3, value4);
++        assertEmptyEnd(get(bounds, 1));
  
-         slice = newMultiSlice(cfMetaData, 0, Bound.END, true, value1);
+         // (clustering_0, clustering1, clustering_2, clustering_3) <= (1, 2, 3, 4)
+         slice = newMultiSlice(cfMetaData, 0, Bound.END, true, value1, value2, value3, value4);
 -        restrictions = new PrimaryKeyRestrictionSet(cfMetaData.comparator);
 +        restrictions = new PrimaryKeyRestrictionSet(cfMetaData.comparator, false);
          restrictions = restrictions.mergeWith(slice);
  
 -        bounds = restrictions.boundsAsComposites(Bound.START, QueryOptions.DEFAULT);
 +        bounds = restrictions.boundsAsClustering(Bound.START, QueryOptions.DEFAULT);
-         assertEquals(1, bounds.size());
+         assertEquals(2, bounds.size());
 -        assertEmptyComposite(bounds.get(0));
 -        assertComposite(bounds.get(1), value1, value2, value3, value4, EOC.NONE);
 +        assertEmptyStart(get(bounds, 0));
++        assertStartBound(get(bounds, 1), true, value1, value2, value3, value4);
  
 -        bounds = restrictions.boundsAsComposites(Bound.END, QueryOptions.DEFAULT);
 +        bounds = restrictions.boundsAsClustering(Bound.END, QueryOptions.DEFAULT);
-         assertEquals(1, bounds.size());
-         assertEndBound(get(bounds, 0), true, value1);
+         assertEquals(2, bounds.size());
 -        assertComposite(bounds.get(0), value1, value2, EOC.START);
 -        assertComposite(bounds.get(1), value1, value2, EOC.END);
++        assertEndBound(get(bounds, 0), false, value1, value2);
++        assertEndBound(get(bounds, 1), true, value1, value2);
  
-         slice = newMultiSlice(cfMetaData, 0, Bound.END, false, value1);
+         // (clustering_0, clustering1, clustering_2, clustering_3) < (1, 2, 3, 4)
+         slice = newMultiSlice(cfMetaData, 0, Bound.END, false, value1, value2, value3, value4);
 -        restrictions = new PrimaryKeyRestrictionSet(cfMetaData.comparator);
 +        restrictions = new PrimaryKeyRestrictionSet(cfMetaData.comparator, false);
          restrictions = restrictions.mergeWith(slice);
  
 -        bounds = restrictions.boundsAsComposites(Bound.START, QueryOptions.DEFAULT);
 +        bounds = restrictions.boundsAsClustering(Bound.START, QueryOptions.DEFAULT);
-         assertEquals(1, bounds.size());
+         assertEquals(2, bounds.size());
 -        assertEmptyComposite(bounds.get(0));
 -        assertComposite(bounds.get(1), value1, value2, value3, value4, EOC.END);
 +        assertEmptyStart(get(bounds, 0));
++        assertStartBound(get(bounds, 1), false, value1, value2, value3, value4);
  
 -        bounds = restrictions.boundsAsComposites(Bound.END, QueryOptions.DEFAULT);
 +        bounds = restrictions.boundsAsClustering(Bound.END, QueryOptions.DEFAULT);
-         assertEquals(1, bounds.size());
-         assertEndBound(get(bounds, 0), false, value1);
+         assertEquals(2, bounds.size());
 -        assertComposite(bounds.get(0), value1, value2, EOC.START);
 -        assertComposite(bounds.get(1), value1, value2, EOC.END);
++        assertEndBound(get(bounds, 0), false, value1, value2);
++        assertEndBound(get(bounds, 1), true, value1, value2);
  
-         slice = newMultiSlice(cfMetaData, 0, Bound.START, false, value1);
-         Restriction slice2 = newMultiSlice(cfMetaData, 0, Bound.END, false, value2);
+         // (clustering_0, clustering1, clustering_2, clustering_3) > (1, 2, 3, 4) AND (clustering_0, clustering_1) < (2, 3)
+         slice = newMultiSlice(cfMetaData, 0, Bound.START, false, value1, value2, value3, value4);
+         Restriction slice2 = newMultiSlice(cfMetaData, 0, Bound.END, false, value2, value3);
 -        restrictions = new PrimaryKeyRestrictionSet(cfMetaData.comparator);
 +        restrictions = new PrimaryKeyRestrictionSet(cfMetaData.comparator, false);
          restrictions = restrictions.mergeWith(slice).mergeWith(slice2);
  
 -        bounds = restrictions.boundsAsComposites(Bound.START, QueryOptions.DEFAULT);
 +        bounds = restrictions.boundsAsClustering(Bound.START, QueryOptions.DEFAULT);
-         assertEquals(1, bounds.size());
-         assertStartBound(get(bounds, 0), false, value1);
+         assertEquals(2, bounds.size());
 -        assertComposite(bounds.get(0), value1, value2, EOC.START);
 -        assertComposite(bounds.get(1), value1, value2, EOC.END);
++        assertStartBound(get(bounds, 0), true, value1, value2);
++        assertStartBound(get(bounds, 1), false, value1, value2);
  
 -        bounds = restrictions.boundsAsComposites(Bound.END, QueryOptions.DEFAULT);
 +        bounds = restrictions.boundsAsClustering(Bound.END, QueryOptions.DEFAULT);
-         assertEquals(1, bounds.size());
-         assertEndBound(get(bounds, 0), false, value2);
+         assertEquals(2, bounds.size());
 -        assertComposite(bounds.get(0), value1, value2, value3, value4, EOC.START);
 -        assertComposite(bounds.get(1), value2, value3, EOC.START);
++        assertEndBound(get(bounds, 0), false, value1, value2, value3, value4);
++        assertEndBound(get(bounds, 1), false, value2, value3);
  
-         slice = newMultiSlice(cfMetaData, 0, Bound.START, true, value1);
-         slice2 = newMultiSlice(cfMetaData, 0, Bound.END, true, value2);
+         // (clustering_0, clustering1, clustering_2, clustering_3) >= (1, 2, 3, 4) AND (clustering_0, clustering1, clustering_2, clustering_3) <= (4, 3, 2, 1)
+         slice = newMultiSlice(cfMetaData, 0, Bound.START, true, value1, value2, value3, value4);
+         slice2 = newMultiSlice(cfMetaData, 0, Bound.END, true, value4, value3, value2, value1);
 -        restrictions = new PrimaryKeyRestrictionSet(cfMetaData.comparator);
 +        restrictions = new PrimaryKeyRestrictionSet(cfMetaData.comparator, false);
          restrictions = restrictions.mergeWith(slice).mergeWith(slice2);
  
 -        bounds = restrictions.boundsAsComposites(Bound.START, QueryOptions.DEFAULT);
 +        bounds = restrictions.boundsAsClustering(Bound.START, QueryOptions.DEFAULT);
-         assertEquals(1, bounds.size());
-         assertStartBound(get(bounds, 0), true, value1);
+         assertEquals(3, bounds.size());
 -        assertComposite(bounds.get(0), value1, value2,  EOC.START);
 -        assertComposite(bounds.get(1), value1, value2, EOC.END);
 -        assertComposite(bounds.get(2), value4, value3, value2, value1, EOC.NONE);
++        assertStartBound(get(bounds, 0), true, value1, value2);
++        assertStartBound(get(bounds, 1), false, value1, value2);
++        assertStartBound(get(bounds, 2), true, value4, value3, value2, value1);
  
 -        bounds = restrictions.boundsAsComposites(Bound.END, QueryOptions.DEFAULT);
 +        bounds = restrictions.boundsAsClustering(Bound.END, QueryOptions.DEFAULT);
-         assertEquals(1, bounds.size());
-         assertEndBound(get(bounds, 0), true, value2);
+         assertEquals(3, bounds.size());
 -        assertComposite(bounds.get(0), value1, value2, value3, value4, EOC.END);
 -        assertComposite(bounds.get(1), value4, value3, EOC.START);
 -        assertComposite(bounds.get(2), value4, value3, EOC.END);
++        assertEndBound(get(bounds, 0), true, value1, value2, value3, value4);
++        assertEndBound(get(bounds, 1), false, value4, value3);
++        assertEndBound(get(bounds, 2), true, value4, value3);
      }
  
      /**
-      * Test multi-column slice restrictions (e.g '(clustering_0, clustering_1) > (1, 2)')
+      * Test multi-column slice restrictions with ascending, descending, ascending and descending columns
+      * (e.g '(clustering_0, clustering1, clustering_3, clustering4) > (1, 2, 3, 4)')
       */
      @Test
-     public void testboundsAsClusteringWithMultiSliceRestrictionsWithTwo

<TRUNCATED>

[3/6] cassandra git commit: Fix SELECT on tuple relations for mixed ASC/DESC clustering order

Posted by bl...@apache.org.
Fix SELECT on tuple relations for mixed ASC/DESC clustering order

patch by Marcin Szymaniuk and Benjamin Lerer; reviewed by Sylvain Lebresne for CASSANDRA-7281


Project: http://git-wip-us.apache.org/repos/asf/cassandra/repo
Commit: http://git-wip-us.apache.org/repos/asf/cassandra/commit/90fc8969
Tree: http://git-wip-us.apache.org/repos/asf/cassandra/tree/90fc8969
Diff: http://git-wip-us.apache.org/repos/asf/cassandra/diff/90fc8969

Branch: refs/heads/trunk
Commit: 90fc8969a656b183e81496e479db35f321c8a3aa
Parents: 984e174
Author: Marcin Szymaniuk <ma...@gmail.com>
Authored: Wed Feb 10 18:29:54 2016 +0100
Committer: Benjamin Lerer <b....@gmail.com>
Committed: Thu Feb 11 10:28:45 2016 +0100

----------------------------------------------------------------------
 CHANGES.txt                                     |    1 +
 .../cql3/restrictions/AbstractRestriction.java  |   15 +-
 .../ForwardingPrimaryKeyRestrictions.java       |    3 +-
 .../restrictions/MultiColumnRestriction.java    |   80 +-
 .../restrictions/PrimaryKeyRestrictionSet.java  |   95 +-
 .../cql3/restrictions/Restriction.java          |    3 +-
 .../cql3/restrictions/RestrictionSet.java       |    4 +-
 .../restrictions/SingleColumnRestriction.java   |    9 +-
 .../cql3/restrictions/TokenRestriction.java     |    3 +-
 .../cql3/statements/ModificationStatement.java  |   19 +-
 .../db/composites/CompositesBuilder.java        |   20 +-
 .../PrimaryKeyRestrictionSetTest.java           | 1164 +++++++++++++++---
 .../SelectMultiColumnRelationTest.java          |  859 ++++++++++++-
 13 files changed, 2043 insertions(+), 232 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/cassandra/blob/90fc8969/CHANGES.txt
----------------------------------------------------------------------
diff --git a/CHANGES.txt b/CHANGES.txt
index 7565386..5674b9d 100644
--- a/CHANGES.txt
+++ b/CHANGES.txt
@@ -1,4 +1,5 @@
 2.2.6
+ * Fix SELECT on tuple relations for mixed ASC/DESC clustering order (CASSANDRA-7281)
  * (cqlsh) Support utf-8/cp65001 encoding on Windows (CASSANDRA-11030)
  * Fix paging on DISTINCT queries repeats result when first row in partition changes (CASSANDRA-10010)
 Merged from 2.1:

http://git-wip-us.apache.org/repos/asf/cassandra/blob/90fc8969/src/java/org/apache/cassandra/cql3/restrictions/AbstractRestriction.java
----------------------------------------------------------------------
diff --git a/src/java/org/apache/cassandra/cql3/restrictions/AbstractRestriction.java b/src/java/org/apache/cassandra/cql3/restrictions/AbstractRestriction.java
index 64c94f4..dac7203 100644
--- a/src/java/org/apache/cassandra/cql3/restrictions/AbstractRestriction.java
+++ b/src/java/org/apache/cassandra/cql3/restrictions/AbstractRestriction.java
@@ -19,12 +19,13 @@ package org.apache.cassandra.cql3.restrictions;
 
 import java.nio.ByteBuffer;
 
+import org.apache.cassandra.config.ColumnDefinition;
+
 import org.apache.cassandra.cql3.ColumnSpecification;
 import org.apache.cassandra.cql3.QueryOptions;
 import org.apache.cassandra.cql3.statements.Bound;
 import org.apache.cassandra.db.composites.CompositesBuilder;
 import org.apache.cassandra.exceptions.InvalidRequestException;
-
 import static org.apache.cassandra.cql3.statements.RequestValidations.checkBindValueSet;
 import static org.apache.cassandra.cql3.statements.RequestValidations.checkFalse;
 import static org.apache.cassandra.cql3.statements.RequestValidations.checkNotNull;
@@ -97,4 +98,16 @@ abstract class AbstractRestriction  implements Restriction
         checkFalse(value.remaining() > 0xFFFF, "Index expression values may not be larger than 64K");
         return value;
     }
+
+    /**
+     * Reverses the specified bound if the column type is a reversed one.
+     *
+     * @param columnDefinition the column definition
+     * @param bound the bound
+     * @return the bound reversed if the column type was a reversed one or the original bound
+     */
+    protected static Bound reverseBoundIfNeeded(ColumnDefinition columnDefinition, Bound bound)
+    {
+        return columnDefinition.isReversedType() ? bound.reverse() : bound;
+    }
 }

http://git-wip-us.apache.org/repos/asf/cassandra/blob/90fc8969/src/java/org/apache/cassandra/cql3/restrictions/ForwardingPrimaryKeyRestrictions.java
----------------------------------------------------------------------
diff --git a/src/java/org/apache/cassandra/cql3/restrictions/ForwardingPrimaryKeyRestrictions.java b/src/java/org/apache/cassandra/cql3/restrictions/ForwardingPrimaryKeyRestrictions.java
index 03c6cbc..71855a0 100644
--- a/src/java/org/apache/cassandra/cql3/restrictions/ForwardingPrimaryKeyRestrictions.java
+++ b/src/java/org/apache/cassandra/cql3/restrictions/ForwardingPrimaryKeyRestrictions.java
@@ -18,7 +18,6 @@
 package org.apache.cassandra.cql3.restrictions;
 
 import java.nio.ByteBuffer;
-import java.util.Collection;
 import java.util.List;
 
 import org.apache.cassandra.config.ColumnDefinition;
@@ -51,7 +50,7 @@ abstract class ForwardingPrimaryKeyRestrictions implements PrimaryKeyRestriction
     }
 
     @Override
-    public Collection<ColumnDefinition> getColumnDefs()
+    public List<ColumnDefinition> getColumnDefs()
     {
         return getDelegate().getColumnDefs();
     }

http://git-wip-us.apache.org/repos/asf/cassandra/blob/90fc8969/src/java/org/apache/cassandra/cql3/restrictions/MultiColumnRestriction.java
----------------------------------------------------------------------
diff --git a/src/java/org/apache/cassandra/cql3/restrictions/MultiColumnRestriction.java b/src/java/org/apache/cassandra/cql3/restrictions/MultiColumnRestriction.java
index 84a3952..96e6f2b 100644
--- a/src/java/org/apache/cassandra/cql3/restrictions/MultiColumnRestriction.java
+++ b/src/java/org/apache/cassandra/cql3/restrictions/MultiColumnRestriction.java
@@ -20,11 +20,9 @@ package org.apache.cassandra.cql3.restrictions;
 import java.nio.ByteBuffer;
 import java.util.*;
 
-import org.apache.cassandra.cql3.Term.Terminal;
-
-import org.apache.cassandra.cql3.Term;
 import org.apache.cassandra.config.ColumnDefinition;
 import org.apache.cassandra.cql3.*;
+import org.apache.cassandra.cql3.Term.Terminal;
 import org.apache.cassandra.cql3.functions.Function;
 import org.apache.cassandra.cql3.statements.Bound;
 import org.apache.cassandra.db.IndexExpression;
@@ -68,7 +66,7 @@ public abstract class MultiColumnRestriction extends AbstractRestriction
     }
 
     @Override
-    public Collection<ColumnDefinition> getColumnDefs()
+    public List<ColumnDefinition> getColumnDefs()
     {
         return columnDefs;
     }
@@ -361,14 +359,62 @@ public abstract class MultiColumnRestriction extends AbstractRestriction
         @Override
         public CompositesBuilder appendBoundTo(CompositesBuilder builder, Bound bound, QueryOptions options)
         {
-            List<ByteBuffer> vals = componentBounds(bound, options);
+            boolean reversed = getFirstColumn().isReversedType();
+
+            EnumMap<Bound, List<ByteBuffer>> componentBounds = new EnumMap<Bound, List<ByteBuffer>>(Bound.class);
+            componentBounds.put(Bound.START, componentBounds(Bound.START, options));
+            componentBounds.put(Bound.END, componentBounds(Bound.END, options));
 
-            for (int i = 0, m = vals.size(); i < m; i++)
+            List<List<ByteBuffer>> toAdd = new ArrayList<>();
+            List<ByteBuffer> values = new ArrayList<>();
+
+            for (int i = 0, m = columnDefs.size(); i < m; i++)
             {
-                ByteBuffer v = checkNotNull(vals.get(i), "Invalid null value in condition for column %s", columnDefs.get(i).name);
-                builder.addElementToAll(v);
+                ColumnDefinition column = columnDefs.get(i);
+                Bound b = reverseBoundIfNeeded(column, bound);
+
+                // For mixed order columns, we need to create additional slices when 2 columns are in reverse order
+                if (reversed != column.isReversedType())
+                {
+                    reversed = column.isReversedType();
+                    // As we are switching direction we need to add the current composite
+                    toAdd.add(values);
+
+                    // The new bound side has no value for this component.  just stop
+                    if (!hasComponent(b, i, componentBounds))
+                        continue;
+
+                    // The other side has still some components. We need to end the slice that we have just open.
+                    if (hasComponent(b.reverse(), i, componentBounds))
+                        toAdd.add(values);
+
+                    // We need to rebuild where we are in this bound side
+                    values = new ArrayList<ByteBuffer>();
+
+                    List<ByteBuffer> vals = componentBounds.get(b);
+
+                    int n = Math.min(i, vals.size());
+                    for (int j = 0; j < n; j++)
+                    {
+                        ByteBuffer v = checkNotNull(vals.get(j),
+                                                    "Invalid null value in condition for column %s",
+                                                    columnDefs.get(j).name);
+                        values.add(v);
+                    }
+                }
+
+                if (!hasComponent(b, i, componentBounds))
+                    continue;
+
+                ByteBuffer v = checkNotNull(componentBounds.get(b).get(i), "Invalid null value in condition for column %s", columnDefs.get(i).name);
+                values.add(v);
             }
-            return builder;
+            toAdd.add(values);
+
+            if (bound.isEnd())
+                Collections.reverse(toAdd);
+
+            return builder.addAllElementsToAll(toAdd);
         }
 
         @Override
@@ -378,9 +424,9 @@ public abstract class MultiColumnRestriction extends AbstractRestriction
         }
 
         @Override
-        public boolean hasBound(Bound b)
+        public boolean hasBound(Bound bound)
         {
-            return slice.hasBound(b);
+            return slice.hasBound(bound);
         }
 
         @Override
@@ -390,9 +436,9 @@ public abstract class MultiColumnRestriction extends AbstractRestriction
         }
 
         @Override
-        public boolean isInclusive(Bound b)
+        public boolean isInclusive(Bound bound)
         {
-            return slice.isInclusive(b);
+            return slice.isInclusive(bound);
         }
 
         @Override
@@ -448,6 +494,9 @@ public abstract class MultiColumnRestriction extends AbstractRestriction
          */
         private List<ByteBuffer> componentBounds(Bound b, QueryOptions options) throws InvalidRequestException
         {
+            if (!slice.hasBound(b))
+                return Collections.emptyList();
+
             Terminal terminal = slice.bound(b).bind(options);
 
             if (terminal instanceof Tuples.Value)
@@ -457,5 +506,10 @@ public abstract class MultiColumnRestriction extends AbstractRestriction
 
             return Collections.singletonList(terminal.get(options.getProtocolVersion()));
         }
+
+        private boolean hasComponent(Bound b, int index, EnumMap<Bound, List<ByteBuffer>> componentBounds)
+        {
+            return componentBounds.get(b).size() > index;
+        }
     }
 }

http://git-wip-us.apache.org/repos/asf/cassandra/blob/90fc8969/src/java/org/apache/cassandra/cql3/restrictions/PrimaryKeyRestrictionSet.java
----------------------------------------------------------------------
diff --git a/src/java/org/apache/cassandra/cql3/restrictions/PrimaryKeyRestrictionSet.java b/src/java/org/apache/cassandra/cql3/restrictions/PrimaryKeyRestrictionSet.java
index 3a20f6a..936dbd6 100644
--- a/src/java/org/apache/cassandra/cql3/restrictions/PrimaryKeyRestrictionSet.java
+++ b/src/java/org/apache/cassandra/cql3/restrictions/PrimaryKeyRestrictionSet.java
@@ -18,10 +18,9 @@
 package org.apache.cassandra.cql3.restrictions;
 
 import java.nio.ByteBuffer;
-import java.util.Collection;
-import java.util.Collections;
-import java.util.List;
+import java.util.*;
 
+import org.apache.cassandra.db.composites.Composite;
 import org.apache.cassandra.config.ColumnDefinition;
 import org.apache.cassandra.cql3.QueryOptions;
 import org.apache.cassandra.cql3.functions.Function;
@@ -31,7 +30,6 @@ import org.apache.cassandra.db.composites.*;
 import org.apache.cassandra.db.composites.Composite.EOC;
 import org.apache.cassandra.db.index.SecondaryIndexManager;
 import org.apache.cassandra.exceptions.InvalidRequestException;
-
 import static org.apache.cassandra.cql3.statements.RequestValidations.checkFalse;
 import static org.apache.cassandra.cql3.statements.RequestValidations.invalidRequest;
 
@@ -163,7 +161,7 @@ final class PrimaryKeyRestrictionSet extends AbstractPrimaryKeyRestrictions
     @Override
     public List<Composite> valuesAsComposites(QueryOptions options) throws InvalidRequestException
     {
-        return appendTo(new CompositesBuilder(ctype), options).build();
+        return filterAndSort(appendTo(new CompositesBuilder(ctype), options).build());
     }
 
     @Override
@@ -197,29 +195,16 @@ final class PrimaryKeyRestrictionSet extends AbstractPrimaryKeyRestrictions
         {
             ColumnDefinition def = r.getFirstColumn();
 
-            // In a restriction, we always have Bound.START < Bound.END for the "base" comparator.
-            // So if we're doing a reverse slice, we must inverse the bounds when giving them as start and end of the slice filter.
-            // But if the actual comparator itself is reversed, we must inversed the bounds too.
-            Bound b = !def.isReversedType() ? bound : bound.reverse();
             if (keyPosition != def.position() || r.isContains())
                 break;
 
             if (r.isSlice())
             {
-                if (!r.hasBound(b))
-                {
-                    // There wasn't any non EQ relation on that key, we select all records having the preceding component as prefix.
-                    // For composites, if there was preceding component and we're computing the end, we must change the last component
-                    // End-Of-Component, otherwise we would be selecting only one record.
-                    return builder.buildWithEOC(bound.isEnd() ? EOC.END : EOC.START);
-                }
-
-                r.appendBoundTo(builder, b, options);
-                Composite.EOC eoc = eocFor(r, bound, b);
-                return builder.buildWithEOC(eoc);
+                r.appendBoundTo(builder, bound, options);
+                return filterAndSort(setEocs(r, bound, builder.build()));
             }
 
-            r.appendBoundTo(builder, b, options);
+            r.appendBoundTo(builder, bound, options);
 
             if (builder.hasMissingElements())
                 return Collections.emptyList();
@@ -233,7 +218,71 @@ final class PrimaryKeyRestrictionSet extends AbstractPrimaryKeyRestrictions
         // case using the eoc would be bad, since for the random partitioner we have no guarantee that
         // prefix.end() will sort after prefix (see #5240).
         EOC eoc = !builder.hasRemaining() ? EOC.NONE : (bound.isEnd() ? EOC.END : EOC.START);
-        return builder.buildWithEOC(eoc);
+        return filterAndSort(builder.buildWithEOC(eoc));
+    }
+
+    /**
+     * Removes duplicates and sort the specified composites.
+     *
+     * @param composites the composites to filter and sort
+     * @return the composites sorted and without duplicates
+     */
+    private List<Composite> filterAndSort(List<Composite> composites)
+    {
+        if (composites.size() <= 1)
+            return composites;
+
+        TreeSet<Composite> set = new TreeSet<Composite>(ctype);
+        set.addAll(composites);
+        return new ArrayList<>(set);
+    }
+
+    /**
+     * Sets EOCs for the composites returned by the specified slice restriction for the given bound.
+     *
+     * @param r the slice restriction
+     * @param bound the bound
+     * @param composites the composites
+     * @return the composites with their EOCs properly set
+     */
+    private List<Composite> setEocs(Restriction r, Bound bound, List<Composite> composites)
+    {
+        List<Composite> list = new ArrayList<>(composites.size());
+
+        // The first column of the slice might not be the first clustering column (e.g. clustering_0 = ? AND (clustering_1, clustering_2) >= (?, ?)
+        int offset = r.getFirstColumn().position();
+
+        for (int i = 0, m = composites.size(); i < m; i++)
+        {
+            Composite composite = composites.get(i);
+
+            // Handle the no bound case
+            if (composite.size() == offset)
+            {
+                list.add(composite.withEOC(bound.isEnd() ? EOC.END : EOC.START));
+                continue;
+            }
+
+            // In the case of mixed order columns, we will have some extra slices where the columns change directions.
+            // For example: if we have clustering_0 DESC and clustering_1 ASC a slice like (clustering_0, clustering_1) > (1, 2)
+            // will produce 2 slices: [EMPTY, 1.START] and [1.2.END, 1.END]
+            // So, the END bound will return 2 composite with the same values 1
+            if (composite.size() <= r.getLastColumn().position() && i < m - 1 && composite.equals(composites.get(i + 1)))
+            {
+                list.add(composite.withEOC(EOC.START));
+                list.add(composites.get(i++).withEOC(EOC.END));
+                continue;
+            }
+
+            // Handle the normal bounds
+            ColumnDefinition column = r.getColumnDefs().get(composite.size() - 1 - offset);
+            Bound b = reverseBoundIfNeeded(column, bound);
+
+            Composite.EOC eoc = eocFor(r, bound, b);
+            list.add(composite.withEOC(eoc));
+        }
+
+        return list;
     }
 
     @Override
@@ -307,7 +356,7 @@ final class PrimaryKeyRestrictionSet extends AbstractPrimaryKeyRestrictions
     }
 
     @Override
-    public Collection<ColumnDefinition> getColumnDefs()
+    public List<ColumnDefinition> getColumnDefs()
     {
         return restrictions.getColumnDefs();
     }

http://git-wip-us.apache.org/repos/asf/cassandra/blob/90fc8969/src/java/org/apache/cassandra/cql3/restrictions/Restriction.java
----------------------------------------------------------------------
diff --git a/src/java/org/apache/cassandra/cql3/restrictions/Restriction.java b/src/java/org/apache/cassandra/cql3/restrictions/Restriction.java
index c115a3b..f0ea6a7 100644
--- a/src/java/org/apache/cassandra/cql3/restrictions/Restriction.java
+++ b/src/java/org/apache/cassandra/cql3/restrictions/Restriction.java
@@ -17,7 +17,6 @@
  */
 package org.apache.cassandra.cql3.restrictions;
 
-import java.util.Collection;
 import java.util.List;
 
 import org.apache.cassandra.config.ColumnDefinition;
@@ -60,7 +59,7 @@ public interface Restriction
      * Returns the column definitions in position order.
      * @return the column definitions in position order.
      */
-    public Collection<ColumnDefinition> getColumnDefs();
+    public List<ColumnDefinition> getColumnDefs();
 
     /**
      * Return an Iterable over all of the functions (both native and user-defined) used by any component

http://git-wip-us.apache.org/repos/asf/cassandra/blob/90fc8969/src/java/org/apache/cassandra/cql3/restrictions/RestrictionSet.java
----------------------------------------------------------------------
diff --git a/src/java/org/apache/cassandra/cql3/restrictions/RestrictionSet.java b/src/java/org/apache/cassandra/cql3/restrictions/RestrictionSet.java
index 6bf7666..676ed13 100644
--- a/src/java/org/apache/cassandra/cql3/restrictions/RestrictionSet.java
+++ b/src/java/org/apache/cassandra/cql3/restrictions/RestrictionSet.java
@@ -75,9 +75,9 @@ final class RestrictionSet implements Restrictions, Iterable<Restriction>
     }
 
     @Override
-    public final Set<ColumnDefinition> getColumnDefs()
+    public final List<ColumnDefinition> getColumnDefs()
     {
-        return restrictions.keySet();
+        return new ArrayList<>(restrictions.keySet());
     }
 
     @Override

http://git-wip-us.apache.org/repos/asf/cassandra/blob/90fc8969/src/java/org/apache/cassandra/cql3/restrictions/SingleColumnRestriction.java
----------------------------------------------------------------------
diff --git a/src/java/org/apache/cassandra/cql3/restrictions/SingleColumnRestriction.java b/src/java/org/apache/cassandra/cql3/restrictions/SingleColumnRestriction.java
index afa0419..735a2e2 100644
--- a/src/java/org/apache/cassandra/cql3/restrictions/SingleColumnRestriction.java
+++ b/src/java/org/apache/cassandra/cql3/restrictions/SingleColumnRestriction.java
@@ -52,7 +52,7 @@ public abstract class SingleColumnRestriction extends AbstractRestriction
     }
 
     @Override
-    public Collection<ColumnDefinition> getColumnDefs()
+    public List<ColumnDefinition> getColumnDefs()
     {
         return Collections.singletonList(columnDef);
     }
@@ -351,7 +351,12 @@ public abstract class SingleColumnRestriction extends AbstractRestriction
         @Override
         public CompositesBuilder appendBoundTo(CompositesBuilder builder, Bound bound, QueryOptions options)
         {
-            ByteBuffer value = slice.bound(bound).bindAndGet(options);
+            Bound b = reverseBoundIfNeeded(getFirstColumn(), bound);
+
+            if (!hasBound(b))
+                return builder;
+
+            ByteBuffer value = slice.bound(b).bindAndGet(options);
             checkBindValueSet(value, "Invalid unset value for column %s", columnDef.name);
             return builder.addElementToAll(value);
 

http://git-wip-us.apache.org/repos/asf/cassandra/blob/90fc8969/src/java/org/apache/cassandra/cql3/restrictions/TokenRestriction.java
----------------------------------------------------------------------
diff --git a/src/java/org/apache/cassandra/cql3/restrictions/TokenRestriction.java b/src/java/org/apache/cassandra/cql3/restrictions/TokenRestriction.java
index f8cd0dc..3cd3304 100644
--- a/src/java/org/apache/cassandra/cql3/restrictions/TokenRestriction.java
+++ b/src/java/org/apache/cassandra/cql3/restrictions/TokenRestriction.java
@@ -18,7 +18,6 @@
 package org.apache.cassandra.cql3.restrictions;
 
 import java.nio.ByteBuffer;
-import java.util.Collection;
 import java.util.Collections;
 import java.util.List;
 
@@ -67,7 +66,7 @@ public abstract class TokenRestriction extends AbstractPrimaryKeyRestrictions
     }
 
     @Override
-    public Collection<ColumnDefinition> getColumnDefs()
+    public List<ColumnDefinition> getColumnDefs()
     {
         return columnDefs;
     }

http://git-wip-us.apache.org/repos/asf/cassandra/blob/90fc8969/src/java/org/apache/cassandra/cql3/statements/ModificationStatement.java
----------------------------------------------------------------------
diff --git a/src/java/org/apache/cassandra/cql3/statements/ModificationStatement.java b/src/java/org/apache/cassandra/cql3/statements/ModificationStatement.java
index 8b594dd..fbdfc0c 100644
--- a/src/java/org/apache/cassandra/cql3/statements/ModificationStatement.java
+++ b/src/java/org/apache/cassandra/cql3/statements/ModificationStatement.java
@@ -48,7 +48,6 @@ import org.apache.cassandra.transport.messages.ResultMessage;
 import org.apache.cassandra.triggers.TriggerExecutor;
 import org.apache.cassandra.utils.Pair;
 import org.apache.cassandra.utils.UUIDGen;
-
 import static org.apache.cassandra.cql3.statements.RequestValidations.checkFalse;
 import static org.apache.cassandra.cql3.statements.RequestValidations.checkNotNull;
 import static org.apache.cassandra.cql3.statements.RequestValidations.invalidRequest;
@@ -310,7 +309,7 @@ public abstract class ModificationStatement implements CQLStatement
             r.appendTo(keyBuilder, options);
         }
 
-        return Lists.transform(keyBuilder.build(), new com.google.common.base.Function<Composite, ByteBuffer>()
+        return Lists.transform(filterAndSort(keyBuilder.build()), new com.google.common.base.Function<Composite, ByteBuffer>()
         {
             @Override
             public ByteBuffer apply(Composite composite)
@@ -404,6 +403,22 @@ public abstract class ModificationStatement implements CQLStatement
         return builder.build().get(0); // We only allow IN for row keys so far
     }
 
+    /**
+     * Removes duplicates and sort the specified composites.
+     *
+     * @param composites the composites to filter and sort
+     * @return the composites sorted and without duplicates
+     */
+    private List<Composite> filterAndSort(List<Composite> composites)
+    {
+        if (composites.size() <= 1)
+            return composites;
+
+        TreeSet<Composite> set = new TreeSet<Composite>(cfm.getKeyValidatorAsCType());
+        set.addAll(composites);
+        return new ArrayList<>(set);
+    }
+
     protected ColumnDefinition getFirstEmptyKey()
     {
         for (ColumnDefinition def : cfm.clusteringColumns())

http://git-wip-us.apache.org/repos/asf/cassandra/blob/90fc8969/src/java/org/apache/cassandra/db/composites/CompositesBuilder.java
----------------------------------------------------------------------
diff --git a/src/java/org/apache/cassandra/db/composites/CompositesBuilder.java b/src/java/org/apache/cassandra/db/composites/CompositesBuilder.java
index 25a510f..9a4da9e 100644
--- a/src/java/org/apache/cassandra/db/composites/CompositesBuilder.java
+++ b/src/java/org/apache/cassandra/db/composites/CompositesBuilder.java
@@ -181,9 +181,6 @@ public final class CompositesBuilder
 
                     List<ByteBuffer> value = values.get(j);
 
-                    if (value.isEmpty())
-                        hasMissingElements = true;
-
                     if (value.contains(null))
                         containsNull = true;
                     if (value.contains(ByteBufferUtil.UNSET_BYTE_BUFFER))
@@ -283,26 +280,15 @@ public final class CompositesBuilder
         if (elementsList.isEmpty())
             return singletonList(builder.build().withEOC(eoc));
 
-        // Use a Set to sort if needed and eliminate duplicates
-        Set<Composite> set = newSet();
+        List<Composite> list = new ArrayList<>();
 
         for (int i = 0, m = elementsList.size(); i < m; i++)
         {
             List<ByteBuffer> elements = elementsList.get(i);
-            set.add(builder.buildWith(elements).withEOC(eoc));
+            list.add(builder.buildWith(elements).withEOC(eoc));
         }
 
-        return new ArrayList<>(set);
-    }
-
-    /**
-     * Returns a new <code>Set</code> instance that will be used to eliminate duplicates and sort the results.
-     *
-     * @return a new <code>Set</code> instance.
-     */
-    private Set<Composite> newSet()
-    {
-        return new TreeSet<>(ctype);
+        return list;
     }
 
     private void checkUpdateable()