You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@cassandra.apache.org by ad...@apache.org on 2021/03/01 12:52:01 UTC

[cassandra] branch cassandra-3.11 updated (a311109 -> 0541c51)

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

adelapena pushed a change to branch cassandra-3.11
in repository https://gitbox.apache.org/repos/asf/cassandra.git.


    from a311109  Merge branch 'cassandra-3.0' into cassandra-3.11
     new 865b67b  Fix ColumnFilter behaviour to prevent digest mitmatches during upgrades
     new 0541c51  Merge branch 'cassandra-3.0' into cassandra-3.11

The 2 revisions listed above as "new" are entirely new to this
repository and will be described in separate emails.  The revisions
listed as "add" were already present in the repository and have only
been added to this reference.


Summary of changes:
 CHANGES.txt                                        |   1 +
 build.xml                                          |   2 +-
 .../apache/cassandra/db/filter/ColumnFilter.java   |  84 ++--
 src/java/org/apache/cassandra/gms/Gossiper.java    |   6 +
 .../test/ReadDigestConsistencyTest.java            | 113 +++++
 .../distributed/upgrade/MixedModeReadTest.java     |  65 +--
 .../cassandra/db/filter/ColumnFilterTest.java      | 521 ++++++++++++++++-----
 7 files changed, 578 insertions(+), 214 deletions(-)
 create mode 100644 test/distributed/org/apache/cassandra/distributed/test/ReadDigestConsistencyTest.java


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


[cassandra] 01/01: Merge branch 'cassandra-3.0' into cassandra-3.11

Posted by ad...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

adelapena pushed a commit to branch cassandra-3.11
in repository https://gitbox.apache.org/repos/asf/cassandra.git

commit 0541c5199690757294c40d9adfd1b86419158675
Merge: a311109 865b67b
Author: Andrés de la Peña <a....@gmail.com>
AuthorDate: Mon Mar 1 12:33:02 2021 +0000

    Merge branch 'cassandra-3.0' into cassandra-3.11

 CHANGES.txt                                        |   1 +
 build.xml                                          |   2 +-
 .../apache/cassandra/db/filter/ColumnFilter.java   |  84 ++--
 src/java/org/apache/cassandra/gms/Gossiper.java    |   6 +
 .../test/ReadDigestConsistencyTest.java            | 113 +++++
 .../distributed/upgrade/MixedModeReadTest.java     |  65 +--
 .../cassandra/db/filter/ColumnFilterTest.java      | 521 ++++++++++++++++-----
 7 files changed, 578 insertions(+), 214 deletions(-)

diff --cc CHANGES.txt
index ba1b42c,c174cd3..6bd40ad
--- a/CHANGES.txt
+++ b/CHANGES.txt
@@@ -1,5 -1,5 +1,6 @@@
 -3.0.25:
 +3.11.11
 +Merged from 3.0:
+  * Fix ColumnFilter behaviour to prevent digest mitmatches during upgrades (CASSANDRA-16415)
   * Update debian packaging for python3 (CASSANDRA-16396)
   * Avoid pushing schema mutations when setting up distributed system keyspaces locally (CASSANDRA-16387)
  Merged from 2.2:
diff --cc build.xml
index 9e0bb73,939ccd8..326a1af
--- a/build.xml
+++ b/build.xml
@@@ -422,7 -396,7 +422,7 @@@
            <dependency groupId="org.apache.thrift" artifactId="libthrift" version="0.9.2">
  	         <exclusion groupId="commons-logging" artifactId="commons-logging"/>
            </dependency>
--          <dependency groupId="junit" artifactId="junit" version="4.6" />
++          <dependency groupId="junit" artifactId="junit" version="4.12" />
            <dependency groupId="org.mockito" artifactId="mockito-core" version="3.2.4" />
            <dependency groupId="org.apache.cassandra" artifactId="dtest-api" version="0.0.7" />
            <dependency groupId="org.reflections" artifactId="reflections" version="0.9.12" />
diff --cc src/java/org/apache/cassandra/db/filter/ColumnFilter.java
index e18717c,520c43c..f405431
--- a/src/java/org/apache/cassandra/db/filter/ColumnFilter.java
+++ b/src/java/org/apache/cassandra/db/filter/ColumnFilter.java
@@@ -23,6 -23,6 +23,9 @@@ import java.util.*
  import com.google.common.collect.SortedSetMultimap;
  import com.google.common.collect.TreeMultimap;
  
++import org.slf4j.Logger;
++import org.slf4j.LoggerFactory;
++
  import org.apache.cassandra.config.CFMetaData;
  import org.apache.cassandra.cql3.ColumnIdentifier;
  import org.apache.cassandra.db.*;
@@@ -62,16 -49,20 +65,18 @@@ import org.apache.cassandra.net.Messagi
   */
  public class ColumnFilter
  {
++    private final static Logger logger = LoggerFactory.getLogger(ColumnFilter.class);
++
      public static final Serializer serializer = new Serializer();
  
 -    // Distinguish between the 2 cases described above: if 'isFetchAll' is true, then all columns will be retrieved
 -    // by the query, but the values for column/cells not selected by 'queried' and 'subSelections' will be skipped.
 -    // Otherwise, only the column/cells returned by 'queried' and 'subSelections' will be returned at all.
 +    // True if _fetched_ is all the columns, in which case metadata must not be null. If false,
 +    // then _fetched_ == _queried_ and we only store _queried_.
      private final boolean isFetchAll;
  
 -    private final PartitionColumns queried; // can be null if isFetchAll and we don't want to skip any value
      private final PartitionColumns fetched;
 +    private final PartitionColumns queried; // can be null if isFetchAll and _fetched_ == _queried_
      private final SortedSetMultimap<ColumnIdentifier, ColumnSubselection> subSelections; // can be null
  
 -    /**
 -     * Used on replica for deserialisation
 -     */
      private ColumnFilter(boolean isFetchAll,
                           PartitionColumns fetched,
                           PartitionColumns queried,
@@@ -111,7 -102,7 +116,19 @@@
       */
      public static ColumnFilter selection(CFMetaData metadata, PartitionColumns queried)
      {
--        return new ColumnFilter(true, metadata.partitionColumns(), queried, null);
++        // When fetchAll is enabled on pre CASSANDRA-10657 (3.4-), queried columns are not considered at all, and it
++        // is assumed that all columns are queried. CASSANDRA-10657 (3.4+) brings back skipping values of columns
++        // which are not in queried set when fetchAll is enabled. That makes exactly the same filter being
++        // interpreted in a different way on 3.4- and 3.4+.
++        //
++        // Moreover, there is no way to convert the filter with fetchAll and queried != null so that it is
++        // interpreted the same way on 3.4- because that Cassandra version does not support such filtering.
++        //
++        // In order to avoid inconsitencies in data read by 3.4- and 3.4+ we need to avoid creation of incompatible
++        // filters when the cluster contains 3.4- nodes. We do that by forcibly setting queried to null.
++        //
++        // see CASSANDRA-10657, CASSANDRA-15833, CASSANDRA-16415
++        return new ColumnFilter(true, metadata.partitionColumns(), Gossiper.instance.isAnyNodeOn30() ? null : queried, null);
      }
  
      /**
@@@ -357,11 -280,7 +374,14 @@@
                      s.put(subSelection.column().name, subSelection);
              }
  
-             // see CASSANDRA-15833
-             if (isFetchAll && Gossiper.instance.isAnyNodeOn30())
 -            return new ColumnFilter(isFetchAll, isFetchAll ? metadata.partitionColumns() : selectedColumns, selectedColumns, s);
++            // See the comment in {@link ColumnFilter#selection(CFMetaData, PartitionColumns)}
++            if (isFetchAll && queried != null && Gossiper.instance.isAnyNodeOn30())
++            {
++                logger.trace("Column filter will be automatically converted to query all columns because 3.0 nodes are present in the cluster");
 +                queried = null;
++            }
 +
 +            return new ColumnFilter(isFetchAll, isFetchAll ? metadata.partitionColumns() : null, queried, s);
          }
      }
  
@@@ -385,44 -305,27 +405,29 @@@
      @Override
      public String toString()
      {
+         String prefix = "";
++        if (isFetchAll && queried == null)
++            return "*/*";
+ 
          if (isFetchAll)
-             return "*";
 -            return "*/*";
++            prefix = "*/";
  
          if (queried.isEmpty())
-             return "";
- 
-         Iterator<ColumnDefinition> defs = queried.selectOrderIterator();
-         if (!defs.hasNext())
-             return "<none>";
- 
-         StringBuilder sb = new StringBuilder();
-         while (defs.hasNext())
-         {
-             appendColumnDef(sb, defs.next());
-             if (defs.hasNext())
-                 sb.append(", ");
-         }
-         return sb.toString();
-     }
+             return prefix + "[]";
  
-     private void appendColumnDef(StringBuilder sb, ColumnDefinition column)
-     {
-         if (subSelections == null)
+         StringJoiner joiner = new StringJoiner(", ", "[", "]");
+         Iterator<ColumnDefinition> it = queried.selectOrderIterator();
+         while (it.hasNext())
          {
-             sb.append(column.name);
-             return;
-         }
+             ColumnDefinition column = it.next();
+             SortedSet<ColumnSubselection> s = subSelections != null ? subSelections.get(column.name) : Collections.emptySortedSet();
  
-         SortedSet<ColumnSubselection> s = subSelections.get(column.name);
-         if (s.isEmpty())
-         {
-             sb.append(column.name);
-             return;
+             if (s.isEmpty())
+                 joiner.add(String.valueOf(column.name));
+             else
 -                s.forEach(subSel -> joiner.add(String.format("%s%s", column.name, subSel)));
++                s.forEach(subSel -> joiner.add(String.format("%s%s", column.name , subSel)));
          }
- 
-         int i = 0;
-         for (ColumnSubselection subSel : s)
-             sb.append(i++ == 0 ? "" : ", ").append(column.name).append(subSel);
+         return prefix + joiner.toString();
      }
  
      public static class Serializer
@@@ -505,11 -408,7 +510,18 @@@
                  }
              }
  
-             // See CASSANDRA-15833
-             if (version <= MessagingService.VERSION_3014 && isFetchAll)
 -            return new ColumnFilter(isFetchAll, fetched, selection, subSelections);
++            // Since nodes with and without CASSANDRA-10657 are not distinguishable by messaging version, we need to
++            // check whether there are any nodes running pre CASSANDRA-10657 Cassandra and apply conversion to the
++            // column filter so that it is interpreted in the same way as on the nodes without CASSANDRA-10657.
++            //
++            // See the comment in {@link ColumnFilter#selection(CFMetaData, PartitionColumns)}
++            if (isFetchAll && queried != null && Gossiper.instance.isAnyNodeOn30())
++            {
++                logger.trace("Deserialized column filter will be automatically converted to query all columns because 3.0 nodes are present in the cluster");
 +                queried = null;
++            }
 +
 +            return new ColumnFilter(isFetchAll, fetched, queried, subSelections);
          }
  
          public long serializedSize(ColumnFilter selection, int version)
diff --cc src/java/org/apache/cassandra/gms/Gossiper.java
index 0e46767,b09d9e1..819078d
--- a/src/java/org/apache/cassandra/gms/Gossiper.java
+++ b/src/java/org/apache/cassandra/gms/Gossiper.java
@@@ -1833,4 -1673,4 +1833,10 @@@ public class Gossiper implements IFailu
          stop();
          ExecutorUtils.shutdownAndWait(timeout, unit, executor);
      }
++
++    @VisibleForTesting
++    public void setAnyNodeOn30(boolean anyNodeOn30)
++    {
++        this.anyNodeOn30 = anyNodeOn30;
++    }
  }
diff --cc test/distributed/org/apache/cassandra/distributed/upgrade/MixedModeReadTest.java
index 249e1b8,0000000..d908cd5
mode 100644,000000..100644
--- a/test/distributed/org/apache/cassandra/distributed/upgrade/MixedModeReadTest.java
+++ b/test/distributed/org/apache/cassandra/distributed/upgrade/MixedModeReadTest.java
@@@ -1,102 -1,0 +1,65 @@@
 +/*
 + * 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.distributed.upgrade;
 +
- import java.util.UUID;
- 
- import org.junit.Assert;
 +import org.junit.Test;
 +
- import org.apache.cassandra.distributed.UpgradeableCluster;
- import org.apache.cassandra.distributed.api.ConsistencyLevel;
 +import org.apache.cassandra.distributed.api.Feature;
- import org.apache.cassandra.distributed.impl.DelegatingInvokableInstance;
- import org.apache.cassandra.distributed.shared.DistributedTestBase;
++import org.apache.cassandra.distributed.api.IInvokableInstance;
 +import org.apache.cassandra.distributed.shared.Versions;
 +import org.apache.cassandra.gms.Gossiper;
 +
++import static org.apache.cassandra.distributed.test.ReadDigestConsistencyTest.CREATE_TABLE;
++import static org.apache.cassandra.distributed.test.ReadDigestConsistencyTest.insertData;
++import static org.apache.cassandra.distributed.test.ReadDigestConsistencyTest.testDigestConsistency;
++
 +public class MixedModeReadTest extends UpgradeTestBase
 +{
-     public static final String TABLE_NAME = "tbl";
-     public static final String CREATE_TABLE = String.format(
-       "CREATE TABLE %s.%s (key int, c1 text, c2 text, c3 text, PRIMARY KEY (key))",
-       DistributedTestBase.KEYSPACE, TABLE_NAME);
- 
-     public static final String INSERT = String.format(
-       "INSERT INTO %s.%s (key, c1, c2, c3) VALUES (?, ?, ?, ?)",
-       DistributedTestBase.KEYSPACE, TABLE_NAME);
- 
-     public static final String SELECT_C1 = String.format("SELECT key, c1 FROM %s.%s WHERE key = ?",
-                                                          DistributedTestBase.KEYSPACE, TABLE_NAME);
-     public static final String SELECT_TRACE = "SELECT activity FROM system_traces.events where session_id = ? and source = ? ALLOW FILTERING;";
- 
 +    @Test
 +    public void mixedModeReadColumnSubsetDigestCheck() throws Throwable
 +    {
 +        new TestCase()
 +        .nodes(2)
 +        .nodesToUpgrade(1)
 +        .upgrade(Versions.Major.v30, Versions.Major.v3X)
 +        .withConfig(config -> config.with(Feature.GOSSIP, Feature.NETWORK))
 +        .setup(cluster -> {
 +            cluster.schemaChange(CREATE_TABLE);
-             cluster.coordinator(1).execute(INSERT, ConsistencyLevel.ALL, 1, "foo", "bar", "baz");
-             cluster.coordinator(1).execute(INSERT, ConsistencyLevel.ALL, 2, "foo", "bar", "baz");
- 
-             // baseline to show no digest mismatches before upgrade
-             checkTraceForDigestMismatch(cluster, 1);
-             checkTraceForDigestMismatch(cluster, 2);
++            insertData(cluster.coordinator(1));
++            testDigestConsistency(cluster.coordinator(1));
++            testDigestConsistency(cluster.coordinator(2));
 +        })
-         .runAfterNodeUpgrade((cluster, node) -> {
-             if (node != 1)
-                 return; // shouldn't happen but guard for future test changes
- 
- 
++        .runAfterClusterUpgrade(cluster -> {
 +            // we need to let gossip settle or the test will fail
 +            int attempts = 1;
-             while (!((DelegatingInvokableInstance) (cluster.get(1))).delegate().callOnInstance(() -> Gossiper.instance.isAnyNodeOn30()))
++            //noinspection Convert2MethodRef
++            while (!((IInvokableInstance) (cluster.get(1))).callOnInstance(() -> Gossiper.instance.isAnyNodeOn30()))
 +            {
-                 if (attempts > 30)
++                if (attempts++ > 30)
 +                    throw new RuntimeException("Gossiper.instance.isAnyNodeOn30() continually returns false despite expecting to be true");
 +                Thread.sleep(1000);
 +            }
 +
 +            // should not cause a disgest mismatch in mixed mode
-             checkTraceForDigestMismatch(cluster, 1);
-             checkTraceForDigestMismatch(cluster, 2);
++            testDigestConsistency(cluster.coordinator(1));
++            testDigestConsistency(cluster.coordinator(2));
 +        })
 +        .run();
 +    }
- 
-     private void checkTraceForDigestMismatch(UpgradeableCluster cluster, int coordinatorNode)
-     {
-         UUID sessionId = UUID.randomUUID();
-         cluster.coordinator(coordinatorNode).executeWithTracing(sessionId, SELECT_C1, ConsistencyLevel.ALL, 1);
-         Object[][] results = cluster.coordinator(coordinatorNode)
-                                     .execute(SELECT_TRACE, ConsistencyLevel.ALL,
-                                              sessionId, cluster.get(coordinatorNode).broadcastAddress().getAddress());
-         for (Object[] result : results)
-         {
-             String activity = (String) result[0];
-             Assert.assertFalse("Found Digest Mismatch", activity.toLowerCase().contains("mismatch for key"));
-         }
-     }
- 
- 
 +}
diff --cc test/unit/org/apache/cassandra/db/filter/ColumnFilterTest.java
index 5bbd2f5,80c1e42..1ddf039
--- a/test/unit/org/apache/cassandra/db/filter/ColumnFilterTest.java
+++ b/test/unit/org/apache/cassandra/db/filter/ColumnFilterTest.java
@@@ -18,16 -18,20 +18,28 @@@
  
  package org.apache.cassandra.db.filter;
  
+ import java.io.IOException;
++import java.util.Arrays;
++import java.util.Collection;
+ import java.util.function.Consumer;
+ 
+ import com.google.common.base.Throwables;
+ import org.junit.Assert;
++import org.junit.Before;
++import org.junit.BeforeClass;
  import org.junit.Test;
++import org.junit.runner.RunWith;
++import org.junit.runners.Parameterized;
  
- import junit.framework.Assert;
  import org.apache.cassandra.config.CFMetaData;
  import org.apache.cassandra.config.ColumnDefinition;
++import org.apache.cassandra.config.DatabaseDescriptor;
  import org.apache.cassandra.db.PartitionColumns;
  import org.apache.cassandra.db.marshal.Int32Type;
  import org.apache.cassandra.db.marshal.SetType;
  import org.apache.cassandra.db.rows.CellPath;
  import org.apache.cassandra.dht.Murmur3Partitioner;
++import org.apache.cassandra.gms.Gossiper;
  import org.apache.cassandra.io.util.DataInputBuffer;
  import org.apache.cassandra.io.util.DataInputPlus;
  import org.apache.cassandra.io.util.DataOutputBuffer;
@@@ -36,143 -40,337 +48,400 @@@ import org.apache.cassandra.utils.ByteB
  
  import static org.junit.Assert.assertEquals;
  
++@RunWith(Parameterized.class)
  public class ColumnFilterTest
  {
      private static final ColumnFilter.Serializer serializer = new ColumnFilter.Serializer();
  
+     private final CFMetaData metadata = CFMetaData.Builder.create("ks", "table")
+                                                           .withPartitioner(Murmur3Partitioner.instance)
+                                                           .addPartitionKey("pk", Int32Type.instance)
+                                                           .addClusteringColumn("ck", Int32Type.instance)
+                                                           .addStaticColumn("s1", Int32Type.instance)
+                                                           .addStaticColumn("s2", SetType.getInstance(Int32Type.instance, true))
+                                                           .addRegularColumn("v1", Int32Type.instance)
+                                                           .addRegularColumn("v2", SetType.getInstance(Int32Type.instance, true))
+                                                           .build();
+ 
+     private final ColumnDefinition s1 = metadata.getColumnDefinition(ByteBufferUtil.bytes("s1"));
+     private final ColumnDefinition s2 = metadata.getColumnDefinition(ByteBufferUtil.bytes("s2"));
+     private final ColumnDefinition v1 = metadata.getColumnDefinition(ByteBufferUtil.bytes("v1"));
+     private final ColumnDefinition v2 = metadata.getColumnDefinition(ByteBufferUtil.bytes("v2"));
+     private final CellPath path0 = CellPath.create(ByteBufferUtil.bytes(0));
+     private final CellPath path1 = CellPath.create(ByteBufferUtil.bytes(1));
+     private final CellPath path2 = CellPath.create(ByteBufferUtil.bytes(2));
+     private final CellPath path3 = CellPath.create(ByteBufferUtil.bytes(3));
+     private final CellPath path4 = CellPath.create(ByteBufferUtil.bytes(4));
+ 
++    @Parameterized.Parameter
++    public boolean anyNodeOn30;
++
++    @Parameterized.Parameters(name = "{index}: anyNodeOn30={0}")
++    public static Collection<Object[]> data()
++    {
++        return Arrays.asList(new Object[]{ true }, new Object[]{ false });
++    }
++
++    @BeforeClass
++    public static void beforeClass()
++    {
++        DatabaseDescriptor.clientInitialization();
++    }
++
++    @Before
++    public void before()
++    {
++        Gossiper.instance.setAnyNodeOn30(anyNodeOn30);
++    }
++
+     // Select all
+ 
      @Test
-     public void columnFilterSerialisationRoundTrip() throws Exception
-     {
-         CFMetaData metadata = CFMetaData.Builder.create("ks", "table")
-                                                 .withPartitioner(Murmur3Partitioner.instance)
-                                                 .addPartitionKey("pk", Int32Type.instance)
-                                                 .addClusteringColumn("ck", Int32Type.instance)
-                                                 .addRegularColumn("v1", Int32Type.instance)
-                                                 .addRegularColumn("v2", Int32Type.instance)
-                                                 .addRegularColumn("v3", Int32Type.instance)
-                                                 .build();
- 
-         ColumnDefinition v1 = metadata.getColumnDefinition(ByteBufferUtil.bytes("v1"));
- 
-         testRoundTrip(ColumnFilter.all(metadata), metadata, MessagingService.VERSION_30);
-         testRoundTrip(ColumnFilter.all(metadata), metadata, MessagingService.VERSION_3014);
- 
-         testRoundTrip(ColumnFilter.selection(metadata.partitionColumns().without(v1)), metadata, MessagingService.VERSION_30);
-         testRoundTrip(ColumnFilter.selection(metadata.partitionColumns().without(v1)), metadata, MessagingService.VERSION_3014);
-     }
- 
-     private static void testRoundTrip(ColumnFilter columnFilter, CFMetaData metadata, int version) throws Exception
-     {
-         DataOutputBuffer output = new DataOutputBuffer();
-         serializer.serialize(columnFilter, output, version);
-         Assert.assertEquals(serializer.serializedSize(columnFilter, version), output.position());
-         DataInputPlus input = new DataInputBuffer(output.buffer(), false);
-         Assert.assertEquals(serializer.deserialize(input, version, metadata), columnFilter);
-     }
- 
-     /**
-      * Tests whether a filter fetches and/or queries columns and cells.
-      */
-     @Test
-     public void testFetchedQueried()
-     {
-         CFMetaData metadata = CFMetaData.Builder.create("ks", "table")
-                                               .withPartitioner(Murmur3Partitioner.instance)
-                                               .addPartitionKey("k", Int32Type.instance)
-                                               .addRegularColumn("simple", Int32Type.instance)
-                                               .addRegularColumn("complex", SetType.getInstance(Int32Type.instance, true))
-                                               .build();
- 
-         ColumnDefinition simple = metadata.getColumnDefinition(ByteBufferUtil.bytes("simple"));
-         ColumnDefinition complex = metadata.getColumnDefinition(ByteBufferUtil.bytes("complex"));
-         CellPath path1 = CellPath.create(ByteBufferUtil.bytes(1));
-         CellPath path2 = CellPath.create(ByteBufferUtil.bytes(2));
-         ColumnFilter filter;
- 
-         // select only the simple column, without table metadata
-         filter = ColumnFilter.selection(PartitionColumns.builder().add(simple).build());
-         assertFetchedQueried(true, true, filter, simple);
-         assertFetchedQueried(false, false, filter, complex);
-         assertFetchedQueried(false, false, filter, complex, path1);
-         assertFetchedQueried(false, false, filter, complex, path2);
- 
-         // select only the complex column, without table metadata
-         filter = ColumnFilter.selection(PartitionColumns.builder().add(complex).build());
-         assertFetchedQueried(false, false, filter, simple);
-         assertFetchedQueried(true, true, filter, complex);
-         assertFetchedQueried(true, true, filter, complex, path1);
-         assertFetchedQueried(true, true, filter, complex, path2);
- 
-         // select both the simple and complex columns, without table metadata
-         filter = ColumnFilter.selection(PartitionColumns.builder().add(simple).add(complex).build());
-         assertFetchedQueried(true, true, filter, simple);
-         assertFetchedQueried(true, true, filter, complex);
-         assertFetchedQueried(true, true, filter, complex, path1);
-         assertFetchedQueried(true, true, filter, complex, path2);
- 
-         // select only the simple column, with table metadata
-         filter = ColumnFilter.selection(metadata, PartitionColumns.builder().add(simple).build());
-         assertFetchedQueried(true, true, filter, simple);
-         assertFetchedQueried(true, false, filter, complex);
-         assertFetchedQueried(true, false, filter, complex, path1);
-         assertFetchedQueried(true, false, filter, complex, path2);
- 
-         // select only the complex column, with table metadata
-         filter = ColumnFilter.selection(metadata, PartitionColumns.builder().add(complex).build());
-         assertFetchedQueried(true, false, filter, simple);
-         assertFetchedQueried(true, true, filter, complex);
-         assertFetchedQueried(true, true, filter, complex, path1);
-         assertFetchedQueried(true, true, filter, complex, path2);
- 
-         // select both the simple and complex columns, with table metadata
-         filter = ColumnFilter.selection(metadata, PartitionColumns.builder().add(simple).add(complex).build());
-         assertFetchedQueried(true, true, filter, simple);
-         assertFetchedQueried(true, true, filter, complex);
-         assertFetchedQueried(true, true, filter, complex, path1);
-         assertFetchedQueried(true, true, filter, complex, path2);
- 
-         // select only the simple column, with selection builder
-         filter = ColumnFilter.selectionBuilder().add(simple).build();
-         assertFetchedQueried(true, true, filter, simple);
-         assertFetchedQueried(false, false, filter, complex);
-         assertFetchedQueried(false, false, filter, complex, path1);
-         assertFetchedQueried(false, false, filter, complex, path2);
- 
-         // select only a cell of the complex column, with selection builder
-         filter = ColumnFilter.selectionBuilder().select(complex, path1).build();
-         assertFetchedQueried(false, false, filter, simple);
-         assertFetchedQueried(true, true, filter, complex);
-         assertFetchedQueried(true, true, filter, complex, path1);
-         assertFetchedQueried(true, false, filter, complex, path2);
- 
-         // select both the simple column and a cell of the complex column, with selection builder
-         filter = ColumnFilter.selectionBuilder().add(simple).select(complex, path1).build();
-         assertFetchedQueried(true, true, filter, simple);
-         assertFetchedQueried(true, true, filter, complex);
-         assertFetchedQueried(true, true, filter, complex, path1);
-         assertFetchedQueried(true, false, filter, complex, path2);
+     public void testSelectAll()
+     {
+         Consumer<ColumnFilter> check = filter -> {
+             testRoundTrips(filter);
+             assertEquals("*/*", filter.toString());
+             assertFetchedQueried(true, true, filter, v1, v2, s1, s2);
+             assertCellFetchedQueried(true, true, filter, v2, path0, path1, path2, path3, path4);
+             assertCellFetchedQueried(true, true, filter, s2, path0, path1, path2, path3, path4);
+         };
+ 
+         check.accept(ColumnFilter.all(metadata));
+         check.accept(ColumnFilter.allColumnsBuilder(metadata).build());
      }
  
-     private static void assertFetchedQueried(boolean expectedFetched,
-                                              boolean expectedQueried,
-                                              ColumnFilter filter,
-                                              ColumnDefinition column)
+     // Selections
+ 
+     @Test
+     public void testSelectNothing()
      {
-         assert !expectedQueried || expectedFetched;
-         boolean actualFetched = filter.fetches(column);
-         assertEquals(expectedFetched, actualFetched);
-         assertEquals(expectedQueried, actualFetched && filter.fetchedColumnIsQueried(column));
+         Consumer<ColumnFilter> check = filter -> {
+             testRoundTrips(filter);
+             assertEquals("[]", filter.toString());
+             assertFetchedQueried(false, false, filter, v1, v2, s1, s2);
+             assertCellFetchedQueried(false, false, filter, v2, path0, path1, path2, path3, path4);
+             assertCellFetchedQueried(false, false, filter, s2, path0, path1, path2, path3, path4);
+         };
+ 
+         check.accept(ColumnFilter.selection(PartitionColumns.NONE));
+         check.accept(ColumnFilter.selectionBuilder().build());
+     }
+ 
+     @Test
+     public void testSelectSimpleColumn()
+     {
+         Consumer<ColumnFilter> check = filter -> {
+             testRoundTrips(filter);
+             assertEquals("[v1]", filter.toString());
+             assertFetchedQueried(true, true, filter, v1);
+             assertFetchedQueried(false, false, filter, v2, s1, s2);
+             assertCellFetchedQueried(false, false, filter, v2, path0, path1, path2, path3, path4);
+             assertCellFetchedQueried(false, false, filter, s2, path0, path1, path2, path3, path4);
+         };
+ 
+         check.accept(ColumnFilter.selection(PartitionColumns.builder().add(v1).build()));
+         check.accept(ColumnFilter.selectionBuilder().add(v1).build());
+     }
+ 
+     @Test
+     public void testSelectComplexColumn()
+     {
+         Consumer<ColumnFilter> check = filter -> {
+             testRoundTrips(filter);
+             assertEquals("[v2]", filter.toString());
+             assertFetchedQueried(true, true, filter, v2);
+             assertFetchedQueried(false, false, filter, v1, s1, s2);
+             assertCellFetchedQueried(true, true, filter, v2, path0, path1, path2, path3, path4);
+             assertCellFetchedQueried(false, false, filter, s2, path0, path1, path2, path3, path4);
+         };
+ 
+         check.accept(ColumnFilter.selection(PartitionColumns.builder().add(v2).build()));
+         check.accept(ColumnFilter.selectionBuilder().add(v2).build());
+     }
+ 
+     @Test
+     public void testSelectStaticColumn()
+     {
+         Consumer<ColumnFilter> check = filter -> {
+             testRoundTrips(filter);
+             assertEquals("[s1]", filter.toString());
+             assertFetchedQueried(true, true, filter, s1);
+             assertFetchedQueried(false, false, filter, v1, v2, s2);
+             assertCellFetchedQueried(false, false, filter, v2, path0, path1, path2, path3, path4);
+             assertCellFetchedQueried(false, false, filter, s2, path0, path1, path2, path3, path4);
+         };
+ 
+         check.accept(ColumnFilter.selection(PartitionColumns.builder().add(s1).build()));
+         check.accept(ColumnFilter.selectionBuilder().add(s1).build());
+     }
+ 
+     @Test
+     public void testSelectStaticComplexColumn()
+     {
+         Consumer<ColumnFilter> check = filter -> {
+             testRoundTrips(filter);
+             assertEquals("[s2]", filter.toString());
+             assertFetchedQueried(true, true, filter, s2);
+             assertFetchedQueried(false, false, filter, v1, v2, s1);
+             assertCellFetchedQueried(false, false, filter, v2, path0, path1, path2, path3, path4);
+             assertCellFetchedQueried(true, true, filter, s2, path0, path1, path2, path3, path4);
+         };
+ 
+         check.accept(ColumnFilter.selection(PartitionColumns.builder().add(s2).build()));
+         check.accept(ColumnFilter.selectionBuilder().add(s2).build());
+     }
+ 
+     @Test
+     public void testSelectColumns()
+     {
+         Consumer<ColumnFilter> check = filter -> {
+             testRoundTrips(filter);
+             assertEquals("[s1, s2, v1, v2]", filter.toString());
+             assertFetchedQueried(true, true, filter, v1, v2, s1, s2);
+             assertCellFetchedQueried(true, true, filter, v2, path0, path1, path2, path3, path4);
+             assertCellFetchedQueried(true, true, filter, s2, path0, path1, path2, path3, path4);
+         };
+ 
+         check.accept(ColumnFilter.selection(PartitionColumns.builder().add(v1).add(v2).add(s1).add(s2).build()));
+         check.accept(ColumnFilter.selectionBuilder().add(v1).add(v2).add(s1).add(s2).build());
+     }
+ 
+     @Test
+     public void testSelectIndividualCells()
+     {
+         ColumnFilter filter = ColumnFilter.selectionBuilder().select(v2, path1).select(v2, path3).build();
+         testRoundTrips(filter);
+         assertEquals("[v2[1], v2[3]]", filter.toString());
+         assertFetchedQueried(true, true, filter, v2);
+         assertFetchedQueried(false, false, filter, v1, s1, s2);
+         assertCellFetchedQueried(true, true, filter, v2, path1, path3);
+         assertCellFetchedQueried(false, false, filter, v2, path0, path2, path4);
+         assertCellFetchedQueried(false, false, filter, s2, path0, path1, path2, path3, path4);
+     }
+ 
+     @Test
+     public void testSelectIndividualCellsFromStatic()
+     {
+         ColumnFilter filter = ColumnFilter.selectionBuilder().select(s2, path1).select(s2, path3).build();
+         testRoundTrips(filter);
+         assertEquals("[s2[1], s2[3]]", filter.toString());
+         assertFetchedQueried(true, true, filter, s2);
+         assertFetchedQueried(false, false, filter, v1, v2, s1);
+         assertCellFetchedQueried(false, false, filter, v2, path0, path1, path2, path3, path4);
+         assertCellFetchedQueried(true, true, filter, s2, path1, path3);
+         assertCellFetchedQueried(false, false, filter, s2, path0, path2, path4);
+     }
+ 
+     @Test
+     public void testSelectCellSlice()
+     {
+         ColumnFilter filter = ColumnFilter.selectionBuilder().slice(v2, path1, path3).build();
+         testRoundTrips(filter);
+         assertEquals("[v2[1:3]]", filter.toString());
+         assertFetchedQueried(true, true, filter, v2);
+         assertFetchedQueried(false, false, filter, v1, s1, s2);
+         assertCellFetchedQueried(true, true, filter, v2, path1, path2, path3);
+         assertCellFetchedQueried(false, false, filter, v2, path0, path4);
+         assertCellFetchedQueried(false, false, filter, s2, path0, path1, path2, path3, path4);
+     }
+ 
+     @Test
+     public void testSelectCellSliceFromStatic()
+     {
+         ColumnFilter filter = ColumnFilter.selectionBuilder().slice(s2, path1, path3).build();
+         testRoundTrips(filter);
+         assertEquals("[s2[1:3]]", filter.toString());
+         assertFetchedQueried(true, true, filter, s2);
+         assertFetchedQueried(false, false, filter, v1, v2, s1);
+         assertCellFetchedQueried(false, false, filter, v2, path0, path1, path2, path3, path4);
+         assertCellFetchedQueried(true, true, filter, s2, path1, path2, path3);
+         assertCellFetchedQueried(false, false, filter, s2, path0, path4);
+     }
+ 
+     @Test
+     public void testSelectColumnsWithCellsAndSlices()
+     {
+         ColumnFilter filter = ColumnFilter.selectionBuilder()
+                                           .add(v1)
+                                           .add(s1)
+                                           .slice(v2, path0, path2)
+                                           .select(v2, path4)
+                                           .select(s2, path0)
+                                           .slice(s2, path2, path4)
+                                           .build();
+         testRoundTrips(filter);
+         assertEquals("[s1, s2[0], s2[2:4], v1, v2[0:2], v2[4]]", filter.toString());
+         assertFetchedQueried(true, true, filter, v1, v2, s1, s2);
+         assertCellFetchedQueried(true, true, filter, v2, path0, path1, path2, path4);
+         assertCellFetchedQueried(false, false, filter, v2, path3);
+         assertCellFetchedQueried(true, true, filter, s2, path0, path2, path3, path4);
+         assertCellFetchedQueried(false, false, filter, s2, path1);
+     }
+ 
+     // select with metadata
+ 
+     @Test
+     public void testSelectSimpleColumnWithMetadata()
+     {
+         Consumer<ColumnFilter> check = filter -> {
+             testRoundTrips(filter);
+             assertFetchedQueried(true, true, filter, v1);
 -
 -            assertEquals("*/*", filter.toString());
 -            assertFetchedQueried(true, true, filter, s1, s2, v2);
 -            assertCellFetchedQueried(true, true, filter, v2, path0, path1, path2, path3, path4);
 -            assertCellFetchedQueried(true, true, filter, s2, path0, path1, path2, path3, path4);
++            if (anyNodeOn30)
++            {
++                assertEquals("*/*", filter.toString());
++                assertFetchedQueried(true, true, filter, s1, s2, v2);
++                assertCellFetchedQueried(true, true, filter, v2, path0, path1, path2, path3, path4);
++                assertCellFetchedQueried(true, true, filter, s2, path0, path1, path2, path3, path4);
++            }
++            else
++            {
++                assertEquals("*/[v1]", filter.toString());
++                assertFetchedQueried(true, false, filter, s1, s2, v2);
++                assertCellFetchedQueried(true, false, filter, v2, path0, path1, path2, path3, path4);
++                assertCellFetchedQueried(true, false, filter, s2, path0, path1, path2, path3, path4);
++            }
+         };
+ 
+         check.accept(ColumnFilter.selection(metadata, PartitionColumns.builder().add(v1).build()));
+         check.accept(ColumnFilter.allColumnsBuilder(metadata).add(v1).build());
+     }
+ 
+     @Test
+     public void testSelectStaticColumnWithMetadata()
+     {
+         Consumer<ColumnFilter> check = filter -> {
+             testRoundTrips(filter);
+             assertFetchedQueried(true, true, filter, s1);
 -
 -            assertEquals("*/*", filter.toString());
 -            assertFetchedQueried(true, true, filter, v1, v2, s2);
 -            assertCellFetchedQueried(true, true, filter, v2, path0, path1, path2, path3, path4);
 -            assertCellFetchedQueried(true, true, filter, s2, path0, path1, path2, path3, path4);
++            if (anyNodeOn30)
++            {
++                assertEquals("*/*", filter.toString());
++                assertFetchedQueried(true, true, filter, v1, v2, s2);
++                assertCellFetchedQueried(true, true, filter, v2, path0, path1, path2, path3, path4);
++                assertCellFetchedQueried(true, true, filter, s2, path0, path1, path2, path3, path4);
++            }
++            else
++            {
++                assertEquals("*/[s1]", filter.toString());
++                assertFetchedQueried(true, false, filter, v1, v2, s2);
++                assertCellFetchedQueried(true, false, filter, v2, path0, path1, path2, path3, path4);
++                assertCellFetchedQueried(false, false, filter, s2, path0, path1, path2, path3, path4);
++            }
+         };
+ 
+         check.accept(ColumnFilter.selection(metadata, PartitionColumns.builder().add(s1).build()));
+         check.accept(ColumnFilter.allColumnsBuilder(metadata).add(s1).build());
+     }
+ 
+     @Test
+     public void testSelectCellWithMetadata()
+     {
+         ColumnFilter filter = ColumnFilter.allColumnsBuilder(metadata).select(v2, path1).build();
+         testRoundTrips(filter);
+         assertFetchedQueried(true, true, filter, v2);
 -
 -        assertEquals("*/*", filter.toString());
 -        assertFetchedQueried(true, true, filter, s1, s2, v1);
 -        assertCellFetchedQueried(true, true, filter, v2, path1);
 -        assertCellFetchedQueried(true, false, filter, v2, path0, path2, path3, path4);
 -        assertCellFetchedQueried(true, true, filter, s2, path0, path1, path2, path3, path4);
++        if (anyNodeOn30)
++        {
++            assertEquals("*/*", filter.toString());
++            assertFetchedQueried(true, true, filter, s1, s2, v1);
++            assertCellFetchedQueried(true, true, filter, v2, path1);
++            assertCellFetchedQueried(true, false, filter, v2, path0, path2, path3, path4);
++            assertCellFetchedQueried(true, true, filter, s2, path0, path1, path2, path3, path4);
++        }
++        else
++        {
++            assertEquals("*/[v2[1]]", filter.toString());
++            assertFetchedQueried(true, false, filter, s1, s2, v1);
++            assertCellFetchedQueried(true, true, filter, v2, path1);
++            assertCellFetchedQueried(true, false, filter, v2, path0, path2, path3, path4);
++            assertCellFetchedQueried(true, false, filter, s2, path0, path1, path2, path3, path4);
++        }
+     }
+ 
+     @Test
+     public void testSelectStaticColumnCellWithMetadata()
+     {
+         ColumnFilter filter = ColumnFilter.allColumnsBuilder(metadata).select(s2, path1).build();
+         testRoundTrips(filter);
+         assertFetchedQueried(true, true, filter, s2);
 -
 -        assertEquals("*/*", filter.toString());
 -        assertFetchedQueried(true, true, filter, v1, v2, s1);
 -        assertCellFetchedQueried(true, true, filter, v2, path0, path1, path2, path3, path4);
 -        assertCellFetchedQueried(true, true, filter, s2, path1);
 -        assertCellFetchedQueried(true, false, filter, s2, path0, path2, path3, path4);
++        if (anyNodeOn30)
++        {
++            assertEquals("*/*", filter.toString());
++            assertFetchedQueried(true, true, filter, v1, v2, s1);
++            assertCellFetchedQueried(true, true, filter, v2, path0, path1, path2, path3, path4);
++            assertCellFetchedQueried(true, true, filter, s2, path1);
++            assertCellFetchedQueried(true, false, filter, s2, path0, path2, path3, path4);
++        }
++        else
++        {
++            assertEquals("*/[s2[1]]", filter.toString());
++            assertFetchedQueried(true, false, filter, v1, v2, s1);
++            assertCellFetchedQueried(true, false, filter, v2, path0, path1, path2, path3, path4);
++            assertCellFetchedQueried(true, true, filter, s2, path1);
++            assertCellFetchedQueried(true, false, filter, s2, path0, path2, path3, path4);
++        }
+     }
+ 
+     private void testRoundTrips(ColumnFilter cf)
+     {
+         testRoundTrip(cf, MessagingService.VERSION_30);
+         testRoundTrip(cf, MessagingService.VERSION_3014);
+     }
+ 
+     private void testRoundTrip(ColumnFilter columnFilter, int version)
+     {
+         try
+         {
+             DataOutputBuffer output = new DataOutputBuffer();
+             serializer.serialize(columnFilter, output, version);
+             Assert.assertEquals(serializer.serializedSize(columnFilter, version), output.position());
+             DataInputPlus input = new DataInputBuffer(output.buffer(), false);
+             ColumnFilter deserialized = serializer.deserialize(input, version, metadata);
+             Assert.assertEquals(deserialized, columnFilter);
+         }
+         catch (IOException e)
+         {
+             throw Throwables.propagate(e);
+         }
      }
  
 -    private static void assertFetchedQueried(boolean expectedIncluded,
 -                                             boolean expectedNotSkipped,
 +    private static void assertFetchedQueried(boolean expectedFetched,
 +                                             boolean expectedQueried,
                                               ColumnFilter filter,
-                                              ColumnDefinition column,
-                                              CellPath path)
+                                              ColumnDefinition... columns)
+     {
+         for (ColumnDefinition column : columns)
+         {
 -            assertEquals(String.format("Expected includes(%s) to be %s", column.name, expectedIncluded),
 -                         expectedIncluded, filter.includes(column));
 -            if (expectedIncluded)
 -                assertEquals(String.format("Expected canSkipValue(%s) to be %s", column.name, !expectedNotSkipped),
 -                             !expectedNotSkipped, filter.canSkipValue(column));
++            assertEquals(String.format("Expected fetches(%s) to be %s", column.name, expectedFetched),
++                         expectedFetched, filter.fetches(column));
++            if (expectedFetched)
++                assertEquals(String.format("Expected fetchedColumnIsQueried(%s) to be %s", column.name, expectedQueried),
++                             expectedQueried, filter.fetchedColumnIsQueried(column));
+         }
+     }
+ 
 -    private static void assertCellFetchedQueried(boolean expectedIncluded,
 -                                                 boolean expectedNotSkipped,
++    private static void assertCellFetchedQueried(boolean expectedFetched,
++                                                 boolean expectedQueried,
+                                                  ColumnFilter filter,
+                                                  ColumnDefinition column,
+                                                  CellPath... paths)
      {
-         assert !expectedQueried || expectedFetched;
-         boolean actualFetched = filter.fetches(column);
-         assertEquals(expectedFetched, actualFetched);
-         assertEquals(expectedQueried, actualFetched && filter.fetchedCellIsQueried(column, path));
+         ColumnFilter.Tester tester = filter.newTester(column);
+ 
+         for (CellPath path : paths)
+         {
+             int p = ByteBufferUtil.toInt(path.get(0));
++            if (expectedFetched)
++                assertEquals(String.format("Expected fetchedCellIsQueried(%s:%s) to be %s", column.name, p, expectedQueried),
++                             expectedQueried, filter.fetchedCellIsQueried(column, path));
+ 
+             if (tester != null)
+             {
 -                assertEquals(String.format("Expected tester.includes(%s:%s) to be %s", column.name, p, expectedIncluded),
 -                             expectedIncluded, tester.includes(path));
 -                if (expectedIncluded)
 -                    assertEquals(String.format("Expected tester.canSkipValue(%s:%s) to be %s", column.name, p, !expectedNotSkipped),
 -                                 !expectedNotSkipped, tester.canSkipValue(path));
++                assertEquals(String.format("Expected tester.fetches(%s:%s) to be %s", column.name, p, expectedFetched),
++                             expectedFetched, tester.fetches(path));
++                if (expectedFetched)
++                    assertEquals(String.format("Expected tester.fetchedCellIsQueried(%s:%s) to be %s", column.name, p, expectedQueried),
++                                 expectedQueried, tester.fetchedCellIsQueried(path));
+             }
+         }
      }
- }
+ }


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