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)
+ {
+ }
+ }
+ }
}