You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@cassandra.apache.org by ty...@apache.org on 2014/11/19 18:31:09 UTC

[2/5] cassandra git commit: Fix multicolumn relation + COMPACT STORAGE issues

Fix multicolumn relation + COMPACT STORAGE issues

Patch by Tyler Hobbs; reviewed by Benjamin Lerer for CASSANDRA-8264


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

Branch: refs/heads/trunk
Commit: 084d93daf6b6031909fc318e57a2a205ad32c237
Parents: 1945384
Author: Tyler Hobbs <ty...@datastax.com>
Authored: Wed Nov 19 11:25:09 2014 -0600
Committer: Tyler Hobbs <ty...@datastax.com>
Committed: Wed Nov 19 11:25:09 2014 -0600

----------------------------------------------------------------------
 CHANGES.txt                                     |    2 +
 .../cql3/statements/SelectStatement.java        |   95 +-
 .../cassandra/cql3/MultiColumnRelationTest.java | 1464 +++++++++---------
 3 files changed, 840 insertions(+), 721 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/cassandra/blob/084d93da/CHANGES.txt
----------------------------------------------------------------------
diff --git a/CHANGES.txt b/CHANGES.txt
index fff6d3a..01ea887 100644
--- a/CHANGES.txt
+++ b/CHANGES.txt
@@ -1,4 +1,6 @@
 2.0.12:
+ * Fix some failing queries that use multi-column relations
+   on COMPACT STORAGE tables (CASSANDRA-8264)
  * Fix InvalidRequestException with ORDER BY (CASSANDRA-8286)
  * Disable SSLv3 for POODLE (CASSANDRA-8265)
  * Fix millisecond timestamps in Tracing (CASSANDRA-8297)

http://git-wip-us.apache.org/repos/asf/cassandra/blob/084d93da/src/java/org/apache/cassandra/cql3/statements/SelectStatement.java
----------------------------------------------------------------------
diff --git a/src/java/org/apache/cassandra/cql3/statements/SelectStatement.java b/src/java/org/apache/cassandra/cql3/statements/SelectStatement.java
index f1d1aab..db25716 100644
--- a/src/java/org/apache/cassandra/cql3/statements/SelectStatement.java
+++ b/src/java/org/apache/cassandra/cql3/statements/SelectStatement.java
@@ -713,9 +713,9 @@ public class SelectStatement implements CQLStatement, MeasurableForPreparedCache
             CFDefinition.Name name = idIter.next();
             assert r != null && !r.isSlice();
 
-            List<ByteBuffer> values = r.values(variables);
-            if (values.size() == 1)
+            if (r.isEQ())
             {
+                List<ByteBuffer> values = r.values(variables);
                 ByteBuffer val = values.get(0);
                 if (val == null)
                     throw new InvalidRequestException(String.format("Invalid null value for clustering key part %s", name.name));
@@ -723,26 +723,56 @@ public class SelectStatement implements CQLStatement, MeasurableForPreparedCache
             }
             else
             {
-                // We have a IN, which we only support for the last column.
-                // If compact, just add all values and we're done. Otherwise,
-                // for each value of the IN, creates all the columns corresponding to the selection.
-                if (values.isEmpty())
-                    return null;
-                SortedSet<ByteBuffer> columns = new TreeSet<ByteBuffer>(cfDef.cfm.comparator);
-                Iterator<ByteBuffer> iter = values.iterator();
-                while (iter.hasNext())
+                if (!r.isMultiColumn())
                 {
-                    ByteBuffer val = iter.next();
-                    ColumnNameBuilder b = iter.hasNext() ? builder.copy() : builder;
-                    if (val == null)
-                        throw new InvalidRequestException(String.format("Invalid null value for clustering key part %s", name.name));
-                    b.add(val);
-                    if (cfDef.isCompact)
-                        columns.add(b.build());
-                    else
-                        columns.addAll(addSelectedColumns(b));
+                    // We have a IN, which we only support for the last column.
+                    // If compact, just add all values and we're done. Otherwise,
+                    // for each value of the IN, creates all the columns corresponding to the selection.
+                    List<ByteBuffer> values = r.values(variables);
+                    if (values.isEmpty())
+                        return null;
+                    SortedSet<ByteBuffer> columns = new TreeSet<ByteBuffer>(cfDef.cfm.comparator);
+                    Iterator<ByteBuffer> iter = values.iterator();
+                    while (iter.hasNext())
+                    {
+                        ByteBuffer val = iter.next();
+                        ColumnNameBuilder b = iter.hasNext() ? builder.copy() : builder;
+                        if (val == null)
+                            throw new InvalidRequestException(String.format("Invalid null value for clustering key part %s", name.name));
+                        b.add(val);
+                        if (cfDef.isCompact)
+                            columns.add(b.build());
+                        else
+                            columns.addAll(addSelectedColumns(b));
+                    }
+                    return columns;
+                }
+                else
+                {
+                    // we have a multi-column IN restriction
+                    List<List<ByteBuffer>> values = ((MultiColumnRestriction.IN) r).splitValues(variables);
+                    if (values.isEmpty())
+                        return null;
+                    TreeSet<ByteBuffer> inValues = new TreeSet<>(cfDef.cfm.comparator);
+                    for (List<ByteBuffer> components : values)
+                    {
+                        ColumnNameBuilder b = builder.copy();
+                        for (int i = 0; i < components.size(); i++)
+                        {
+                            if (components.get(i) == null)
+                            {
+                                List<CFDefinition.Name> clusteringCols = new ArrayList<>(cfDef.clusteringColumns());
+                                throw new InvalidRequestException("Invalid null value in condition for clustering column " + clusteringCols.get(i + name.position));
+                            }
+                            b.add(components.get(i));
+                        }
+                        if (cfDef.isCompact)
+                            inValues.add(b.build());
+                        else
+                            inValues.addAll(addSelectedColumns(b));
+                    }
+                    return inValues;
                 }
-                return columns;
             }
         }
 
@@ -1127,10 +1157,27 @@ public class SelectStatement implements CQLStatement, MeasurableForPreparedCache
                 {
                     Comparator<ByteBuffer> comp = cfDef.cfm.comparator;
                     // For dynamic CF, the column could be out of the requested bounds, filter here
-                    if (!sliceRestriction.isInclusive(Bound.START) && comp.compare(c.name(), sliceRestriction.bound(Bound.START, variables)) == 0)
-                        continue;
-                    if (!sliceRestriction.isInclusive(Bound.END) && comp.compare(c.name(), sliceRestriction.bound(Bound.END, variables)) == 0)
-                        continue;
+
+                    if (!sliceRestriction.isInclusive(Bound.START))
+                    {
+                        // even though it's a multi-column restriction, we know it can only contain a bound for one
+                        // column because we've already checked that the comparator is not composite
+                        ByteBuffer bounds = sliceRestriction.isMultiColumn()
+                                            ? ((MultiColumnRestriction.Slice) sliceRestriction).componentBounds(Bound.START, variables).get(0)
+                                            : sliceRestriction.bound(Bound.START, variables);
+                        if (comp.compare(c.name(), bounds) == 0)
+                            continue;
+                    }
+
+                    if (!sliceRestriction.isInclusive(Bound.END))
+                    {
+                        // see the above comment on using the first bound from the multi-column restriction
+                        ByteBuffer bounds = sliceRestriction.isMultiColumn()
+                                            ? ((MultiColumnRestriction.Slice) sliceRestriction).componentBounds(Bound.END, variables).get(0)
+                                            : sliceRestriction.bound(Bound.END, variables);
+                        if (comp.compare(c.name(), bounds) == 0)
+                            continue;
+                    }
                 }
 
                 result.newRow();