You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@cassandra.apache.org by sn...@apache.org on 2016/02/25 16:50:22 UTC

[2/3] cassandra git commit: Failed aggregate creation breaks server permanently

Failed aggregate creation breaks server permanently

patch by Robert Stupp; reviewed by Sylvain Lebresne for CASSANDRA-11064


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

Branch: refs/heads/trunk
Commit: c6ed2e0bb30b9e96764858b93bd2021d195be510
Parents: 71b1c4a
Author: Robert Stupp <sn...@snazy.de>
Authored: Thu Feb 25 07:47:21 2016 -0800
Committer: Robert Stupp <sn...@snazy.de>
Committed: Thu Feb 25 07:49:01 2016 -0800

----------------------------------------------------------------------
 CHANGES.txt                                     |   1 +
 .../org/apache/cassandra/cql3/CQL3Type.java     | 238 ++++++++-----------
 .../org/apache/cassandra/cql3/Constants.java    |   9 +-
 .../statements/CreateAggregateStatement.java    |  21 ++
 .../apache/cassandra/schema/SchemaKeyspace.java |   2 +-
 .../serializers/AbstractTextSerializer.java     |  30 +--
 .../cassandra/serializers/BytesSerializer.java  |  14 +-
 .../serializers/TimestampSerializer.java        |   9 +-
 .../cassandra/serializers/TypeSerializer.java   |   9 +-
 .../cassandra/cql3/CQL3TypeLiteralTest.java     |  88 +++----
 .../validation/operations/AggregationTest.java  |  72 ++++++
 11 files changed, 250 insertions(+), 243 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/cassandra/blob/c6ed2e0b/CHANGES.txt
----------------------------------------------------------------------
diff --git a/CHANGES.txt b/CHANGES.txt
index aefc02e..bdcf328 100644
--- a/CHANGES.txt
+++ b/CHANGES.txt
@@ -1,4 +1,5 @@
 3.0.4
+ * Failed aggregate creation breaks server permanently (CASSANDRA-11064)
  * Add sstabledump tool (CASSANDRA-7464)
  * Introduce backpressure for hints (CASSANDRA-10972)
  * Fix ClusteringPrefix not being able to read tombstone range boundaries (CASSANDRA-11158)

http://git-wip-us.apache.org/repos/asf/cassandra/blob/c6ed2e0b/src/java/org/apache/cassandra/cql3/CQL3Type.java
----------------------------------------------------------------------
diff --git a/src/java/org/apache/cassandra/cql3/CQL3Type.java b/src/java/org/apache/cassandra/cql3/CQL3Type.java
index 4e67346..95524d9 100644
--- a/src/java/org/apache/cassandra/cql3/CQL3Type.java
+++ b/src/java/org/apache/cassandra/cql3/CQL3Type.java
@@ -43,21 +43,13 @@ public interface CQL3Type
     public AbstractType<?> getType();
 
     /**
-     * Generate CQL literal from this type's serialized representation using the specified protocol version.
-     * Convinience method for {@link #toCQLLiteral(ByteBuffer, int, StringBuilder)} that just returns a {@code String}.
+     * Generates CQL literal from a binary value of this type.
+     *
+     * @param buffer the value to convert to a CQL literal. This value must be
+     * serialized with {@code version} of the native protocol.
+     * @param version the native protocol version in which {@code buffer} is encoded.
      */
-    public default String asCQLLiteral(ByteBuffer buffer, int version)
-    {
-        StringBuilder sb = new StringBuilder();
-        toCQLLiteral(buffer, version, sb);
-        return sb.toString();
-    }
-
-    /**
-     * Generate CQL literal from this type's serialized representation using the specified protocol version.
-     * Some work is delegated to {@link org.apache.cassandra.serializers.TypeSerializer#toCQLLiteral(ByteBuffer, StringBuilder)}.
-     */
-    public void toCQLLiteral(ByteBuffer buffer, int version, StringBuilder target);
+    public String toCQLLiteral(ByteBuffer buffer, int version);
 
     public enum Native implements CQL3Type
     {
@@ -102,14 +94,14 @@ public interface CQL3Type
 
         /**
          * Delegate to
-         * {@link org.apache.cassandra.serializers.TypeSerializer#toCQLLiteral(ByteBuffer, StringBuilder)}
+         * {@link org.apache.cassandra.serializers.TypeSerializer#toCQLLiteral(ByteBuffer)}
          * for native types as most CQL literal representations work fine with the default
          * {@link org.apache.cassandra.serializers.TypeSerializer#toString(Object)}
          * {@link org.apache.cassandra.serializers.TypeSerializer#deserialize(ByteBuffer)} implementations.
          */
-        public void toCQLLiteral(ByteBuffer buffer, int version, StringBuilder target)
+        public String toCQLLiteral(ByteBuffer buffer, int version)
         {
-            type.getSerializer().toCQLLiteral(buffer, target);
+            return type.getSerializer().toCQLLiteral(buffer);
         }
 
         @Override
@@ -143,12 +135,10 @@ public interface CQL3Type
             return type;
         }
 
-        public void toCQLLiteral(ByteBuffer buffer, int version, StringBuilder target)
+        public String toCQLLiteral(ByteBuffer buffer, int version)
         {
-            if (buffer == null)
-                target.append("null");
-            else
-                target.append(type.getString(buffer));
+            // *always* use the 'blob' syntax to express custom types in CQL
+            return Native.BLOB.toCQLLiteral(buffer, version);
         }
 
         @Override
@@ -193,59 +183,36 @@ public interface CQL3Type
             return true;
         }
 
-        public void toCQLLiteral(ByteBuffer buffer, int version, StringBuilder target)
+        public String toCQLLiteral(ByteBuffer buffer, int version)
         {
-            // Not sure whether the !buffer.hasRemaining() check is correct here or whether an empty
-            // BB should be returned as "[]" resp "{}" or whether it is not valid at all.
-            //
-            // Currently, all empty collections return '[]' or '{}'. Except frozen collections with
-            // a null BB return 'null'.
-            //
-            if (buffer == null || !buffer.hasRemaining())
-            {
-                if (buffer == null && type.isFrozenCollection())
-                {
-                    target.append("null");
-                }
-                else
-                {
-                    switch (type.kind)
-                    {
-                        case LIST:
-                            target.append("[]");
-                            break;
-                        case SET:
-                        case MAP:
-                            target.append("{}");
-                            break;
-                    }
-                }
-            }
-            else
-            {
-                int size = CollectionSerializer.readCollectionSize(buffer, version);
+            if (buffer == null)
+                return "null";
 
-                switch (type.kind)
-                {
-                    case LIST:
-                        CQL3Type elements = ((ListType) type).getElementsType().asCQL3Type();
-                        target.append('[');
-                        generateSetOrListCQLLiteral(buffer, version, target, size, elements);
-                        target.append(']');
-                        break;
-                    case SET:
-                        elements = ((SetType) type).getElementsType().asCQL3Type();
-                        target.append('{');
-                        generateSetOrListCQLLiteral(buffer, version, target, size, elements);
-                        target.append('}');
-                        break;
-                    case MAP:
-                        target.append('{');
-                        generateMapCQLLiteral(buffer, version, target, size);
-                        target.append('}');
-                        break;
-                }
+            StringBuilder target = new StringBuilder();
+            buffer = buffer.duplicate();
+            int size = CollectionSerializer.readCollectionSize(buffer, version);
+
+            switch (type.kind)
+            {
+                case LIST:
+                    CQL3Type elements = ((ListType) type).getElementsType().asCQL3Type();
+                    target.append('[');
+                    generateSetOrListCQLLiteral(buffer, version, target, size, elements);
+                    target.append(']');
+                    break;
+                case SET:
+                    elements = ((SetType) type).getElementsType().asCQL3Type();
+                    target.append('{');
+                    generateSetOrListCQLLiteral(buffer, version, target, size, elements);
+                    target.append('}');
+                    break;
+                case MAP:
+                    target.append('{');
+                    generateMapCQLLiteral(buffer, version, target, size);
+                    target.append('}');
+                    break;
             }
+            return target.toString();
         }
 
         private void generateMapCQLLiteral(ByteBuffer buffer, int version, StringBuilder target, int size)
@@ -257,10 +224,10 @@ public interface CQL3Type
                 if (i > 0)
                     target.append(", ");
                 ByteBuffer element = CollectionSerializer.readValue(buffer, version);
-                keys.toCQLLiteral(element, version, target);
+                target.append(keys.toCQLLiteral(element, version));
                 target.append(": ");
                 element = CollectionSerializer.readValue(buffer, version);
-                values.toCQLLiteral(element, version, target);
+                target.append(values.toCQLLiteral(element, version));
             }
         }
 
@@ -271,7 +238,7 @@ public interface CQL3Type
                 if (i > 0)
                     target.append(", ");
                 ByteBuffer element = CollectionSerializer.readValue(buffer, version);
-                elements.toCQLLiteral(element, version, target);
+                target.append(elements.toCQLLiteral(element, version));
             }
         }
 
@@ -348,47 +315,47 @@ public interface CQL3Type
             return type;
         }
 
-        public void toCQLLiteral(ByteBuffer buffer, int version, StringBuilder target)
+        public String toCQLLiteral(ByteBuffer buffer, int version)
         {
             if (buffer == null)
-            {
-                target.append("null");
-            }
-            else
-            {
-                target.append('{');
-                for (int i = 0; i < type.size(); i++)
-                {
-                    // we allow the input to have less fields than declared so as to support field addition.
-                    if (!buffer.hasRemaining())
-                        break;
+                return "null";
 
-                    if (buffer.remaining() < 4)
-                        throw new MarshalException(String.format("Not enough bytes to read size of %dth field %s", i, type.fieldName(i)));
 
-                    int size = buffer.getInt();
+            StringBuilder target = new StringBuilder();
+            buffer = buffer.duplicate();
+            target.append('{');
+            for (int i = 0; i < type.size(); i++)
+            {
+                // we allow the input to have less fields than declared so as to support field addition.
+                if (!buffer.hasRemaining())
+                    break;
 
-                    if (i > 0)
-                        target.append(", ");
+                if (buffer.remaining() < 4)
+                    throw new MarshalException(String.format("Not enough bytes to read size of %dth field %s", i, type.fieldName(i)));
 
-                    target.append(ColumnIdentifier.maybeQuote(type.fieldNameAsString(i)));
-                    target.append(": ");
+                int size = buffer.getInt();
 
-                    // size < 0 means null value
-                    if (size < 0)
-                    {
-                        target.append("null");
-                        continue;
-                    }
+                if (i > 0)
+                    target.append(", ");
 
-                    if (buffer.remaining() < size)
-                        throw new MarshalException(String.format("Not enough bytes to read %dth field %s", i, type.fieldName(i)));
+                target.append(ColumnIdentifier.maybeQuote(type.fieldNameAsString(i)));
+                target.append(": ");
 
-                    ByteBuffer field = ByteBufferUtil.readBytes(buffer, size);
-                    type.fieldType(i).asCQL3Type().toCQLLiteral(field, version, target);
+                // size < 0 means null value
+                if (size < 0)
+                {
+                    target.append("null");
+                    continue;
                 }
-                target.append('}');
+
+                if (buffer.remaining() < size)
+                    throw new MarshalException(String.format("Not enough bytes to read %dth field %s", i, type.fieldName(i)));
+
+                ByteBuffer field = ByteBufferUtil.readBytes(buffer, size);
+                target.append(type.fieldType(i).asCQL3Type().toCQLLiteral(field, version));
             }
+            target.append('}');
+            return target.toString();
         }
 
         @Override
@@ -438,47 +405,46 @@ public interface CQL3Type
             return type;
         }
 
-        public void toCQLLiteral(ByteBuffer buffer, int version, StringBuilder target)
+        public String toCQLLiteral(ByteBuffer buffer, int version)
         {
             if (buffer == null)
+                return "null";
+
+            StringBuilder target = new StringBuilder();
+            buffer = buffer.duplicate();
+            target.append('(');
+            boolean first = true;
+            for (int i = 0; i < type.size(); i++)
             {
-                target.append("null");
-            }
-            else
-            {
-                target.append('(');
-                boolean first = true;
-                for (int i = 0; i < type.size(); i++)
-                {
-                    // we allow the input to have less fields than declared so as to support field addition.
-                    if (!buffer.hasRemaining())
-                        break;
+                // we allow the input to have less fields than declared so as to support field addition.
+                if (!buffer.hasRemaining())
+                    break;
 
-                    if (buffer.remaining() < 4)
-                        throw new MarshalException(String.format("Not enough bytes to read size of %dth component", i));
+                if (buffer.remaining() < 4)
+                    throw new MarshalException(String.format("Not enough bytes to read size of %dth component", i));
 
-                    int size = buffer.getInt();
+                int size = buffer.getInt();
 
-                    if (first)
-                        first = false;
-                    else
-                        target.append(", ");
+                if (first)
+                    first = false;
+                else
+                    target.append(", ");
 
-                    // size < 0 means null value
-                    if (size < 0)
-                    {
-                        target.append("null");
-                        continue;
-                    }
+                // size < 0 means null value
+                if (size < 0)
+                {
+                    target.append("null");
+                    continue;
+                }
 
-                    if (buffer.remaining() < size)
-                        throw new MarshalException(String.format("Not enough bytes to read %dth component", i));
+                if (buffer.remaining() < size)
+                    throw new MarshalException(String.format("Not enough bytes to read %dth component", i));
 
-                    ByteBuffer field = ByteBufferUtil.readBytes(buffer, size);
-                    type.type(i).asCQL3Type().toCQLLiteral(field, version, target);
-                }
-                target.append(')');
+                ByteBuffer field = ByteBufferUtil.readBytes(buffer, size);
+                target.append(type.type(i).asCQL3Type().toCQLLiteral(field, version));
             }
+            target.append(')');
+            return target.toString();
         }
 
         @Override

http://git-wip-us.apache.org/repos/asf/cassandra/blob/c6ed2e0b/src/java/org/apache/cassandra/cql3/Constants.java
----------------------------------------------------------------------
diff --git a/src/java/org/apache/cassandra/cql3/Constants.java b/src/java/org/apache/cassandra/cql3/Constants.java
index 425dd85..4982c49 100644
--- a/src/java/org/apache/cassandra/cql3/Constants.java
+++ b/src/java/org/apache/cassandra/cql3/Constants.java
@@ -143,9 +143,12 @@ public abstract class Constants
                 validator = ((ReversedType<?>) validator).baseType;
             try
             {
-                // BytesType doesn't want it's input prefixed by '0x'.
-                if (type == Type.HEX && validator instanceof BytesType)
-                    return validator.fromString(text.substring(2));
+                if (type == Type.HEX)
+                    // Note that validator could be BytesType, but it could also be a custom type, so
+                    // we hardcode BytesType (rather than using 'validator') in the call below.
+                    // Further note that BytesType doesn't want it's input prefixed by '0x', hence the substring.
+                    return BytesType.instance.fromString(text.substring(2));
+
                 if (validator instanceof CounterColumnType)
                     return LongType.instance.fromString(text);
                 return validator.fromString(text);

http://git-wip-us.apache.org/repos/asf/cassandra/blob/c6ed2e0b/src/java/org/apache/cassandra/cql3/statements/CreateAggregateStatement.java
----------------------------------------------------------------------
diff --git a/src/java/org/apache/cassandra/cql3/statements/CreateAggregateStatement.java b/src/java/org/apache/cassandra/cql3/statements/CreateAggregateStatement.java
index 50f4f12..7066570 100644
--- a/src/java/org/apache/cassandra/cql3/statements/CreateAggregateStatement.java
+++ b/src/java/org/apache/cassandra/cql3/statements/CreateAggregateStatement.java
@@ -20,6 +20,7 @@ package org.apache.cassandra.cql3.statements;
 import java.nio.ByteBuffer;
 import java.util.ArrayList;
 import java.util.Collections;
+import java.util.Objects;
 import java.util.List;
 
 import org.apache.cassandra.auth.*;
@@ -29,11 +30,13 @@ import org.apache.cassandra.cql3.*;
 import org.apache.cassandra.cql3.functions.*;
 import org.apache.cassandra.db.marshal.AbstractType;
 import org.apache.cassandra.exceptions.*;
+import org.apache.cassandra.serializers.MarshalException;
 import org.apache.cassandra.service.ClientState;
 import org.apache.cassandra.service.MigrationManager;
 import org.apache.cassandra.service.QueryState;
 import org.apache.cassandra.thrift.ThriftValidation;
 import org.apache.cassandra.transport.Event;
+import org.apache.cassandra.transport.Server;
 
 /**
  * A {@code CREATE AGGREGATE} statement parsed from a CQL query.
@@ -111,6 +114,24 @@ public final class CreateAggregateStatement extends SchemaAlteringStatement
         if (ival != null)
         {
             initcond = Terms.asBytes(functionName.keyspace, ival.toString(), stateType);
+
+            if (initcond != null)
+            {
+                try
+                {
+                    stateType.validate(initcond);
+                }
+                catch (MarshalException e)
+                {
+                    throw new InvalidRequestException(String.format("Invalid value for INITCOND of type %s%s", stateType.asCQL3Type(),
+                                                                    e.getMessage() == null ? "" : String.format(" (%s)", e.getMessage())));
+                }
+            }
+
+            // Sanity check that converts the initcond to a CQL literal and parse it back to avoid getting in CASSANDRA-11064.
+            String initcondAsCql = stateType.asCQL3Type().toCQLLiteral(initcond, Server.CURRENT_VERSION);
+            assert Objects.equals(initcond, Terms.asBytes(functionName.keyspace, initcondAsCql, stateType));
+
             if (Constants.NULL_LITERAL != ival && UDHelper.isNullOrEmpty(stateType, initcond))
                 throw new InvalidRequestException("INITCOND must not be empty for all types except TEXT, ASCII, BLOB");
         }

http://git-wip-us.apache.org/repos/asf/cassandra/blob/c6ed2e0b/src/java/org/apache/cassandra/schema/SchemaKeyspace.java
----------------------------------------------------------------------
diff --git a/src/java/org/apache/cassandra/schema/SchemaKeyspace.java b/src/java/org/apache/cassandra/schema/SchemaKeyspace.java
index 9e05a73..6e9d44b 100644
--- a/src/java/org/apache/cassandra/schema/SchemaKeyspace.java
+++ b/src/java/org/apache/cassandra/schema/SchemaKeyspace.java
@@ -845,7 +845,7 @@ public final class SchemaKeyspace
              .add("final_func", aggregate.finalFunction() != null ? aggregate.finalFunction().name().name : null)
              .add("initcond", aggregate.initialCondition() != null
                               // must use the frozen state type here, as 'null' for unfrozen collections may mean 'empty'
-                              ? aggregate.stateType().freeze().asCQL3Type().asCQLLiteral(aggregate.initialCondition(), Server.CURRENT_VERSION)
+                              ? aggregate.stateType().freeze().asCQL3Type().toCQLLiteral(aggregate.initialCondition(), Server.CURRENT_VERSION)
                               : null)
              .build();
     }

http://git-wip-us.apache.org/repos/asf/cassandra/blob/c6ed2e0b/src/java/org/apache/cassandra/serializers/AbstractTextSerializer.java
----------------------------------------------------------------------
diff --git a/src/java/org/apache/cassandra/serializers/AbstractTextSerializer.java b/src/java/org/apache/cassandra/serializers/AbstractTextSerializer.java
index 4d2694e..7a3afed 100644
--- a/src/java/org/apache/cassandra/serializers/AbstractTextSerializer.java
+++ b/src/java/org/apache/cassandra/serializers/AbstractTextSerializer.java
@@ -17,12 +17,14 @@
  */
 package org.apache.cassandra.serializers;
 
-import org.apache.cassandra.utils.ByteBufferUtil;
-
 import java.nio.ByteBuffer;
 import java.nio.charset.CharacterCodingException;
 import java.nio.charset.Charset;
 
+import org.apache.commons.lang3.StringUtils;
+
+import org.apache.cassandra.utils.ByteBufferUtil;
+
 public abstract class AbstractTextSerializer implements TypeSerializer<String>
 {
     private final Charset charset;
@@ -64,26 +66,10 @@ public abstract class AbstractTextSerializer implements TypeSerializer<String>
      * Caveat: it does only generate literals with single quotes and not pg-style literals.
      */
     @Override
-    public void toCQLLiteral(ByteBuffer buffer, StringBuilder target)
+    public String toCQLLiteral(ByteBuffer buffer)
     {
-        if (buffer == null)
-        {
-            target.append("null");
-        }
-        else
-        {
-            String s = deserialize(buffer);
-
-            target.append('\'');
-            for (int i=0; i<s.length(); i++)
-            {
-                char c = s.charAt(i);
-                if (c == '\'')
-                    target.append("''");
-                else
-                    target.append(c);
-            }
-            target.append('\'');
-        }
+        return buffer == null
+             ? "null"
+             : '\'' + StringUtils.replace(deserialize(buffer), "'", "''") + '\'';
     }
 }

http://git-wip-us.apache.org/repos/asf/cassandra/blob/c6ed2e0b/src/java/org/apache/cassandra/serializers/BytesSerializer.java
----------------------------------------------------------------------
diff --git a/src/java/org/apache/cassandra/serializers/BytesSerializer.java b/src/java/org/apache/cassandra/serializers/BytesSerializer.java
index 85251b8..ed0bf77 100644
--- a/src/java/org/apache/cassandra/serializers/BytesSerializer.java
+++ b/src/java/org/apache/cassandra/serializers/BytesSerializer.java
@@ -54,16 +54,10 @@ public class BytesSerializer implements TypeSerializer<ByteBuffer>
     }
 
     @Override
-    public void toCQLLiteral(ByteBuffer buffer, StringBuilder target)
+    public String toCQLLiteral(ByteBuffer buffer)
     {
-        if (buffer == null)
-        {
-            target.append("null");
-        }
-        else
-        {
-            target.append("0x");
-            target.append(toString(deserialize(buffer)));
-        }
+        return buffer == null
+             ? "null"
+             : "0x" + toString(deserialize(buffer));
     }
 }

http://git-wip-us.apache.org/repos/asf/cassandra/blob/c6ed2e0b/src/java/org/apache/cassandra/serializers/TimestampSerializer.java
----------------------------------------------------------------------
diff --git a/src/java/org/apache/cassandra/serializers/TimestampSerializer.java b/src/java/org/apache/cassandra/serializers/TimestampSerializer.java
index ad56cd5..fbd98d1 100644
--- a/src/java/org/apache/cassandra/serializers/TimestampSerializer.java
+++ b/src/java/org/apache/cassandra/serializers/TimestampSerializer.java
@@ -190,11 +190,10 @@ public class TimestampSerializer implements TypeSerializer<Date>
      * @see #FORMATTER_UTC
      */
     @Override
-    public void toCQLLiteral(ByteBuffer buffer, StringBuilder target)
+    public String toCQLLiteral(ByteBuffer buffer)
     {
-        if (buffer == null || !buffer.hasRemaining())
-            target.append("null");
-        else
-            target.append(FORMATTER_UTC.get().format(deserialize(buffer)));
+        return buffer == null || !buffer.hasRemaining()
+             ? "null"
+             : FORMATTER_UTC.get().format(deserialize(buffer));
     }
 }

http://git-wip-us.apache.org/repos/asf/cassandra/blob/c6ed2e0b/src/java/org/apache/cassandra/serializers/TypeSerializer.java
----------------------------------------------------------------------
diff --git a/src/java/org/apache/cassandra/serializers/TypeSerializer.java b/src/java/org/apache/cassandra/serializers/TypeSerializer.java
index e7bb830..e66c36d 100644
--- a/src/java/org/apache/cassandra/serializers/TypeSerializer.java
+++ b/src/java/org/apache/cassandra/serializers/TypeSerializer.java
@@ -35,12 +35,11 @@ public interface TypeSerializer<T>
 
     public Class<T> getType();
 
-    public default void toCQLLiteral(ByteBuffer buffer, StringBuilder target)
+    public default String toCQLLiteral(ByteBuffer buffer)
     {
-        if (buffer == null || !buffer.hasRemaining())
-            target.append("null");
-        else
-            target.append(toString(deserialize(buffer)));
+        return buffer == null || !buffer.hasRemaining()
+             ? "null"
+             : toString(deserialize(buffer));
     }
 }
 

http://git-wip-us.apache.org/repos/asf/cassandra/blob/c6ed2e0b/test/unit/org/apache/cassandra/cql3/CQL3TypeLiteralTest.java
----------------------------------------------------------------------
diff --git a/test/unit/org/apache/cassandra/cql3/CQL3TypeLiteralTest.java b/test/unit/org/apache/cassandra/cql3/CQL3TypeLiteralTest.java
index f6cd8df..02ed1a8 100644
--- a/test/unit/org/apache/cassandra/cql3/CQL3TypeLiteralTest.java
+++ b/test/unit/org/apache/cassandra/cql3/CQL3TypeLiteralTest.java
@@ -23,15 +23,7 @@ import java.math.BigInteger;
 import java.net.InetAddress;
 import java.net.UnknownHostException;
 import java.nio.ByteBuffer;
-import java.util.ArrayList;
-import java.util.Collection;
-import java.util.Date;
-import java.util.EnumMap;
-import java.util.HashSet;
-import java.util.List;
-import java.util.Map;
-import java.util.Set;
-import java.util.UUID;
+import java.util.*;
 import java.util.concurrent.ThreadLocalRandom;
 import java.util.regex.Pattern;
 
@@ -302,39 +294,30 @@ public class CQL3TypeLiteralTest
     @Test
     public void testCollectionNullAndEmpty()
     {
+        // An empty collection is one with a size of 0 (note that rely on the fact that protocol version < 3 are not
+        // supported anymore and so the size of a collection is always on 4 bytes).
+        ByteBuffer emptyCollection = ByteBufferUtil.bytes(0);
+
         for (int version = Server.MIN_SUPPORTED_VERSION; version <= Server.CURRENT_VERSION; version++)
         {
-            // empty, frozen collections
-            Value value = new Value("[]", ListType.getInstance(UTF8Type.instance, false).asCQL3Type(), ByteBufferUtil.EMPTY_BYTE_BUFFER);
-            compareCqlLiteral(version, value);
-            value = new Value("{}", SetType.getInstance(UTF8Type.instance, false).asCQL3Type(), ByteBufferUtil.EMPTY_BYTE_BUFFER);
-            compareCqlLiteral(version, value);
-            value = new Value("{}", MapType.getInstance(UTF8Type.instance, UTF8Type.instance, false).asCQL3Type(), ByteBufferUtil.EMPTY_BYTE_BUFFER);
-            compareCqlLiteral(version, value);
-
-            // empty, non-frozen collections
-            value = new Value("[]", ListType.getInstance(UTF8Type.instance, true).asCQL3Type(), ByteBufferUtil.EMPTY_BYTE_BUFFER);
-            compareCqlLiteral(version, value);
-            value = new Value("{}", SetType.getInstance(UTF8Type.instance, true).asCQL3Type(), ByteBufferUtil.EMPTY_BYTE_BUFFER);
-            compareCqlLiteral(version, value);
-            value = new Value("{}", MapType.getInstance(UTF8Type.instance, UTF8Type.instance, true).asCQL3Type(), ByteBufferUtil.EMPTY_BYTE_BUFFER);
-            compareCqlLiteral(version, value);
-
-            // null, frozen collections
-            value = new Value("null", ListType.getInstance(UTF8Type.instance, false).asCQL3Type(), null);
-            compareCqlLiteral(version, value);
-            value = new Value("null", SetType.getInstance(UTF8Type.instance, false).asCQL3Type(), null);
-            compareCqlLiteral(version, value);
-            value = new Value("null", MapType.getInstance(UTF8Type.instance, UTF8Type.instance, false).asCQL3Type(), null);
-            compareCqlLiteral(version, value);
-
-            // null, non-frozen collections
-            value = new Value("[]", ListType.getInstance(UTF8Type.instance, true).asCQL3Type(), null);
-            compareCqlLiteral(version, value);
-            value = new Value("{}", SetType.getInstance(UTF8Type.instance, true).asCQL3Type(), null);
-            compareCqlLiteral(version, value);
-            value = new Value("{}", MapType.getInstance(UTF8Type.instance, UTF8Type.instance, true).asCQL3Type(), null);
-            compareCqlLiteral(version, value);
+            for (boolean frozen : Arrays.asList(true, false))
+            {
+                // empty
+                Value value = new Value("[]", ListType.getInstance(UTF8Type.instance, frozen).asCQL3Type(), emptyCollection);
+                compareCqlLiteral(version, value);
+                value = new Value("{}", SetType.getInstance(UTF8Type.instance, frozen).asCQL3Type(), emptyCollection);
+                compareCqlLiteral(version, value);
+                value = new Value("{}", MapType.getInstance(UTF8Type.instance, UTF8Type.instance, frozen).asCQL3Type(), emptyCollection);
+                compareCqlLiteral(version, value);
+
+                // null
+                value = new Value("null", ListType.getInstance(UTF8Type.instance, frozen).asCQL3Type(), null);
+                compareCqlLiteral(version, value);
+                value = new Value("null", SetType.getInstance(UTF8Type.instance, frozen).asCQL3Type(), null);
+                compareCqlLiteral(version, value);
+                value = new Value("null", MapType.getInstance(UTF8Type.instance, UTF8Type.instance, frozen).asCQL3Type(), null);
+                compareCqlLiteral(version, value);
+            }
         }
     }
 
@@ -393,7 +376,7 @@ public class CQL3TypeLiteralTest
         {
             assertEquals(msg,
                          value.expected,
-                         value.cql3Type.asCQLLiteral(buffer, version));
+                         value.cql3Type.toCQLLiteral(buffer, version));
         }
         catch (RuntimeException e)
         {
@@ -436,24 +419,7 @@ public class CQL3TypeLiteralTest
 
         if (allowNull && randBool(0.05d))
         {
-            // generate 'null' collection
-            if (collectionType.isMultiCell())
-            {
-                switch (collectionType.kind)
-                {
-                    case LIST:
-                        expected.append("[]");
-                        break;
-                    case SET:
-                    case MAP:
-                        expected.append("{}");
-                        break;
-                }
-            }
-            else
-            {
-                expected.append("null");
-            }
+            expected.append("null");
             buffer = null;
         }
         else
@@ -498,7 +464,7 @@ public class CQL3TypeLiteralTest
                 buffers.add(el.value.duplicate());
                 if (expected.length() > 1)
                     expected.append(", ");
-                el.cql3Type.toCQLLiteral(el.value, version, expected);
+                expected.append(el.cql3Type.toCQLLiteral(el.value, version));
 
                 if (collectionType.kind == CollectionType.Kind.MAP)
                 {
@@ -506,7 +472,7 @@ public class CQL3TypeLiteralTest
                     el = generateAnyValue(version, values);
                     buffers.add(el.value.duplicate());
                     expected.append(": ");
-                    el.cql3Type.toCQLLiteral(el.value, version, expected);
+                    expected.append(el.cql3Type.toCQLLiteral(el.value, version));
                 }
             }
             expected.append(bracketClose);

http://git-wip-us.apache.org/repos/asf/cassandra/blob/c6ed2e0b/test/unit/org/apache/cassandra/cql3/validation/operations/AggregationTest.java
----------------------------------------------------------------------
diff --git a/test/unit/org/apache/cassandra/cql3/validation/operations/AggregationTest.java b/test/unit/org/apache/cassandra/cql3/validation/operations/AggregationTest.java
index c903127..e5420c9 100644
--- a/test/unit/org/apache/cassandra/cql3/validation/operations/AggregationTest.java
+++ b/test/unit/org/apache/cassandra/cql3/validation/operations/AggregationTest.java
@@ -18,6 +18,7 @@
 package org.apache.cassandra.cql3.validation.operations;
 
 import java.math.BigDecimal;
+import java.nio.ByteBuffer;
 import java.text.SimpleDateFormat;
 import java.util.Arrays;
 import java.util.Calendar;
@@ -44,11 +45,15 @@ import org.apache.cassandra.cql3.UntypedResultSet;
 import org.apache.cassandra.cql3.UntypedResultSet.Row;
 import org.apache.cassandra.cql3.functions.UDAggregate;
 import org.apache.cassandra.db.SystemKeyspace;
+import org.apache.cassandra.db.marshal.AbstractType;
+import org.apache.cassandra.db.marshal.DynamicCompositeType;
+import org.apache.cassandra.db.marshal.TypeParser;
 import org.apache.cassandra.exceptions.FunctionExecutionException;
 import org.apache.cassandra.exceptions.InvalidRequestException;
 import org.apache.cassandra.schema.KeyspaceMetadata;
 import org.apache.cassandra.service.ClientState;
 import org.apache.cassandra.transport.Event;
+import org.apache.cassandra.transport.Server;
 import org.apache.cassandra.transport.messages.ResultMessage;
 
 import static org.junit.Assert.assertEquals;
@@ -1878,4 +1883,71 @@ public class AggregationTest extends CQLTester
         assertRows(execute("SELECT final_func, initcond FROM system_schema.aggregates WHERE keyspace_name=? AND aggregate_name=?", KEYSPACE, shortFunctionName(aggregateName)),
                    row(finalFunc, initCond));
     }
+
+    public void testCustomTypeInitcond() throws Throwable
+    {
+        try
+        {
+            String type = "DynamicCompositeType(s => UTF8Type, i => Int32Type)";
+
+            executeNet(Server.CURRENT_VERSION,
+                       "CREATE FUNCTION " + KEYSPACE + ".f11064(i 'DynamicCompositeType(s => UTF8Type, i => Int32Type)')\n" +
+                       "RETURNS NULL ON NULL INPUT\n" +
+                       "RETURNS '" + type + "'\n" +
+                       "LANGUAGE java\n" +
+                       "AS 'return i;'");
+
+            // create aggregate using the 'composite syntax' for composite types
+            executeNet(Server.CURRENT_VERSION,
+                       "CREATE AGGREGATE " + KEYSPACE + ".a11064()\n" +
+                       "SFUNC f11064 " +
+                       "STYPE '" + type + "'\n" +
+                       "INITCOND 's@foo:i@32'");
+
+            AbstractType<?> compositeType = TypeParser.parse(type);
+            ByteBuffer compositeTypeValue = compositeType.fromString("s@foo:i@32");
+            String compositeTypeString = compositeType.asCQL3Type().toCQLLiteral(compositeTypeValue, Server.CURRENT_VERSION);
+            // ensure that the composite type is serialized using the 'blob syntax'
+            assertTrue(compositeTypeString.startsWith("0x"));
+
+            // ensure that the composite type is 'serialized' using the 'blob syntax' in the schema
+            assertRows(execute("SELECT initcond FROM system_schema.aggregates WHERE keyspace_name=? AND aggregate_name=?", KEYSPACE, "a11064"),
+                       row(compositeTypeString));
+
+            // create aggregate using the 'blob syntax' for composite types
+            executeNet(Server.CURRENT_VERSION,
+                       "CREATE AGGREGATE " + KEYSPACE + ".a11064_2()\n" +
+                       "SFUNC f11064 " +
+                       "STYPE '" + type + "'\n" +
+                       "INITCOND " + compositeTypeString);
+
+            // ensure that the composite type is 'serialized' using the 'blob syntax' in the schema
+            assertRows(execute("SELECT initcond FROM system_schema.aggregates WHERE keyspace_name=? AND aggregate_name=?", KEYSPACE, "a11064_2"),
+                       row(compositeTypeString));
+        }
+        finally
+        {
+            try
+            {
+                execute("DROP AGGREGATE " + KEYSPACE + ".a11064_2");
+            }
+            catch (Exception ignore)
+            {
+            }
+            try
+            {
+                execute("DROP AGGREGATE " + KEYSPACE + ".a11064");
+            }
+            catch (Exception ignore)
+            {
+            }
+            try
+            {
+                execute("DROP FUNCTION " + KEYSPACE + ".f11064");
+            }
+            catch (Exception ignore)
+            {
+            }
+        }
+    }
 }