Differentiate between slices and RTs when decoding legacy bounds

Patch by Sam Tunnicliffe; reviewed by Benedict Elliott Smith for CASSANDRA-14919


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

Branch: refs/heads/cassandra-3.11
Commit: 11043610e38281f650f289a7f9286d306f1369e3
Parents: b82a42f
Author: Sam Tunnicliffe <s...@beobal.com>
Authored: Fri Nov 30 13:49:56 2018 +0000
Committer: Sam Tunnicliffe <s...@beobal.com>
Committed: Thu Dec 6 16:12:21 2018 +0000

----------------------------------------------------------------------
 CHANGES.txt                                     |  1 +
 .../org/apache/cassandra/db/LegacyLayout.java   | 60 +++++++++++++-------
 .../org/apache/cassandra/db/ReadCommand.java    |  6 +-
 .../cassandra/thrift/CassandraServer.java       | 10 ++--
 4 files changed, 49 insertions(+), 28 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/cassandra/blob/11043610/CHANGES.txt
----------------------------------------------------------------------
diff --git a/CHANGES.txt b/CHANGES.txt
index e349674..4520989 100644
--- a/CHANGES.txt
+++ b/CHANGES.txt
@@ -1,4 +1,5 @@
 3.0.18
+ * Differentiate between slices and RTs when decoding legacy bounds 
(CASSANDRA-14919)
  * CommitLogReplayer.handleReplayError should print stack traces 
(CASSANDRA-14589)
  * Netty epoll IOExceptions caused by unclean client disconnects being logged 
at INFO (CASSANDRA-14909)
  * Unfiltered.isEmpty conflicts with Row extends AbstractCollection.isEmpty 
(CASSANDRA-14588)

http://git-wip-us.apache.org/repos/asf/cassandra/blob/11043610/src/java/org/apache/cassandra/db/LegacyLayout.java
----------------------------------------------------------------------
diff --git a/src/java/org/apache/cassandra/db/LegacyLayout.java 
b/src/java/org/apache/cassandra/db/LegacyLayout.java
index c80594c..9600355 100644
--- a/src/java/org/apache/cassandra/db/LegacyLayout.java
+++ b/src/java/org/apache/cassandra/db/LegacyLayout.java
@@ -201,7 +201,17 @@ public abstract class LegacyLayout
         return new LegacyCellName(def.isStatic() ? 
Clustering.STATIC_CLUSTERING : clustering, def, collectionElement);
     }
 
-    public static LegacyBound decodeBound(CFMetaData metadata, ByteBuffer 
bound, boolean isStart)
+    public static LegacyBound decodeSliceBound(CFMetaData metadata, ByteBuffer 
bound, boolean isStart)
+    {
+        return decodeBound(metadata, bound, isStart, false);
+    }
+
+    public static LegacyBound decodeTombstoneBound(CFMetaData metadata, 
ByteBuffer bound, boolean isStart)
+    {
+        return decodeBound(metadata, bound, isStart, true);
+    }
+
+    private static LegacyBound decodeBound(CFMetaData metadata, ByteBuffer 
bound, boolean isStart, boolean isDeletion)
     {
         if (!bound.hasRemaining())
             return isStart ? LegacyBound.BOTTOM : LegacyBound.TOP;
@@ -223,25 +233,35 @@ public abstract class LegacyLayout
         assert !isStatic ||
                 (components.size() >= clusteringSize
                         && all(components.subList(0, clusteringSize), 
ByteBufferUtil.EMPTY_BYTE_BUFFER::equals));
-        // There can be  more components than the clustering size only in the 
case this is the bound of a collection
-        // range tombstone. In which case, there is exactly one more 
component, and that component is the name of the
-        // collection being selected/deleted.
         ColumnDefinition collectionName = null;
         if (components.size() > clusteringSize)
         {
+            // For a deletion, there can be more components than the 
clustering size only in the case this is the
+            // bound of a collection range tombstone. In such a case, there is 
exactly one more component, and that
+            // component is the name of the collection being selected/deleted.
+            // If the bound is not part of a deletion, it is from slice query 
filter. In this scnario, the column name
+            // may be a valid, non-collection column or it may be an empty 
buffer, representing a row marker. In either
+            // case, this needn't be included in the returned bound, so we pop 
the last element from the components
+            // list but ensure that the collection name remains null.
+
             assert clusteringSize + 1 == components.size() && 
!metadata.isCompactTable();
-            // pop the collection name from the back of the list of clusterings
-            ByteBuffer collectionNameBytes = components.remove(clusteringSize);
-            collectionName = metadata.getColumnDefinition(collectionNameBytes);
-            if (collectionName == null || !collectionName.isComplex()) {
-                collectionName = 
metadata.getDroppedColumnDefinition(collectionNameBytes, isStatic);
-                // if no record of the column having ever existed is found, 
something is badly wrong
-                if (collectionName == null)
-                    throw new RuntimeException("Unknown collection column " + 
UTF8Type.instance.getString(collectionNameBytes) + " during deserialization");
-                // if we do have a record of dropping this column but it 
wasn't previously complex, use a fake
-                // column definition for safety (see the comment on the 
constant declaration for details)
-                if (!collectionName.isComplex())
-                    collectionName = INVALID_DROPPED_COMPLEX_SUBSTITUTE_COLUMN;
+            // pop the final element from the back of the list of clusterings
+            ByteBuffer columnNameBytes = components.remove(clusteringSize);
+            if (isDeletion)
+            {
+                collectionName = metadata.getColumnDefinition(columnNameBytes);
+                if (collectionName == null || !collectionName.isComplex())
+                {
+                    collectionName = 
metadata.getDroppedColumnDefinition(columnNameBytes, isStatic);
+                    // if no record of the column having ever existed is 
found, something is badly wrong
+                    if (collectionName == null)
+                        throw new RuntimeException("Unknown collection column 
" + UTF8Type.instance.getString(columnNameBytes) + " during deserialization");
+
+                    // if we do have a record of dropping this column but it 
wasn't previously complex, use a fake
+                    // column definition for safety (see the comment on the 
constant declaration for details)
+                    if (!collectionName.isComplex())
+                        collectionName = 
INVALID_DROPPED_COMPLEX_SUBSTITUTE_COLUMN;
+                }
             }
         }
 
@@ -1157,8 +1177,8 @@ public abstract class LegacyLayout
 
     public static LegacyRangeTombstone readLegacyRangeTombstoneBody(CFMetaData 
metadata, DataInputPlus in, ByteBuffer boundname) throws IOException
     {
-        LegacyBound min = decodeBound(metadata, boundname, true);
-        LegacyBound max = decodeBound(metadata, 
ByteBufferUtil.readWithShortLength(in), false);
+        LegacyBound min = decodeTombstoneBound(metadata, boundname, true);
+        LegacyBound max = decodeTombstoneBound(metadata, 
ByteBufferUtil.readWithShortLength(in), false);
         DeletionTime dt = DeletionTime.serializer.deserialize(in);
         return new LegacyRangeTombstone(min, max, dt);
     }
@@ -1903,8 +1923,8 @@ public abstract class LegacyLayout
             LegacyDeletionInfo delInfo = new LegacyDeletionInfo(new 
MutableDeletionInfo(topLevel));
             for (int i = 0; i < rangeCount; i++)
             {
-                LegacyBound start = decodeBound(metadata, 
ByteBufferUtil.readWithShortLength(in), true);
-                LegacyBound end = decodeBound(metadata, 
ByteBufferUtil.readWithShortLength(in), false);
+                LegacyBound start = decodeTombstoneBound(metadata, 
ByteBufferUtil.readWithShortLength(in), true);
+                LegacyBound end = decodeTombstoneBound(metadata, 
ByteBufferUtil.readWithShortLength(in), false);
                 int delTime =  in.readInt();
                 long markedAt = in.readLong();
 

http://git-wip-us.apache.org/repos/asf/cassandra/blob/11043610/src/java/org/apache/cassandra/db/ReadCommand.java
----------------------------------------------------------------------
diff --git a/src/java/org/apache/cassandra/db/ReadCommand.java 
b/src/java/org/apache/cassandra/db/ReadCommand.java
index 0135d1e..fd453ef 100644
--- a/src/java/org/apache/cassandra/db/ReadCommand.java
+++ b/src/java/org/apache/cassandra/db/ReadCommand.java
@@ -1090,7 +1090,7 @@ public abstract class ReadCommand implements ReadQuery
             int compositesToGroup = in.readInt();
 
             // command-level Composite "start" and "stop"
-            LegacyLayout.LegacyBound startBound = 
LegacyLayout.decodeBound(metadata, ByteBufferUtil.readWithShortLength(in), 
true);
+            LegacyLayout.LegacyBound startBound = 
LegacyLayout.decodeSliceBound(metadata, ByteBufferUtil.readWithShortLength(in), 
true);
 
             ByteBufferUtil.readWithShortLength(in);  // the composite "stop", 
which isn't actually needed
 
@@ -1583,8 +1583,8 @@ public abstract class ReadCommand implements ReadQuery
             Slices.Builder slicesBuilder = new 
Slices.Builder(metadata.comparator);
             for (int i = 0; i < numSlices; i++)
             {
-                LegacyLayout.LegacyBound start = 
LegacyLayout.decodeBound(metadata, startBuffers[i], true);
-                LegacyLayout.LegacyBound finish = 
LegacyLayout.decodeBound(metadata, finishBuffers[i], false);
+                LegacyLayout.LegacyBound start = 
LegacyLayout.decodeSliceBound(metadata, startBuffers[i], true);
+                LegacyLayout.LegacyBound finish = 
LegacyLayout.decodeSliceBound(metadata, finishBuffers[i], false);
 
                 if (start.isStatic)
                 {

http://git-wip-us.apache.org/repos/asf/cassandra/blob/11043610/src/java/org/apache/cassandra/thrift/CassandraServer.java
----------------------------------------------------------------------
diff --git a/src/java/org/apache/cassandra/thrift/CassandraServer.java 
b/src/java/org/apache/cassandra/thrift/CassandraServer.java
index 256f651..163eb2d 100644
--- a/src/java/org/apache/cassandra/thrift/CassandraServer.java
+++ b/src/java/org/apache/cassandra/thrift/CassandraServer.java
@@ -371,7 +371,7 @@ public class CassandraServer implements Cassandra.Iface
         // Note that in thrift, the bounds are reversed if the query is 
reversed, but not internally.
         ByteBuffer start = range.reversed ? range.finish : range.start;
         ByteBuffer finish = range.reversed ? range.start : range.finish;
-        return Slices.with(metadata.comparator, 
Slice.make(LegacyLayout.decodeBound(metadata, start, true).bound, 
LegacyLayout.decodeBound(metadata, finish, false).bound));
+        return Slices.with(metadata.comparator, 
Slice.make(LegacyLayout.decodeSliceBound(metadata, start, true).bound, 
LegacyLayout.decodeSliceBound(metadata, finish, false).bound));
     }
 
     private ClusteringIndexFilter toInternalFilter(CFMetaData metadata, 
ColumnParent parent, SlicePredicate predicate)
@@ -1242,8 +1242,8 @@ public class CassandraServer implements Cassandra.Iface
         {
             if (del.super_column == null)
             {
-                LegacyLayout.LegacyBound start = LegacyLayout.decodeBound(cfm, 
del.predicate.getSlice_range().start, true);
-                LegacyLayout.LegacyBound end = LegacyLayout.decodeBound(cfm, 
del.predicate.getSlice_range().finish, false);
+                LegacyLayout.LegacyBound start = 
LegacyLayout.decodeTombstoneBound(cfm, del.predicate.getSlice_range().start, 
true);
+                LegacyLayout.LegacyBound end = 
LegacyLayout.decodeTombstoneBound(cfm, del.predicate.getSlice_range().finish, 
false);
                 delInfo.add(cfm, new LegacyLayout.LegacyRangeTombstone(start, 
end, new DeletionTime(del.timestamp, nowInSec)));
             }
             else
@@ -2426,8 +2426,8 @@ public class CassandraServer implements Cassandra.Iface
             for (int i = 0 ; i < request.getColumn_slices().size() ; i++)
             {
                 fixOptionalSliceParameters(request.getColumn_slices().get(i));
-                Slice.Bound start = LegacyLayout.decodeBound(metadata, 
request.getColumn_slices().get(i).start, true).bound;
-                Slice.Bound finish = LegacyLayout.decodeBound(metadata, 
request.getColumn_slices().get(i).finish, false).bound;
+                Slice.Bound start = LegacyLayout.decodeSliceBound(metadata, 
request.getColumn_slices().get(i).start, true).bound;
+                Slice.Bound finish = LegacyLayout.decodeSliceBound(metadata, 
request.getColumn_slices().get(i).finish, false).bound;
 
                 int compare = metadata.comparator.compare(start, finish);
                 if (!request.reversed && compare > 0)


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

Reply via email to