You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@cassandra.apache.org by pa...@apache.org on 2018/02/11 13:26:13 UTC

[28/29] cassandra git commit: Merge branch 'cassandra-3.0' into cassandra-3.11

Merge branch 'cassandra-3.0' into cassandra-3.11


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

Branch: refs/heads/trunk
Commit: 0a6b6f506b012d4d491757d6216c5fa1c53bedc9
Parents: 6eb65e5 c231ed5
Author: Paulo Motta <pa...@apache.org>
Authored: Sat Feb 10 14:58:13 2018 -0200
Committer: Paulo Motta <pa...@apache.org>
Committed: Sat Feb 10 14:59:28 2018 -0200

----------------------------------------------------------------------
 CASSANDRA-14092.txt                             |  81 +++++
 CHANGES.txt                                     |   1 +
 NEWS.txt                                        |  21 ++
 conf/jvm.options                                |  11 +
 debian/rules                                    |   2 +-
 redhat/cassandra.spec                           |   2 +-
 .../org/apache/cassandra/cql3/Attributes.java   |  15 +-
 .../cql3/statements/ModificationStatement.java  |   2 +-
 .../apache/cassandra/db/ColumnFamilyStore.java  |   8 +-
 .../db/ExpirationDateOverflowHandling.java      | 121 +++++++
 .../org/apache/cassandra/db/LegacyLayout.java   |   6 +-
 .../org/apache/cassandra/db/LivenessInfo.java   |  13 +-
 .../db/compaction/CompactionManager.java        |  16 +-
 .../cassandra/db/compaction/Scrubber.java       | 225 +++++++++++--
 .../apache/cassandra/db/rows/BufferCell.java    |   8 +-
 src/java/org/apache/cassandra/db/rows/Cell.java |  10 +
 .../apache/cassandra/db/rows/NativeCell.java    |   5 +
 .../cassandra/service/StorageService.java       |   7 +-
 .../cassandra/service/StorageServiceMBean.java  |   3 +
 .../cassandra/thrift/ThriftValidation.java      |   8 +-
 .../org/apache/cassandra/tools/NodeProbe.java   |   8 +-
 .../cassandra/tools/StandaloneScrubber.java     |   6 +-
 .../apache/cassandra/tools/nodetool/Scrub.java  |   7 +-
 .../table1/mc-1-big-CompressionInfo.db          | Bin 0 -> 43 bytes
 .../table1/mc-1-big-Data.db                     | Bin 0 -> 58 bytes
 .../table1/mc-1-big-Digest.crc32                |   1 +
 .../table1/mc-1-big-Filter.db                   | Bin 0 -> 16 bytes
 .../table1/mc-1-big-Index.db                    | Bin 0 -> 16 bytes
 .../table1/mc-1-big-Statistics.db               | Bin 0 -> 4676 bytes
 .../table1/mc-1-big-Summary.db                  | Bin 0 -> 56 bytes
 .../table1/mc-1-big-TOC.txt                     |   8 +
 .../table2/mc-1-big-CompressionInfo.db          | Bin 0 -> 43 bytes
 .../table2/mc-1-big-Data.db                     | Bin 0 -> 60 bytes
 .../table2/mc-1-big-Digest.crc32                |   1 +
 .../table2/mc-1-big-Filter.db                   | Bin 0 -> 16 bytes
 .../table2/mc-1-big-Index.db                    | Bin 0 -> 16 bytes
 .../table2/mc-1-big-Statistics.db               | Bin 0 -> 4686 bytes
 .../table2/mc-1-big-Summary.db                  | Bin 0 -> 56 bytes
 .../table2/mc-1-big-TOC.txt                     |   8 +
 .../table3/mc-1-big-CompressionInfo.db          | Bin 0 -> 43 bytes
 .../table3/mc-1-big-Data.db                     | Bin 0 -> 111 bytes
 .../table3/mc-1-big-Digest.crc32                |   1 +
 .../table3/mc-1-big-Filter.db                   | Bin 0 -> 16 bytes
 .../table3/mc-1-big-Index.db                    | Bin 0 -> 16 bytes
 .../table3/mc-1-big-Statistics.db               | Bin 0 -> 4732 bytes
 .../table3/mc-1-big-Summary.db                  | Bin 0 -> 56 bytes
 .../table3/mc-1-big-TOC.txt                     |   8 +
 .../table4/mc-1-big-CompressionInfo.db          | Bin 0 -> 43 bytes
 .../table4/mc-1-big-Data.db                     | Bin 0 -> 108 bytes
 .../table4/mc-1-big-Digest.crc32                |   1 +
 .../table4/mc-1-big-Filter.db                   | Bin 0 -> 16 bytes
 .../table4/mc-1-big-Index.db                    | Bin 0 -> 16 bytes
 .../table4/mc-1-big-Statistics.db               | Bin 0 -> 4742 bytes
 .../table4/mc-1-big-Summary.db                  | Bin 0 -> 56 bytes
 .../table4/mc-1-big-TOC.txt                     |   8 +
 .../cql3/validation/operations/TTLTest.java     | 327 ++++++++++++++++++-
 test/unit/org/apache/cassandra/db/CellTest.java |  22 +-
 .../unit/org/apache/cassandra/db/ScrubTest.java |   6 +-
 58 files changed, 893 insertions(+), 84 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/cassandra/blob/0a6b6f50/CHANGES.txt
----------------------------------------------------------------------
diff --cc CHANGES.txt
index c38b69b,a492c42..5b49f48
--- a/CHANGES.txt
+++ b/CHANGES.txt
@@@ -48,26 -28,12 +48,27 @@@ Merged from 2.2
   * Fix the inspectJvmOptions startup check (CASSANDRA-14112)
   * Fix race that prevents submitting compaction for a table when executor is full (CASSANDRA-13801)
   * Rely on the JVM to handle OutOfMemoryErrors (CASSANDRA-13006)
 + * Grab refs during scrub/index redistribution/cleanup (CASSANDRA-13873)
  Merged from 2.1:
 - * More PEP8 compliance for cqlsh (CASSANDRA-14021)
++ * Protect against overflow of local expiration time (CASSANDRA-14092)
   * RPM package spec: fix permissions for installed jars and config files (CASSANDRA-14181)
 -
 -
 -3.0.15
 + * More PEP8 compiance for cqlsh (CASSANDRA-14021)
 +
 +
 +3.11.1
 + * Fix the computation of cdc_total_space_in_mb for exabyte filesystems (CASSANDRA-13808)
 + * AbstractTokenTreeBuilder#serializedSize returns wrong value when there is a single leaf and overflow collisions (CASSANDRA-13869)
 + * Add a compaction option to TWCS to ignore sstables overlapping checks (CASSANDRA-13418)
 + * BTree.Builder memory leak (CASSANDRA-13754)
 + * Revert CASSANDRA-10368 of supporting non-pk column filtering due to correctness (CASSANDRA-13798)
 + * Add a skip read validation flag to cassandra-stress (CASSANDRA-13772)
 + * Fix cassandra-stress hang issues when an error during cluster connection happens (CASSANDRA-12938)
 + * Better bootstrap failure message when blocked by (potential) range movement (CASSANDRA-13744)
 + * "ignore" option is ignored in sstableloader (CASSANDRA-13721)
 + * Deadlock in AbstractCommitLogSegmentManager (CASSANDRA-13652)
 + * Duplicate the buffer before passing it to analyser in SASI operation (CASSANDRA-13512)
 + * Properly evict pstmts from prepared statements cache (CASSANDRA-13641)
 +Merged from 3.0:
   * Improve TRUNCATE performance (CASSANDRA-13909)
   * Implement short read protection on partition boundaries (CASSANDRA-13595)
   * Fix ISE thrown by UPI.Serializer.hasNext() for some SELECT queries (CASSANDRA-13911)

http://git-wip-us.apache.org/repos/asf/cassandra/blob/0a6b6f50/NEWS.txt
----------------------------------------------------------------------
diff --cc NEWS.txt
index f4b15e7,f574c33..fb1dafe
--- a/NEWS.txt
+++ b/NEWS.txt
@@@ -18,22 -38,14 +38,23 @@@ using the provided 'sstableupgrade' too
  
  Upgrading
  ---------
+    - See MAXIMUM TTL EXPIRATION DATE NOTICE above.
 -   - Cassandra is now relying on the JVM options to properly shutdown on OutOfMemoryError. By default it will
 -     rely on the OnOutOfMemoryError option as the ExitOnOutOfMemoryError and CrashOnOutOfMemoryError options
 -     are not supported by the older 1.7 and 1.8 JVMs. A warning will be logged at startup if none of those JVM
 -     options are used. See CASSANDRA-13006 for more details.
 -   - Cassandra is not logging anymore by default an Heap histogram on OutOfMemoryError. To enable that behavior
 -     set the 'cassandra.printHeapHistogramOnOutOfMemoryError' System property to 'true'. See CASSANDRA-13006
 -     for more details.
 +    - Cassandra is now relying on the JVM options to properly shutdown on OutOfMemoryError. By default it will
 +      rely on the OnOutOfMemoryError option as the ExitOnOutOfMemoryError and CrashOnOutOfMemoryError options
 +      are not supported by the older 1.7 and 1.8 JVMs. A warning will be logged at startup if none of those JVM
 +      options are used. See CASSANDRA-13006 for more details
 +    - Cassandra is not logging anymore by default an Heap histogram on OutOfMemoryError. To enable that behavior
 +      set the 'cassandra.printHeapHistogramOnOutOfMemoryError' System property to 'true'. See CASSANDRA-13006
 +      for more details.
 +    - Upgrades from 3.0 might have produced unnecessary schema migrations while
 +      there was at least one 3.0 node in the cluster. It is therefore highly
 +      recommended to upgrade from 3.0 to at least 3.11.2. The root cause of
 +      this schema mismatch was a difference in the way how schema digests were computed
 +      in 3.0 and 3.11.2. To mitigate this issue, 3.11.2 and newer announce
 +      3.0 compatible digests as long as there is at least one 3.0 node in the
 +      cluster. Once all nodes have been upgraded, the "real" schema version will be
 +      announced. Note: this fix is only necessary in 3.11.2 and therefore only applies
 +      to 3.11. (CASSANDRA-14109)
  
  Materialized Views
  -------------------

http://git-wip-us.apache.org/repos/asf/cassandra/blob/0a6b6f50/conf/jvm.options
----------------------------------------------------------------------

http://git-wip-us.apache.org/repos/asf/cassandra/blob/0a6b6f50/redhat/cassandra.spec
----------------------------------------------------------------------

http://git-wip-us.apache.org/repos/asf/cassandra/blob/0a6b6f50/src/java/org/apache/cassandra/cql3/Attributes.java
----------------------------------------------------------------------
diff --cc src/java/org/apache/cassandra/cql3/Attributes.java
index d915560,832d0a7..d4e230f
--- a/src/java/org/apache/cassandra/cql3/Attributes.java
+++ b/src/java/org/apache/cassandra/cql3/Attributes.java
@@@ -20,8 -20,9 +20,10 @@@ package org.apache.cassandra.cql3
  import java.nio.ByteBuffer;
  import java.util.List;
  
+ import org.apache.cassandra.config.CFMetaData;
  import org.apache.cassandra.cql3.functions.Function;
+ import org.apache.cassandra.db.ExpirationDateOverflowHandling;
 +import org.apache.cassandra.db.LivenessInfo;
  import org.apache.cassandra.db.marshal.Int32Type;
  import org.apache.cassandra.db.marshal.LongType;
  import org.apache.cassandra.exceptions.InvalidRequestException;
@@@ -105,11 -109,11 +110,11 @@@ public class Attribute
  
          ByteBuffer tval = timeToLive.bindAndGet(options);
          if (tval == null)
 -            throw new InvalidRequestException("Invalid null value of TTL");
 -
 -        if (tval == ByteBufferUtil.UNSET_BYTE_BUFFER) // treat as unlimited
              return 0;
  
 +        if (tval == ByteBufferUtil.UNSET_BYTE_BUFFER)
-             return defaultTimeToLive;
++            return metadata.params.defaultTimeToLive;
 +
          try
          {
              Int32Type.instance.validate(tval);
@@@ -126,9 -130,8 +131,11 @@@
          if (ttl > MAX_TTL)
              throw new InvalidRequestException(String.format("ttl is too large. requested (%d) maximum (%d)", ttl, MAX_TTL));
  
-         if (defaultTimeToLive != LivenessInfo.NO_TTL && ttl == LivenessInfo.NO_TTL)
++        if (metadata.params.defaultTimeToLive != LivenessInfo.NO_TTL && ttl == LivenessInfo.NO_TTL)
 +            return LivenessInfo.NO_TTL;
 +
+         ExpirationDateOverflowHandling.maybeApplyExpirationDateOverflowPolicy(metadata, ttl, false);
+ 
          return ttl;
      }
  

http://git-wip-us.apache.org/repos/asf/cassandra/blob/0a6b6f50/src/java/org/apache/cassandra/cql3/statements/ModificationStatement.java
----------------------------------------------------------------------

http://git-wip-us.apache.org/repos/asf/cassandra/blob/0a6b6f50/src/java/org/apache/cassandra/db/ColumnFamilyStore.java
----------------------------------------------------------------------

http://git-wip-us.apache.org/repos/asf/cassandra/blob/0a6b6f50/src/java/org/apache/cassandra/db/LegacyLayout.java
----------------------------------------------------------------------

http://git-wip-us.apache.org/repos/asf/cassandra/blob/0a6b6f50/src/java/org/apache/cassandra/db/LivenessInfo.java
----------------------------------------------------------------------
diff --cc src/java/org/apache/cassandra/db/LivenessInfo.java
index 5d17aea,f6c9b62..c2a2291
--- a/src/java/org/apache/cassandra/db/LivenessInfo.java
+++ b/src/java/org/apache/cassandra/db/LivenessInfo.java
@@@ -66,13 -72,13 +68,13 @@@ public class LivenessInf
      public static LivenessInfo expiring(long timestamp, int ttl, int nowInSec)
      {
          assert ttl != EXPIRED_LIVENESS_TTL;
-         return new ExpiringLivenessInfo(timestamp, ttl, nowInSec + ttl);
+         return new ExpiringLivenessInfo(timestamp, ttl, ExpirationDateOverflowHandling.computeLocalExpirationTime(nowInSec, ttl));
      }
  
 -    public static LivenessInfo create(CFMetaData metadata, long timestamp, int ttl, int nowInSec)
 +    public static LivenessInfo create(long timestamp, int ttl, int nowInSec)
      {
          return ttl == NO_TTL
 -             ? create(metadata, timestamp, nowInSec)
 +             ? create(timestamp, nowInSec)
               : expiring(timestamp, ttl, nowInSec);
      }
  

http://git-wip-us.apache.org/repos/asf/cassandra/blob/0a6b6f50/src/java/org/apache/cassandra/db/compaction/CompactionManager.java
----------------------------------------------------------------------

http://git-wip-us.apache.org/repos/asf/cassandra/blob/0a6b6f50/src/java/org/apache/cassandra/db/compaction/Scrubber.java
----------------------------------------------------------------------
diff --cc src/java/org/apache/cassandra/db/compaction/Scrubber.java
index ed107d7,bc11504..f8fa548
--- a/src/java/org/apache/cassandra/db/compaction/Scrubber.java
+++ b/src/java/org/apache/cassandra/db/compaction/Scrubber.java
@@@ -69,10 -73,10 +73,10 @@@ public class Scrubber implements Closea
  
      private static final Comparator<Partition> partitionComparator = new Comparator<Partition>()
      {
--         public int compare(Partition r1, Partition r2)
--         {
--             return r1.partitionKey().compareTo(r2.partitionKey());
--         }
++        public int compare(Partition r1, Partition r2)
++        {
++            return r1.partitionKey().compareTo(r2.partitionKey());
++        }
      };
      private final SortedSet<Partition> outOfOrder = new TreeSet<>(partitionComparator);
  
@@@ -112,8 -126,8 +124,8 @@@
          }
          this.checkData = checkData && !this.isIndex; //LocalByPartitionerType does not support validation
          this.expectedBloomFilterSize = Math.max(
--            cfs.metadata.params.minIndexInterval,
--            hasIndexFile ? SSTableReader.getApproximateKeyCount(toScrub) : 0);
++        cfs.metadata.params.minIndexInterval,
++        hasIndexFile ? SSTableReader.getApproximateKeyCount(toScrub) : 0);
  
          // loop through each row, deserializing to check for damage.
          // we'll also loop through the index at the same time, using the position from the index to recover if the
@@@ -124,8 -138,8 +136,8 @@@
                          : sstable.openDataReader(CompactionManager.instance.getRateLimiter());
  
          this.indexFile = hasIndexFile
--                ? RandomAccessReader.open(new File(sstable.descriptor.filenameFor(Component.PRIMARY_INDEX)))
--                : null;
++                         ? RandomAccessReader.open(new File(sstable.descriptor.filenameFor(Component.PRIMARY_INDEX)))
++                         : null;
  
          this.scrubInfo = new ScrubInfo(dataFile, sstable);
  
@@@ -203,8 -220,8 +218,8 @@@
                      if (currentIndexKey != null && !key.getKey().equals(currentIndexKey))
                      {
                          throw new IOError(new IOException(String.format("Key from data file (%s) does not match key from index file (%s)",
--                                //ByteBufferUtil.bytesToHex(key.getKey()), ByteBufferUtil.bytesToHex(currentIndexKey))));
--                                "_too big_", ByteBufferUtil.bytesToHex(currentIndexKey))));
++                                                                        //ByteBufferUtil.bytesToHex(key.getKey()), ByteBufferUtil.bytesToHex(currentIndexKey))));
++                                                                        "_too big_", ByteBufferUtil.bytesToHex(currentIndexKey))));
                      }
  
                      if (indexFile != null && dataSizeFromIndex > dataFile.length())
@@@ -225,7 -242,7 +240,7 @@@
                          && (key == null || !key.getKey().equals(currentIndexKey) || dataStart != dataStartFromIndex))
                      {
                          outputHandler.output(String.format("Retrying from row index; data is %s bytes starting at %s",
--                                                  dataSizeFromIndex, dataStartFromIndex));
++                                                           dataSizeFromIndex, dataStartFromIndex));
                          key = sstable.decorateKey(currentIndexKey);
                          try
                          {
@@@ -333,6 -352,18 +350,18 @@@
          return true;
      }
  
+     /**
+      * Only wrap with {@link FixNegativeLocalDeletionTimeIterator} if {@link #reinsertOverflowedTTLRows} option
+      * is specified
+      */
+     private UnfilteredRowIterator getIterator(DecoratedKey key)
+     {
 -        RowMergingSSTableIterator rowMergingIterator = new RowMergingSSTableIterator(sstable, dataFile, key);
++        RowMergingSSTableIterator rowMergingIterator = new RowMergingSSTableIterator(SSTableIdentityIterator.create(sstable, dataFile, key));
+         return reinsertOverflowedTTLRows ? new FixNegativeLocalDeletionTimeIterator(rowMergingIterator,
+                                                                                     outputHandler,
+                                                                                     negativeLocalDeletionInfoMetrics) : rowMergingIterator;
+     }
+ 
      private void updateIndexKey()
      {
          currentIndexKey = nextIndexKey;
@@@ -342,8 -373,8 +371,8 @@@
              nextIndexKey = !indexAvailable() ? null : ByteBufferUtil.readWithShortLength(indexFile);
  
              nextRowPositionFromIndex = !indexAvailable()
--                    ? dataFile.length()
-                     : rowIndexEntrySerializer.deserializePositionAndSkip(indexFile);
 -                    : rowIndexEntrySerializer.deserialize(indexFile).position;
++                                       ? dataFile.length()
++                                       : rowIndexEntrySerializer.deserializePositionAndSkip(indexFile);
          }
          catch (Throwable th)
          {
@@@ -620,6 -652,152 +654,153 @@@
              previous = next;
              return next;
          }
+     }
  
+     /**
+      * This iterator converts negative {@link AbstractCell#localDeletionTime()} into {@link AbstractCell#MAX_DELETION_TIME}
+      *
+      * This is to recover entries with overflowed localExpirationTime due to CASSANDRA-14092
+      */
+     private static final class FixNegativeLocalDeletionTimeIterator extends AbstractIterator<Unfiltered> implements UnfilteredRowIterator
+     {
+         /**
+          * The decorated iterator.
+          */
+         private final UnfilteredRowIterator iterator;
+ 
+         private final OutputHandler outputHandler;
+         private final NegativeLocalDeletionInfoMetrics negativeLocalExpirationTimeMetrics;
+ 
+         public FixNegativeLocalDeletionTimeIterator(UnfilteredRowIterator iterator, OutputHandler outputHandler,
+                                                     NegativeLocalDeletionInfoMetrics negativeLocalDeletionInfoMetrics)
+         {
+             this.iterator = iterator;
+             this.outputHandler = outputHandler;
+             this.negativeLocalExpirationTimeMetrics = negativeLocalDeletionInfoMetrics;
+         }
+ 
+         public CFMetaData metadata()
+         {
+             return iterator.metadata();
+         }
+ 
+         public boolean isReverseOrder()
+         {
+             return iterator.isReverseOrder();
+         }
+ 
+         public PartitionColumns columns()
+         {
+             return iterator.columns();
+         }
+ 
+         public DecoratedKey partitionKey()
+         {
+             return iterator.partitionKey();
+         }
+ 
+         public Row staticRow()
+         {
+             return iterator.staticRow();
+         }
+ 
+         @Override
+         public boolean isEmpty()
+         {
+             return iterator.isEmpty();
+         }
+ 
+         public void close()
+         {
+             iterator.close();
+         }
+ 
+         public DeletionTime partitionLevelDeletion()
+         {
+             return iterator.partitionLevelDeletion();
+         }
+ 
+         public EncodingStats stats()
+         {
+             return iterator.stats();
+         }
+ 
+         protected Unfiltered computeNext()
+         {
+             if (!iterator.hasNext())
+                 return endOfData();
+ 
+             Unfiltered next = iterator.next();
+             if (!next.isRow())
+                 return next;
+ 
+             if (hasNegativeLocalExpirationTime((Row) next))
+             {
+                 outputHandler.debug(String.format("Found row with negative local expiration time: %s", next.toString(metadata(), false)));
+                 negativeLocalExpirationTimeMetrics.fixedRows++;
+                 return fixNegativeLocalExpirationTime((Row) next);
+             }
+ 
+             return next;
+         }
+ 
+         private boolean hasNegativeLocalExpirationTime(Row next)
+         {
+             Row row = next;
+             if (row.primaryKeyLivenessInfo().isExpiring() && row.primaryKeyLivenessInfo().localExpirationTime() < 0)
+             {
+                 return true;
+             }
+ 
+             for (ColumnData cd : row)
+             {
+                 if (cd.column().isSimple())
+                 {
+                     Cell cell = (Cell)cd;
+                     if (cell.isExpiring() && cell.localDeletionTime() < 0)
+                         return true;
+                 }
+                 else
+                 {
+                     ComplexColumnData complexData = (ComplexColumnData)cd;
+                     for (Cell cell : complexData)
+                     {
+                         if (cell.isExpiring() && cell.localDeletionTime() < 0)
+                             return true;
+                     }
+                 }
+             }
+ 
+             return false;
+         }
+ 
+         private Unfiltered fixNegativeLocalExpirationTime(Row row)
+         {
+             Row.Builder builder = HeapAllocator.instance.cloningBTreeRowBuilder();
+             builder.newRow(row.clustering());
+             builder.addPrimaryKeyLivenessInfo(row.primaryKeyLivenessInfo().isExpiring() && row.primaryKeyLivenessInfo().localExpirationTime() < 0 ?
+                                               row.primaryKeyLivenessInfo().withUpdatedTimestampAndLocalDeletionTime(row.primaryKeyLivenessInfo().timestamp() + 1, AbstractCell.MAX_DELETION_TIME)
+                                               :row.primaryKeyLivenessInfo());
+             builder.addRowDeletion(row.deletion());
+             for (ColumnData cd : row)
+             {
+                 if (cd.column().isSimple())
+                 {
+                     Cell cell = (Cell)cd;
+                     builder.addCell(cell.isExpiring() && cell.localDeletionTime() < 0 ? cell.withUpdatedTimestampAndLocalDeletionTime(cell.timestamp() + 1, AbstractCell.MAX_DELETION_TIME) : cell);
+                 }
+                 else
+                 {
+                     ComplexColumnData complexData = (ComplexColumnData)cd;
+                     builder.addComplexDeletion(complexData.column(), complexData.complexDeletion());
+                     for (Cell cell : complexData)
+                     {
+                         builder.addCell(cell.isExpiring() && cell.localDeletionTime() < 0 ? cell.withUpdatedTimestampAndLocalDeletionTime(cell.timestamp() + 1, AbstractCell.MAX_DELETION_TIME) : cell);
+                     }
+                 }
+             }
+             return builder.build();
+         }
      }
++
  }

http://git-wip-us.apache.org/repos/asf/cassandra/blob/0a6b6f50/src/java/org/apache/cassandra/db/rows/BufferCell.java
----------------------------------------------------------------------
diff --cc src/java/org/apache/cassandra/db/rows/BufferCell.java
index 9b31c16,df2619c..b62d95a
--- a/src/java/org/apache/cassandra/db/rows/BufferCell.java
+++ b/src/java/org/apache/cassandra/db/rows/BufferCell.java
@@@ -17,12 -17,18 +17,13 @@@
   */
  package org.apache.cassandra.db.rows;
  
 -import java.io.IOException;
  import java.nio.ByteBuffer;
  
 -import org.apache.cassandra.config.*;
 -import org.apache.cassandra.db.*;
 -import org.apache.cassandra.db.context.CounterContext;
 +import org.apache.cassandra.config.ColumnDefinition;
++import org.apache.cassandra.db.ExpirationDateOverflowHandling;
  import org.apache.cassandra.db.marshal.ByteType;
 -import org.apache.cassandra.io.util.DataInputPlus;
 -import org.apache.cassandra.io.util.DataOutputPlus;
  import org.apache.cassandra.utils.ByteBufferUtil;
  import org.apache.cassandra.utils.ObjectSizes;
 -import org.apache.cassandra.utils.FBUtilities;
  import org.apache.cassandra.utils.memory.AbstractAllocator;
  
  public class BufferCell extends AbstractCell

http://git-wip-us.apache.org/repos/asf/cassandra/blob/0a6b6f50/src/java/org/apache/cassandra/db/rows/Cell.java
----------------------------------------------------------------------
diff --cc src/java/org/apache/cassandra/db/rows/Cell.java
index 19d1f30,c69e11f..1205b7d
--- a/src/java/org/apache/cassandra/db/rows/Cell.java
+++ b/src/java/org/apache/cassandra/db/rows/Cell.java
@@@ -21,7 -21,13 +21,14 @@@ import java.io.IOException
  import java.nio.ByteBuffer;
  import java.util.Comparator;
  
+ import com.google.common.annotations.VisibleForTesting;
+ import org.slf4j.Logger;
+ import org.slf4j.LoggerFactory;
+ 
+ import org.apache.cassandra.config.CFMetaData;
+ import org.apache.cassandra.config.ColumnDefinition;
+ import org.apache.cassandra.cql3.Attributes;
 +import org.apache.cassandra.config.*;
  import org.apache.cassandra.db.*;
  import org.apache.cassandra.io.util.DataOutputPlus;
  import org.apache.cassandra.io.util.DataInputPlus;

http://git-wip-us.apache.org/repos/asf/cassandra/blob/0a6b6f50/src/java/org/apache/cassandra/db/rows/NativeCell.java
----------------------------------------------------------------------
diff --cc src/java/org/apache/cassandra/db/rows/NativeCell.java
index 5930332,0000000..31ce0b7
mode 100644,000000..100644
--- a/src/java/org/apache/cassandra/db/rows/NativeCell.java
+++ b/src/java/org/apache/cassandra/db/rows/NativeCell.java
@@@ -1,156 -1,0 +1,161 @@@
 +/*
 + * 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.rows;
 +
 +import java.nio.ByteBuffer;
 +import java.nio.ByteOrder;
 +
 +import org.apache.cassandra.config.ColumnDefinition;
 +import org.apache.cassandra.utils.ObjectSizes;
 +import org.apache.cassandra.utils.concurrent.OpOrder;
 +import org.apache.cassandra.utils.memory.MemoryUtil;
 +import org.apache.cassandra.utils.memory.NativeAllocator;
 +
 +public class NativeCell extends AbstractCell
 +{
 +    private static final long EMPTY_SIZE = ObjectSizes.measure(new NativeCell());
 +
 +    private static final long HAS_CELLPATH = 0;
 +    private static final long TIMESTAMP = 1;
 +    private static final long TTL = 9;
 +    private static final long DELETION = 13;
 +    private static final long LENGTH = 17;
 +    private static final long VALUE = 21;
 +
 +    private final long peer;
 +
 +    private NativeCell()
 +    {
 +        super(null);
 +        this.peer = 0;
 +    }
 +
 +    public NativeCell(NativeAllocator allocator,
 +                      OpOrder.Group writeOp,
 +                      Cell cell)
 +    {
 +        this(allocator,
 +             writeOp,
 +             cell.column(),
 +             cell.timestamp(),
 +             cell.ttl(),
 +             cell.localDeletionTime(),
 +             cell.value(),
 +             cell.path());
 +    }
 +
 +    public NativeCell(NativeAllocator allocator,
 +                      OpOrder.Group writeOp,
 +                      ColumnDefinition column,
 +                      long timestamp,
 +                      int ttl,
 +                      int localDeletionTime,
 +                      ByteBuffer value,
 +                      CellPath path)
 +    {
 +        super(column);
 +        long size = simpleSize(value.remaining());
 +
 +        assert value.order() == ByteOrder.BIG_ENDIAN;
 +        assert column.isComplex() == (path != null);
 +        if (path != null)
 +        {
 +            assert path.size() == 1;
 +            size += 4 + path.get(0).remaining();
 +        }
 +
 +        if (size > Integer.MAX_VALUE)
 +            throw new IllegalStateException();
 +
 +        // cellpath? : timestamp : ttl : localDeletionTime : length : <data> : [cell path length] : [<cell path data>]
 +        peer = allocator.allocate((int) size, writeOp);
 +        MemoryUtil.setByte(peer + HAS_CELLPATH, (byte)(path == null ? 0 : 1));
 +        MemoryUtil.setLong(peer + TIMESTAMP, timestamp);
 +        MemoryUtil.setInt(peer + TTL, ttl);
 +        MemoryUtil.setInt(peer + DELETION, localDeletionTime);
 +        MemoryUtil.setInt(peer + LENGTH, value.remaining());
 +        MemoryUtil.setBytes(peer + VALUE, value);
 +
 +        if (path != null)
 +        {
 +            ByteBuffer pathbuffer = path.get(0);
 +            assert pathbuffer.order() == ByteOrder.BIG_ENDIAN;
 +
 +            long offset = peer + VALUE + value.remaining();
 +            MemoryUtil.setInt(offset, pathbuffer.remaining());
 +            MemoryUtil.setBytes(offset + 4, pathbuffer);
 +        }
 +    }
 +
 +    private static long simpleSize(int length)
 +    {
 +        return VALUE + length;
 +    }
 +
 +    public long timestamp()
 +    {
 +        return MemoryUtil.getLong(peer + TIMESTAMP);
 +    }
 +
 +    public int ttl()
 +    {
 +        return MemoryUtil.getInt(peer + TTL);
 +    }
 +
 +    public int localDeletionTime()
 +    {
 +        return MemoryUtil.getInt(peer + DELETION);
 +    }
 +
 +    public ByteBuffer value()
 +    {
 +        int length = MemoryUtil.getInt(peer + LENGTH);
 +        return MemoryUtil.getByteBuffer(peer + VALUE, length, ByteOrder.BIG_ENDIAN);
 +    }
 +
 +    public CellPath path()
 +    {
 +        if (MemoryUtil.getByte(peer+ HAS_CELLPATH) == 0)
 +            return null;
 +
 +        long offset = peer + VALUE + MemoryUtil.getInt(peer + LENGTH);
 +        int size = MemoryUtil.getInt(offset);
 +        return CellPath.create(MemoryUtil.getByteBuffer(offset + 4, size, ByteOrder.BIG_ENDIAN));
 +    }
 +
 +    public Cell withUpdatedValue(ByteBuffer newValue)
 +    {
 +        throw new UnsupportedOperationException();
 +    }
 +
++    public Cell withUpdatedTimestampAndLocalDeletionTime(long newTimestamp, int newLocalDeletionTime)
++    {
++        return new BufferCell(column, newTimestamp, ttl(), newLocalDeletionTime, value(), path());
++    }
++
 +    public Cell withUpdatedColumn(ColumnDefinition column)
 +    {
 +        return new BufferCell(column, timestamp(), ttl(), localDeletionTime(), value(), path());
 +    }
 +
 +    public long unsharedHeapSizeExcludingData()
 +    {
 +        return EMPTY_SIZE;
 +    }
 +
 +}

http://git-wip-us.apache.org/repos/asf/cassandra/blob/0a6b6f50/src/java/org/apache/cassandra/service/StorageService.java
----------------------------------------------------------------------

http://git-wip-us.apache.org/repos/asf/cassandra/blob/0a6b6f50/src/java/org/apache/cassandra/service/StorageServiceMBean.java
----------------------------------------------------------------------

http://git-wip-us.apache.org/repos/asf/cassandra/blob/0a6b6f50/src/java/org/apache/cassandra/thrift/ThriftValidation.java
----------------------------------------------------------------------

http://git-wip-us.apache.org/repos/asf/cassandra/blob/0a6b6f50/src/java/org/apache/cassandra/tools/NodeProbe.java
----------------------------------------------------------------------

http://git-wip-us.apache.org/repos/asf/cassandra/blob/0a6b6f50/src/java/org/apache/cassandra/tools/StandaloneScrubber.java
----------------------------------------------------------------------
diff --cc src/java/org/apache/cassandra/tools/StandaloneScrubber.java
index 54b340e,4778d72..ead8fc5
--- a/src/java/org/apache/cassandra/tools/StandaloneScrubber.java
+++ b/src/java/org/apache/cassandra/tools/StandaloneScrubber.java
@@@ -266,6 -274,7 +269,7 @@@ public class StandaloneScrubbe
              options.addOption("m",  MANIFEST_CHECK_OPTION, "only check and repair the leveled manifest, without actually scrubbing the sstables");
              options.addOption("s",  SKIP_CORRUPTED_OPTION, "skip corrupt rows in counter tables");
              options.addOption("n",  NO_VALIDATE_OPTION,    "do not validate columns using column validator");
 -            options.addOption("r", REINSERT_OVERFLOWED_TTL_OPTION, REINSERT_OVERFLOWED_TTL_OPTION_DESCRIPTION);
++            options.addOption("r",  REINSERT_OVERFLOWED_TTL_OPTION,    "Reinsert found rows with overflowed TTL affected by CASSANDRA-14092");
              return options;
          }
  

http://git-wip-us.apache.org/repos/asf/cassandra/blob/0a6b6f50/src/java/org/apache/cassandra/tools/nodetool/Scrub.java
----------------------------------------------------------------------
diff --cc src/java/org/apache/cassandra/tools/nodetool/Scrub.java
index 2345a85,ead2fd4..812202d
--- a/src/java/org/apache/cassandra/tools/nodetool/Scrub.java
+++ b/src/java/org/apache/cassandra/tools/nodetool/Scrub.java
@@@ -48,6 -49,11 +48,11 @@@ public class Scrub extends NodeToolCm
                     description = "Do not validate columns using column validator")
      private boolean noValidation = false;
  
+     @Option(title = "reinsert_overflowed_ttl",
+     name = {"r", "--reinsert-overflowed-ttl"},
 -    description = StandaloneScrubber.REINSERT_OVERFLOWED_TTL_OPTION_DESCRIPTION)
++    description = "Reinsert found rows with overflowed TTL affected by CASSANDRA-14092")
+     private boolean reinsertOverflowedTTL = false;
+ 
      @Option(title = "jobs",
              name = {"-j", "--jobs"},
              description = "Number of sstables to scrub simultanously, set to 0 to use all available compaction threads")

http://git-wip-us.apache.org/repos/asf/cassandra/blob/0a6b6f50/test/data/negative-local-expiration-test/table1/mc-1-big-Data.db
----------------------------------------------------------------------
diff --cc test/data/negative-local-expiration-test/table1/mc-1-big-Data.db
index 0000000,e7a72da..cb96af3
mode 000000,100644..100644
Binary files differ

http://git-wip-us.apache.org/repos/asf/cassandra/blob/0a6b6f50/test/data/negative-local-expiration-test/table1/mc-1-big-Digest.crc32
----------------------------------------------------------------------
diff --cc test/data/negative-local-expiration-test/table1/mc-1-big-Digest.crc32
index 0000000,a3c633a..44c47fb
mode 000000,100644..100644
--- a/test/data/negative-local-expiration-test/table1/mc-1-big-Digest.crc32
+++ b/test/data/negative-local-expiration-test/table1/mc-1-big-Digest.crc32
@@@ -1,0 -1,1 +1,1 @@@
 -203700622
++4223695539

http://git-wip-us.apache.org/repos/asf/cassandra/blob/0a6b6f50/test/data/negative-local-expiration-test/table1/mc-1-big-Statistics.db
----------------------------------------------------------------------
diff --cc test/data/negative-local-expiration-test/table1/mc-1-big-Statistics.db
index 0000000,faf367b..ebcf4c8
mode 000000,100644..100644
Binary files differ

http://git-wip-us.apache.org/repos/asf/cassandra/blob/0a6b6f50/test/data/negative-local-expiration-test/table1/mc-1-big-TOC.txt
----------------------------------------------------------------------
diff --cc test/data/negative-local-expiration-test/table1/mc-1-big-TOC.txt
index 0000000,45113dc..831e376
mode 000000,100644..100644
--- a/test/data/negative-local-expiration-test/table1/mc-1-big-TOC.txt
+++ b/test/data/negative-local-expiration-test/table1/mc-1-big-TOC.txt
@@@ -1,0 -1,8 +1,8 @@@
++Digest.crc32
+ CompressionInfo.db
++Index.db
++TOC.txt
+ Data.db
 -Summary.db
 -Filter.db
+ Statistics.db
 -TOC.txt
 -Digest.crc32
 -Index.db
++Filter.db
++Summary.db

http://git-wip-us.apache.org/repos/asf/cassandra/blob/0a6b6f50/test/data/negative-local-expiration-test/table2/mc-1-big-Data.db
----------------------------------------------------------------------
diff --cc test/data/negative-local-expiration-test/table2/mc-1-big-Data.db
index 0000000,c1de572..8f41a21
mode 000000,100644..100644
Binary files differ

http://git-wip-us.apache.org/repos/asf/cassandra/blob/0a6b6f50/test/data/negative-local-expiration-test/table2/mc-1-big-Digest.crc32
----------------------------------------------------------------------
diff --cc test/data/negative-local-expiration-test/table2/mc-1-big-Digest.crc32
index 0000000,0403b5b..da919fe
mode 000000,100644..100644
--- a/test/data/negative-local-expiration-test/table2/mc-1-big-Digest.crc32
+++ b/test/data/negative-local-expiration-test/table2/mc-1-big-Digest.crc32
@@@ -1,0 -1,1 +1,1 @@@
 -82785930
++2886964045

http://git-wip-us.apache.org/repos/asf/cassandra/blob/0a6b6f50/test/data/negative-local-expiration-test/table2/mc-1-big-Statistics.db
----------------------------------------------------------------------
diff --cc test/data/negative-local-expiration-test/table2/mc-1-big-Statistics.db
index 0000000,e9d6577..549dabe
mode 000000,100644..100644
Binary files differ

http://git-wip-us.apache.org/repos/asf/cassandra/blob/0a6b6f50/test/data/negative-local-expiration-test/table2/mc-1-big-TOC.txt
----------------------------------------------------------------------
diff --cc test/data/negative-local-expiration-test/table2/mc-1-big-TOC.txt
index 0000000,45113dc..831e376
mode 000000,100644..100644
--- a/test/data/negative-local-expiration-test/table2/mc-1-big-TOC.txt
+++ b/test/data/negative-local-expiration-test/table2/mc-1-big-TOC.txt
@@@ -1,0 -1,8 +1,8 @@@
++Digest.crc32
+ CompressionInfo.db
++Index.db
++TOC.txt
+ Data.db
 -Summary.db
 -Filter.db
+ Statistics.db
 -TOC.txt
 -Digest.crc32
 -Index.db
++Filter.db
++Summary.db

http://git-wip-us.apache.org/repos/asf/cassandra/blob/0a6b6f50/test/data/negative-local-expiration-test/table3/mc-1-big-Data.db
----------------------------------------------------------------------
diff --cc test/data/negative-local-expiration-test/table3/mc-1-big-Data.db
index 0000000,e96f772..008d3e8
mode 000000,100644..100644
Binary files differ

http://git-wip-us.apache.org/repos/asf/cassandra/blob/0a6b6f50/test/data/negative-local-expiration-test/table3/mc-1-big-Digest.crc32
----------------------------------------------------------------------
diff --cc test/data/negative-local-expiration-test/table3/mc-1-big-Digest.crc32
index 0000000,459804b..0bdc0bf
mode 000000,100644..100644
--- a/test/data/negative-local-expiration-test/table3/mc-1-big-Digest.crc32
+++ b/test/data/negative-local-expiration-test/table3/mc-1-big-Digest.crc32
@@@ -1,0 -1,1 +1,1 @@@
 -3064924389
++3254141434

http://git-wip-us.apache.org/repos/asf/cassandra/blob/0a6b6f50/test/data/negative-local-expiration-test/table3/mc-1-big-Statistics.db
----------------------------------------------------------------------
diff --cc test/data/negative-local-expiration-test/table3/mc-1-big-Statistics.db
index 0000000,1ee01e6..62bf84e
mode 000000,100644..100644
Binary files differ

http://git-wip-us.apache.org/repos/asf/cassandra/blob/0a6b6f50/test/data/negative-local-expiration-test/table3/mc-1-big-TOC.txt
----------------------------------------------------------------------
diff --cc test/data/negative-local-expiration-test/table3/mc-1-big-TOC.txt
index 0000000,f445537..831e376
mode 000000,100644..100644
--- a/test/data/negative-local-expiration-test/table3/mc-1-big-TOC.txt
+++ b/test/data/negative-local-expiration-test/table3/mc-1-big-TOC.txt
@@@ -1,0 -1,8 +1,8 @@@
 -Summary.db
 -TOC.txt
 -Filter.db
 -Index.db
+ Digest.crc32
+ CompressionInfo.db
++Index.db
++TOC.txt
+ Data.db
+ Statistics.db
++Filter.db
++Summary.db

http://git-wip-us.apache.org/repos/asf/cassandra/blob/0a6b6f50/test/data/negative-local-expiration-test/table4/mc-1-big-Data.db
----------------------------------------------------------------------
diff --cc test/data/negative-local-expiration-test/table4/mc-1-big-Data.db
index 0000000,a22a7a3..128ea47
mode 000000,100644..100644
Binary files differ

http://git-wip-us.apache.org/repos/asf/cassandra/blob/0a6b6f50/test/data/negative-local-expiration-test/table4/mc-1-big-Digest.crc32
----------------------------------------------------------------------
diff --cc test/data/negative-local-expiration-test/table4/mc-1-big-Digest.crc32
index 0000000,db7a6c7..9d52209
mode 000000,100644..100644
--- a/test/data/negative-local-expiration-test/table4/mc-1-big-Digest.crc32
+++ b/test/data/negative-local-expiration-test/table4/mc-1-big-Digest.crc32
@@@ -1,0 -1,1 +1,1 @@@
 -1803989939
++3231150985

http://git-wip-us.apache.org/repos/asf/cassandra/blob/0a6b6f50/test/data/negative-local-expiration-test/table4/mc-1-big-Statistics.db
----------------------------------------------------------------------
diff --cc test/data/negative-local-expiration-test/table4/mc-1-big-Statistics.db
index 0000000,4ee9294..4eee729
mode 000000,100644..100644
Binary files differ

http://git-wip-us.apache.org/repos/asf/cassandra/blob/0a6b6f50/test/data/negative-local-expiration-test/table4/mc-1-big-TOC.txt
----------------------------------------------------------------------
diff --cc test/data/negative-local-expiration-test/table4/mc-1-big-TOC.txt
index 0000000,f445537..831e376
mode 000000,100644..100644
--- a/test/data/negative-local-expiration-test/table4/mc-1-big-TOC.txt
+++ b/test/data/negative-local-expiration-test/table4/mc-1-big-TOC.txt
@@@ -1,0 -1,8 +1,8 @@@
 -Summary.db
 -TOC.txt
 -Filter.db
 -Index.db
+ Digest.crc32
+ CompressionInfo.db
++Index.db
++TOC.txt
+ Data.db
+ Statistics.db
++Filter.db
++Summary.db

http://git-wip-us.apache.org/repos/asf/cassandra/blob/0a6b6f50/test/unit/org/apache/cassandra/db/CellTest.java
----------------------------------------------------------------------
diff --cc test/unit/org/apache/cassandra/db/CellTest.java
index ea009f6,22f1b78..c68b4ec
--- a/test/unit/org/apache/cassandra/db/CellTest.java
+++ b/test/unit/org/apache/cassandra/db/CellTest.java
@@@ -6,9 -6,9 +6,9 @@@
   * 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
@@@ -148,88 -141,19 +148,88 @@@ public class CellTes
          // But this should be valid even though the underlying value is an empty BB (catches bug #11618)
          assertValid(BufferCell.tombstone(c, 0, 4));
          // And of course, this should be valid with a proper value
 -        assertValid(BufferCell.live(fakeMetadata, c, 0, ByteBufferUtil.bytes((short)4)));
 +        assertValid(BufferCell.live(c, 0, bbs(4)));
  
          // Invalid ttl
 -        assertInvalid(BufferCell.expiring(c, 0, -4, 4, ByteBufferUtil.bytes(4)));
 -        // Invalid local deletion times
 -        assertInvalid(BufferCell.expiring(c, 0, 4, -4, ByteBufferUtil.bytes(4)));
 -        assertInvalid(BufferCell.expiring(c, 0, 4, Cell.NO_DELETION_TIME, ByteBufferUtil.bytes(4)));
 +        assertInvalid(BufferCell.expiring(c, 0, -4, 4, bbs(4)));
-         // Invalid local deletion times
-         assertInvalid(BufferCell.expiring(c, 0, 4, -5, bbs(4)));
-         assertInvalid(BufferCell.expiring(c, 0, 4, Cell.NO_DELETION_TIME, bbs(4)));
++        // Cells with overflowed localExpirationTime are valid after CASSANDRA-14092
++        assertValid(BufferCell.expiring(c, 0, 4, -5, bbs(4)));
++        assertValid(BufferCell.expiring(c, 0, 4, Cell.NO_DELETION_TIME, bbs(4)));
  
          c = fakeColumn("c", MapType.getInstance(Int32Type.instance, Int32Type.instance, true));
          // Valid cell path
 -        assertValid(BufferCell.live(fakeMetadata, c, 0, ByteBufferUtil.bytes(4), CellPath.create(ByteBufferUtil.bytes(4))));
 +        assertValid(BufferCell.live(c, 0, ByteBufferUtil.bytes(4), CellPath.create(ByteBufferUtil.bytes(4))));
          // Invalid cell path (int values should be 0 or 4 bytes)
 -        assertInvalid(BufferCell.live(fakeMetadata, c, 0, ByteBufferUtil.bytes(4), CellPath.create(ByteBufferUtil.bytes((long)4))));
 +        assertInvalid(BufferCell.live(c, 0, ByteBufferUtil.bytes(4), CellPath.create(ByteBufferUtil.bytes((long)4))));
 +    }
 +
 +    @Test
 +    public void testValidateNonFrozenUDT()
 +    {
 +        FieldIdentifier f1 = field("f1");  // has field position 0
 +        FieldIdentifier f2 = field("f2");  // has field position 1
 +        UserType udt = new UserType("ks",
 +                                    bb("myType"),
 +                                    asList(f1, f2),
 +                                    asList(Int32Type.instance, UTF8Type.instance),
 +                                    true);
 +        ColumnDefinition c;
 +
 +        // Valid cells
 +        c = fakeColumn("c", udt);
 +        assertValid(BufferCell.live(c, 0, bb(1), CellPath.create(bbs(0))));
 +        assertValid(BufferCell.live(c, 0, bb("foo"), CellPath.create(bbs(1))));
 +        assertValid(BufferCell.expiring(c, 0, 4, 4, bb(1), CellPath.create(bbs(0))));
 +        assertValid(BufferCell.expiring(c, 0, 4, 4, bb("foo"), CellPath.create(bbs(1))));
 +        assertValid(BufferCell.tombstone(c, 0, 4, CellPath.create(bbs(0))));
 +
 +        // Invalid value (text in an int field)
 +        assertInvalid(BufferCell.live(c, 0, bb("foo"), CellPath.create(bbs(0))));
 +
 +        // Invalid ttl
 +        assertInvalid(BufferCell.expiring(c, 0, -4, 4, bb(1), CellPath.create(bbs(0))));
-         // Invalid local deletion times
-         assertInvalid(BufferCell.expiring(c, 0, 4, -5, bb(1), CellPath.create(bbs(0))));
-         assertInvalid(BufferCell.expiring(c, 0, 4, Cell.NO_DELETION_TIME, bb(1), CellPath.create(bbs(0))));
++        // Cells with overflowed localExpirationTime are valid after CASSANDRA-14092
++        assertValid(BufferCell.expiring(c, 0, 4, -5, bb(1), CellPath.create(bbs(0))));
++        assertValid((BufferCell.expiring(c, 0, 4, Cell.NO_DELETION_TIME, bb(1), CellPath.create(bbs(0)))));
 +
 +        // Invalid cell path (int values should be 0 or 2 bytes)
 +        assertInvalid(BufferCell.live(c, 0, bb(1), CellPath.create(ByteBufferUtil.bytes((long)4))));
 +    }
 +
 +    @Test
 +    public void testValidateFrozenUDT()
 +    {
 +        FieldIdentifier f1 = field("f1");  // has field position 0
 +        FieldIdentifier f2 = field("f2");  // has field position 1
 +        UserType udt = new UserType("ks",
 +                                    bb("myType"),
 +                                    asList(f1, f2),
 +                                    asList(Int32Type.instance, UTF8Type.instance),
 +                                    false);
 +
 +        ColumnDefinition c = fakeColumn("c", udt);
 +        ByteBuffer val = udt(bb(1), bb("foo"));
 +
 +        // Valid cells
 +        assertValid(BufferCell.live(c, 0, val));
 +        assertValid(BufferCell.live(c, 0, val));
 +        assertValid(BufferCell.expiring(c, 0, 4, 4, val));
 +        assertValid(BufferCell.expiring(c, 0, 4, 4, val));
 +        assertValid(BufferCell.tombstone(c, 0, 4));
 +        // fewer values than types is accepted
 +        assertValid(BufferCell.live(c, 0, udt(bb(1))));
 +
 +        // Invalid values
 +        // invalid types
 +        assertInvalid(BufferCell.live(c, 0, udt(bb("foo"), bb(1))));
 +        // too many types
 +        assertInvalid(BufferCell.live(c, 0, udt(bb(1), bb("foo"), bb("bar"))));
 +
 +        // Invalid ttl
 +        assertInvalid(BufferCell.expiring(c, 0, -4, 4, val));
-         // Invalid local deletion times
-         assertInvalid(BufferCell.expiring(c, 0, 4, -5, val));
-         assertInvalid(BufferCell.expiring(c, 0, 4, Cell.NO_DELETION_TIME, val));
++        // Cells with overflowed localExpirationTime are valid after CASSANDRA-14092
++        assertValid(BufferCell.expiring(c, 0, 4, -5, val));
++        assertValid(BufferCell.expiring(c, 0, 4, Cell.NO_DELETION_TIME, val));
      }
  
      @Test

http://git-wip-us.apache.org/repos/asf/cassandra/blob/0a6b6f50/test/unit/org/apache/cassandra/db/ScrubTest.java
----------------------------------------------------------------------


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