cassandra-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From sn...@apache.org
Subject [2/3] cassandra git commit: Empty INITCOND treated as null in aggregate
Date Thu, 04 Jun 2015 14:02:09 GMT
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 <snazy@snazy.de>
Authored: Thu Jun 4 16:01:23 2015 +0200
Committer: Robert Stupp <snazy@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}));
+    }
 }


Mime
View raw message