Merge branch 'cassandra-2.0' into trunk

Conflicts:
        src/java/org/apache/cassandra/cql3/statements/ModificationStatement.java
        src/java/org/apache/cassandra/service/StorageProxy.java


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

Branch: refs/heads/trunk
Commit: 7ce5e062ed602dd1c9593a03b554c11ff3cc52d5
Parents: fc01759 e59ef16
Author: Sylvain Lebresne <sylv...@datastax.com>
Authored: Thu Feb 6 08:50:34 2014 +0100
Committer: Sylvain Lebresne <sylv...@datastax.com>
Committed: Thu Feb 6 08:50:34 2014 +0100

----------------------------------------------------------------------
 CHANGES.txt                                     |   1 +
 .../cql3/statements/ModificationStatement.java  | 109 +++++++++++++++----
 .../apache/cassandra/service/CASConditions.java |  38 +++++++
 .../apache/cassandra/service/StorageProxy.java  |  76 ++-----------
 .../cassandra/thrift/CassandraServer.java       |  65 ++++++++++-
 5 files changed, 200 insertions(+), 89 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/cassandra/blob/7ce5e062/CHANGES.txt
----------------------------------------------------------------------
diff --cc CHANGES.txt
index a139fdc,7ba8044..657bf9e
--- a/CHANGES.txt
+++ b/CHANGES.txt
@@@ -1,34 -1,5 +1,35 @@@
 +2.1
 + * add listsnapshots command to nodetool (CASSANDRA-5742)
 + * Introduce AtomicBTreeColumns (CASSANDRA-6271)
 + * Multithreaded commitlog (CASSANDRA-3578)
 + * allocate fixed index summary memory pool and resample cold index summaries 
 +   to use less memory (CASSANDRA-5519)
 + * Removed multithreaded compaction (CASSANDRA-6142)
 + * Parallelize fetching rows for low-cardinality indexes (CASSANDRA-1337)
 + * change logging from log4j to logback (CASSANDRA-5883)
 + * switch to LZ4 compression for internode communication (CASSANDRA-5887)
 + * Stop using Thrift-generated Index* classes internally (CASSANDRA-5971)
 + * Remove 1.2 network compatibility code (CASSANDRA-5960)
 + * Remove leveled json manifest migration code (CASSANDRA-5996)
 + * Remove CFDefinition (CASSANDRA-6253)
 + * Use AtomicIntegerFieldUpdater in RefCountedMemory (CASSANDRA-6278)
 + * User-defined types for CQL3 (CASSANDRA-5590)
 + * Use of o.a.c.metrics in nodetool (CASSANDRA-5871, 6406)
 + * Batch read from OTC's queue and cleanup (CASSANDRA-1632)
 + * Secondary index support for collections (CASSANDRA-4511, 6383)
 + * SSTable metadata(Stats.db) format change (CASSANDRA-6356)
 + * Push composites support in the storage engine
 +   (CASSANDRA-5417, CASSANDRA-6520)
 + * Add snapshot space used to cfstats (CASSANDRA-6231)
 + * Add cardinality estimator for key count estimation (CASSANDRA-5906)
 + * CF id is changed to be non-deterministic. Data dir/key cache are created
 +   uniquely for CF id (CASSANDRA-5202)
 + * New counters implementation (CASSANDRA-6504)
 + * Replace UnsortedColumns usage with ArrayBackedSortedColumns 
(CASSANDRA-6630)
 +
 +
  2.0.6
+  * Correctly handle null with IF conditions and TTL (CASSANDRA-6623)
  Merged from 1.2:
   * Fix partition and range deletes not triggering flush (CASSANDRA-6655)
  

http://git-wip-us.apache.org/repos/asf/cassandra/blob/7ce5e062/src/java/org/apache/cassandra/cql3/statements/ModificationStatement.java
----------------------------------------------------------------------
diff --cc 
src/java/org/apache/cassandra/cql3/statements/ModificationStatement.java
index e551187,2567043..f35690b
--- a/src/java/org/apache/cassandra/cql3/statements/ModificationStatement.java
+++ b/src/java/org/apache/cassandra/cql3/statements/ModificationStatement.java
@@@ -24,13 -24,13 +24,14 @@@ import org.github.jamm.MemoryMeter
  
  import org.apache.cassandra.auth.Permission;
  import org.apache.cassandra.config.CFMetaData;
 +import org.apache.cassandra.config.ColumnDefinition;
  import org.apache.cassandra.cql3.*;
  import org.apache.cassandra.db.*;
 +import org.apache.cassandra.db.composites.CBuilder;
 +import org.apache.cassandra.db.composites.Composite;
  import org.apache.cassandra.db.filter.ColumnSlice;
+ import org.apache.cassandra.db.filter.IDiskAtomFilter;
  import org.apache.cassandra.db.filter.SliceQueryFilter;
 -import org.apache.cassandra.db.marshal.CompositeType;
 -import org.apache.cassandra.db.marshal.UTF8Type;
  import org.apache.cassandra.db.marshal.ListType;
  import org.apache.cassandra.db.marshal.BooleanType;
  import org.apache.cassandra.exceptions.*;
@@@ -533,29 -539,96 +535,96 @@@ public abstract class ModificationState
          }
          else
          {
 -            rm = new RowMutation(cfm.ksName, key, cf);
 +            mutation = new Mutation(cfm.ksName, key, cf);
          }
 -        return isCounter() ? new CounterMutation(rm, cl) : rm;
 +        return isCounter() ? new CounterMutation(mutation, cl) : mutation;
      }
  
-     private ColumnFamily buildConditions(ByteBuffer key, Composite 
clusteringPrefix, UpdateParameters params)
-     throws InvalidRequestException
+     private static abstract class CQL3CasConditions implements CASConditions
      {
-         if (ifNotExists)
-             return null;
 -        protected final ColumnNameBuilder rowPrefix;
++        protected final Composite rowPrefix;
+         protected final long now;
  
-         ColumnFamily cf = TreeMapBackedSortedColumns.factory.create(cfm);
 -        protected CQL3CasConditions(ColumnNameBuilder rowPrefix, long now)
++        protected CQL3CasConditions(Composite rowPrefix, long now)
+         {
+             this.rowPrefix = rowPrefix;
+             this.now = now;
+         }
  
-         // CQL row marker
-         if (cfm.isCQL3Table())
-             
cf.addColumn(params.makeColumn(cfm.comparator.rowMarker(clusteringPrefix), 
ByteBufferUtil.EMPTY_BYTE_BUFFER));
+         public IDiskAtomFilter readFilter()
+         {
+             // We always read the row entirely as on CAS failure we want to 
be able to distinguish between "row exists
+             // but all values on why there were conditions are null" and "row 
doesn't exists", and we can't rely on the
+             // row marker for that (see #6623)
 -            return new SliceQueryFilter(rowPrefix.build(), 
rowPrefix.buildAsEndOfRange(), false, 1, rowPrefix.componentCount());
++            return new SliceQueryFilter(rowPrefix.slice(), false, 1, 
rowPrefix.size());
+         }
+     }
  
-         // Conditions
-         for (Operation condition : columnConditions)
-             condition.execute(key, cf, clusteringPrefix, params);
+     private static class NotExistCondition extends CQL3CasConditions
+     {
 -        private NotExistCondition(ColumnNameBuilder rowPrefix, long now)
++        private NotExistCondition(Composite rowPrefix, long now)
+         {
+             super(rowPrefix, now);
+         }
+ 
+         public boolean appliesTo(ColumnFamily current)
+         {
+             return current == null || current.hasOnlyTombstones(now);
+         }
+     }
+ 
+     private static class ColumnsConditions extends CQL3CasConditions
+     {
+         private final ColumnFamily expected;
  
-         assert !cf.isEmpty();
-         return cf;
 -        private ColumnsConditions(ColumnNameBuilder rowPrefix,
++        private ColumnsConditions(Composite rowPrefix,
+                                   CFMetaData cfm,
+                                   ByteBuffer key,
+                                   Collection<Operation> conditions,
+                                   List<ByteBuffer> variables,
+                                   long now) throws InvalidRequestException
+         {
+             super(rowPrefix, now);
+             this.expected = TreeMapBackedSortedColumns.factory.create(cfm);
+ 
+             // When building the conditions, we should not use a TTL. It's 
not useful, and if a very low ttl (1 seconds) is used, it's possible
+             // for it to expire before the actual build of the conditions 
which would break since we would then testing for the presence of tombstones.
+             UpdateParameters params = new UpdateParameters(cfm, variables, 
now, 0, null);
+ 
+             // Conditions
+             for (Operation condition : conditions)
 -                condition.execute(key, expected, rowPrefix.copy(), params);
++                condition.execute(key, expected, rowPrefix, params);
+         }
+ 
+         public boolean appliesTo(ColumnFamily current)
+         {
+             if (current == null)
+                 return false;
+ 
 -            for (Column e : expected)
++            for (Cell e : expected)
+             {
 -                Column c = current.getColumn(e.name());
++                Cell c = current.getColumn(e.name());
+                 if (e.isLive(now))
+                 {
+                     if (c == null || !c.isLive(now) || 
!c.value().equals(e.value()))
+                         return false;
+                 }
+                 else
+                 {
+                     // If we have a tombstone in expected, it means the 
condition tests that the column is
+                     // null, so check that we have no value
+                     if (c != null && c.isLive(now))
+                         return false;
+                 }
+             }
+             return true;
+         }
+ 
+         @Override
+         public String toString()
+         {
+             return expected.toString();
+         }
      }
  
      public static abstract class Parsed extends CFStatement

http://git-wip-us.apache.org/repos/asf/cassandra/blob/7ce5e062/src/java/org/apache/cassandra/service/StorageProxy.java
----------------------------------------------------------------------
diff --cc src/java/org/apache/cassandra/service/StorageProxy.java
index 6e8d231,8d1f913..69d7123
--- a/src/java/org/apache/cassandra/service/StorageProxy.java
+++ b/src/java/org/apache/cassandra/service/StorageProxy.java
@@@ -43,12 -43,6 +43,8 @@@ import org.apache.cassandra.config.Data
  import org.apache.cassandra.config.Schema;
  import org.apache.cassandra.db.*;
  import org.apache.cassandra.db.Keyspace;
- import org.apache.cassandra.db.composites.Composite;
- import org.apache.cassandra.db.filter.ColumnSlice;
- import org.apache.cassandra.db.filter.NamesQueryFilter;
- import org.apache.cassandra.db.filter.SliceQueryFilter;
 +import org.apache.cassandra.db.index.SecondaryIndex;
 +import org.apache.cassandra.db.index.SecondaryIndexSearcher;
  import org.apache.cassandra.db.marshal.UUIDType;
  import org.apache.cassandra.dht.AbstractBounds;
  import org.apache.cassandra.dht.Bounds;

http://git-wip-us.apache.org/repos/asf/cassandra/blob/7ce5e062/src/java/org/apache/cassandra/thrift/CassandraServer.java
----------------------------------------------------------------------
diff --cc src/java/org/apache/cassandra/thrift/CassandraServer.java
index 44fb22e,ef5eeb8..650c74e
--- a/src/java/org/apache/cassandra/thrift/CassandraServer.java
+++ b/src/java/org/apache/cassandra/thrift/CassandraServer.java
@@@ -47,8 -47,7 +48,9 @@@ import org.apache.cassandra.cql.CQLStat
  import org.apache.cassandra.cql.QueryProcessor;
  import org.apache.cassandra.cql3.QueryOptions;
  import org.apache.cassandra.db.*;
 +import org.apache.cassandra.db.composites.*;
  import org.apache.cassandra.db.context.CounterContext;
++import org.apache.cassandra.db.filter.ColumnSlice;
  import org.apache.cassandra.db.filter.IDiskAtomFilter;
  import org.apache.cassandra.db.filter.NamesQueryFilter;
  import org.apache.cassandra.db.filter.SliceQueryFilter;
@@@ -2171,5 -2159,62 +2173,62 @@@ public class CassandraServer implement
              }
          });
      }
-     // main method moved to CassandraDaemon
+ 
+     private static class ThriftCASConditions implements CASConditions
+     {
+         private final ColumnFamily expected;
+ 
+         private ThriftCASConditions(ColumnFamily expected)
+         {
+             this.expected = expected;
+         }
+ 
+         public IDiskAtomFilter readFilter()
+         {
+             return expected == null || expected.isEmpty()
 -                 ? new SliceQueryFilter(ByteBufferUtil.EMPTY_BYTE_BUFFER, 
ByteBufferUtil.EMPTY_BYTE_BUFFER, false, 1)
++                 ? new SliceQueryFilter(ColumnSlice.ALL_COLUMNS_ARRAY, false, 
1)
+                  : new 
NamesQueryFilter(ImmutableSortedSet.copyOf(expected.getComparator(), 
expected.getColumnNames()));
+         }
+ 
+         public boolean appliesTo(ColumnFamily current)
+         {
+             long now = System.currentTimeMillis();
+ 
 -            if (!hasLiveColumns(expected, now))
 -                return !hasLiveColumns(current, now);
 -            else if (!hasLiveColumns(current, now))
++            if (!hasLiveCells(expected, now))
++                return !hasLiveCells(current, now);
++            else if (!hasLiveCells(current, now))
+                 return false;
+ 
+             // current has been built from expected, so we know that it can't 
have columns
+             // that excepted don't have. So we just check that for each 
columns in expected:
+             //   - if it is a tombstone, whether current has no column or a 
tombstone;
+             //   - otherwise, that current has a live column with the same 
value.
 -            for (org.apache.cassandra.db.Column e : expected)
++            for (Cell e : expected)
+             {
 -                org.apache.cassandra.db.Column c = 
current.getColumn(e.name());
++                Cell c = current.getColumn(e.name());
+                 if (e.isLive(now))
+                 {
+                     if (c == null || !c.isLive(now) || 
!c.value().equals(e.value()))
+                         return false;
+                 }
+                 else
+                 {
+                     if (c != null && c.isLive(now))
+                         return false;
+                 }
+             }
+             return true;
+         }
+ 
 -        private static boolean hasLiveColumns(ColumnFamily cf, long now)
++        private static boolean hasLiveCells(ColumnFamily cf, long now)
+         {
+             return cf != null && !cf.hasOnlyTombstones(now);
+         }
+ 
+         @Override
+         public String toString()
+         {
+             return expected.toString();
+         }
+     }
  }

Reply via email to