Merge commit 'e4c344c58f8ca8f69224855080de4ec266fb671e' into cassandra-3.9

* commit 'e4c344c58f8ca8f69224855080de4ec266fb671e':
  Avoid deserialization error after altering column type


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

Branch: refs/heads/trunk
Commit: 1f014b2cac739439bcf97adb34ae5b8ede6d2fed
Parents: c7547e0 e4c344c
Author: Sylvain Lebresne <sylv...@datastax.com>
Authored: Thu Jun 30 11:14:48 2016 +0200
Committer: Sylvain Lebresne <sylv...@datastax.com>
Committed: Thu Jun 30 11:16:06 2016 +0200

----------------------------------------------------------------------
 CHANGES.txt                                     |  2 +
 .../apache/cassandra/db/rows/BufferCell.java    |  1 -
 src/java/org/apache/cassandra/db/rows/Cell.java | 16 +++----
 .../cassandra/db/rows/UnfilteredSerializer.java | 30 +++++++++----
 .../org/apache/cassandra/cql3/CQLTester.java    | 39 +++++++++++++++--
 .../cql3/validation/operations/AlterTest.java   | 46 +++++++++++++-------
 6 files changed, 97 insertions(+), 37 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/cassandra/blob/1f014b2c/CHANGES.txt
----------------------------------------------------------------------
diff --cc CHANGES.txt
index 463cf78,573f704..ed884a9
--- a/CHANGES.txt
+++ b/CHANGES.txt
@@@ -1,35 -1,10 +1,37 @@@
 -3.0.9
 +3.9
++Merged from 3.0:
+  * Fix EOF exception when altering column type (CASSANDRA-11820)
  Merged from 2.2:
   * MemoryUtil.getShort() should return an unsigned short also for 
architectures not supporting unaligned memory accesses (CASSANDRA-11973)
 +Merged from 2.1:
 + * Avoid stalling paxos when the paxos state expires (CASSANDRA-12043)
 + * Remove finished incoming streaming connections from MessagingService 
(CASSANDRA-11854)
 +
  
 -3.0.8
 - * Fix potential race in schema during new table creation (CASSANDRA-12083)
 +3.8
 + * Improve details in compaction log message (CASSANDRA-12080)
 + * Allow unset values in CQLSSTableWriter (CASSANDRA-11911)
 + * Chunk cache to request compressor-compatible buffers if pool space is 
exhausted (CASSANDRA-11993)
 + * Remove DatabaseDescriptor dependencies from SequentialWriter 
(CASSANDRA-11579)
 + * Move skip_stop_words filter before stemming (CASSANDRA-12078)
 + * Support seek() in EncryptedFileSegmentInputStream (CASSANDRA-11957)
 + * SSTable tools mishandling LocalPartitioner (CASSANDRA-12002)
 + * When SEPWorker assigned work, set thread name to match pool 
(CASSANDRA-11966)
 + * Add cross-DC latency metrics (CASSANDRA-11596)
 + * Allow terms in selection clause (CASSANDRA-10783)
 + * Add bind variables to trace (CASSANDRA-11719)
 + * Switch counter shards' clock to timestamps (CASSANDRA-9811)
 + * Introduce HdrHistogram and response/service/wait separation to stress tool 
(CASSANDRA-11853)
 + * entry-weighers in QueryProcessor should respect partitionKeyBindIndexes 
field (CASSANDRA-11718)
 + * Support older ant versions (CASSANDRA-11807)
 + * Estimate compressed on disk size when deciding if sstable size limit 
reached (CASSANDRA-11623)
 + * cassandra-stress profiles should support case sensitive schemas 
(CASSANDRA-11546)
 + * Remove DatabaseDescriptor dependency from FileUtils (CASSANDRA-11578)
 + * Faster streaming (CASSANDRA-9766)
 + * Add prepared query parameter to trace for "Execute CQL3 prepared query" 
session (CASSANDRA-11425)
 + * Add repaired percentage metric (CASSANDRA-11503)
 + * Add Change-Data-Capture (CASSANDRA-8844)
 +Merged from 3.0:
   * cqlsh: fix error handling in rare COPY FROM failure scenario 
(CASSANDRA-12070)
   * Disable autocompaction during drain (CASSANDRA-11878)
   * Add a metrics timer to MemtablePool and use it to track time spent blocked 
on memory in MemtableAllocator (CASSANDRA-11327)

http://git-wip-us.apache.org/repos/asf/cassandra/blob/1f014b2c/src/java/org/apache/cassandra/db/rows/BufferCell.java
----------------------------------------------------------------------
diff --cc src/java/org/apache/cassandra/db/rows/BufferCell.java
index d998d69,db0ded5..7bb1f4b
--- a/src/java/org/apache/cassandra/db/rows/BufferCell.java
+++ b/src/java/org/apache/cassandra/db/rows/BufferCell.java
@@@ -125,5 -200,166 +125,4 @@@ public class BufferCell extends Abstrac
      {
          return EMPTY_SIZE + ObjectSizes.sizeOnHeapExcludingData(value) + 
(path == null ? 0 : path.unsharedHeapSizeExcludingData());
      }
 -
 -    /**
 -     * The serialization format for cell is:
 -     *     [ flags ][ timestamp ][ deletion time ][    ttl    ][ path size ][ 
path ][ value size ][ value ]
 -     *     [   1b  ][ 8b (vint) ][   4b (vint)   ][ 4b (vint) ][ 4b (vint) ][ 
 arb ][  4b (vint) ][  arb  ]
 -     *
 -     * where not all field are always present (in fact, only the [ flags ] 
are guaranteed to be present). The fields have the following
 -     * meaning:
 -     *   - [ flags ] is the cell flags. It is a byte for which each bit 
represents a flag whose meaning is explained below (*_MASK constants)
 -     *   - [ timestamp ] is the cell timestamp. Present unless the cell has 
the USE_TIMESTAMP_MASK.
 -     *   - [ deletion time]: the local deletion time for the cell. Present if 
either the cell is deleted (IS_DELETED_MASK)
 -     *       or it is expiring (IS_EXPIRING_MASK) but doesn't have the 
USE_ROW_TTL_MASK.
 -     *   - [ ttl ]: the ttl for the cell. Present if the row is expiring 
(IS_EXPIRING_MASK) but doesn't have the
 -     *       USE_ROW_TTL_MASK.
 -     *   - [ value size ] is the size of the [ value ] field. It's present 
unless either the cell has the HAS_EMPTY_VALUE_MASK, or the value
 -     *       for columns of this type have a fixed length.
 -     *   - [ path size ] is the size of the [ path ] field. Present iff this 
is the cell of a complex column.
 -     *   - [ value ]: the cell value, unless it has the HAS_EMPTY_VALUE_MASK.
 -     *   - [ path ]: the cell path if the column this is a cell of is complex.
 -     */
 -    static class Serializer implements Cell.Serializer
 -    {
 -        private final static int IS_DELETED_MASK             = 0x01; // 
Whether the cell is a tombstone or not.
 -        private final static int IS_EXPIRING_MASK            = 0x02; // 
Whether the cell is expiring.
 -        private final static int HAS_EMPTY_VALUE_MASK        = 0x04; // 
Wether the cell has an empty value. This will be the case for tombstone in 
particular.
 -        private final static int USE_ROW_TIMESTAMP_MASK      = 0x08; // 
Wether the cell has the same timestamp than the row this is a cell of.
 -        private final static int USE_ROW_TTL_MASK            = 0x10; // 
Wether the cell has the same ttl than the row this is a cell of.
 -
 -        public void serialize(Cell cell, ColumnDefinition column, 
DataOutputPlus out, LivenessInfo rowLiveness, SerializationHeader header) 
throws IOException
 -        {
 -            assert cell != null;
 -            boolean hasValue = cell.value().hasRemaining();
 -            boolean isDeleted = cell.isTombstone();
 -            boolean isExpiring = cell.isExpiring();
 -            boolean useRowTimestamp = !rowLiveness.isEmpty() && 
cell.timestamp() == rowLiveness.timestamp();
 -            boolean useRowTTL = isExpiring && rowLiveness.isExpiring() && 
cell.ttl() == rowLiveness.ttl() && cell.localDeletionTime() == 
rowLiveness.localExpirationTime();
 -            int flags = 0;
 -            if (!hasValue)
 -                flags |= HAS_EMPTY_VALUE_MASK;
 -
 -            if (isDeleted)
 -                flags |= IS_DELETED_MASK;
 -            else if (isExpiring)
 -                flags |= IS_EXPIRING_MASK;
 -
 -            if (useRowTimestamp)
 -                flags |= USE_ROW_TIMESTAMP_MASK;
 -            if (useRowTTL)
 -                flags |= USE_ROW_TTL_MASK;
 -
 -            out.writeByte((byte)flags);
 -
 -            if (!useRowTimestamp)
 -                header.writeTimestamp(cell.timestamp(), out);
 -
 -            if ((isDeleted || isExpiring) && !useRowTTL)
 -                header.writeLocalDeletionTime(cell.localDeletionTime(), out);
 -            if (isExpiring && !useRowTTL)
 -                header.writeTTL(cell.ttl(), out);
 -
 -            if (column.isComplex())
 -                column.cellPathSerializer().serialize(cell.path(), out);
 -
 -            if (hasValue)
 -                header.getType(column).writeValue(cell.value(), out);
 -        }
 -
 -        public Cell deserialize(DataInputPlus in, LivenessInfo rowLiveness, 
ColumnDefinition column, SerializationHeader header, SerializationHelper 
helper) throws IOException
 -        {
 -            int flags = in.readUnsignedByte();
 -            boolean hasValue = (flags & HAS_EMPTY_VALUE_MASK) == 0;
 -            boolean isDeleted = (flags & IS_DELETED_MASK) != 0;
 -            boolean isExpiring = (flags & IS_EXPIRING_MASK) != 0;
 -            boolean useRowTimestamp = (flags & USE_ROW_TIMESTAMP_MASK) != 0;
 -            boolean useRowTTL = (flags & USE_ROW_TTL_MASK) != 0;
 -
 -            long timestamp = useRowTimestamp ? rowLiveness.timestamp() : 
header.readTimestamp(in);
 -
 -            int localDeletionTime = useRowTTL
 -                                  ? rowLiveness.localExpirationTime()
 -                                  : (isDeleted || isExpiring ? 
header.readLocalDeletionTime(in) : NO_DELETION_TIME);
 -
 -            int ttl = useRowTTL ? rowLiveness.ttl() : (isExpiring ? 
header.readTTL(in) : NO_TTL);
 -
 -            CellPath path = column.isComplex()
 -                          ? column.cellPathSerializer().deserialize(in)
 -                          : null;
 -
 -            boolean isCounter = localDeletionTime == NO_DELETION_TIME && 
column.type.isCounter();
 -
 -            ByteBuffer value = ByteBufferUtil.EMPTY_BYTE_BUFFER;
 -            if (hasValue)
 -            {
 -                if (helper.canSkipValue(column) || (path != null && 
helper.canSkipValue(path)))
 -                {
 -                    header.getType(column).skipValue(in);
 -                }
 -                else
 -                {
 -                    value = header.getType(column).readValue(in, 
DatabaseDescriptor.getMaxValueSize());
 -                    if (isCounter)
 -                        value = helper.maybeClearCounterValue(value);
 -                }
 -            }
 -
 -            return new BufferCell(column, timestamp, ttl, localDeletionTime, 
value, path);
 -        }
 -
 -        public long serializedSize(Cell cell, ColumnDefinition column, 
LivenessInfo rowLiveness, SerializationHeader header)
 -        {
 -            long size = 1; // flags
 -            boolean hasValue = cell.value().hasRemaining();
 -            boolean isDeleted = cell.isTombstone();
 -            boolean isExpiring = cell.isExpiring();
 -            boolean useRowTimestamp = !rowLiveness.isEmpty() && 
cell.timestamp() == rowLiveness.timestamp();
 -            boolean useRowTTL = isExpiring && rowLiveness.isExpiring() && 
cell.ttl() == rowLiveness.ttl() && cell.localDeletionTime() == 
rowLiveness.localExpirationTime();
 -
 -            if (!useRowTimestamp)
 -                size += header.timestampSerializedSize(cell.timestamp());
 -
 -            if ((isDeleted || isExpiring) && !useRowTTL)
 -                size += 
header.localDeletionTimeSerializedSize(cell.localDeletionTime());
 -            if (isExpiring && !useRowTTL)
 -                size += header.ttlSerializedSize(cell.ttl());
 -
 -            if (column.isComplex())
 -                size += 
column.cellPathSerializer().serializedSize(cell.path());
 -
 -            if (hasValue)
 -                size += header.getType(column).writtenLength(cell.value());
 -
 -            return size;
 -        }
 -
 -        // Returns if the skipped cell was an actual cell (i.e. it had its 
presence flag).
 -        public boolean skip(DataInputPlus in, ColumnDefinition column, 
SerializationHeader header) throws IOException
 -        {
 -            int flags = in.readUnsignedByte();
 -            boolean hasValue = (flags & HAS_EMPTY_VALUE_MASK) == 0;
 -            boolean isDeleted = (flags & IS_DELETED_MASK) != 0;
 -            boolean isExpiring = (flags & IS_EXPIRING_MASK) != 0;
 -            boolean useRowTimestamp = (flags & USE_ROW_TIMESTAMP_MASK) != 0;
 -            boolean useRowTTL = (flags & USE_ROW_TTL_MASK) != 0;
 -
 -            if (!useRowTimestamp)
 -                header.skipTimestamp(in);
 -
 -            if (!useRowTTL && (isDeleted || isExpiring))
 -                header.skipLocalDeletionTime(in);
 -
 -            if (!useRowTTL && isExpiring)
 -                header.skipTTL(in);
 -
 -            if (column.isComplex())
 -                column.cellPathSerializer().skip(in);
 -
 -            if (hasValue)
 -                header.getType(column).skipValue(in);
--
 -            return true;
 -        }
 -    }
  }

http://git-wip-us.apache.org/repos/asf/cassandra/blob/1f014b2c/src/java/org/apache/cassandra/db/rows/Cell.java
----------------------------------------------------------------------
diff --cc src/java/org/apache/cassandra/db/rows/Cell.java
index 0b7c46e,d10cc74..19d1f30
--- a/src/java/org/apache/cassandra/db/rows/Cell.java
+++ b/src/java/org/apache/cassandra/db/rows/Cell.java
@@@ -144,165 -143,15 +144,165 @@@ public abstract class Cell extends Colu
      // Overrides super type to provide a more precise return type.
      public abstract Cell purge(DeletionPurger purger, int nowInSec);
  
 -    public interface Serializer
 +    /**
 +     * The serialization format for cell is:
 +     *     [ flags ][ timestamp ][ deletion time ][    ttl    ][ path size ][ 
path ][ value size ][ value ]
 +     *     [   1b  ][ 8b (vint) ][   4b (vint)   ][ 4b (vint) ][ 4b (vint) ][ 
 arb ][  4b (vint) ][  arb  ]
 +     *
 +     * where not all field are always present (in fact, only the [ flags ] 
are guaranteed to be present). The fields have the following
 +     * meaning:
 +     *   - [ flags ] is the cell flags. It is a byte for which each bit 
represents a flag whose meaning is explained below (*_MASK constants)
 +     *   - [ timestamp ] is the cell timestamp. Present unless the cell has 
the USE_TIMESTAMP_MASK.
 +     *   - [ deletion time]: the local deletion time for the cell. Present if 
either the cell is deleted (IS_DELETED_MASK)
 +     *       or it is expiring (IS_EXPIRING_MASK) but doesn't have the 
USE_ROW_TTL_MASK.
 +     *   - [ ttl ]: the ttl for the cell. Present if the row is expiring 
(IS_EXPIRING_MASK) but doesn't have the
 +     *       USE_ROW_TTL_MASK.
 +     *   - [ value size ] is the size of the [ value ] field. It's present 
unless either the cell has the HAS_EMPTY_VALUE_MASK, or the value
 +     *       for columns of this type have a fixed length.
 +     *   - [ path size ] is the size of the [ path ] field. Present iff this 
is the cell of a complex column.
 +     *   - [ value ]: the cell value, unless it has the HAS_EMPTY_VALUE_MASK.
 +     *   - [ path ]: the cell path if the column this is a cell of is complex.
 +     */
 +    static class Serializer
      {
 -        public void serialize(Cell cell, ColumnDefinition column, 
DataOutputPlus out, LivenessInfo rowLiveness, SerializationHeader header) 
throws IOException;
 +        private final static int IS_DELETED_MASK             = 0x01; // 
Whether the cell is a tombstone or not.
 +        private final static int IS_EXPIRING_MASK            = 0x02; // 
Whether the cell is expiring.
 +        private final static int HAS_EMPTY_VALUE_MASK        = 0x04; // 
Wether the cell has an empty value. This will be the case for tombstone in 
particular.
 +        private final static int USE_ROW_TIMESTAMP_MASK      = 0x08; // 
Wether the cell has the same timestamp than the row this is a cell of.
 +        private final static int USE_ROW_TTL_MASK            = 0x10; // 
Wether the cell has the same ttl than the row this is a cell of.
 +
-         public void serialize(Cell cell, DataOutputPlus out, LivenessInfo 
rowLiveness, SerializationHeader header) throws IOException
++        public void serialize(Cell cell, ColumnDefinition column, 
DataOutputPlus out, LivenessInfo rowLiveness, SerializationHeader header) 
throws IOException
 +        {
 +            assert cell != null;
 +            boolean hasValue = cell.value().hasRemaining();
 +            boolean isDeleted = cell.isTombstone();
 +            boolean isExpiring = cell.isExpiring();
 +            boolean useRowTimestamp = !rowLiveness.isEmpty() && 
cell.timestamp() == rowLiveness.timestamp();
 +            boolean useRowTTL = isExpiring && rowLiveness.isExpiring() && 
cell.ttl() == rowLiveness.ttl() && cell.localDeletionTime() == 
rowLiveness.localExpirationTime();
 +            int flags = 0;
 +            if (!hasValue)
 +                flags |= HAS_EMPTY_VALUE_MASK;
 +
 +            if (isDeleted)
 +                flags |= IS_DELETED_MASK;
 +            else if (isExpiring)
 +                flags |= IS_EXPIRING_MASK;
 +
 +            if (useRowTimestamp)
 +                flags |= USE_ROW_TIMESTAMP_MASK;
 +            if (useRowTTL)
 +                flags |= USE_ROW_TTL_MASK;
 +
 +            out.writeByte((byte)flags);
 +
 +            if (!useRowTimestamp)
 +                header.writeTimestamp(cell.timestamp(), out);
 +
 +            if ((isDeleted || isExpiring) && !useRowTTL)
 +                header.writeLocalDeletionTime(cell.localDeletionTime(), out);
 +            if (isExpiring && !useRowTTL)
 +                header.writeTTL(cell.ttl(), out);
 +
-             if (cell.column().isComplex())
-                 cell.column().cellPathSerializer().serialize(cell.path(), 
out);
++            if (column.isComplex())
++                column.cellPathSerializer().serialize(cell.path(), out);
 +
 +            if (hasValue)
-                 header.getType(cell.column()).writeValue(cell.value(), out);
++                header.getType(column).writeValue(cell.value(), out);
 +        }
 +
 +        public Cell deserialize(DataInputPlus in, LivenessInfo rowLiveness, 
ColumnDefinition column, SerializationHeader header, SerializationHelper 
helper) throws IOException
 +        {
 +            int flags = in.readUnsignedByte();
 +            boolean hasValue = (flags & HAS_EMPTY_VALUE_MASK) == 0;
 +            boolean isDeleted = (flags & IS_DELETED_MASK) != 0;
 +            boolean isExpiring = (flags & IS_EXPIRING_MASK) != 0;
 +            boolean useRowTimestamp = (flags & USE_ROW_TIMESTAMP_MASK) != 0;
 +            boolean useRowTTL = (flags & USE_ROW_TTL_MASK) != 0;
 +
 +            long timestamp = useRowTimestamp ? rowLiveness.timestamp() : 
header.readTimestamp(in);
 +
 +            int localDeletionTime = useRowTTL
 +                                    ? rowLiveness.localExpirationTime()
 +                                    : (isDeleted || isExpiring ? 
header.readLocalDeletionTime(in) : NO_DELETION_TIME);
 +
 +            int ttl = useRowTTL ? rowLiveness.ttl() : (isExpiring ? 
header.readTTL(in) : NO_TTL);
  
 -        public Cell deserialize(DataInputPlus in, LivenessInfo rowLiveness, 
ColumnDefinition column, SerializationHeader header, SerializationHelper 
helper) throws IOException;
 +            CellPath path = column.isComplex()
 +                            ? column.cellPathSerializer().deserialize(in)
 +                            : null;
  
 -        public long serializedSize(Cell cell, ColumnDefinition column, 
LivenessInfo rowLiveness, SerializationHeader header);
 +            ByteBuffer value = ByteBufferUtil.EMPTY_BYTE_BUFFER;
 +            if (hasValue)
 +            {
 +                if (helper.canSkipValue(column) || (path != null && 
helper.canSkipValue(path)))
 +                {
 +                    header.getType(column).skipValue(in);
 +                }
 +                else
 +                {
 +                    boolean isCounter = localDeletionTime == NO_DELETION_TIME 
&& column.type.isCounter();
 +
 +                    value = header.getType(column).readValue(in, 
DatabaseDescriptor.getMaxValueSize());
 +                    if (isCounter)
 +                        value = helper.maybeClearCounterValue(value);
 +                }
 +            }
 +
 +            return new BufferCell(column, timestamp, ttl, localDeletionTime, 
value, path);
 +        }
 +
-         public long serializedSize(Cell cell, LivenessInfo rowLiveness, 
SerializationHeader header)
++        public long serializedSize(Cell cell, ColumnDefinition column, 
LivenessInfo rowLiveness, SerializationHeader header)
 +        {
 +            long size = 1; // flags
 +            boolean hasValue = cell.value().hasRemaining();
 +            boolean isDeleted = cell.isTombstone();
 +            boolean isExpiring = cell.isExpiring();
 +            boolean useRowTimestamp = !rowLiveness.isEmpty() && 
cell.timestamp() == rowLiveness.timestamp();
 +            boolean useRowTTL = isExpiring && rowLiveness.isExpiring() && 
cell.ttl() == rowLiveness.ttl() && cell.localDeletionTime() == 
rowLiveness.localExpirationTime();
 +
 +            if (!useRowTimestamp)
 +                size += header.timestampSerializedSize(cell.timestamp());
 +
 +            if ((isDeleted || isExpiring) && !useRowTTL)
 +                size += 
header.localDeletionTimeSerializedSize(cell.localDeletionTime());
 +            if (isExpiring && !useRowTTL)
 +                size += header.ttlSerializedSize(cell.ttl());
 +
-             if (cell.column().isComplex())
-                 size += 
cell.column().cellPathSerializer().serializedSize(cell.path());
++            if (column.isComplex())
++                size += 
column.cellPathSerializer().serializedSize(cell.path());
 +
 +            if (hasValue)
-                 size += 
header.getType(cell.column()).writtenLength(cell.value());
++                size += header.getType(column).writtenLength(cell.value());
 +
 +            return size;
 +        }
  
          // Returns if the skipped cell was an actual cell (i.e. it had its 
presence flag).
 -        public boolean skip(DataInputPlus in, ColumnDefinition column, 
SerializationHeader header) throws IOException;
 +        public boolean skip(DataInputPlus in, ColumnDefinition column, 
SerializationHeader header) throws IOException
 +        {
 +            int flags = in.readUnsignedByte();
 +            boolean hasValue = (flags & HAS_EMPTY_VALUE_MASK) == 0;
 +            boolean isDeleted = (flags & IS_DELETED_MASK) != 0;
 +            boolean isExpiring = (flags & IS_EXPIRING_MASK) != 0;
 +            boolean useRowTimestamp = (flags & USE_ROW_TIMESTAMP_MASK) != 0;
 +            boolean useRowTTL = (flags & USE_ROW_TTL_MASK) != 0;
 +
 +            if (!useRowTimestamp)
 +                header.skipTimestamp(in);
 +
 +            if (!useRowTTL && (isDeleted || isExpiring))
 +                header.skipLocalDeletionTime(in);
 +
 +            if (!useRowTTL && isExpiring)
 +                header.skipTTL(in);
 +
 +            if (column.isComplex())
 +                column.cellPathSerializer().skip(in);
 +
 +            if (hasValue)
 +                header.getType(column).skipValue(in);
 +
 +            return true;
 +        }
      }
  }

http://git-wip-us.apache.org/repos/asf/cassandra/blob/1f014b2c/src/java/org/apache/cassandra/db/rows/UnfilteredSerializer.java
----------------------------------------------------------------------
diff --cc src/java/org/apache/cassandra/db/rows/UnfilteredSerializer.java
index 890806c,dc6f187..5ca7e03
--- a/src/java/org/apache/cassandra/db/rows/UnfilteredSerializer.java
+++ b/src/java/org/apache/cassandra/db/rows/UnfilteredSerializer.java
@@@ -25,8 -24,8 +25,9 @@@ import net.nicoulaj.compilecommand.anno
  import org.apache.cassandra.config.ColumnDefinition;
  import org.apache.cassandra.db.*;
  import org.apache.cassandra.io.util.DataInputPlus;
 +import org.apache.cassandra.io.util.DataOutputBuffer;
  import org.apache.cassandra.io.util.DataOutputPlus;
+ import org.apache.cassandra.utils.SearchIterator;
  
  /**
   * Serialize/deserialize a single Unfiltered (both on-wire and on-disk).
@@@ -222,15 -175,24 +223,24 @@@ public class UnfilteredSerialize
          if ((flags & HAS_DELETION) != 0)
              header.writeDeletionTime(deletion.time(), out);
  
 -        if (!hasAllColumns)
 +        if ((flags & HAS_ALL_COLUMNS) == 0)
              Columns.serializer.serializeSubset(Collections2.transform(row, 
ColumnData::column), headerColumns, out);
  
+         SearchIterator<ColumnDefinition, ColumnDefinition> si = 
headerColumns.iterator();
          for (ColumnData data : row)
          {
+             // We can obtain the column for data directly from data.column(). 
However, if the cell/complex data
+             // originates from a sstable, the column we'll get will have the 
type used when the sstable was serialized,
+             // and if that type have been recently altered, that may not be 
the type we want to serialize the column
+             // with. So we use the ColumnDefinition from the "header" which 
is "current". Also see #11810 for what
+             // happens if we don't do that.
+             ColumnDefinition column = si.next(data.column());
+             assert column != null;
+ 
              if (data.column.isSimple())
-                 Cell.serializer.serialize((Cell) data, out, pkLiveness, 
header);
+                 Cell.serializer.serialize((Cell) data, column, out, 
pkLiveness, header);
              else
-                 writeComplexColumn((ComplexColumnData) data, (flags & 
HAS_COMPLEX_DELETION) != 0, pkLiveness, header, out);
 -                writeComplexColumn((ComplexColumnData) data, column, 
hasComplexDeletion, pkLiveness, header, out);
++                writeComplexColumn((ComplexColumnData) data, column, (flags & 
HAS_COMPLEX_DELETION) != 0, pkLiveness, header, out);
          }
      }
  

http://git-wip-us.apache.org/repos/asf/cassandra/blob/1f014b2c/test/unit/org/apache/cassandra/cql3/CQLTester.java
----------------------------------------------------------------------

http://git-wip-us.apache.org/repos/asf/cassandra/blob/1f014b2c/test/unit/org/apache/cassandra/cql3/validation/operations/AlterTest.java
----------------------------------------------------------------------
diff --cc 
test/unit/org/apache/cassandra/cql3/validation/operations/AlterTest.java
index 3eb55fd,509aeac..bb4bf48
--- a/test/unit/org/apache/cassandra/cql3/validation/operations/AlterTest.java
+++ b/test/unit/org/apache/cassandra/cql3/validation/operations/AlterTest.java
@@@ -325,84 -323,19 +323,100 @@@ public class AlterTest extends CQLTeste
          assertInvalidThrow(InvalidRequestException.class, "ALTER TABLE %s 
ALTER column1 TYPE ascii");
      }
  
 +    /*
 +     * Test case to check addition of one column
 +    */
 +    @Test
 +    public void testAlterAddOneColumn() throws Throwable
 +    {
 +        createTable("CREATE TABLE IF NOT EXISTS %s (id int, name text, 
PRIMARY KEY (id))");
 +        alterTable("ALTER TABLE %s add mail text;");
 +
 +        assertColumnNames(execute("SELECT * FROM %s"), "id", "mail", "name");
 +    }
 +
 +    /*
 +     * Test case to check addition of more than one column
 +     */
 +    @Test
 +    public void testAlterAddMultiColumn() throws Throwable
 +    {
 +        createTable("CREATE TABLE IF NOT EXISTS %s (id int, yearofbirth int, 
PRIMARY KEY (id))");
 +        alterTable("ALTER TABLE %s add (firstname text, password blob, 
lastname text, \"SOME escaped col\" bigint)");
 +
 +        assertColumnNames(execute("SELECT * FROM %s"), "id", "SOME escaped 
col", "firstname", "lastname", "password", "yearofbirth");
 +    }
 +
 +    /*
 +     *  Should throw SyntaxException if multiple columns are added using 
wrong syntax.
 +     *  Expected Syntax : Alter table T1 add (C1 datatype,C2 datatype,C3 
datatype)
 +     */
 +    @Test(expected = SyntaxException.class)
 +    public void testAlterAddMultiColumnWithoutBraces() throws Throwable
 +    {
 +        execute("ALTER TABLE %s.users add lastname text, password blob, 
yearofbirth int;");
 +    }
 +
 +    /*
 +     *  Test case to check deletion of one column
 +     */
 +    @Test
 +    public void testAlterDropOneColumn() throws Throwable
 +    {
 +        createTable("CREATE TABLE IF NOT EXISTS %s (id text, telephone int, 
yearofbirth int, PRIMARY KEY (id))");
 +        alterTable("ALTER TABLE %s drop telephone");
 +
 +        assertColumnNames(execute("SELECT * FROM %s"), "id", "yearofbirth");
 +    }
 +
 +    @Test
 +    /*
 +     * Test case to check deletion of more than one column
 +     */
 +    public void testAlterDropMultiColumn() throws Throwable
 +    {
 +        createTable("CREATE TABLE IF NOT EXISTS %s (id text, address text, 
telephone int, yearofbirth int, \"SOME escaped col\" bigint, PRIMARY KEY 
(id))");
 +        alterTable("ALTER TABLE %s drop (address, telephone, \"SOME escaped 
col\");");
 +
 +        assertColumnNames(execute("SELECT * FROM %s"), "id", "yearofbirth");
 +    }
 +
 +    /*
 +     *  Should throw SyntaxException if multiple columns are dropped using 
wrong syntax.
 +     */
 +    @Test(expected = SyntaxException.class)
 +    public void testAlterDeletionColumnWithoutBraces() throws Throwable
 +    {
 +        execute("ALTER TABLE %s.users drop name,address;");
 +    }
 +
 +    @Test(expected = InvalidRequestException.class)
 +    public void testAlterAddDuplicateColumn() throws Throwable
 +    {
 +        createTable("CREATE TABLE IF NOT EXISTS %s (id text, address text, 
telephone int, yearofbirth int, PRIMARY KEY (id))");
 +        execute("ALTER TABLE %s add (salary int, salary int);");
 +    }
 +
 +    @Test(expected = InvalidRequestException.class)
 +    public void testAlterDropDuplicateColumn() throws Throwable
 +    {
 +        createTable("CREATE TABLE IF NOT EXISTS %s (id text, address text, 
telephone int, yearofbirth int, PRIMARY KEY (id))");
 +        execute("ALTER TABLE %s drop (address, address);");
 +    }
++
+     @Test
+     public void testAlterToBlob() throws Throwable
+     {
+         // This tests for the bug from #11820 in particular
+ 
+         createTable("CREATE TABLE %s (a int PRIMARY KEY, b int)");
+ 
+         execute("INSERT INTO %s (a, b) VALUES (1, 1)");
+ 
+         executeNet(Server.CURRENT_VERSION, "ALTER TABLE %s ALTER b TYPE 
BLOB");
+ 
+         assertRowsNet(Server.CURRENT_VERSION, 
executeNet(Server.CURRENT_VERSION, "SELECT * FROM %s WHERE a = 1"),
+             row(1, ByteBufferUtil.bytes(1))
+         );
+     }
  }

Reply via email to