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 2015/06/04 16:02:08 UTC
[1/3] cassandra git commit: Empty INITCOND treated as null in
aggregate
Repository: cassandra
Updated Branches:
refs/heads/cassandra-2.2 193665757 -> 4df4f79c5
refs/heads/trunk 9718e13d7 -> 5c4a5b3b2
Empty INITCOND treated as null in aggregate
patch by Robert Stupp; reviewed by Sylvain Lebresne for CASSANDRA-9457
Project: http://git-wip-us.apache.org/repos/asf/cassandra/repo
Commit: http://git-wip-us.apache.org/repos/asf/cassandra/commit/4df4f79c
Tree: http://git-wip-us.apache.org/repos/asf/cassandra/tree/4df4f79c
Diff: http://git-wip-us.apache.org/repos/asf/cassandra/diff/4df4f79c
Branch: refs/heads/cassandra-2.2
Commit: 4df4f79c563232285137ab2d506836de4e4efa1d
Parents: 1936657
Author: Robert Stupp <sn...@snazy.de>
Authored: Thu Jun 4 16:01:23 2015 +0200
Committer: Robert Stupp <sn...@snazy.de>
Committed: Thu Jun 4 16:01:23 2015 +0200
----------------------------------------------------------------------
CHANGES.txt | 1 +
.../cassandra/cql3/functions/UDFunction.java | 21 ++-
.../cassandra/cql3/functions/UDHelper.java | 7 +
.../statements/CreateAggregateStatement.java | 2 +
.../cassandra/db/marshal/AbstractType.java | 8 +
.../cassandra/db/marshal/BooleanType.java | 5 +
.../cassandra/db/marshal/CounterColumnType.java | 5 +
.../apache/cassandra/db/marshal/DateType.java | 5 +
.../cassandra/db/marshal/DecimalType.java | 5 +
.../apache/cassandra/db/marshal/DoubleType.java | 5 +
.../apache/cassandra/db/marshal/FloatType.java | 5 +
.../cassandra/db/marshal/InetAddressType.java | 5 +
.../apache/cassandra/db/marshal/Int32Type.java | 5 +
.../cassandra/db/marshal/IntegerType.java | 5 +
.../cassandra/db/marshal/LexicalUUIDType.java | 5 +
.../apache/cassandra/db/marshal/LongType.java | 5 +
.../cassandra/db/marshal/ReversedType.java | 5 +
.../cassandra/db/marshal/TimeUUIDType.java | 5 +
.../cassandra/db/marshal/TimestampType.java | 5 +
.../apache/cassandra/db/marshal/UUIDType.java | 5 +
.../apache/cassandra/cql3/AggregationTest.java | 69 +++++++++
.../org/apache/cassandra/cql3/UDHelperTest.java | 146 +++++++++++++++++++
test/unit/org/apache/cassandra/cql3/UFTest.java | 120 ++++++++++++++-
23 files changed, 439 insertions(+), 10 deletions(-)
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/cassandra/blob/4df4f79c/CHANGES.txt
----------------------------------------------------------------------
diff --git a/CHANGES.txt b/CHANGES.txt
index 882279f..2ed9ce9 100644
--- a/CHANGES.txt
+++ b/CHANGES.txt
@@ -1,4 +1,5 @@
2.2
+ * Empty INITCOND treated as null in aggregate (CASSANDRA-9457)
* Remove use of Cell in Thrift MapReduce classes (CASSANDRA-8609)
* Integrate pre-release Java Driver 2.2-rc1, custom build (CASSANDRA-9493)
* Clean up gossiper logic for old versions (CASSANDRA-9370)
http://git-wip-us.apache.org/repos/asf/cassandra/blob/4df4f79c/src/java/org/apache/cassandra/cql3/functions/UDFunction.java
----------------------------------------------------------------------
diff --git a/src/java/org/apache/cassandra/cql3/functions/UDFunction.java b/src/java/org/apache/cassandra/cql3/functions/UDFunction.java
index a01f08f..0bf6078 100644
--- a/src/java/org/apache/cassandra/cql3/functions/UDFunction.java
+++ b/src/java/org/apache/cassandra/cql3/functions/UDFunction.java
@@ -146,8 +146,8 @@ public abstract class UDFunction extends AbstractFunction implements ScalarFunct
public boolean isCallableWrtNullable(List<ByteBuffer> parameters)
{
if (!calledOnNullInput)
- for (ByteBuffer parameter : parameters)
- if (parameter == null || parameter.remaining() == 0)
+ for (int i = 0; i < parameters.size(); i++)
+ if (UDHelper.isNullOrEmpty(argTypes.get(i), parameters.get(i)))
return false;
return true;
}
@@ -194,37 +194,42 @@ public abstract class UDFunction extends AbstractFunction implements ScalarFunct
*/
protected Object compose(int protocolVersion, int argIndex, ByteBuffer value)
{
- return value == null ? null : argDataTypes[argIndex].deserialize(value, ProtocolVersion.fromInt(protocolVersion));
+ return UDHelper.isNullOrEmpty(argTypes.get(argIndex), value) ? null : argDataTypes[argIndex].deserialize(value, ProtocolVersion.fromInt(protocolVersion));
}
// do not remove - used by generated Java UDFs
protected float compose_float(int protocolVersion, int argIndex, ByteBuffer value)
{
- return value == null ? 0f : (float)DataType.cfloat().deserialize(value, ProtocolVersion.fromInt(protocolVersion));
+ assert value != null && value.remaining() > 0;
+ return (float)DataType.cfloat().deserialize(value, ProtocolVersion.fromInt(protocolVersion));
}
// do not remove - used by generated Java UDFs
protected double compose_double(int protocolVersion, int argIndex, ByteBuffer value)
{
- return value == null ? 0d : (double)DataType.cdouble().deserialize(value, ProtocolVersion.fromInt(protocolVersion));
+ assert value != null && value.remaining() > 0;
+ return (double)DataType.cdouble().deserialize(value, ProtocolVersion.fromInt(protocolVersion));
}
// do not remove - used by generated Java UDFs
protected int compose_int(int protocolVersion, int argIndex, ByteBuffer value)
{
- return value == null ? 0 : (int)DataType.cint().deserialize(value, ProtocolVersion.fromInt(protocolVersion));
+ assert value != null && value.remaining() > 0;
+ return (int)DataType.cint().deserialize(value, ProtocolVersion.fromInt(protocolVersion));
}
// do not remove - used by generated Java UDFs
protected long compose_long(int protocolVersion, int argIndex, ByteBuffer value)
{
- return value == null ? 0L : (long)DataType.bigint().deserialize(value, ProtocolVersion.fromInt(protocolVersion));
+ assert value != null && value.remaining() > 0;
+ return (long)DataType.bigint().deserialize(value, ProtocolVersion.fromInt(protocolVersion));
}
// do not remove - used by generated Java UDFs
protected boolean compose_boolean(int protocolVersion, int argIndex, ByteBuffer value)
{
- return value != null && (boolean) DataType.cboolean().deserialize(value, ProtocolVersion.fromInt(protocolVersion));
+ assert value != null && value.remaining() > 0;
+ return (boolean) DataType.cboolean().deserialize(value, ProtocolVersion.fromInt(protocolVersion));
}
/**
http://git-wip-us.apache.org/repos/asf/cassandra/blob/4df4f79c/src/java/org/apache/cassandra/cql3/functions/UDHelper.java
----------------------------------------------------------------------
diff --git a/src/java/org/apache/cassandra/cql3/functions/UDHelper.java b/src/java/org/apache/cassandra/cql3/functions/UDHelper.java
index 55a0888..d1d12e6 100644
--- a/src/java/org/apache/cassandra/cql3/functions/UDHelper.java
+++ b/src/java/org/apache/cassandra/cql3/functions/UDHelper.java
@@ -20,6 +20,7 @@ package org.apache.cassandra.cql3.functions;
import java.lang.invoke.MethodHandle;
import java.lang.invoke.MethodHandles;
import java.lang.reflect.Method;
+import java.nio.ByteBuffer;
import java.util.List;
import com.datastax.driver.core.DataType;
@@ -119,4 +120,10 @@ public final class UDHelper
throw new RuntimeException("cannot parse driver type " + cqlType.getType().toString(), e);
}
}
+
+ public static boolean isNullOrEmpty(AbstractType<?> type, ByteBuffer bb)
+ {
+ return bb == null ||
+ (bb.remaining() == 0 && type.isEmptyValueMeaningless());
+ }
}
http://git-wip-us.apache.org/repos/asf/cassandra/blob/4df4f79c/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 ae793a3..8b6c8d6 100644
--- a/src/java/org/apache/cassandra/cql3/statements/CreateAggregateStatement.java
+++ b/src/java/org/apache/cassandra/cql3/statements/CreateAggregateStatement.java
@@ -112,6 +112,8 @@ public final class CreateAggregateStatement extends SchemaAlteringStatement
{
ColumnSpecification receiver = new ColumnSpecification(functionName.keyspace, "--dummy--", new ColumnIdentifier("(aggregate_initcond)", true), stateType);
initcond = ival.prepare(functionName.keyspace, receiver).bindAndGet(QueryOptions.DEFAULT);
+ if (Constants.NULL_LITERAL != ival && UDHelper.isNullOrEmpty(stateType, initcond))
+ throw new InvalidRequestException("INITCOND must not be empty for all types except TEXT, ASCII, BLOB");
}
return super.prepare();
http://git-wip-us.apache.org/repos/asf/cassandra/blob/4df4f79c/src/java/org/apache/cassandra/db/marshal/AbstractType.java
----------------------------------------------------------------------
diff --git a/src/java/org/apache/cassandra/db/marshal/AbstractType.java b/src/java/org/apache/cassandra/db/marshal/AbstractType.java
index 6602414..aa25a81 100644
--- a/src/java/org/apache/cassandra/db/marshal/AbstractType.java
+++ b/src/java/org/apache/cassandra/db/marshal/AbstractType.java
@@ -257,6 +257,14 @@ public abstract class AbstractType<T> implements Comparator<ByteBuffer>
}
/**
+ * Returns {@code true} for types where empty should be handled like {@code null} like {@link Int32Type}.
+ */
+ public boolean isEmptyValueMeaningless()
+ {
+ return false;
+ }
+
+ /**
* @param ignoreFreezing if true, the type string will not be wrapped with FrozenType(...), even if this type is frozen.
*/
public String toString(boolean ignoreFreezing)
http://git-wip-us.apache.org/repos/asf/cassandra/blob/4df4f79c/src/java/org/apache/cassandra/db/marshal/BooleanType.java
----------------------------------------------------------------------
diff --git a/src/java/org/apache/cassandra/db/marshal/BooleanType.java b/src/java/org/apache/cassandra/db/marshal/BooleanType.java
index 5c1bf42..bfe8c34 100644
--- a/src/java/org/apache/cassandra/db/marshal/BooleanType.java
+++ b/src/java/org/apache/cassandra/db/marshal/BooleanType.java
@@ -37,6 +37,11 @@ public class BooleanType extends AbstractType<Boolean>
BooleanType() {} // singleton
+ public boolean isEmptyValueMeaningless()
+ {
+ return true;
+ }
+
public int compare(ByteBuffer o1, ByteBuffer o2)
{
if (!o1.hasRemaining() || !o2.hasRemaining())
http://git-wip-us.apache.org/repos/asf/cassandra/blob/4df4f79c/src/java/org/apache/cassandra/db/marshal/CounterColumnType.java
----------------------------------------------------------------------
diff --git a/src/java/org/apache/cassandra/db/marshal/CounterColumnType.java b/src/java/org/apache/cassandra/db/marshal/CounterColumnType.java
index 0fea87b..4b3ce82 100644
--- a/src/java/org/apache/cassandra/db/marshal/CounterColumnType.java
+++ b/src/java/org/apache/cassandra/db/marshal/CounterColumnType.java
@@ -32,6 +32,11 @@ public class CounterColumnType extends AbstractType<Long>
CounterColumnType() {} // singleton
+ public boolean isEmptyValueMeaningless()
+ {
+ return true;
+ }
+
public boolean isCounter()
{
return true;
http://git-wip-us.apache.org/repos/asf/cassandra/blob/4df4f79c/src/java/org/apache/cassandra/db/marshal/DateType.java
----------------------------------------------------------------------
diff --git a/src/java/org/apache/cassandra/db/marshal/DateType.java b/src/java/org/apache/cassandra/db/marshal/DateType.java
index 806bbcf..359ce52 100644
--- a/src/java/org/apache/cassandra/db/marshal/DateType.java
+++ b/src/java/org/apache/cassandra/db/marshal/DateType.java
@@ -39,6 +39,11 @@ public class DateType extends AbstractType<Date>
DateType() {} // singleton
+ public boolean isEmptyValueMeaningless()
+ {
+ return true;
+ }
+
public int compare(ByteBuffer o1, ByteBuffer o2)
{
if (!o1.hasRemaining() || !o2.hasRemaining())
http://git-wip-us.apache.org/repos/asf/cassandra/blob/4df4f79c/src/java/org/apache/cassandra/db/marshal/DecimalType.java
----------------------------------------------------------------------
diff --git a/src/java/org/apache/cassandra/db/marshal/DecimalType.java b/src/java/org/apache/cassandra/db/marshal/DecimalType.java
index f1388ce..4052d70 100644
--- a/src/java/org/apache/cassandra/db/marshal/DecimalType.java
+++ b/src/java/org/apache/cassandra/db/marshal/DecimalType.java
@@ -34,6 +34,11 @@ public class DecimalType extends AbstractType<BigDecimal>
DecimalType() {} // singleton
+ public boolean isEmptyValueMeaningless()
+ {
+ return true;
+ }
+
public int compare(ByteBuffer o1, ByteBuffer o2)
{
if (!o1.hasRemaining() || !o2.hasRemaining())
http://git-wip-us.apache.org/repos/asf/cassandra/blob/4df4f79c/src/java/org/apache/cassandra/db/marshal/DoubleType.java
----------------------------------------------------------------------
diff --git a/src/java/org/apache/cassandra/db/marshal/DoubleType.java b/src/java/org/apache/cassandra/db/marshal/DoubleType.java
index fdfd2d2..661b3c9 100644
--- a/src/java/org/apache/cassandra/db/marshal/DoubleType.java
+++ b/src/java/org/apache/cassandra/db/marshal/DoubleType.java
@@ -33,6 +33,11 @@ public class DoubleType extends AbstractType<Double>
DoubleType() {} // singleton
+ public boolean isEmptyValueMeaningless()
+ {
+ return true;
+ }
+
public int compare(ByteBuffer o1, ByteBuffer o2)
{
if (!o1.hasRemaining() || !o2.hasRemaining())
http://git-wip-us.apache.org/repos/asf/cassandra/blob/4df4f79c/src/java/org/apache/cassandra/db/marshal/FloatType.java
----------------------------------------------------------------------
diff --git a/src/java/org/apache/cassandra/db/marshal/FloatType.java b/src/java/org/apache/cassandra/db/marshal/FloatType.java
index 722df87..af02cad 100644
--- a/src/java/org/apache/cassandra/db/marshal/FloatType.java
+++ b/src/java/org/apache/cassandra/db/marshal/FloatType.java
@@ -34,6 +34,11 @@ public class FloatType extends AbstractType<Float>
FloatType() {} // singleton
+ public boolean isEmptyValueMeaningless()
+ {
+ return true;
+ }
+
public int compare(ByteBuffer o1, ByteBuffer o2)
{
if (!o1.hasRemaining() || !o2.hasRemaining())
http://git-wip-us.apache.org/repos/asf/cassandra/blob/4df4f79c/src/java/org/apache/cassandra/db/marshal/InetAddressType.java
----------------------------------------------------------------------
diff --git a/src/java/org/apache/cassandra/db/marshal/InetAddressType.java b/src/java/org/apache/cassandra/db/marshal/InetAddressType.java
index a4eac07..4901c74 100644
--- a/src/java/org/apache/cassandra/db/marshal/InetAddressType.java
+++ b/src/java/org/apache/cassandra/db/marshal/InetAddressType.java
@@ -34,6 +34,11 @@ public class InetAddressType extends AbstractType<InetAddress>
InetAddressType() {} // singleton
+ public boolean isEmptyValueMeaningless()
+ {
+ return true;
+ }
+
public int compare(ByteBuffer o1, ByteBuffer o2)
{
return ByteBufferUtil.compareUnsigned(o1, o2);
http://git-wip-us.apache.org/repos/asf/cassandra/blob/4df4f79c/src/java/org/apache/cassandra/db/marshal/Int32Type.java
----------------------------------------------------------------------
diff --git a/src/java/org/apache/cassandra/db/marshal/Int32Type.java b/src/java/org/apache/cassandra/db/marshal/Int32Type.java
index 9ea8f78..67d8142 100644
--- a/src/java/org/apache/cassandra/db/marshal/Int32Type.java
+++ b/src/java/org/apache/cassandra/db/marshal/Int32Type.java
@@ -35,6 +35,11 @@ public class Int32Type extends AbstractType<Integer>
{
} // singleton
+ public boolean isEmptyValueMeaningless()
+ {
+ return true;
+ }
+
public int compare(ByteBuffer o1, ByteBuffer o2)
{
if (!o1.hasRemaining() || !o2.hasRemaining())
http://git-wip-us.apache.org/repos/asf/cassandra/blob/4df4f79c/src/java/org/apache/cassandra/db/marshal/IntegerType.java
----------------------------------------------------------------------
diff --git a/src/java/org/apache/cassandra/db/marshal/IntegerType.java b/src/java/org/apache/cassandra/db/marshal/IntegerType.java
index 5fe8fd8..a3741d4 100644
--- a/src/java/org/apache/cassandra/db/marshal/IntegerType.java
+++ b/src/java/org/apache/cassandra/db/marshal/IntegerType.java
@@ -60,6 +60,11 @@ public final class IntegerType extends AbstractType<BigInteger>
IntegerType() {/* singleton */}
+ public boolean isEmptyValueMeaningless()
+ {
+ return true;
+ }
+
public int compare(ByteBuffer lhs, ByteBuffer rhs)
{
return IntegerType.compareIntegers(lhs, rhs);
http://git-wip-us.apache.org/repos/asf/cassandra/blob/4df4f79c/src/java/org/apache/cassandra/db/marshal/LexicalUUIDType.java
----------------------------------------------------------------------
diff --git a/src/java/org/apache/cassandra/db/marshal/LexicalUUIDType.java b/src/java/org/apache/cassandra/db/marshal/LexicalUUIDType.java
index 3ca5c74..3e00d71 100644
--- a/src/java/org/apache/cassandra/db/marshal/LexicalUUIDType.java
+++ b/src/java/org/apache/cassandra/db/marshal/LexicalUUIDType.java
@@ -36,6 +36,11 @@ public class LexicalUUIDType extends AbstractType<UUID>
{
} // singleton
+ public boolean isEmptyValueMeaningless()
+ {
+ return true;
+ }
+
public int compare(ByteBuffer o1, ByteBuffer o2)
{
if (!o1.hasRemaining() || !o2.hasRemaining())
http://git-wip-us.apache.org/repos/asf/cassandra/blob/4df4f79c/src/java/org/apache/cassandra/db/marshal/LongType.java
----------------------------------------------------------------------
diff --git a/src/java/org/apache/cassandra/db/marshal/LongType.java b/src/java/org/apache/cassandra/db/marshal/LongType.java
index 31bad93..d77d7d0 100644
--- a/src/java/org/apache/cassandra/db/marshal/LongType.java
+++ b/src/java/org/apache/cassandra/db/marshal/LongType.java
@@ -33,6 +33,11 @@ public class LongType extends AbstractType<Long>
LongType() {} // singleton
+ public boolean isEmptyValueMeaningless()
+ {
+ return true;
+ }
+
public int compare(ByteBuffer o1, ByteBuffer o2)
{
return compareLongs(o1, o2);
http://git-wip-us.apache.org/repos/asf/cassandra/blob/4df4f79c/src/java/org/apache/cassandra/db/marshal/ReversedType.java
----------------------------------------------------------------------
diff --git a/src/java/org/apache/cassandra/db/marshal/ReversedType.java b/src/java/org/apache/cassandra/db/marshal/ReversedType.java
index 14d069a..2181f74 100644
--- a/src/java/org/apache/cassandra/db/marshal/ReversedType.java
+++ b/src/java/org/apache/cassandra/db/marshal/ReversedType.java
@@ -60,6 +60,11 @@ public class ReversedType<T> extends AbstractType<T>
this.baseType = baseType;
}
+ public boolean isEmptyValueMeaningless()
+ {
+ return baseType.isEmptyValueMeaningless();
+ }
+
public int compare(ByteBuffer o1, ByteBuffer o2)
{
// An empty byte buffer is always smaller
http://git-wip-us.apache.org/repos/asf/cassandra/blob/4df4f79c/src/java/org/apache/cassandra/db/marshal/TimeUUIDType.java
----------------------------------------------------------------------
diff --git a/src/java/org/apache/cassandra/db/marshal/TimeUUIDType.java b/src/java/org/apache/cassandra/db/marshal/TimeUUIDType.java
index 3b38582..a1d8d82 100644
--- a/src/java/org/apache/cassandra/db/marshal/TimeUUIDType.java
+++ b/src/java/org/apache/cassandra/db/marshal/TimeUUIDType.java
@@ -35,6 +35,11 @@ public class TimeUUIDType extends AbstractType<UUID>
{
} // singleton
+ public boolean isEmptyValueMeaningless()
+ {
+ return true;
+ }
+
public int compare(ByteBuffer b1, ByteBuffer b2)
{
// Compare for length
http://git-wip-us.apache.org/repos/asf/cassandra/blob/4df4f79c/src/java/org/apache/cassandra/db/marshal/TimestampType.java
----------------------------------------------------------------------
diff --git a/src/java/org/apache/cassandra/db/marshal/TimestampType.java b/src/java/org/apache/cassandra/db/marshal/TimestampType.java
index 095f2c2..38e0296 100644
--- a/src/java/org/apache/cassandra/db/marshal/TimestampType.java
+++ b/src/java/org/apache/cassandra/db/marshal/TimestampType.java
@@ -45,6 +45,11 @@ public class TimestampType extends AbstractType<Date>
private TimestampType() {} // singleton
+ public boolean isEmptyValueMeaningless()
+ {
+ return true;
+ }
+
public int compare(ByteBuffer o1, ByteBuffer o2)
{
return LongType.compareLongs(o1, o2);
http://git-wip-us.apache.org/repos/asf/cassandra/blob/4df4f79c/src/java/org/apache/cassandra/db/marshal/UUIDType.java
----------------------------------------------------------------------
diff --git a/src/java/org/apache/cassandra/db/marshal/UUIDType.java b/src/java/org/apache/cassandra/db/marshal/UUIDType.java
index eba696e..0250eb20 100644
--- a/src/java/org/apache/cassandra/db/marshal/UUIDType.java
+++ b/src/java/org/apache/cassandra/db/marshal/UUIDType.java
@@ -50,6 +50,11 @@ public class UUIDType extends AbstractType<UUID>
{
}
+ public boolean isEmptyValueMeaningless()
+ {
+ return true;
+ }
+
public int compare(ByteBuffer b1, ByteBuffer b2)
{
// Compare for length
http://git-wip-us.apache.org/repos/asf/cassandra/blob/4df4f79c/test/unit/org/apache/cassandra/cql3/AggregationTest.java
----------------------------------------------------------------------
diff --git a/test/unit/org/apache/cassandra/cql3/AggregationTest.java b/test/unit/org/apache/cassandra/cql3/AggregationTest.java
index af775e6..af68ddc 100644
--- a/test/unit/org/apache/cassandra/cql3/AggregationTest.java
+++ b/test/unit/org/apache/cassandra/cql3/AggregationTest.java
@@ -1378,4 +1378,73 @@ public class AggregationTest extends CQLTester
assertInvalidMessage("The function arguments should not be frozen",
"DROP AGGREGATE %s (frozen<" + myType + ">);");
}
+
+ @Test
+ public void testEmptyValues() throws Throwable
+ {
+ createTable("CREATE TABLE %s (a int primary key, b text)");
+ execute("INSERT INTO %s (a, b) VALUES (1, '')");
+ execute("INSERT INTO %s (a, b) VALUES (2, '')");
+ execute("INSERT INTO %s (a, b) VALUES (3, '')");
+
+ String fCON = createFunction(KEYSPACE,
+ "text, text",
+ "CREATE FUNCTION %s(a text, b text) " +
+ "CALLED ON NULL INPUT " +
+ "RETURNS text " +
+ "LANGUAGE java " +
+ "AS 'return a + \"x\" + b + \"y\";'");
+
+ String fCONf = createFunction(KEYSPACE,
+ "text",
+ "CREATE FUNCTION %s(a text) " +
+ "CALLED ON NULL INPUT " +
+ "RETURNS text " +
+ "LANGUAGE java " +
+ "AS 'return \"fin\" + a;'");
+
+ String aCON = createAggregate(KEYSPACE,
+ "text, text",
+ "CREATE AGGREGATE %s(text) " +
+ "SFUNC " + shortFunctionName(fCON) + ' ' +
+ "STYPE text " +
+ "FINALFUNC " + shortFunctionName(fCONf) + ' ' +
+ "INITCOND ''");
+
+ String fRNON = createFunction(KEYSPACE,
+ "text",
+ "CREATE FUNCTION %s(a text, b text) " +
+ "RETURNS NULL ON NULL INPUT " +
+ "RETURNS text " +
+ "LANGUAGE java " +
+ "AS 'return a + \"x\" + b + \"y\";'");
+
+ String fRNONf = createFunction(KEYSPACE,
+ "text",
+ "CREATE FUNCTION %s(a text) " +
+ "RETURNS NULL ON NULL INPUT " +
+ "RETURNS text " +
+ "LANGUAGE java " +
+ "AS 'return \"fin\" + a;'");
+
+ String aRNON = createAggregate(KEYSPACE,
+ "int, int",
+ "CREATE AGGREGATE %s(text) " +
+ "SFUNC " + shortFunctionName(fRNON) + ' ' +
+ "STYPE text " +
+ "FINALFUNC " + shortFunctionName(fRNONf) + ' ' +
+ "INITCOND ''");
+
+ assertRows(execute("SELECT " + aCON + "(b) FROM %s"), row("finxyxyxy"));
+ assertRows(execute("SELECT " + aRNON + "(b) FROM %s"), row("finxyxyxy"));
+
+ createTable("CREATE TABLE %s (a int primary key, b text)");
+ execute("INSERT INTO %s (a, b) VALUES (1, null)");
+ execute("INSERT INTO %s (a, b) VALUES (2, null)");
+ execute("INSERT INTO %s (a, b) VALUES (3, null)");
+
+ assertRows(execute("SELECT " + aCON + "(b) FROM %s"), row("finxnullyxnullyxnully"));
+ assertRows(execute("SELECT " + aRNON + "(b) FROM %s"), row("fin"));
+
+ }
}
http://git-wip-us.apache.org/repos/asf/cassandra/blob/4df4f79c/test/unit/org/apache/cassandra/cql3/UDHelperTest.java
----------------------------------------------------------------------
diff --git a/test/unit/org/apache/cassandra/cql3/UDHelperTest.java b/test/unit/org/apache/cassandra/cql3/UDHelperTest.java
new file mode 100644
index 0000000..4a5e78e
--- /dev/null
+++ b/test/unit/org/apache/cassandra/cql3/UDHelperTest.java
@@ -0,0 +1,146 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.cassandra.cql3;
+
+import java.nio.ByteBuffer;
+
+import org.junit.Assert;
+import org.junit.Test;
+
+import org.apache.cassandra.cql3.functions.UDHelper;
+import org.apache.cassandra.db.marshal.AbstractType;
+import org.apache.cassandra.db.marshal.AsciiType;
+import org.apache.cassandra.db.marshal.BooleanType;
+import org.apache.cassandra.db.marshal.ByteType;
+import org.apache.cassandra.db.marshal.BytesType;
+import org.apache.cassandra.db.marshal.CounterColumnType;
+import org.apache.cassandra.db.marshal.DateType;
+import org.apache.cassandra.db.marshal.DecimalType;
+import org.apache.cassandra.db.marshal.DoubleType;
+import org.apache.cassandra.db.marshal.FloatType;
+import org.apache.cassandra.db.marshal.InetAddressType;
+import org.apache.cassandra.db.marshal.Int32Type;
+import org.apache.cassandra.db.marshal.IntegerType;
+import org.apache.cassandra.db.marshal.LongType;
+import org.apache.cassandra.db.marshal.ReversedType;
+import org.apache.cassandra.db.marshal.ShortType;
+import org.apache.cassandra.db.marshal.SimpleDateType;
+import org.apache.cassandra.db.marshal.TimeType;
+import org.apache.cassandra.db.marshal.TimeUUIDType;
+import org.apache.cassandra.db.marshal.TimestampType;
+import org.apache.cassandra.db.marshal.UTF8Type;
+import org.apache.cassandra.db.marshal.UUIDType;
+import org.apache.cassandra.serializers.MarshalException;
+import org.apache.cassandra.serializers.TypeSerializer;
+import org.apache.cassandra.utils.ByteBufferUtil;
+
+public class UDHelperTest
+{
+ static class UFTestCustomType extends AbstractType<String>
+ {
+
+ public ByteBuffer fromString(String source) throws MarshalException
+ {
+ return ByteBuffer.wrap(source.getBytes());
+ }
+
+ public Term fromJSONObject(Object parsed) throws MarshalException
+ {
+ throw new UnsupportedOperationException();
+ }
+
+ public TypeSerializer<String> getSerializer()
+ {
+ return UTF8Type.instance.getSerializer();
+ }
+
+ public int compare(ByteBuffer o1, ByteBuffer o2)
+ {
+ return o1.compareTo(o2);
+ }
+ }
+
+ @Test
+ public void testEmptyVariableLengthTypes()
+ {
+ AbstractType<?>[] types = new AbstractType<?>[]{
+ AsciiType.instance,
+ BytesType.instance,
+ UTF8Type.instance,
+ new UFTestCustomType()
+ };
+
+ for (AbstractType<?> type : types)
+ {
+ Assert.assertFalse("type " + type.getClass().getName(),
+ UDHelper.isNullOrEmpty(type, ByteBufferUtil.EMPTY_BYTE_BUFFER));
+ }
+ }
+
+ @Test
+ public void testNonEmptyPrimitiveTypes()
+ {
+ AbstractType<?>[] types = new AbstractType<?>[]{
+ TimeType.instance,
+ SimpleDateType.instance,
+ ByteType.instance,
+ ShortType.instance
+ };
+
+ for (AbstractType<?> type : types)
+ {
+ try
+ {
+ type.getSerializer().validate(ByteBufferUtil.EMPTY_BYTE_BUFFER);
+ Assert.fail(type.getClass().getSimpleName());
+ }
+ catch (MarshalException e)
+ {
+ //
+ }
+ }
+ }
+
+ @Test
+ public void testEmptiableTypes()
+ {
+ AbstractType<?>[] types = new AbstractType<?>[]{
+ BooleanType.instance,
+ CounterColumnType.instance,
+ DateType.instance,
+ DecimalType.instance,
+ DoubleType.instance,
+ FloatType.instance,
+ InetAddressType.instance,
+ Int32Type.instance,
+ IntegerType.instance,
+ LongType.instance,
+ TimestampType.instance,
+ TimeUUIDType.instance,
+ UUIDType.instance
+ };
+
+ for (AbstractType<?> type : types)
+ {
+ Assert.assertTrue(type.getClass().getSimpleName(), UDHelper.isNullOrEmpty(type, ByteBufferUtil.EMPTY_BYTE_BUFFER));
+ Assert.assertTrue("reversed " + type.getClass().getSimpleName(),
+ UDHelper.isNullOrEmpty(ReversedType.getInstance(type), ByteBufferUtil.EMPTY_BYTE_BUFFER));
+ }
+ }
+}
http://git-wip-us.apache.org/repos/asf/cassandra/blob/4df4f79c/test/unit/org/apache/cassandra/cql3/UFTest.java
----------------------------------------------------------------------
diff --git a/test/unit/org/apache/cassandra/cql3/UFTest.java b/test/unit/org/apache/cassandra/cql3/UFTest.java
index e1b2b5f..f041b3a 100644
--- a/test/unit/org/apache/cassandra/cql3/UFTest.java
+++ b/test/unit/org/apache/cassandra/cql3/UFTest.java
@@ -18,10 +18,17 @@
package org.apache.cassandra.cql3;
import java.math.BigDecimal;
import java.math.BigInteger;
-import java.util.*;
+import java.nio.ByteBuffer;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Date;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.TreeMap;
+import java.util.TreeSet;
import org.junit.Assert;
-import org.junit.Ignore;
import org.junit.Test;
import com.datastax.driver.core.*;
@@ -36,6 +43,7 @@ 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 org.apache.cassandra.utils.ByteBufferUtil;
public class UFTest extends CQLTester
{
@@ -2442,4 +2450,112 @@ public class UFTest extends CQLTester
assertInvalidMessage("The function arguments should not be frozen",
"DROP FUNCTION " + functionName + "(frozen<" + myType + ">);");
}
+
+ @Test
+ public void testEmptyString() throws Throwable
+ {
+ createTable("CREATE TABLE %s (key int primary key, sval text, aval ascii, bval blob, empty_int int)");
+ execute("INSERT INTO %s (key, sval, aval, bval, empty_int) VALUES (?, ?, ?, ?, blobAsInt(0x))", 1, "", "", ByteBuffer.allocate(0));
+
+ String fNameSRC = createFunction(KEYSPACE_PER_TEST, "text",
+ "CREATE OR REPLACE FUNCTION %s(val text) " +
+ "CALLED ON NULL INPUT " +
+ "RETURNS text " +
+ "LANGUAGE JAVA\n" +
+ "AS 'return val;'");
+
+ String fNameSCC = createFunction(KEYSPACE_PER_TEST, "text",
+ "CREATE OR REPLACE FUNCTION %s(val text) " +
+ "CALLED ON NULL INPUT " +
+ "RETURNS text " +
+ "LANGUAGE JAVA\n" +
+ "AS 'return \"\";'");
+
+ String fNameSRN = createFunction(KEYSPACE_PER_TEST, "text",
+ "CREATE OR REPLACE FUNCTION %s(val text) " +
+ "RETURNS NULL ON NULL INPUT " +
+ "RETURNS text " +
+ "LANGUAGE JAVA\n" +
+ "AS 'return val;'");
+
+ String fNameSCN = createFunction(KEYSPACE_PER_TEST, "text",
+ "CREATE OR REPLACE FUNCTION %s(val text) " +
+ "RETURNS NULL ON NULL INPUT " +
+ "RETURNS text " +
+ "LANGUAGE JAVA\n" +
+ "AS 'return \"\";'");
+
+ String fNameBRC = createFunction(KEYSPACE_PER_TEST, "blob",
+ "CREATE OR REPLACE FUNCTION %s(val blob) " +
+ "CALLED ON NULL INPUT " +
+ "RETURNS blob " +
+ "LANGUAGE JAVA\n" +
+ "AS 'return val;'");
+
+ String fNameBCC = createFunction(KEYSPACE_PER_TEST, "blob",
+ "CREATE OR REPLACE FUNCTION %s(val blob) " +
+ "CALLED ON NULL INPUT " +
+ "RETURNS blob " +
+ "LANGUAGE JAVA\n" +
+ "AS 'return ByteBuffer.allocate(0);'");
+
+ String fNameBRN = createFunction(KEYSPACE_PER_TEST, "blob",
+ "CREATE OR REPLACE FUNCTION %s(val blob) " +
+ "RETURNS NULL ON NULL INPUT " +
+ "RETURNS blob " +
+ "LANGUAGE JAVA\n" +
+ "AS 'return val;'");
+
+ String fNameBCN = createFunction(KEYSPACE_PER_TEST, "blob",
+ "CREATE OR REPLACE FUNCTION %s(val blob) " +
+ "RETURNS NULL ON NULL INPUT " +
+ "RETURNS blob " +
+ "LANGUAGE JAVA\n" +
+ "AS 'return ByteBuffer.allocate(0);'");
+
+ String fNameIRC = createFunction(KEYSPACE_PER_TEST, "int",
+ "CREATE OR REPLACE FUNCTION %s(val int) " +
+ "CALLED ON NULL INPUT " +
+ "RETURNS int " +
+ "LANGUAGE JAVA\n" +
+ "AS 'return val;'");
+
+ String fNameICC = createFunction(KEYSPACE_PER_TEST, "int",
+ "CREATE OR REPLACE FUNCTION %s(val int) " +
+ "CALLED ON NULL INPUT " +
+ "RETURNS int " +
+ "LANGUAGE JAVA\n" +
+ "AS 'return 0;'");
+
+ String fNameIRN = createFunction(KEYSPACE_PER_TEST, "int",
+ "CREATE OR REPLACE FUNCTION %s(val int) " +
+ "RETURNS NULL ON NULL INPUT " +
+ "RETURNS int " +
+ "LANGUAGE JAVA\n" +
+ "AS 'return val;'");
+
+ String fNameICN = createFunction(KEYSPACE_PER_TEST, "blob",
+ "CREATE OR REPLACE FUNCTION %s(val int) " +
+ "RETURNS NULL ON NULL INPUT " +
+ "RETURNS int " +
+ "LANGUAGE JAVA\n" +
+ "AS 'return 0;'");
+
+ assertRows(execute("SELECT " + fNameSRC + "(sval) FROM %s"), row(""));
+ assertRows(execute("SELECT " + fNameSRN + "(sval) FROM %s"), row(""));
+ assertRows(execute("SELECT " + fNameSCC + "(sval) FROM %s"), row(""));
+ assertRows(execute("SELECT " + fNameSCN + "(sval) FROM %s"), row(""));
+ assertRows(execute("SELECT " + fNameSRC + "(aval) FROM %s"), row(""));
+ assertRows(execute("SELECT " + fNameSRN + "(aval) FROM %s"), row(""));
+ assertRows(execute("SELECT " + fNameSCC + "(aval) FROM %s"), row(""));
+ assertRows(execute("SELECT " + fNameSCN + "(aval) FROM %s"), row(""));
+ assertRows(execute("SELECT " + fNameBRC + "(bval) FROM %s"), row(ByteBufferUtil.EMPTY_BYTE_BUFFER));
+ assertRows(execute("SELECT " + fNameBRN + "(bval) FROM %s"), row(ByteBufferUtil.EMPTY_BYTE_BUFFER));
+ assertRows(execute("SELECT " + fNameBCC + "(bval) FROM %s"), row(ByteBufferUtil.EMPTY_BYTE_BUFFER));
+ assertRows(execute("SELECT " + fNameBCN + "(bval) FROM %s"), row(ByteBufferUtil.EMPTY_BYTE_BUFFER));
+ assertRows(execute("SELECT " + fNameIRC + "(empty_int) FROM %s"), row(new Object[]{null}));
+ assertRows(execute("SELECT " + fNameIRN + "(empty_int) FROM %s"), row(new Object[]{null}));
+ assertRows(execute("SELECT " + fNameICC + "(empty_int) FROM %s"), row(0));
+ assertRows(execute("SELECT " + fNameICN + "(empty_int) FROM %s"), row(new Object[]{null}));
+ }
}
[3/3] cassandra git commit: Merge branch 'cassandra-2.2' into trunk
Posted by sn...@apache.org.
Merge branch 'cassandra-2.2' into trunk
Project: http://git-wip-us.apache.org/repos/asf/cassandra/repo
Commit: http://git-wip-us.apache.org/repos/asf/cassandra/commit/5c4a5b3b
Tree: http://git-wip-us.apache.org/repos/asf/cassandra/tree/5c4a5b3b
Diff: http://git-wip-us.apache.org/repos/asf/cassandra/diff/5c4a5b3b
Branch: refs/heads/trunk
Commit: 5c4a5b3b29e24bb0f4335bdb89d5c134ba88f921
Parents: 9718e13 4df4f79
Author: Robert Stupp <sn...@snazy.de>
Authored: Thu Jun 4 16:01:36 2015 +0200
Committer: Robert Stupp <sn...@snazy.de>
Committed: Thu Jun 4 16:01:36 2015 +0200
----------------------------------------------------------------------
CHANGES.txt | 1 +
.../cassandra/cql3/functions/UDFunction.java | 21 ++-
.../cassandra/cql3/functions/UDHelper.java | 7 +
.../statements/CreateAggregateStatement.java | 2 +
.../cassandra/db/marshal/AbstractType.java | 8 +
.../cassandra/db/marshal/BooleanType.java | 5 +
.../cassandra/db/marshal/CounterColumnType.java | 5 +
.../apache/cassandra/db/marshal/DateType.java | 5 +
.../cassandra/db/marshal/DecimalType.java | 5 +
.../apache/cassandra/db/marshal/DoubleType.java | 5 +
.../apache/cassandra/db/marshal/FloatType.java | 5 +
.../cassandra/db/marshal/InetAddressType.java | 5 +
.../apache/cassandra/db/marshal/Int32Type.java | 5 +
.../cassandra/db/marshal/IntegerType.java | 5 +
.../cassandra/db/marshal/LexicalUUIDType.java | 5 +
.../apache/cassandra/db/marshal/LongType.java | 5 +
.../cassandra/db/marshal/ReversedType.java | 5 +
.../cassandra/db/marshal/TimeUUIDType.java | 5 +
.../cassandra/db/marshal/TimestampType.java | 5 +
.../apache/cassandra/db/marshal/UUIDType.java | 5 +
.../apache/cassandra/cql3/AggregationTest.java | 69 +++++++++
.../org/apache/cassandra/cql3/UDHelperTest.java | 146 +++++++++++++++++++
test/unit/org/apache/cassandra/cql3/UFTest.java | 120 ++++++++++++++-
23 files changed, 439 insertions(+), 10 deletions(-)
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/cassandra/blob/5c4a5b3b/CHANGES.txt
----------------------------------------------------------------------
diff --cc CHANGES.txt
index ea619e6,2ed9ce9..c6e8651
--- a/CHANGES.txt
+++ b/CHANGES.txt
@@@ -1,10 -1,5 +1,11 @@@
+3.0:
+ * Decommissioned nodes will not rejoin the cluster (CASSANDRA-8801)
+ * Change gossip stabilization to use endpoit size (CASSANDRA-9401)
+ * Change default garbage collector to G1 (CASSANDRA-7486)
+
+
2.2
+ * Empty INITCOND treated as null in aggregate (CASSANDRA-9457)
* Remove use of Cell in Thrift MapReduce classes (CASSANDRA-8609)
* Integrate pre-release Java Driver 2.2-rc1, custom build (CASSANDRA-9493)
* Clean up gossiper logic for old versions (CASSANDRA-9370)
[2/3] cassandra git commit: Empty INITCOND treated as null in
aggregate
Posted by sn...@apache.org.
Empty INITCOND treated as null in aggregate
patch by Robert Stupp; reviewed by Sylvain Lebresne for CASSANDRA-9457
Project: http://git-wip-us.apache.org/repos/asf/cassandra/repo
Commit: http://git-wip-us.apache.org/repos/asf/cassandra/commit/4df4f79c
Tree: http://git-wip-us.apache.org/repos/asf/cassandra/tree/4df4f79c
Diff: http://git-wip-us.apache.org/repos/asf/cassandra/diff/4df4f79c
Branch: refs/heads/trunk
Commit: 4df4f79c563232285137ab2d506836de4e4efa1d
Parents: 1936657
Author: Robert Stupp <sn...@snazy.de>
Authored: Thu Jun 4 16:01:23 2015 +0200
Committer: Robert Stupp <sn...@snazy.de>
Committed: Thu Jun 4 16:01:23 2015 +0200
----------------------------------------------------------------------
CHANGES.txt | 1 +
.../cassandra/cql3/functions/UDFunction.java | 21 ++-
.../cassandra/cql3/functions/UDHelper.java | 7 +
.../statements/CreateAggregateStatement.java | 2 +
.../cassandra/db/marshal/AbstractType.java | 8 +
.../cassandra/db/marshal/BooleanType.java | 5 +
.../cassandra/db/marshal/CounterColumnType.java | 5 +
.../apache/cassandra/db/marshal/DateType.java | 5 +
.../cassandra/db/marshal/DecimalType.java | 5 +
.../apache/cassandra/db/marshal/DoubleType.java | 5 +
.../apache/cassandra/db/marshal/FloatType.java | 5 +
.../cassandra/db/marshal/InetAddressType.java | 5 +
.../apache/cassandra/db/marshal/Int32Type.java | 5 +
.../cassandra/db/marshal/IntegerType.java | 5 +
.../cassandra/db/marshal/LexicalUUIDType.java | 5 +
.../apache/cassandra/db/marshal/LongType.java | 5 +
.../cassandra/db/marshal/ReversedType.java | 5 +
.../cassandra/db/marshal/TimeUUIDType.java | 5 +
.../cassandra/db/marshal/TimestampType.java | 5 +
.../apache/cassandra/db/marshal/UUIDType.java | 5 +
.../apache/cassandra/cql3/AggregationTest.java | 69 +++++++++
.../org/apache/cassandra/cql3/UDHelperTest.java | 146 +++++++++++++++++++
test/unit/org/apache/cassandra/cql3/UFTest.java | 120 ++++++++++++++-
23 files changed, 439 insertions(+), 10 deletions(-)
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/cassandra/blob/4df4f79c/CHANGES.txt
----------------------------------------------------------------------
diff --git a/CHANGES.txt b/CHANGES.txt
index 882279f..2ed9ce9 100644
--- a/CHANGES.txt
+++ b/CHANGES.txt
@@ -1,4 +1,5 @@
2.2
+ * Empty INITCOND treated as null in aggregate (CASSANDRA-9457)
* Remove use of Cell in Thrift MapReduce classes (CASSANDRA-8609)
* Integrate pre-release Java Driver 2.2-rc1, custom build (CASSANDRA-9493)
* Clean up gossiper logic for old versions (CASSANDRA-9370)
http://git-wip-us.apache.org/repos/asf/cassandra/blob/4df4f79c/src/java/org/apache/cassandra/cql3/functions/UDFunction.java
----------------------------------------------------------------------
diff --git a/src/java/org/apache/cassandra/cql3/functions/UDFunction.java b/src/java/org/apache/cassandra/cql3/functions/UDFunction.java
index a01f08f..0bf6078 100644
--- a/src/java/org/apache/cassandra/cql3/functions/UDFunction.java
+++ b/src/java/org/apache/cassandra/cql3/functions/UDFunction.java
@@ -146,8 +146,8 @@ public abstract class UDFunction extends AbstractFunction implements ScalarFunct
public boolean isCallableWrtNullable(List<ByteBuffer> parameters)
{
if (!calledOnNullInput)
- for (ByteBuffer parameter : parameters)
- if (parameter == null || parameter.remaining() == 0)
+ for (int i = 0; i < parameters.size(); i++)
+ if (UDHelper.isNullOrEmpty(argTypes.get(i), parameters.get(i)))
return false;
return true;
}
@@ -194,37 +194,42 @@ public abstract class UDFunction extends AbstractFunction implements ScalarFunct
*/
protected Object compose(int protocolVersion, int argIndex, ByteBuffer value)
{
- return value == null ? null : argDataTypes[argIndex].deserialize(value, ProtocolVersion.fromInt(protocolVersion));
+ return UDHelper.isNullOrEmpty(argTypes.get(argIndex), value) ? null : argDataTypes[argIndex].deserialize(value, ProtocolVersion.fromInt(protocolVersion));
}
// do not remove - used by generated Java UDFs
protected float compose_float(int protocolVersion, int argIndex, ByteBuffer value)
{
- return value == null ? 0f : (float)DataType.cfloat().deserialize(value, ProtocolVersion.fromInt(protocolVersion));
+ assert value != null && value.remaining() > 0;
+ return (float)DataType.cfloat().deserialize(value, ProtocolVersion.fromInt(protocolVersion));
}
// do not remove - used by generated Java UDFs
protected double compose_double(int protocolVersion, int argIndex, ByteBuffer value)
{
- return value == null ? 0d : (double)DataType.cdouble().deserialize(value, ProtocolVersion.fromInt(protocolVersion));
+ assert value != null && value.remaining() > 0;
+ return (double)DataType.cdouble().deserialize(value, ProtocolVersion.fromInt(protocolVersion));
}
// do not remove - used by generated Java UDFs
protected int compose_int(int protocolVersion, int argIndex, ByteBuffer value)
{
- return value == null ? 0 : (int)DataType.cint().deserialize(value, ProtocolVersion.fromInt(protocolVersion));
+ assert value != null && value.remaining() > 0;
+ return (int)DataType.cint().deserialize(value, ProtocolVersion.fromInt(protocolVersion));
}
// do not remove - used by generated Java UDFs
protected long compose_long(int protocolVersion, int argIndex, ByteBuffer value)
{
- return value == null ? 0L : (long)DataType.bigint().deserialize(value, ProtocolVersion.fromInt(protocolVersion));
+ assert value != null && value.remaining() > 0;
+ return (long)DataType.bigint().deserialize(value, ProtocolVersion.fromInt(protocolVersion));
}
// do not remove - used by generated Java UDFs
protected boolean compose_boolean(int protocolVersion, int argIndex, ByteBuffer value)
{
- return value != null && (boolean) DataType.cboolean().deserialize(value, ProtocolVersion.fromInt(protocolVersion));
+ assert value != null && value.remaining() > 0;
+ return (boolean) DataType.cboolean().deserialize(value, ProtocolVersion.fromInt(protocolVersion));
}
/**
http://git-wip-us.apache.org/repos/asf/cassandra/blob/4df4f79c/src/java/org/apache/cassandra/cql3/functions/UDHelper.java
----------------------------------------------------------------------
diff --git a/src/java/org/apache/cassandra/cql3/functions/UDHelper.java b/src/java/org/apache/cassandra/cql3/functions/UDHelper.java
index 55a0888..d1d12e6 100644
--- a/src/java/org/apache/cassandra/cql3/functions/UDHelper.java
+++ b/src/java/org/apache/cassandra/cql3/functions/UDHelper.java
@@ -20,6 +20,7 @@ package org.apache.cassandra.cql3.functions;
import java.lang.invoke.MethodHandle;
import java.lang.invoke.MethodHandles;
import java.lang.reflect.Method;
+import java.nio.ByteBuffer;
import java.util.List;
import com.datastax.driver.core.DataType;
@@ -119,4 +120,10 @@ public final class UDHelper
throw new RuntimeException("cannot parse driver type " + cqlType.getType().toString(), e);
}
}
+
+ public static boolean isNullOrEmpty(AbstractType<?> type, ByteBuffer bb)
+ {
+ return bb == null ||
+ (bb.remaining() == 0 && type.isEmptyValueMeaningless());
+ }
}
http://git-wip-us.apache.org/repos/asf/cassandra/blob/4df4f79c/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 ae793a3..8b6c8d6 100644
--- a/src/java/org/apache/cassandra/cql3/statements/CreateAggregateStatement.java
+++ b/src/java/org/apache/cassandra/cql3/statements/CreateAggregateStatement.java
@@ -112,6 +112,8 @@ public final class CreateAggregateStatement extends SchemaAlteringStatement
{
ColumnSpecification receiver = new ColumnSpecification(functionName.keyspace, "--dummy--", new ColumnIdentifier("(aggregate_initcond)", true), stateType);
initcond = ival.prepare(functionName.keyspace, receiver).bindAndGet(QueryOptions.DEFAULT);
+ if (Constants.NULL_LITERAL != ival && UDHelper.isNullOrEmpty(stateType, initcond))
+ throw new InvalidRequestException("INITCOND must not be empty for all types except TEXT, ASCII, BLOB");
}
return super.prepare();
http://git-wip-us.apache.org/repos/asf/cassandra/blob/4df4f79c/src/java/org/apache/cassandra/db/marshal/AbstractType.java
----------------------------------------------------------------------
diff --git a/src/java/org/apache/cassandra/db/marshal/AbstractType.java b/src/java/org/apache/cassandra/db/marshal/AbstractType.java
index 6602414..aa25a81 100644
--- a/src/java/org/apache/cassandra/db/marshal/AbstractType.java
+++ b/src/java/org/apache/cassandra/db/marshal/AbstractType.java
@@ -257,6 +257,14 @@ public abstract class AbstractType<T> implements Comparator<ByteBuffer>
}
/**
+ * Returns {@code true} for types where empty should be handled like {@code null} like {@link Int32Type}.
+ */
+ public boolean isEmptyValueMeaningless()
+ {
+ return false;
+ }
+
+ /**
* @param ignoreFreezing if true, the type string will not be wrapped with FrozenType(...), even if this type is frozen.
*/
public String toString(boolean ignoreFreezing)
http://git-wip-us.apache.org/repos/asf/cassandra/blob/4df4f79c/src/java/org/apache/cassandra/db/marshal/BooleanType.java
----------------------------------------------------------------------
diff --git a/src/java/org/apache/cassandra/db/marshal/BooleanType.java b/src/java/org/apache/cassandra/db/marshal/BooleanType.java
index 5c1bf42..bfe8c34 100644
--- a/src/java/org/apache/cassandra/db/marshal/BooleanType.java
+++ b/src/java/org/apache/cassandra/db/marshal/BooleanType.java
@@ -37,6 +37,11 @@ public class BooleanType extends AbstractType<Boolean>
BooleanType() {} // singleton
+ public boolean isEmptyValueMeaningless()
+ {
+ return true;
+ }
+
public int compare(ByteBuffer o1, ByteBuffer o2)
{
if (!o1.hasRemaining() || !o2.hasRemaining())
http://git-wip-us.apache.org/repos/asf/cassandra/blob/4df4f79c/src/java/org/apache/cassandra/db/marshal/CounterColumnType.java
----------------------------------------------------------------------
diff --git a/src/java/org/apache/cassandra/db/marshal/CounterColumnType.java b/src/java/org/apache/cassandra/db/marshal/CounterColumnType.java
index 0fea87b..4b3ce82 100644
--- a/src/java/org/apache/cassandra/db/marshal/CounterColumnType.java
+++ b/src/java/org/apache/cassandra/db/marshal/CounterColumnType.java
@@ -32,6 +32,11 @@ public class CounterColumnType extends AbstractType<Long>
CounterColumnType() {} // singleton
+ public boolean isEmptyValueMeaningless()
+ {
+ return true;
+ }
+
public boolean isCounter()
{
return true;
http://git-wip-us.apache.org/repos/asf/cassandra/blob/4df4f79c/src/java/org/apache/cassandra/db/marshal/DateType.java
----------------------------------------------------------------------
diff --git a/src/java/org/apache/cassandra/db/marshal/DateType.java b/src/java/org/apache/cassandra/db/marshal/DateType.java
index 806bbcf..359ce52 100644
--- a/src/java/org/apache/cassandra/db/marshal/DateType.java
+++ b/src/java/org/apache/cassandra/db/marshal/DateType.java
@@ -39,6 +39,11 @@ public class DateType extends AbstractType<Date>
DateType() {} // singleton
+ public boolean isEmptyValueMeaningless()
+ {
+ return true;
+ }
+
public int compare(ByteBuffer o1, ByteBuffer o2)
{
if (!o1.hasRemaining() || !o2.hasRemaining())
http://git-wip-us.apache.org/repos/asf/cassandra/blob/4df4f79c/src/java/org/apache/cassandra/db/marshal/DecimalType.java
----------------------------------------------------------------------
diff --git a/src/java/org/apache/cassandra/db/marshal/DecimalType.java b/src/java/org/apache/cassandra/db/marshal/DecimalType.java
index f1388ce..4052d70 100644
--- a/src/java/org/apache/cassandra/db/marshal/DecimalType.java
+++ b/src/java/org/apache/cassandra/db/marshal/DecimalType.java
@@ -34,6 +34,11 @@ public class DecimalType extends AbstractType<BigDecimal>
DecimalType() {} // singleton
+ public boolean isEmptyValueMeaningless()
+ {
+ return true;
+ }
+
public int compare(ByteBuffer o1, ByteBuffer o2)
{
if (!o1.hasRemaining() || !o2.hasRemaining())
http://git-wip-us.apache.org/repos/asf/cassandra/blob/4df4f79c/src/java/org/apache/cassandra/db/marshal/DoubleType.java
----------------------------------------------------------------------
diff --git a/src/java/org/apache/cassandra/db/marshal/DoubleType.java b/src/java/org/apache/cassandra/db/marshal/DoubleType.java
index fdfd2d2..661b3c9 100644
--- a/src/java/org/apache/cassandra/db/marshal/DoubleType.java
+++ b/src/java/org/apache/cassandra/db/marshal/DoubleType.java
@@ -33,6 +33,11 @@ public class DoubleType extends AbstractType<Double>
DoubleType() {} // singleton
+ public boolean isEmptyValueMeaningless()
+ {
+ return true;
+ }
+
public int compare(ByteBuffer o1, ByteBuffer o2)
{
if (!o1.hasRemaining() || !o2.hasRemaining())
http://git-wip-us.apache.org/repos/asf/cassandra/blob/4df4f79c/src/java/org/apache/cassandra/db/marshal/FloatType.java
----------------------------------------------------------------------
diff --git a/src/java/org/apache/cassandra/db/marshal/FloatType.java b/src/java/org/apache/cassandra/db/marshal/FloatType.java
index 722df87..af02cad 100644
--- a/src/java/org/apache/cassandra/db/marshal/FloatType.java
+++ b/src/java/org/apache/cassandra/db/marshal/FloatType.java
@@ -34,6 +34,11 @@ public class FloatType extends AbstractType<Float>
FloatType() {} // singleton
+ public boolean isEmptyValueMeaningless()
+ {
+ return true;
+ }
+
public int compare(ByteBuffer o1, ByteBuffer o2)
{
if (!o1.hasRemaining() || !o2.hasRemaining())
http://git-wip-us.apache.org/repos/asf/cassandra/blob/4df4f79c/src/java/org/apache/cassandra/db/marshal/InetAddressType.java
----------------------------------------------------------------------
diff --git a/src/java/org/apache/cassandra/db/marshal/InetAddressType.java b/src/java/org/apache/cassandra/db/marshal/InetAddressType.java
index a4eac07..4901c74 100644
--- a/src/java/org/apache/cassandra/db/marshal/InetAddressType.java
+++ b/src/java/org/apache/cassandra/db/marshal/InetAddressType.java
@@ -34,6 +34,11 @@ public class InetAddressType extends AbstractType<InetAddress>
InetAddressType() {} // singleton
+ public boolean isEmptyValueMeaningless()
+ {
+ return true;
+ }
+
public int compare(ByteBuffer o1, ByteBuffer o2)
{
return ByteBufferUtil.compareUnsigned(o1, o2);
http://git-wip-us.apache.org/repos/asf/cassandra/blob/4df4f79c/src/java/org/apache/cassandra/db/marshal/Int32Type.java
----------------------------------------------------------------------
diff --git a/src/java/org/apache/cassandra/db/marshal/Int32Type.java b/src/java/org/apache/cassandra/db/marshal/Int32Type.java
index 9ea8f78..67d8142 100644
--- a/src/java/org/apache/cassandra/db/marshal/Int32Type.java
+++ b/src/java/org/apache/cassandra/db/marshal/Int32Type.java
@@ -35,6 +35,11 @@ public class Int32Type extends AbstractType<Integer>
{
} // singleton
+ public boolean isEmptyValueMeaningless()
+ {
+ return true;
+ }
+
public int compare(ByteBuffer o1, ByteBuffer o2)
{
if (!o1.hasRemaining() || !o2.hasRemaining())
http://git-wip-us.apache.org/repos/asf/cassandra/blob/4df4f79c/src/java/org/apache/cassandra/db/marshal/IntegerType.java
----------------------------------------------------------------------
diff --git a/src/java/org/apache/cassandra/db/marshal/IntegerType.java b/src/java/org/apache/cassandra/db/marshal/IntegerType.java
index 5fe8fd8..a3741d4 100644
--- a/src/java/org/apache/cassandra/db/marshal/IntegerType.java
+++ b/src/java/org/apache/cassandra/db/marshal/IntegerType.java
@@ -60,6 +60,11 @@ public final class IntegerType extends AbstractType<BigInteger>
IntegerType() {/* singleton */}
+ public boolean isEmptyValueMeaningless()
+ {
+ return true;
+ }
+
public int compare(ByteBuffer lhs, ByteBuffer rhs)
{
return IntegerType.compareIntegers(lhs, rhs);
http://git-wip-us.apache.org/repos/asf/cassandra/blob/4df4f79c/src/java/org/apache/cassandra/db/marshal/LexicalUUIDType.java
----------------------------------------------------------------------
diff --git a/src/java/org/apache/cassandra/db/marshal/LexicalUUIDType.java b/src/java/org/apache/cassandra/db/marshal/LexicalUUIDType.java
index 3ca5c74..3e00d71 100644
--- a/src/java/org/apache/cassandra/db/marshal/LexicalUUIDType.java
+++ b/src/java/org/apache/cassandra/db/marshal/LexicalUUIDType.java
@@ -36,6 +36,11 @@ public class LexicalUUIDType extends AbstractType<UUID>
{
} // singleton
+ public boolean isEmptyValueMeaningless()
+ {
+ return true;
+ }
+
public int compare(ByteBuffer o1, ByteBuffer o2)
{
if (!o1.hasRemaining() || !o2.hasRemaining())
http://git-wip-us.apache.org/repos/asf/cassandra/blob/4df4f79c/src/java/org/apache/cassandra/db/marshal/LongType.java
----------------------------------------------------------------------
diff --git a/src/java/org/apache/cassandra/db/marshal/LongType.java b/src/java/org/apache/cassandra/db/marshal/LongType.java
index 31bad93..d77d7d0 100644
--- a/src/java/org/apache/cassandra/db/marshal/LongType.java
+++ b/src/java/org/apache/cassandra/db/marshal/LongType.java
@@ -33,6 +33,11 @@ public class LongType extends AbstractType<Long>
LongType() {} // singleton
+ public boolean isEmptyValueMeaningless()
+ {
+ return true;
+ }
+
public int compare(ByteBuffer o1, ByteBuffer o2)
{
return compareLongs(o1, o2);
http://git-wip-us.apache.org/repos/asf/cassandra/blob/4df4f79c/src/java/org/apache/cassandra/db/marshal/ReversedType.java
----------------------------------------------------------------------
diff --git a/src/java/org/apache/cassandra/db/marshal/ReversedType.java b/src/java/org/apache/cassandra/db/marshal/ReversedType.java
index 14d069a..2181f74 100644
--- a/src/java/org/apache/cassandra/db/marshal/ReversedType.java
+++ b/src/java/org/apache/cassandra/db/marshal/ReversedType.java
@@ -60,6 +60,11 @@ public class ReversedType<T> extends AbstractType<T>
this.baseType = baseType;
}
+ public boolean isEmptyValueMeaningless()
+ {
+ return baseType.isEmptyValueMeaningless();
+ }
+
public int compare(ByteBuffer o1, ByteBuffer o2)
{
// An empty byte buffer is always smaller
http://git-wip-us.apache.org/repos/asf/cassandra/blob/4df4f79c/src/java/org/apache/cassandra/db/marshal/TimeUUIDType.java
----------------------------------------------------------------------
diff --git a/src/java/org/apache/cassandra/db/marshal/TimeUUIDType.java b/src/java/org/apache/cassandra/db/marshal/TimeUUIDType.java
index 3b38582..a1d8d82 100644
--- a/src/java/org/apache/cassandra/db/marshal/TimeUUIDType.java
+++ b/src/java/org/apache/cassandra/db/marshal/TimeUUIDType.java
@@ -35,6 +35,11 @@ public class TimeUUIDType extends AbstractType<UUID>
{
} // singleton
+ public boolean isEmptyValueMeaningless()
+ {
+ return true;
+ }
+
public int compare(ByteBuffer b1, ByteBuffer b2)
{
// Compare for length
http://git-wip-us.apache.org/repos/asf/cassandra/blob/4df4f79c/src/java/org/apache/cassandra/db/marshal/TimestampType.java
----------------------------------------------------------------------
diff --git a/src/java/org/apache/cassandra/db/marshal/TimestampType.java b/src/java/org/apache/cassandra/db/marshal/TimestampType.java
index 095f2c2..38e0296 100644
--- a/src/java/org/apache/cassandra/db/marshal/TimestampType.java
+++ b/src/java/org/apache/cassandra/db/marshal/TimestampType.java
@@ -45,6 +45,11 @@ public class TimestampType extends AbstractType<Date>
private TimestampType() {} // singleton
+ public boolean isEmptyValueMeaningless()
+ {
+ return true;
+ }
+
public int compare(ByteBuffer o1, ByteBuffer o2)
{
return LongType.compareLongs(o1, o2);
http://git-wip-us.apache.org/repos/asf/cassandra/blob/4df4f79c/src/java/org/apache/cassandra/db/marshal/UUIDType.java
----------------------------------------------------------------------
diff --git a/src/java/org/apache/cassandra/db/marshal/UUIDType.java b/src/java/org/apache/cassandra/db/marshal/UUIDType.java
index eba696e..0250eb20 100644
--- a/src/java/org/apache/cassandra/db/marshal/UUIDType.java
+++ b/src/java/org/apache/cassandra/db/marshal/UUIDType.java
@@ -50,6 +50,11 @@ public class UUIDType extends AbstractType<UUID>
{
}
+ public boolean isEmptyValueMeaningless()
+ {
+ return true;
+ }
+
public int compare(ByteBuffer b1, ByteBuffer b2)
{
// Compare for length
http://git-wip-us.apache.org/repos/asf/cassandra/blob/4df4f79c/test/unit/org/apache/cassandra/cql3/AggregationTest.java
----------------------------------------------------------------------
diff --git a/test/unit/org/apache/cassandra/cql3/AggregationTest.java b/test/unit/org/apache/cassandra/cql3/AggregationTest.java
index af775e6..af68ddc 100644
--- a/test/unit/org/apache/cassandra/cql3/AggregationTest.java
+++ b/test/unit/org/apache/cassandra/cql3/AggregationTest.java
@@ -1378,4 +1378,73 @@ public class AggregationTest extends CQLTester
assertInvalidMessage("The function arguments should not be frozen",
"DROP AGGREGATE %s (frozen<" + myType + ">);");
}
+
+ @Test
+ public void testEmptyValues() throws Throwable
+ {
+ createTable("CREATE TABLE %s (a int primary key, b text)");
+ execute("INSERT INTO %s (a, b) VALUES (1, '')");
+ execute("INSERT INTO %s (a, b) VALUES (2, '')");
+ execute("INSERT INTO %s (a, b) VALUES (3, '')");
+
+ String fCON = createFunction(KEYSPACE,
+ "text, text",
+ "CREATE FUNCTION %s(a text, b text) " +
+ "CALLED ON NULL INPUT " +
+ "RETURNS text " +
+ "LANGUAGE java " +
+ "AS 'return a + \"x\" + b + \"y\";'");
+
+ String fCONf = createFunction(KEYSPACE,
+ "text",
+ "CREATE FUNCTION %s(a text) " +
+ "CALLED ON NULL INPUT " +
+ "RETURNS text " +
+ "LANGUAGE java " +
+ "AS 'return \"fin\" + a;'");
+
+ String aCON = createAggregate(KEYSPACE,
+ "text, text",
+ "CREATE AGGREGATE %s(text) " +
+ "SFUNC " + shortFunctionName(fCON) + ' ' +
+ "STYPE text " +
+ "FINALFUNC " + shortFunctionName(fCONf) + ' ' +
+ "INITCOND ''");
+
+ String fRNON = createFunction(KEYSPACE,
+ "text",
+ "CREATE FUNCTION %s(a text, b text) " +
+ "RETURNS NULL ON NULL INPUT " +
+ "RETURNS text " +
+ "LANGUAGE java " +
+ "AS 'return a + \"x\" + b + \"y\";'");
+
+ String fRNONf = createFunction(KEYSPACE,
+ "text",
+ "CREATE FUNCTION %s(a text) " +
+ "RETURNS NULL ON NULL INPUT " +
+ "RETURNS text " +
+ "LANGUAGE java " +
+ "AS 'return \"fin\" + a;'");
+
+ String aRNON = createAggregate(KEYSPACE,
+ "int, int",
+ "CREATE AGGREGATE %s(text) " +
+ "SFUNC " + shortFunctionName(fRNON) + ' ' +
+ "STYPE text " +
+ "FINALFUNC " + shortFunctionName(fRNONf) + ' ' +
+ "INITCOND ''");
+
+ assertRows(execute("SELECT " + aCON + "(b) FROM %s"), row("finxyxyxy"));
+ assertRows(execute("SELECT " + aRNON + "(b) FROM %s"), row("finxyxyxy"));
+
+ createTable("CREATE TABLE %s (a int primary key, b text)");
+ execute("INSERT INTO %s (a, b) VALUES (1, null)");
+ execute("INSERT INTO %s (a, b) VALUES (2, null)");
+ execute("INSERT INTO %s (a, b) VALUES (3, null)");
+
+ assertRows(execute("SELECT " + aCON + "(b) FROM %s"), row("finxnullyxnullyxnully"));
+ assertRows(execute("SELECT " + aRNON + "(b) FROM %s"), row("fin"));
+
+ }
}
http://git-wip-us.apache.org/repos/asf/cassandra/blob/4df4f79c/test/unit/org/apache/cassandra/cql3/UDHelperTest.java
----------------------------------------------------------------------
diff --git a/test/unit/org/apache/cassandra/cql3/UDHelperTest.java b/test/unit/org/apache/cassandra/cql3/UDHelperTest.java
new file mode 100644
index 0000000..4a5e78e
--- /dev/null
+++ b/test/unit/org/apache/cassandra/cql3/UDHelperTest.java
@@ -0,0 +1,146 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.cassandra.cql3;
+
+import java.nio.ByteBuffer;
+
+import org.junit.Assert;
+import org.junit.Test;
+
+import org.apache.cassandra.cql3.functions.UDHelper;
+import org.apache.cassandra.db.marshal.AbstractType;
+import org.apache.cassandra.db.marshal.AsciiType;
+import org.apache.cassandra.db.marshal.BooleanType;
+import org.apache.cassandra.db.marshal.ByteType;
+import org.apache.cassandra.db.marshal.BytesType;
+import org.apache.cassandra.db.marshal.CounterColumnType;
+import org.apache.cassandra.db.marshal.DateType;
+import org.apache.cassandra.db.marshal.DecimalType;
+import org.apache.cassandra.db.marshal.DoubleType;
+import org.apache.cassandra.db.marshal.FloatType;
+import org.apache.cassandra.db.marshal.InetAddressType;
+import org.apache.cassandra.db.marshal.Int32Type;
+import org.apache.cassandra.db.marshal.IntegerType;
+import org.apache.cassandra.db.marshal.LongType;
+import org.apache.cassandra.db.marshal.ReversedType;
+import org.apache.cassandra.db.marshal.ShortType;
+import org.apache.cassandra.db.marshal.SimpleDateType;
+import org.apache.cassandra.db.marshal.TimeType;
+import org.apache.cassandra.db.marshal.TimeUUIDType;
+import org.apache.cassandra.db.marshal.TimestampType;
+import org.apache.cassandra.db.marshal.UTF8Type;
+import org.apache.cassandra.db.marshal.UUIDType;
+import org.apache.cassandra.serializers.MarshalException;
+import org.apache.cassandra.serializers.TypeSerializer;
+import org.apache.cassandra.utils.ByteBufferUtil;
+
+public class UDHelperTest
+{
+ static class UFTestCustomType extends AbstractType<String>
+ {
+
+ public ByteBuffer fromString(String source) throws MarshalException
+ {
+ return ByteBuffer.wrap(source.getBytes());
+ }
+
+ public Term fromJSONObject(Object parsed) throws MarshalException
+ {
+ throw new UnsupportedOperationException();
+ }
+
+ public TypeSerializer<String> getSerializer()
+ {
+ return UTF8Type.instance.getSerializer();
+ }
+
+ public int compare(ByteBuffer o1, ByteBuffer o2)
+ {
+ return o1.compareTo(o2);
+ }
+ }
+
+ @Test
+ public void testEmptyVariableLengthTypes()
+ {
+ AbstractType<?>[] types = new AbstractType<?>[]{
+ AsciiType.instance,
+ BytesType.instance,
+ UTF8Type.instance,
+ new UFTestCustomType()
+ };
+
+ for (AbstractType<?> type : types)
+ {
+ Assert.assertFalse("type " + type.getClass().getName(),
+ UDHelper.isNullOrEmpty(type, ByteBufferUtil.EMPTY_BYTE_BUFFER));
+ }
+ }
+
+ @Test
+ public void testNonEmptyPrimitiveTypes()
+ {
+ AbstractType<?>[] types = new AbstractType<?>[]{
+ TimeType.instance,
+ SimpleDateType.instance,
+ ByteType.instance,
+ ShortType.instance
+ };
+
+ for (AbstractType<?> type : types)
+ {
+ try
+ {
+ type.getSerializer().validate(ByteBufferUtil.EMPTY_BYTE_BUFFER);
+ Assert.fail(type.getClass().getSimpleName());
+ }
+ catch (MarshalException e)
+ {
+ //
+ }
+ }
+ }
+
+ @Test
+ public void testEmptiableTypes()
+ {
+ AbstractType<?>[] types = new AbstractType<?>[]{
+ BooleanType.instance,
+ CounterColumnType.instance,
+ DateType.instance,
+ DecimalType.instance,
+ DoubleType.instance,
+ FloatType.instance,
+ InetAddressType.instance,
+ Int32Type.instance,
+ IntegerType.instance,
+ LongType.instance,
+ TimestampType.instance,
+ TimeUUIDType.instance,
+ UUIDType.instance
+ };
+
+ for (AbstractType<?> type : types)
+ {
+ Assert.assertTrue(type.getClass().getSimpleName(), UDHelper.isNullOrEmpty(type, ByteBufferUtil.EMPTY_BYTE_BUFFER));
+ Assert.assertTrue("reversed " + type.getClass().getSimpleName(),
+ UDHelper.isNullOrEmpty(ReversedType.getInstance(type), ByteBufferUtil.EMPTY_BYTE_BUFFER));
+ }
+ }
+}
http://git-wip-us.apache.org/repos/asf/cassandra/blob/4df4f79c/test/unit/org/apache/cassandra/cql3/UFTest.java
----------------------------------------------------------------------
diff --git a/test/unit/org/apache/cassandra/cql3/UFTest.java b/test/unit/org/apache/cassandra/cql3/UFTest.java
index e1b2b5f..f041b3a 100644
--- a/test/unit/org/apache/cassandra/cql3/UFTest.java
+++ b/test/unit/org/apache/cassandra/cql3/UFTest.java
@@ -18,10 +18,17 @@
package org.apache.cassandra.cql3;
import java.math.BigDecimal;
import java.math.BigInteger;
-import java.util.*;
+import java.nio.ByteBuffer;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Date;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.TreeMap;
+import java.util.TreeSet;
import org.junit.Assert;
-import org.junit.Ignore;
import org.junit.Test;
import com.datastax.driver.core.*;
@@ -36,6 +43,7 @@ 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 org.apache.cassandra.utils.ByteBufferUtil;
public class UFTest extends CQLTester
{
@@ -2442,4 +2450,112 @@ public class UFTest extends CQLTester
assertInvalidMessage("The function arguments should not be frozen",
"DROP FUNCTION " + functionName + "(frozen<" + myType + ">);");
}
+
+ @Test
+ public void testEmptyString() throws Throwable
+ {
+ createTable("CREATE TABLE %s (key int primary key, sval text, aval ascii, bval blob, empty_int int)");
+ execute("INSERT INTO %s (key, sval, aval, bval, empty_int) VALUES (?, ?, ?, ?, blobAsInt(0x))", 1, "", "", ByteBuffer.allocate(0));
+
+ String fNameSRC = createFunction(KEYSPACE_PER_TEST, "text",
+ "CREATE OR REPLACE FUNCTION %s(val text) " +
+ "CALLED ON NULL INPUT " +
+ "RETURNS text " +
+ "LANGUAGE JAVA\n" +
+ "AS 'return val;'");
+
+ String fNameSCC = createFunction(KEYSPACE_PER_TEST, "text",
+ "CREATE OR REPLACE FUNCTION %s(val text) " +
+ "CALLED ON NULL INPUT " +
+ "RETURNS text " +
+ "LANGUAGE JAVA\n" +
+ "AS 'return \"\";'");
+
+ String fNameSRN = createFunction(KEYSPACE_PER_TEST, "text",
+ "CREATE OR REPLACE FUNCTION %s(val text) " +
+ "RETURNS NULL ON NULL INPUT " +
+ "RETURNS text " +
+ "LANGUAGE JAVA\n" +
+ "AS 'return val;'");
+
+ String fNameSCN = createFunction(KEYSPACE_PER_TEST, "text",
+ "CREATE OR REPLACE FUNCTION %s(val text) " +
+ "RETURNS NULL ON NULL INPUT " +
+ "RETURNS text " +
+ "LANGUAGE JAVA\n" +
+ "AS 'return \"\";'");
+
+ String fNameBRC = createFunction(KEYSPACE_PER_TEST, "blob",
+ "CREATE OR REPLACE FUNCTION %s(val blob) " +
+ "CALLED ON NULL INPUT " +
+ "RETURNS blob " +
+ "LANGUAGE JAVA\n" +
+ "AS 'return val;'");
+
+ String fNameBCC = createFunction(KEYSPACE_PER_TEST, "blob",
+ "CREATE OR REPLACE FUNCTION %s(val blob) " +
+ "CALLED ON NULL INPUT " +
+ "RETURNS blob " +
+ "LANGUAGE JAVA\n" +
+ "AS 'return ByteBuffer.allocate(0);'");
+
+ String fNameBRN = createFunction(KEYSPACE_PER_TEST, "blob",
+ "CREATE OR REPLACE FUNCTION %s(val blob) " +
+ "RETURNS NULL ON NULL INPUT " +
+ "RETURNS blob " +
+ "LANGUAGE JAVA\n" +
+ "AS 'return val;'");
+
+ String fNameBCN = createFunction(KEYSPACE_PER_TEST, "blob",
+ "CREATE OR REPLACE FUNCTION %s(val blob) " +
+ "RETURNS NULL ON NULL INPUT " +
+ "RETURNS blob " +
+ "LANGUAGE JAVA\n" +
+ "AS 'return ByteBuffer.allocate(0);'");
+
+ String fNameIRC = createFunction(KEYSPACE_PER_TEST, "int",
+ "CREATE OR REPLACE FUNCTION %s(val int) " +
+ "CALLED ON NULL INPUT " +
+ "RETURNS int " +
+ "LANGUAGE JAVA\n" +
+ "AS 'return val;'");
+
+ String fNameICC = createFunction(KEYSPACE_PER_TEST, "int",
+ "CREATE OR REPLACE FUNCTION %s(val int) " +
+ "CALLED ON NULL INPUT " +
+ "RETURNS int " +
+ "LANGUAGE JAVA\n" +
+ "AS 'return 0;'");
+
+ String fNameIRN = createFunction(KEYSPACE_PER_TEST, "int",
+ "CREATE OR REPLACE FUNCTION %s(val int) " +
+ "RETURNS NULL ON NULL INPUT " +
+ "RETURNS int " +
+ "LANGUAGE JAVA\n" +
+ "AS 'return val;'");
+
+ String fNameICN = createFunction(KEYSPACE_PER_TEST, "blob",
+ "CREATE OR REPLACE FUNCTION %s(val int) " +
+ "RETURNS NULL ON NULL INPUT " +
+ "RETURNS int " +
+ "LANGUAGE JAVA\n" +
+ "AS 'return 0;'");
+
+ assertRows(execute("SELECT " + fNameSRC + "(sval) FROM %s"), row(""));
+ assertRows(execute("SELECT " + fNameSRN + "(sval) FROM %s"), row(""));
+ assertRows(execute("SELECT " + fNameSCC + "(sval) FROM %s"), row(""));
+ assertRows(execute("SELECT " + fNameSCN + "(sval) FROM %s"), row(""));
+ assertRows(execute("SELECT " + fNameSRC + "(aval) FROM %s"), row(""));
+ assertRows(execute("SELECT " + fNameSRN + "(aval) FROM %s"), row(""));
+ assertRows(execute("SELECT " + fNameSCC + "(aval) FROM %s"), row(""));
+ assertRows(execute("SELECT " + fNameSCN + "(aval) FROM %s"), row(""));
+ assertRows(execute("SELECT " + fNameBRC + "(bval) FROM %s"), row(ByteBufferUtil.EMPTY_BYTE_BUFFER));
+ assertRows(execute("SELECT " + fNameBRN + "(bval) FROM %s"), row(ByteBufferUtil.EMPTY_BYTE_BUFFER));
+ assertRows(execute("SELECT " + fNameBCC + "(bval) FROM %s"), row(ByteBufferUtil.EMPTY_BYTE_BUFFER));
+ assertRows(execute("SELECT " + fNameBCN + "(bval) FROM %s"), row(ByteBufferUtil.EMPTY_BYTE_BUFFER));
+ assertRows(execute("SELECT " + fNameIRC + "(empty_int) FROM %s"), row(new Object[]{null}));
+ assertRows(execute("SELECT " + fNameIRN + "(empty_int) FROM %s"), row(new Object[]{null}));
+ assertRows(execute("SELECT " + fNameICC + "(empty_int) FROM %s"), row(0));
+ assertRows(execute("SELECT " + fNameICN + "(empty_int) FROM %s"), row(new Object[]{null}));
+ }
}