cassandra-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From sn...@apache.org
Subject [3/5] cassandra git commit: Better support of null for UDF
Date Fri, 15 May 2015 17:37:11 GMT
http://git-wip-us.apache.org/repos/asf/cassandra/blob/1937bed9/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 c9779b9..78c6dd9 100644
--- a/test/unit/org/apache/cassandra/cql3/UFTest.java
+++ b/test/unit/org/apache/cassandra/cql3/UFTest.java
@@ -46,6 +46,7 @@ public class UFTest extends CQLTester
         String f = createFunction(KEYSPACE,
                                   "double, double",
                                   "CREATE OR REPLACE FUNCTION %s(state double, val double) " +
+                                  "RETURNS NULL ON NULL INPUT " +
                                   "RETURNS double " +
                                   "LANGUAGE javascript " +
                                   "AS '\"string\";';");
@@ -57,6 +58,7 @@ public class UFTest extends CQLTester
         createFunctionOverload(f,
                                "double, double",
                                "CREATE OR REPLACE FUNCTION %s(state int, val int) " +
+                               "RETURNS NULL ON NULL INPUT " +
                                "RETURNS int " +
                                "LANGUAGE javascript " +
                                "AS '\"string\";';");
@@ -66,6 +68,7 @@ public class UFTest extends CQLTester
                                "int", "int");
 
         schemaChange("CREATE OR REPLACE FUNCTION " + f + "(state int, val int) " +
+                     "RETURNS NULL ON NULL INPUT " +
                      "RETURNS int " +
                      "LANGUAGE javascript " +
                      "AS '\"string\";';");
@@ -86,6 +89,7 @@ public class UFTest extends CQLTester
     {
         String fSin = createFunction(KEYSPACE_PER_TEST, "double",
                                      "CREATE FUNCTION %s ( input double ) " +
+                                     "CALLED ON NULL INPUT " +
                                      "RETURNS double " +
                                      "LANGUAGE java " +
                                      "AS 'return Double.valueOf(Math.sin(input.doubleValue()));'");
@@ -111,6 +115,7 @@ public class UFTest extends CQLTester
 
         String fSin = createFunction(KEYSPACE_PER_TEST, "double",
                                      "CREATE FUNCTION %s ( input double ) " +
+                                     "CALLED ON NULL INPUT " +
                                      "RETURNS double " +
                                      "LANGUAGE java " +
                                      "AS 'return Double.valueOf(Math.sin(input.doubleValue()));'");
@@ -150,9 +155,10 @@ public class UFTest extends CQLTester
         Assert.assertNotNull(QueryProcessor.instance.getPrepared(preparedInsert2.statementId));
 
         execute("CREATE FUNCTION " + fSin + " ( input double ) " +
+                "RETURNS NULL ON NULL INPUT " +
                 "RETURNS double " +
                 "LANGUAGE java " +
-                "AS 'return Double.valueOf(Math.sin(input.doubleValue()));'");
+                "AS 'return Double.valueOf(Math.sin(input));'");
 
         Assert.assertEquals(1, Functions.find(fSinName).size());
 
@@ -220,7 +226,7 @@ public class UFTest extends CQLTester
 
         createTable("CREATE TABLE %s (" +
                     " key int PRIMARY KEY," +
-                    " val " + collectionType + ")");
+                    " val " + collectionType + ')');
 
         ResultMessage.Prepared prepared = QueryProcessor.prepare(
                                                                 String.format("INSERT INTO %s.%s (key, val) VALUES (?, %s)",
@@ -265,6 +271,7 @@ public class UFTest extends CQLTester
         // provide a DelayedValue are removed from the cache in QueryProcessor
         String function = createFunction(KEYSPACE_PER_TEST, "double",
                                         "CREATE FUNCTION %s ( input double ) " +
+                                        "CALLED ON NULL INPUT " +
                                         "RETURNS double " +
                                         "LANGUAGE javascript " +
                                         "AS 'input'");
@@ -304,13 +311,22 @@ public class UFTest extends CQLTester
         // simple creation
         String fSin = createFunction(KEYSPACE_PER_TEST, "double",
                                      "CREATE FUNCTION %s ( input double ) " +
+                                     "CALLED ON NULL INPUT " +
                                      "RETURNS double " +
                                      "LANGUAGE java " +
                                      "AS 'return Math.sin(input);'");
         // check we can't recreate the same function
-        assertInvalidMessage("already exists", "CREATE FUNCTION " + fSin + " ( input double ) RETURNS double LANGUAGE java AS 'return Math.sin(input);'");
+        assertInvalidMessage("already exists",
+                             "CREATE FUNCTION " + fSin + " ( input double ) " +
+                             "CALLED ON NULL INPUT " +
+                             "RETURNS double " +
+                             "LANGUAGE java AS 'return Double.valueOf(Math.sin(input.doubleValue()));'");
+
         // but that it doesn't comply with "IF NOT EXISTS"
-        execute("CREATE FUNCTION IF NOT EXISTS " + fSin + " ( input double ) RETURNS double LANGUAGE java AS 'return Math.sin(input);'");
+        execute("CREATE FUNCTION IF NOT EXISTS " + fSin + " ( input double ) " +
+                "CALLED ON NULL INPUT " +
+                "RETURNS double " +
+                "LANGUAGE java AS 'return Double.valueOf(Math.sin(input.doubleValue()));'");
 
         // Validate that it works as expected
         assertRows(execute("SELECT key, " + fSin + "(d) FROM %s"),
@@ -321,9 +337,16 @@ public class UFTest extends CQLTester
 
         // Replace the method with incompatible return type
         assertInvalidMessage("the new return type text is not compatible with the return type double of existing function",
-                             "CREATE OR REPLACE FUNCTION " + fSin + " ( input double ) RETURNS text LANGUAGE java AS 'return Double.valueOf(42d);'");
+                             "CREATE OR REPLACE FUNCTION " + fSin + " ( input double ) " +
+                             "CALLED ON NULL INPUT " +
+                             "RETURNS text " +
+                             "LANGUAGE java AS 'return Double.valueOf(42d);'");
+
         // proper replacement
-        execute("CREATE OR REPLACE FUNCTION " + fSin + " ( input double ) RETURNS double LANGUAGE java AS 'return Double.valueOf(42d);'");
+        execute("CREATE OR REPLACE FUNCTION " + fSin + " ( input double ) " +
+                "CALLED ON NULL INPUT " +
+                "RETURNS double " +
+                "LANGUAGE java AS 'return Double.valueOf(42d);'");
 
         // Validate the method as been replaced
         assertRows(execute("SELECT key, " + fSin + "(d) FROM %s"),
@@ -335,6 +358,7 @@ public class UFTest extends CQLTester
         // same function but other keyspace
         String fSin2 = createFunction(KEYSPACE, "double",
                                       "CREATE FUNCTION %s ( input double ) " +
+                                      "RETURNS NULL ON NULL INPUT " +
                                       "RETURNS double " +
                                       "LANGUAGE java " +
                                       "AS 'return Math.sin(input);'");
@@ -370,6 +394,7 @@ public class UFTest extends CQLTester
 
         String fRepeat = createFunction(KEYSPACE_PER_TEST, "text,int",
                                         "CREATE FUNCTION %s(v text, n int) " +
+                                        "RETURNS NULL ON NULL INPUT " +
                                         "RETURNS text " +
                                         "LANGUAGE java " +
                                         "AS 'StringBuilder sb = new StringBuilder();\n" +
@@ -390,22 +415,38 @@ public class UFTest extends CQLTester
 
         String fOverload = createFunction(KEYSPACE_PER_TEST, "varchar",
                                           "CREATE FUNCTION %s ( input varchar ) " +
+                                          "RETURNS NULL ON NULL INPUT " +
                                           "RETURNS text " +
                                           "LANGUAGE java " +
                                           "AS 'return \"f1\";'");
         createFunctionOverload(fOverload,
                                "int",
-                               "CREATE OR REPLACE FUNCTION %s(i int) RETURNS text LANGUAGE java AS 'return \"f2\";'");
+                               "CREATE OR REPLACE FUNCTION %s(i int) " +
+                               "RETURNS NULL ON NULL INPUT " +
+                               "RETURNS text " +
+                               "LANGUAGE java " +
+                               "AS 'return \"f2\";'");
         createFunctionOverload(fOverload,
                                "text,text",
-                               "CREATE OR REPLACE FUNCTION %s(v1 text, v2 text) RETURNS text LANGUAGE java AS 'return \"f3\";'");
+                               "CREATE OR REPLACE FUNCTION %s(v1 text, v2 text) " +
+                               "RETURNS NULL ON NULL INPUT " +
+                               "RETURNS text " +
+                               "LANGUAGE java " +
+                               "AS 'return \"f3\";'");
         createFunctionOverload(fOverload,
                                "ascii",
-                               "CREATE OR REPLACE FUNCTION %s(v ascii) RETURNS text LANGUAGE java AS 'return \"f1\";'");
+                               "CREATE OR REPLACE FUNCTION %s(v ascii) " +
+                               "RETURNS NULL ON NULL INPUT " +
+                               "RETURNS text " +
+                               "LANGUAGE java " +
+                               "AS 'return \"f1\";'");
 
         // text == varchar, so this should be considered as a duplicate
         assertInvalidMessage("already exists",
-                             "CREATE FUNCTION " + fOverload + "(v varchar) RETURNS text LANGUAGE java AS 'return \"f1\";'");
+                             "CREATE FUNCTION " + fOverload + "(v varchar) " +
+                             "RETURNS NULL ON NULL INPUT " +
+                             "RETURNS text " +
+                             "LANGUAGE java AS 'return \"f1\";'");
 
         assertRows(execute("SELECT " + fOverload + "(k), " + fOverload + "(v), " + fOverload + "(k, k) FROM %s"),
             row("f1", "f2", "f3")
@@ -451,7 +492,9 @@ public class UFTest extends CQLTester
 
         String fName = createFunction(KEYSPACE_PER_TEST, "double",
                 "CREATE FUNCTION %s( input double ) " +
-                "RETURNS double LANGUAGE java\n" +
+                "CALLED ON NULL INPUT " +
+                "RETURNS double " +
+                "LANGUAGE java " +
                 "AS '\n" +
                 "  // parameter val is of type java.lang.Double\n" +
                 "  /* return type is of type java.lang.Double */\n" +
@@ -469,7 +512,9 @@ public class UFTest extends CQLTester
         );
 
         execute("CREATE OR REPLACE FUNCTION " + fName + "( input double ) " +
-                "RETURNS double LANGUAGE java\n" +
+                "CALLED ON NULL INPUT " +
+                "RETURNS double " +
+                "LANGUAGE java\n" +
                 "AS '\n" +
                 "  return input;\n" +
                 "';");
@@ -490,7 +535,10 @@ public class UFTest extends CQLTester
         String functionBody = "\n  return 1L;\n";
 
         String fName = createFunction(KEYSPACE, "",
-                                      "CREATE OR REPLACE FUNCTION %s() RETURNS bigint LANGUAGE JAVA\n" +
+                                      "CREATE OR REPLACE FUNCTION %s() " +
+                                      "RETURNS NULL ON NULL INPUT " +
+                                      "RETURNS bigint " +
+                                      "LANGUAGE JAVA\n" +
                                       "AS '" +functionBody + "';");
 
         assertRows(execute("SELECT language, body FROM system.schema_functions WHERE keyspace_name=? AND function_name=?",
@@ -512,7 +560,10 @@ public class UFTest extends CQLTester
     {
         try
         {
-            execute("CREATE OR REPLACE FUNCTION " + KEYSPACE + ".jfinv() RETURNS bigint LANGUAGE JAVA\n" +
+            execute("CREATE OR REPLACE FUNCTION " + KEYSPACE + ".jfinv() " +
+                    "RETURNS NULL ON NULL INPUT " +
+                    "RETURNS bigint " +
+                    "LANGUAGE JAVA\n" +
                     "AS '\n" +
                     "foobarbaz" +
                     "\n';");
@@ -526,7 +577,10 @@ public class UFTest extends CQLTester
 
         try
         {
-            execute("CREATE OR REPLACE FUNCTION " + KEYSPACE + ".jfinv() RETURNS bigint LANGUAGE JAVA\n" +
+            execute("CREATE OR REPLACE FUNCTION " + KEYSPACE + ".jfinv() " +
+                    "RETURNS NULL ON NULL INPUT " +
+                    "RETURNS bigint " +
+                    "LANGUAGE JAVA\n" +
                     "AS '\n" +
                     "foobarbaz;" +
                     "\n';");
@@ -543,7 +597,10 @@ public class UFTest extends CQLTester
     public void testJavaFunctionInvalidReturn() throws Throwable
     {
         assertInvalidMessage("system keyspace is not user-modifiable",
-                             "CREATE OR REPLACE FUNCTION jfir(val double) RETURNS double LANGUAGE JAVA\n" +
+                             "CREATE OR REPLACE FUNCTION jfir(val double) " +
+                             "RETURNS NULL ON NULL INPUT " +
+                             "RETURNS double " +
+                             "LANGUAGE JAVA\n" +
                              "AS 'return 1L;';");
     }
 
@@ -554,8 +611,10 @@ public class UFTest extends CQLTester
 
         String fName = createFunction(KEYSPACE, "double",
                                       "CREATE OR REPLACE FUNCTION %s(val double)" +
-                                      "RETURNS double LANGUAGE JAVA " +
-                                      "AS 'return val;';");
+                                      "RETURNS NULL ON NULL INPUT " +
+                                      "RETURNS double " +
+                                      "LANGUAGE JAVA " +
+                                      "AS 'return Double.valueOf(val);';");
 
         execute("INSERT INTO %s (key, val) VALUES (?, ?)", 1, 1L);
         execute("INSERT INTO %s (key, val) VALUES (?, ?)", 2, 2L);
@@ -579,6 +638,7 @@ public class UFTest extends CQLTester
 
         String fName = createFunction(KEYSPACE, "double",
                                       "CREATE OR REPLACE FUNCTION %s(val double) " +
+                                      "CALLED ON NULL INPUT " +
                                       "RETURNS double " +
                                       "LANGUAGE JAVA " +
                                       "AS '" + functionBody + "';");
@@ -607,8 +667,11 @@ public class UFTest extends CQLTester
         execute("CREATE TABLE " + KEYSPACE_PER_TEST + ".second_tab (key int primary key, val double)");
 
         String fName = createFunction(KEYSPACE_PER_TEST, "double",
-                                      "CREATE OR REPLACE FUNCTION %s(val double) RETURNS double LANGUAGE JAVA " +
-                                      "AS 'return val;';");
+                                      "CREATE OR REPLACE FUNCTION %s(val double) " +
+                                      "RETURNS NULL ON NULL INPUT " +
+                                      "RETURNS double " +
+                                      "LANGUAGE JAVA " +
+                                      "AS 'return Double.valueOf(val);';");
 
         execute("INSERT INTO %s (key, val) VALUES (?, ?)", 1, 1d);
         execute("INSERT INTO %s (key, val) VALUES (?, ?)", 2, 2d);
@@ -632,7 +695,10 @@ public class UFTest extends CQLTester
         execute("CREATE TABLE " + KEYSPACE_PER_TEST + ".second_tab (key int primary key, val double)");
 
         String fName = createFunction(KEYSPACE_PER_TEST, "",
-                                      "CREATE OR REPLACE FUNCTION %s() RETURNS timestamp LANGUAGE JAVA " +
+                                      "CREATE OR REPLACE FUNCTION %s() " +
+                                      "RETURNS NULL ON NULL INPUT " +
+                                      "RETURNS timestamp " +
+                                      "LANGUAGE JAVA " +
                                       "AS 'return null;';");
 
         execute("INSERT INTO " + KEYSPACE_PER_TEST + ".second_tab (key, val) VALUES (?, ?)", 1, 1d);
@@ -656,24 +722,39 @@ public class UFTest extends CQLTester
     @Test
     public void testFunctionInSystemKS() throws Throwable
     {
-        execute("CREATE OR REPLACE FUNCTION " + KEYSPACE + ".dateof(val timeuuid) RETURNS timestamp LANGUAGE JAVA\n" +
+        execute("CREATE OR REPLACE FUNCTION " + KEYSPACE + ".dateof(val timeuuid) " +
+                "RETURNS NULL ON NULL INPUT " +
+                "RETURNS timestamp " +
+                "LANGUAGE JAVA\n" +
                 "AS 'return null;';");
 
         assertInvalidMessage("system keyspace is not user-modifiable",
-                             "CREATE OR REPLACE FUNCTION system.jnft(val double) RETURNS double LANGUAGE JAVA\n" +
+                             "CREATE OR REPLACE FUNCTION system.jnft(val double) " +
+                             "RETURNS NULL ON NULL INPUT " +
+                             "RETURNS double " +
+                             "LANGUAGE JAVA\n" +
                              "AS 'return null;';");
         assertInvalidMessage("system keyspace is not user-modifiable",
-                             "CREATE OR REPLACE FUNCTION system.dateof(val timeuuid) RETURNS timestamp LANGUAGE JAVA\n" +
+                             "CREATE OR REPLACE FUNCTION system.dateof(val timeuuid) " +
+                             "RETURNS NULL ON NULL INPUT " +
+                             "RETURNS timestamp " +
+                             "LANGUAGE JAVA\n" +
                              "AS 'return null;';");
         assertInvalidMessage("system keyspace is not user-modifiable",
                              "DROP FUNCTION system.now");
 
         // KS for executeInternal() is system
         assertInvalidMessage("system keyspace is not user-modifiable",
-                             "CREATE OR REPLACE FUNCTION jnft(val double) RETURNS double LANGUAGE JAVA\n" +
+                             "CREATE OR REPLACE FUNCTION jnft(val double) " +
+                             "RETURNS NULL ON NULL INPUT " +
+                             "RETURNS double " +
+                             "LANGUAGE JAVA\n" +
                              "AS 'return null;';");
         assertInvalidMessage("system keyspace is not user-modifiable",
-                             "CREATE OR REPLACE FUNCTION dateof(val timeuuid) RETURNS timestamp LANGUAGE JAVA\n" +
+                             "CREATE OR REPLACE FUNCTION dateof(val timeuuid) " +
+                             "RETURNS NULL ON NULL INPUT " +
+                             "RETURNS timestamp " +
+                             "LANGUAGE JAVA\n" +
                              "AS 'return null;';");
         assertInvalidMessage("system keyspace is not user-modifiable",
                              "DROP FUNCTION now");
@@ -683,7 +764,10 @@ public class UFTest extends CQLTester
     public void testFunctionNonExistingKeyspace() throws Throwable
     {
         assertInvalidMessage("to non existing keyspace",
-                             "CREATE OR REPLACE FUNCTION this_ks_does_not_exist.jnft(val double) RETURNS double LANGUAGE JAVA\n" +
+                             "CREATE OR REPLACE FUNCTION this_ks_does_not_exist.jnft(val double) " +
+                             "RETURNS NULL ON NULL INPUT " +
+                             "RETURNS double " +
+                             "LANGUAGE JAVA\n" +
                              "AS 'return null;';");
     }
 
@@ -693,7 +777,10 @@ public class UFTest extends CQLTester
         dropPerTestKeyspace();
 
         assertInvalidMessage("to non existing keyspace",
-                             "CREATE OR REPLACE FUNCTION " + KEYSPACE_PER_TEST + ".jnft(val double) RETURNS double LANGUAGE JAVA\n" +
+                             "CREATE OR REPLACE FUNCTION " + KEYSPACE_PER_TEST + ".jnft(val double) " +
+                             "RETURNS NULL ON NULL INPUT " +
+                             "RETURNS double " +
+                             "LANGUAGE JAVA\n" +
                              "AS 'return null;';");
     }
 
@@ -711,7 +798,10 @@ public class UFTest extends CQLTester
                               "  return Math.sin( val );\n";
 
         String fName = createFunction(KEYSPACE_PER_TEST, "double",
-                                     "CREATE OR REPLACE FUNCTION %s(val double) RETURNS double LANGUAGE JAVA " +
+                                     "CREATE OR REPLACE FUNCTION %s(val double) " +
+                                     "CALLED ON NULL INPUT " +
+                                     "RETURNS double " +
+                                     "LANGUAGE JAVA " +
                                      "AS '" + functionBody + "';");
 
         FunctionName fNameName = parseFunctionName(fName);
@@ -739,7 +829,10 @@ public class UFTest extends CQLTester
                               "  throw new RuntimeException(\"oh no!\");\n";
 
         String fName = createFunction(KEYSPACE_PER_TEST, "double",
-                                      "CREATE OR REPLACE FUNCTION %s(val double) RETURNS double LANGUAGE JAVA\n" +
+                                      "CREATE OR REPLACE FUNCTION %s(val double) " +
+                                      "RETURNS NULL ON NULL INPUT " +
+                                      "RETURNS double " +
+                                      "LANGUAGE JAVA\n" +
                                       "AS '" + functionBody + "';");
 
         FunctionName fNameName = parseFunctionName(fName);
@@ -770,6 +863,7 @@ public class UFTest extends CQLTester
 
         String fName = createFunction(KEYSPACE_PER_TEST, "double",
                                       "CREATE FUNCTION %s( input double ) " +
+                                      "CALLED ON NULL INPUT " +
                                       "RETURNS text " +
                                       "LANGUAGE java\n" +
                                       "AS $$" + functionBody + "$$;");
@@ -788,15 +882,21 @@ public class UFTest extends CQLTester
 
         String fList = createFunction(KEYSPACE_PER_TEST, "list<double>",
                                      "CREATE FUNCTION %s( lst list<double> ) " +
-                                     "RETURNS list<double> LANGUAGE java\n" +
+                                     "RETURNS NULL ON NULL INPUT " +
+                                     "RETURNS list<double> " +
+                                     "LANGUAGE java\n" +
                                      "AS $$return lst;$$;");
         String fSet = createFunction(KEYSPACE_PER_TEST, "set<text>",
                                      "CREATE FUNCTION %s( st set<text> ) " +
-                                     "RETURNS set<text> LANGUAGE java\n" +
+                                     "RETURNS NULL ON NULL INPUT " +
+                                     "RETURNS set<text> " +
+                                     "LANGUAGE java\n" +
                                      "AS $$return st;$$;");
         String fMap = createFunction(KEYSPACE_PER_TEST, "map<int, boolean>",
                                      "CREATE FUNCTION %s( mp map<int, boolean> ) " +
-                                     "RETURNS map<int, boolean> LANGUAGE java\n" +
+                                     "RETURNS NULL ON NULL INPUT " +
+                                     "RETURNS map<int, boolean> " +
+                                     "LANGUAGE java\n" +
                                      "AS $$return mp;$$;");
 
         List<Double> list = Arrays.asList(1d, 2d, 3d);
@@ -828,26 +928,31 @@ public class UFTest extends CQLTester
 
         String fList = createFunction(KEYSPACE, "list<double>",
                                       "CREATE FUNCTION %s( coll list<double> ) " +
+                                      "CALLED ON NULL INPUT " +
                                       "RETURNS list<double> " +
                                       "LANGUAGE java\n" +
                                       "AS $$return coll;$$;");
         String fSet = createFunction(KEYSPACE, "set<text>",
                                      "CREATE FUNCTION %s( coll set<text> ) " +
+                                     "CALLED ON NULL INPUT " +
                                      "RETURNS set<text> " +
                                      "LANGUAGE java\n" +
                                      "AS $$return coll;$$;");
         String fMap = createFunction(KEYSPACE, "map<int, boolean>",
                                      "CREATE FUNCTION %s( coll map<int, boolean> ) " +
+                                     "CALLED ON NULL INPUT " +
                                      "RETURNS map<int, boolean> " +
                                      "LANGUAGE java\n" +
                                      "AS $$return coll;$$;");
         String fTup = createFunction(KEYSPACE, "frozen<tuple<double, text, int, boolean>>",
                                      "CREATE FUNCTION %s( val frozen<tuple<double, text, int, boolean>> ) " +
+                                     "CALLED ON NULL INPUT " +
                                      "RETURNS frozen<tuple<double, text, int, boolean>> " +
                                      "LANGUAGE java\n" +
                                      "AS $$return val;$$;");
         String fUdt = createFunction(KEYSPACE, "frozen<" + type+'>',
                                      "CREATE FUNCTION %s( val frozen<" + type + "> ) " +
+                                     "CALLED ON NULL INPUT " +
                                      "RETURNS frozen<" + type + "> " +
                                      "LANGUAGE java\n" +
                                      "AS $$return val;$$;");
@@ -929,6 +1034,7 @@ public class UFTest extends CQLTester
 
         String fName = createFunction(KEYSPACE, "frozen<tuple<double, text, int, boolean>>",
                                      "CREATE FUNCTION %s( tup frozen<tuple<double, text, int, boolean>> ) " +
+                                     "RETURNS NULL ON NULL INPUT " +
                                      "RETURNS frozen<tuple<double, text, int, boolean>> " +
                                      "LANGUAGE java\n" +
                                      "AS $$return tup;$$;");
@@ -953,30 +1059,35 @@ public class UFTest extends CQLTester
 
         String fTup0 = createFunction(KEYSPACE_PER_TEST, tupleTypeDef,
                 "CREATE FUNCTION %s( tup " + tupleTypeDef + " ) " +
+                "CALLED ON NULL INPUT " +
                 "RETURNS " + tupleTypeDef + ' ' +
                 "LANGUAGE java\n" +
                 "AS $$return " +
                 "       tup;$$;");
         String fTup1 = createFunction(KEYSPACE_PER_TEST, tupleTypeDef,
                 "CREATE FUNCTION %s( tup " + tupleTypeDef + " ) " +
+                "CALLED ON NULL INPUT " +
                 "RETURNS double " +
                 "LANGUAGE java\n" +
                 "AS $$return " +
                 "       Double.valueOf(tup.getDouble(0));$$;");
         String fTup2 = createFunction(KEYSPACE_PER_TEST, tupleTypeDef,
-                "CREATE FUNCTION %s( tup " + tupleTypeDef + " ) " +
-                "RETURNS list<double> " +
-                "LANGUAGE java\n" +
-                "AS $$return " +
-                "       tup.getList(1, Double.class);$$;");
+                                      "CREATE FUNCTION %s( tup " + tupleTypeDef + " ) " +
+                                      "RETURNS NULL ON NULL INPUT " +
+                                      "RETURNS list<double> " +
+                                      "LANGUAGE java\n" +
+                                      "AS $$return " +
+                                      "       tup.getList(1, Double.class);$$;");
         String fTup3 = createFunction(KEYSPACE_PER_TEST, tupleTypeDef,
                 "CREATE FUNCTION %s( tup " + tupleTypeDef + " ) " +
+                "RETURNS NULL ON NULL INPUT " +
                 "RETURNS set<text> " +
                 "LANGUAGE java\n" +
                 "AS $$return " +
                 "       tup.getSet(2, String.class);$$;");
         String fTup4 = createFunction(KEYSPACE_PER_TEST, tupleTypeDef,
                 "CREATE FUNCTION %s( tup " + tupleTypeDef + " ) " +
+                "RETURNS NULL ON NULL INPUT " +
                 "RETURNS map<int, boolean> " +
                 "LANGUAGE java\n" +
                 "AS $$return " +
@@ -1042,6 +1153,7 @@ public class UFTest extends CQLTester
 
             executeNet(version,
                        "CREATE FUNCTION f_use1( udt frozen<" + type + "> ) " +
+                       "RETURNS NULL ON NULL INPUT " +
                        "RETURNS frozen<" + type + "> " +
                        "LANGUAGE java " +
                        "AS $$return " +
@@ -1068,6 +1180,7 @@ public class UFTest extends CQLTester
 
         String fName = createFunction(KEYSPACE_PER_TEST, "frozen<" + type + '>',
                                       "CREATE FUNCTION %s( udt frozen<" + type + "> ) " +
+                                      "RETURNS NULL ON NULL INPUT " +
                                       "RETURNS frozen<" + type + "> " +
                                       "LANGUAGE java " +
                                       "AS $$return " +
@@ -1085,18 +1198,21 @@ public class UFTest extends CQLTester
 
         String fUdt0 = createFunction(KEYSPACE, "frozen<" + type + '>',
                                       "CREATE FUNCTION %s( udt frozen<" + type + "> ) " +
+                                      "RETURNS NULL ON NULL INPUT " +
                                       "RETURNS frozen<" + type + "> " +
                                       "LANGUAGE java " +
                                       "AS $$return " +
                                       "     udt;$$;");
         String fUdt1 = createFunction(KEYSPACE, "frozen<" + type + '>',
                                       "CREATE FUNCTION %s( udt frozen<" + type + "> ) " +
+                                      "RETURNS NULL ON NULL INPUT " +
                                       "RETURNS text " +
                                       "LANGUAGE java " +
                                       "AS $$return " +
                                       "     udt.getString(\"txt\");$$;");
         String fUdt2 = createFunction(KEYSPACE, "frozen<" + type + '>',
                                       "CREATE FUNCTION %s( udt frozen<" + type + "> ) " +
+                                      "CALLED ON NULL INPUT " +
                                       "RETURNS int " +
                                       "LANGUAGE java " +
                                       "AS $$return " +
@@ -1136,6 +1252,7 @@ public class UFTest extends CQLTester
 
         String fName = createFunction(KEYSPACE, "frozen<" + type + '>',
                                       "CREATE FUNCTION %s( udt frozen<" + type + "> ) " +
+                                      "CALLED ON NULL INPUT " +
                                       "RETURNS int " +
                                       "LANGUAGE java " +
                                       "AS $$return " +
@@ -1172,7 +1289,9 @@ public class UFTest extends CQLTester
 
         String fName = createFunction(KEYSPACE, "frozen<" + type + '>',
                                       "CREATE FUNCTION %s( udt frozen<" + type + "> ) " +
-                                      "RETURNS text LANGUAGE java\n" +
+                                      "RETURNS NULL ON NULL INPUT " +
+                                      "RETURNS text " +
+                                      "LANGUAGE java\n" +
                                       "AS $$return udt.getString(\"txt\");$$;");
 
         execute("INSERT INTO %s (key, udt) VALUES (1, {txt: 'one', i:1})");
@@ -1200,36 +1319,52 @@ public class UFTest extends CQLTester
 
         String fName1replace = createFunction(KEYSPACE, "frozen<" + type + '>',
                                               "CREATE FUNCTION %s( udt frozen<" + type + "> ) " +
-                                              "RETURNS text LANGUAGE java\n" +
+                                              "RETURNS NULL ON NULL INPUT " +
+                                              "RETURNS text " +
+                                              "LANGUAGE java\n" +
                                               "AS $$return udt.getString(\"txt\");$$;");
         String fName2replace = createFunction(KEYSPACE, "frozen<" + type + '>',
                                               "CREATE FUNCTION %s( udt frozen<" + type + "> ) " +
-                                              "RETURNS int LANGUAGE java\n" +
+                                              "CALLED ON NULL INPUT " +
+                                              "RETURNS int " +
+                                              "LANGUAGE java\n" +
                                               "AS $$return Integer.valueOf(udt.getInt(\"i\"));$$;");
         String fName3replace = createFunction(KEYSPACE, "frozen<" + type + '>',
                                               "CREATE FUNCTION %s( udt frozen<" + type + "> ) " +
-                                              "RETURNS double LANGUAGE java\n" +
+                                              "CALLED ON NULL INPUT " +
+                                              "RETURNS double " +
+                                              "LANGUAGE java\n" +
                                               "AS $$return Double.valueOf(udt.getDouble(\"added\"));$$;");
         String fName4replace = createFunction(KEYSPACE, "frozen<" + type + '>',
                                               "CREATE FUNCTION %s( udt frozen<" + type + "> ) " +
-                                              "RETURNS frozen<" + type + "> LANGUAGE java\n" +
+                                              "RETURNS NULL ON NULL INPUT " +
+                                              "RETURNS frozen<" + type + "> " +
+                                              "LANGUAGE java\n" +
                                               "AS $$return udt;$$;");
 
         String fName1noReplace = createFunction(KEYSPACE, "frozen<" + type + '>',
                                               "CREATE FUNCTION %s( udt frozen<" + type + "> ) " +
-                                              "RETURNS text LANGUAGE java\n" +
+                                              "RETURNS NULL ON NULL INPUT " +
+                                              "RETURNS text " +
+                                              "LANGUAGE java\n" +
                                               "AS $$return udt.getString(\"txt\");$$;");
         String fName2noReplace = createFunction(KEYSPACE, "frozen<" + type + '>',
                                               "CREATE FUNCTION %s( udt frozen<" + type + "> ) " +
-                                              "RETURNS int LANGUAGE java\n" +
+                                              "CALLED ON NULL INPUT " +
+                                              "RETURNS int " +
+                                              "LANGUAGE java\n" +
                                               "AS $$return Integer.valueOf(udt.getInt(\"i\"));$$;");
         String fName3noReplace = createFunction(KEYSPACE, "frozen<" + type + '>',
                                                 "CREATE FUNCTION %s( udt frozen<" + type + "> ) " +
-                                                "RETURNS double LANGUAGE java\n" +
+                                                "CALLED ON NULL INPUT " +
+                                                "RETURNS double " +
+                                                "LANGUAGE java\n" +
                                                 "AS $$return Double.valueOf(udt.getDouble(\"added\"));$$;");
         String fName4noReplace = createFunction(KEYSPACE, "frozen<" + type + '>',
                                                 "CREATE FUNCTION %s( udt frozen<" + type + "> ) " +
-                                                "RETURNS frozen<" + type + "> LANGUAGE java\n" +
+                                                "RETURNS NULL ON NULL INPUT " +
+                                                "RETURNS frozen<" + type + "> " +
+                                                "LANGUAGE java\n" +
                                                 "AS $$return udt;$$;");
 
         execute("INSERT INTO %s (key, udt) VALUES (1, {txt: 'one', i:1})");
@@ -1249,25 +1384,33 @@ public class UFTest extends CQLTester
         // means we need to recreate the functions
 
         execute(String.format("CREATE OR REPLACE FUNCTION %s( udt frozen<%s> ) " +
-                              "RETURNS text LANGUAGE java\n" +
+                              "RETURNS NULL ON NULL INPUT " +
+                              "RETURNS text " +
+                              "LANGUAGE java\n" +
                               "AS $$return " +
                               "     udt.getString(\"txt\");$$;",
                               fName1replace, type));
         Assert.assertEquals(1, Functions.find(parseFunctionName(fName1replace)).size());
         execute(String.format("CREATE OR REPLACE FUNCTION %s( udt frozen<%s> ) " +
-                              "RETURNS int LANGUAGE java\n" +
+                              "CALLED ON NULL INPUT " +
+                              "RETURNS int " +
+                              "LANGUAGE java\n" +
                               "AS $$return " +
                               "     Integer.valueOf(udt.getInt(\"i\"));$$;",
                               fName2replace, type));
         Assert.assertEquals(1, Functions.find(parseFunctionName(fName2replace)).size());
         execute(String.format("CREATE OR REPLACE FUNCTION %s( udt frozen<%s> ) " +
-                              "RETURNS double LANGUAGE java\n" +
+                              "CALLED ON NULL INPUT " +
+                              "RETURNS double " +
+                              "LANGUAGE java\n" +
                               "AS $$return " +
                               "     Double.valueOf(udt.getDouble(\"added\"));$$;",
                               fName3replace, type));
         Assert.assertEquals(1, Functions.find(parseFunctionName(fName3replace)).size());
         execute(String.format("CREATE OR REPLACE FUNCTION %s( udt frozen<%s> ) " +
-                              "RETURNS frozen<%s> LANGUAGE java\n" +
+                              "RETURNS NULL ON NULL INPUT " +
+                              "RETURNS frozen<%s> " +
+                              "LANGUAGE java\n" +
                               "AS $$return " +
                               "     udt;$$;",
                               fName4replace, type, type));
@@ -1315,19 +1458,25 @@ public class UFTest extends CQLTester
 
         String fName1 = createFunction(KEYSPACE, "list<frozen<" + type + ">>",
                               "CREATE FUNCTION %s( lst list<frozen<" + type + ">> ) " +
-                              "RETURNS text LANGUAGE java\n" +
+                              "RETURNS NULL ON NULL INPUT " +
+                              "RETURNS text " +
+                              "LANGUAGE java\n" +
                               "AS $$" +
                               "     com.datastax.driver.core.UDTValue udtVal = (com.datastax.driver.core.UDTValue)lst.get(1);" +
                               "     return udtVal.getString(\"txt\");$$;");
         String fName2 = createFunction(KEYSPACE, "set<frozen<" + type + ">>",
-                              "CREATE FUNCTION %s( st set<frozen<" + type + ">> ) " +
-                              "RETURNS text LANGUAGE java\n" +
-                              "AS $$" +
-                              "     com.datastax.driver.core.UDTValue udtVal = (com.datastax.driver.core.UDTValue)st.iterator().next();" +
-                              "     return udtVal.getString(\"txt\");$$;");
+                                       "CREATE FUNCTION %s( st set<frozen<" + type + ">> ) " +
+                                       "RETURNS NULL ON NULL INPUT " +
+                                       "RETURNS text " +
+                                       "LANGUAGE java\n" +
+                                       "AS $$" +
+                                       "     com.datastax.driver.core.UDTValue udtVal = (com.datastax.driver.core.UDTValue)st.iterator().next();" +
+                                       "     return udtVal.getString(\"txt\");$$;");
         String fName3 = createFunction(KEYSPACE, "map<int, frozen<" + type + ">>",
                               "CREATE FUNCTION %s( mp map<int, frozen<" + type + ">> ) " +
-                              "RETURNS text LANGUAGE java\n" +
+                              "RETURNS NULL ON NULL INPUT " +
+                              "RETURNS text " +
+                              "LANGUAGE java\n" +
                               "AS $$" +
                               "     com.datastax.driver.core.UDTValue udtVal = (com.datastax.driver.core.UDTValue)mp.get(Integer.valueOf(3));" +
                               "     return udtVal.getString(\"txt\");$$;");
@@ -1353,16 +1502,19 @@ public class UFTest extends CQLTester
 
         String fName1 = createFunction(KEYSPACE_PER_TEST, "list<double>",
                 "CREATE FUNCTION %s( lst list<double> ) " +
+                "RETURNS NULL ON NULL INPUT " +
                 "RETURNS list<double> " +
                 "LANGUAGE javascript\n" +
                 "AS 'lst;';");
         String fName2 = createFunction(KEYSPACE_PER_TEST, "set<text>",
                 "CREATE FUNCTION %s( st set<text> ) " +
+                "RETURNS NULL ON NULL INPUT " +
                 "RETURNS set<text> " +
                 "LANGUAGE javascript\n" +
                 "AS 'st;';");
         String fName3 = createFunction(KEYSPACE_PER_TEST, "map<int, boolean>",
                 "CREATE FUNCTION %s( mp map<int, boolean> ) " +
+                "RETURNS NULL ON NULL INPUT " +
                 "RETURNS map<int, boolean> " +
                 "LANGUAGE javascript\n" +
                 "AS 'mp;';");
@@ -1395,6 +1547,7 @@ public class UFTest extends CQLTester
 
         String fName = createFunction(KEYSPACE_PER_TEST, "frozen<tuple<double, text, int, boolean>>",
                 "CREATE FUNCTION %s( tup frozen<tuple<double, text, int, boolean>> ) " +
+                "RETURNS NULL ON NULL INPUT " +
                 "RETURNS frozen<tuple<double, text, int, boolean>> " +
                 "LANGUAGE javascript\n" +
                 "AS $$tup;$$;");
@@ -1418,30 +1571,35 @@ public class UFTest extends CQLTester
 
         String fTup1 = createFunction(KEYSPACE_PER_TEST, tupleTypeDef,
                 "CREATE FUNCTION %s( tup " + tupleTypeDef + " ) " +
+                "RETURNS NULL ON NULL INPUT " +
                 "RETURNS frozen<tuple<double, list<double>, set<text>, map<int, boolean>>> " +
                 "LANGUAGE javascript\n" +
                 "AS $$" +
                 "       tup;$$;");
         String fTup2 = createFunction(KEYSPACE_PER_TEST, tupleTypeDef,
                 "CREATE FUNCTION %s( tup " + tupleTypeDef + " ) " +
-                "RETURNS double LANGUAGE " +
-                "javascript\n" +
+                "RETURNS NULL ON NULL INPUT " +
+                "RETURNS double " +
+                "LANGUAGE javascript\n" +
                 "AS $$" +
                 "       tup.getDouble(0);$$;");
         String fTup3 = createFunction(KEYSPACE_PER_TEST, tupleTypeDef,
                 "CREATE FUNCTION %s( tup " + tupleTypeDef + " ) " +
+                "RETURNS NULL ON NULL INPUT " +
                 "RETURNS list<double> " +
                 "LANGUAGE javascript\n" +
                 "AS $$" +
                 "       tup.getList(1, java.lang.Class.forName(\"java.lang.Double\"));$$;");
         String fTup4 = createFunction(KEYSPACE_PER_TEST, tupleTypeDef,
                 "CREATE FUNCTION %s( tup " + tupleTypeDef + " ) " +
+                "RETURNS NULL ON NULL INPUT " +
                 "RETURNS set<text> " +
                 "LANGUAGE javascript\n" +
                 "AS $$" +
                 "       tup.getSet(2, java.lang.Class.forName(\"java.lang.String\"));$$;");
         String fTup5 = createFunction(KEYSPACE_PER_TEST, tupleTypeDef,
                 "CREATE FUNCTION %s( tup " + tupleTypeDef + " ) " +
+                "RETURNS NULL ON NULL INPUT " +
                 "RETURNS map<int, boolean> " +
                 "LANGUAGE javascript\n" +
                 "AS $$" +
@@ -1504,22 +1662,25 @@ public class UFTest extends CQLTester
 
         String fUdt1 = createFunction(KEYSPACE, "frozen<" + type + '>',
                               "CREATE FUNCTION %s( udt frozen<" + type + "> ) " +
+                              "RETURNS NULL ON NULL INPUT " +
                               "RETURNS frozen<" + type + "> " +
                               "LANGUAGE javascript\n" +
                               "AS $$" +
                               "     udt;$$;");
         String fUdt2 = createFunction(KEYSPACE, "frozen<" + type + '>',
                               "CREATE FUNCTION %s( udt frozen<" + type + "> ) " +
+                              "RETURNS NULL ON NULL INPUT " +
                               "RETURNS text " +
                               "LANGUAGE javascript\n" +
                               "AS $$" +
                               "     udt.getString(\"txt\");$$;");
         String fUdt3 = createFunction(KEYSPACE, "frozen<" + type + '>',
-                              "CREATE FUNCTION %s( udt frozen<" + type + "> ) " +
-                              "RETURNS int " +
-                              "LANGUAGE javascript\n" +
-                              "AS $$" +
-                              "     udt.getInt(\"i\");$$;");
+                                      "CREATE FUNCTION %s( udt frozen<" + type + "> ) " +
+                                      "RETURNS NULL ON NULL INPUT " +
+                                      "RETURNS int " +
+                                      "LANGUAGE javascript\n" +
+                                      "AS $$" +
+                                      "     udt.getInt(\"i\");$$;");
 
         execute("INSERT INTO %s (key, udt) VALUES (1, {txt: 'one', i:1})");
 
@@ -1541,19 +1702,22 @@ public class UFTest extends CQLTester
                                   type, type, type));
 
         String fName = createFunction(KEYSPACE, "list<frozen<" + type + ">>",
-                       "CREATE FUNCTION %s( lst list<frozen<" + type + ">> ) " +
-                       "RETURNS text " +
-                       "LANGUAGE javascript\n" +
-                       "AS $$" +
-                       "        lst.get(1).getString(\"txt\");$$;");
+                                      "CREATE FUNCTION %s( lst list<frozen<" + type + ">> ) " +
+                                      "RETURNS NULL ON NULL INPUT " +
+                                      "RETURNS text " +
+                                      "LANGUAGE javascript\n" +
+                                      "AS $$" +
+                                      "        lst.get(1).getString(\"txt\");$$;");
         createFunctionOverload(fName, "set<frozen<" + type + ">>",
                                "CREATE FUNCTION %s( st set<frozen<" + type + ">> ) " +
+                               "RETURNS NULL ON NULL INPUT " +
                                "RETURNS text " +
                                "LANGUAGE javascript\n" +
                                "AS $$" +
                                "        st.iterator().next().getString(\"txt\");$$;");
         createFunctionOverload(fName, "map<int, frozen<" + type + ">>",
                                "CREATE FUNCTION %s( mp map<int, frozen<" + type + ">> ) " +
+                               "RETURNS NULL ON NULL INPUT " +
                                "RETURNS text " +
                                "LANGUAGE javascript\n" +
                                "AS $$" +
@@ -1595,7 +1759,9 @@ public class UFTest extends CQLTester
 
         String fName = createFunction(KEYSPACE, "double",
                                       "CREATE OR REPLACE FUNCTION %s(val double) " +
-                                      "RETURNS double LANGUAGE javascript\n" +
+                                      "RETURNS NULL ON NULL INPUT " +
+                                      "RETURNS double " +
+                                      "LANGUAGE javascript\n" +
                                       "AS '" + functionBody + "';");
 
         FunctionName fNameName = parseFunctionName(fName);
@@ -1621,6 +1787,7 @@ public class UFTest extends CQLTester
 
         String fName = createFunction(KEYSPACE, "double",
                                       "CREATE OR REPLACE FUNCTION %s(val double) " +
+                                      "RETURNS NULL ON NULL INPUT " +
                                       "RETURNS double " +
                                       "LANGUAGE javascript\n" +
                                       "AS '\"string\";';");
@@ -1637,6 +1804,7 @@ public class UFTest extends CQLTester
 
         String fName = createFunction(KEYSPACE, "double",
                        "CREATE OR REPLACE FUNCTION %s(val double) " +
+                       "RETURNS NULL ON NULL INPUT " +
                        "RETURNS double " +
                        "LANGUAGE javascript\n" +
                        "AS 'throw \"fool\";';");
@@ -1652,7 +1820,9 @@ public class UFTest extends CQLTester
     {
         assertInvalidMessage("duplicate argument names for given function",
                              "CREATE OR REPLACE FUNCTION " + KEYSPACE + ".scrinv(val double, val text) " +
-                             "RETURNS text LANGUAGE javascript\n" +
+                             "RETURNS NULL ON NULL INPUT " +
+                             "RETURNS text " +
+                             "LANGUAGE javascript\n" +
                              "AS '\"foo bar\";';");
     }
 
@@ -1661,7 +1831,9 @@ public class UFTest extends CQLTester
     {
         assertInvalidMessage("Failed to compile function 'cql_test_keyspace.scrinv'",
                              "CREATE OR REPLACE FUNCTION " + KEYSPACE + ".scrinv(val double) " +
-                             "RETURNS double LANGUAGE javascript\n" +
+                             "RETURNS NULL ON NULL INPUT " +
+                             "RETURNS double " +
+                             "LANGUAGE javascript\n" +
                              "AS 'foo bar';");
     }
 
@@ -1670,7 +1842,9 @@ public class UFTest extends CQLTester
     {
         assertInvalidMessage("Invalid language 'artificial_intelligence' for function 'cql_test_keyspace.scrinv'",
                              "CREATE OR REPLACE FUNCTION " + KEYSPACE + ".scrinv(val double) " +
-                             "RETURNS double LANGUAGE artificial_intelligence\n" +
+                             "RETURNS NULL ON NULL INPUT " +
+                             "RETURNS double " +
+                             "LANGUAGE artificial_intelligence\n" +
                              "AS 'question for 42?';");
     }
 
@@ -1703,6 +1877,7 @@ public class UFTest extends CQLTester
 
             String fName = createFunction(KEYSPACE, "double",
                                           "CREATE OR REPLACE FUNCTION %s(val double) " +
+                                          "RETURNS NULL ON NULL INPUT " +
                                           "RETURNS " +returnType + ' ' +
                                           "LANGUAGE javascript " +
                                           "AS '" + functionBody + ";';");
@@ -1735,6 +1910,7 @@ public class UFTest extends CQLTester
             Object expected2 = variation[3];
             String fName = createFunction(KEYSPACE, type.toString(),
                            "CREATE OR REPLACE FUNCTION %s(val " + type + ") " +
+                           "RETURNS NULL ON NULL INPUT " +
                            "RETURNS " + type + ' ' +
                            "LANGUAGE javascript " +
                            "AS 'val+1;';");
@@ -1744,18 +1920,212 @@ public class UFTest extends CQLTester
     }
 
     @Test
+    public void testNullOnReturnsNullOnNullInput() throws Throwable
+    {
+        String type = createType("CREATE TYPE %s (txt text, i int)");
+        createTable("CREATE TABLE %s (key int PRIMARY KEY, i int, b bigint, f float, d double, x boolean, t text, u frozen<"+type+">, tup frozen<tuple<int, text>>)");
+
+        execute("INSERT INTO %s (key, i, b, f, d, x, t, u, tup) VALUES (1, null, null, null, null, null, null, null, null)");
+
+        String fI = createFunction(KEYSPACE,
+                                   "int",
+                                   "CREATE OR REPLACE FUNCTION %s(val int) " +
+                                   "RETURNS NULL ON NULL INPUT " +
+                                   "RETURNS text " +
+                                   "LANGUAGE java\n" +
+                                   "AS 'return \"foo bar\";';");
+        String fB = createFunction(KEYSPACE,
+                                   "bigint",
+                                   "CREATE OR REPLACE FUNCTION %s(val bigint) " +
+                                   "RETURNS NULL ON NULL INPUT " +
+                                   "RETURNS text " +
+                                   "LANGUAGE java\n" +
+                                   "AS 'return \"foo bar\";';");
+        String fF = createFunction(KEYSPACE,
+                                   "float",
+                                   "CREATE OR REPLACE FUNCTION %s(val float) " +
+                                   "RETURNS NULL ON NULL INPUT " +
+                                   "RETURNS text " +
+                                   "LANGUAGE java\n" +
+                                   "AS 'return \"foo bar\";';");
+        String fD = createFunction(KEYSPACE,
+                                   "double",
+                                   "CREATE OR REPLACE FUNCTION %s(val double) " +
+                                   "RETURNS NULL ON NULL INPUT " +
+                                   "RETURNS text " +
+                                   "LANGUAGE java\n" +
+                                   "AS 'return \"foo bar\";';");
+        String fX = createFunction(KEYSPACE,
+                                   "boolean",
+                                   "CREATE OR REPLACE FUNCTION %s(val boolean) " +
+                                   "RETURNS NULL ON NULL INPUT " +
+                                   "RETURNS text " +
+                                   "LANGUAGE java\n" +
+                                   "AS 'return \"foo bar\";';");
+        String fT = createFunction(KEYSPACE,
+                                   "text",
+                                   "CREATE OR REPLACE FUNCTION %s(val text) " +
+                                   "RETURNS NULL ON NULL INPUT " +
+                                   "RETURNS text " +
+                                   "LANGUAGE java\n" +
+                                   "AS 'return \"foo bar\";';");
+        String fU = createFunction(KEYSPACE,
+                                   "frozen<" + type + '>',
+                                   "CREATE OR REPLACE FUNCTION %s(val frozen<" + type + ">) " +
+                                   "RETURNS NULL ON NULL INPUT " +
+                                   "RETURNS text " +
+                                   "LANGUAGE java\n" +
+                                   "AS 'return \"foo bar\";';");
+        String fTup = createFunction(KEYSPACE,
+                                     "frozen<tuple<int, text>>",
+                                     "CREATE OR REPLACE FUNCTION %s(val frozen<tuple<int, text>>) " +
+                                     "RETURNS NULL ON NULL INPUT " +
+                                     "RETURNS text " +
+                                     "LANGUAGE java\n" +
+                                     "AS 'return \"foo bar\";';");
+
+        assertRows(execute("SELECT " + fI + "(i) FROM %s WHERE key=1"), row(new Object[]{null}));
+        assertRows(execute("SELECT " + fB + "(b) FROM %s WHERE key=1"), row(new Object[]{null}));
+        assertRows(execute("SELECT " + fF + "(f) FROM %s WHERE key=1"), row(new Object[]{null}));
+        assertRows(execute("SELECT " + fD + "(d) FROM %s WHERE key=1"), row(new Object[]{null}));
+        assertRows(execute("SELECT " + fX + "(x) FROM %s WHERE key=1"), row(new Object[]{null}));
+        assertRows(execute("SELECT " + fT + "(t) FROM %s WHERE key=1"), row(new Object[]{null}));
+        assertRows(execute("SELECT " + fU + "(u) FROM %s WHERE key=1"), row(new Object[]{null}));
+        assertRows(execute("SELECT " + fTup + "(tup) FROM %s WHERE key=1"), row(new Object[]{null}));
+    }
+
+    @Test
+    public void testReplaceAllowNulls() throws Throwable
+    {
+        String fNulls = createFunction(KEYSPACE,
+                                       "int",
+                                       "CREATE OR REPLACE FUNCTION %s(val int) " +
+                                       "CALLED ON NULL INPUT " +
+                                       "RETURNS text " +
+                                       "LANGUAGE java\n" +
+                                       "AS 'return \"foo bar\";';");
+        String fNoNulls = createFunction(KEYSPACE,
+                                         "int",
+                                         "CREATE OR REPLACE FUNCTION %s(val int) " +
+                                         "RETURNS NULL ON NULL INPUT " +
+                                         "RETURNS text " +
+                                         "LANGUAGE java\n" +
+                                         "AS 'return \"foo bar\";';");
+
+        assertInvalid("CREATE OR REPLACE FUNCTION " + fNulls + "(val int) " +
+                      "RETURNS NULL ON NULL INPUT " +
+                      "RETURNS text " +
+                      "LANGUAGE java\n" +
+                      "AS 'return \"foo bar\";';");
+        assertInvalid("CREATE OR REPLACE FUNCTION " + fNoNulls + "(val int) " +
+                      "CALLED ON NULL INPUT " +
+                      "RETURNS text " +
+                      "LANGUAGE java\n" +
+                      "AS 'return \"foo bar\";';");
+
+        execute("CREATE OR REPLACE FUNCTION " + fNulls + "(val int) " +
+                "CALLED ON NULL INPUT " +
+                "RETURNS text " +
+                "LANGUAGE java\n" +
+                "AS 'return \"foo bar\";';");
+        execute("CREATE OR REPLACE FUNCTION " + fNoNulls + "(val int) " +
+                "RETURNS NULL ON NULL INPUT " +
+                "RETURNS text " +
+                "LANGUAGE java\n" +
+                "AS 'return \"foo bar\";';");
+    }
+
+    @Test
+    public void testNullOnCalledOnNullInput() throws Throwable
+    {
+        String type = createType("CREATE TYPE %s (txt text, i int)");
+        createTable("CREATE TABLE %s (key int PRIMARY KEY, i int, b bigint, f float, d double, x boolean, t text, u frozen<"+type+">, tup frozen<tuple<int, text>>)");
+
+        execute("INSERT INTO %s (key, i, b, f, d, x, t, u, tup) VALUES (1, null, null, null, null, null, null, null, null)");
+
+        String fI = createFunction(KEYSPACE,
+                                   "int",
+                                   "CREATE OR REPLACE FUNCTION %s(val int) " +
+                                   "CALLED ON NULL INPUT " +
+                                   "RETURNS text " +
+                                   "LANGUAGE java\n" +
+                                   "AS 'return \"foo bar\";';");
+        String fB = createFunction(KEYSPACE,
+                                   "bigint",
+                                   "CREATE OR REPLACE FUNCTION %s(val bigint) " +
+                                   "CALLED ON NULL INPUT " +
+                                   "RETURNS text " +
+                                   "LANGUAGE java\n" +
+                                   "AS 'return \"foo bar\";';");
+        String fF = createFunction(KEYSPACE,
+                                   "float",
+                                   "CREATE OR REPLACE FUNCTION %s(val float) " +
+                                   "CALLED ON NULL INPUT " +
+                                   "RETURNS text " +
+                                   "LANGUAGE java\n" +
+                                   "AS 'return \"foo bar\";';");
+        String fD = createFunction(KEYSPACE,
+                                   "double",
+                                   "CREATE OR REPLACE FUNCTION %s(val double) " +
+                                   "CALLED ON NULL INPUT " +
+                                   "RETURNS text " +
+                                   "LANGUAGE java\n" +
+                                   "AS 'return \"foo bar\";';");
+        String fX = createFunction(KEYSPACE,
+                                   "boolean",
+                                   "CREATE OR REPLACE FUNCTION %s(val boolean) " +
+                                   "CALLED ON NULL INPUT " +
+                                   "RETURNS text " +
+                                   "LANGUAGE java\n" +
+                                   "AS 'return \"foo bar\";';");
+        String fT = createFunction(KEYSPACE,
+                                   "text",
+                                   "CREATE OR REPLACE FUNCTION %s(val text) " +
+                                   "CALLED ON NULL INPUT " +
+                                   "RETURNS text " +
+                                   "LANGUAGE java\n" +
+                                   "AS 'return \"foo bar\";';");
+        String fU = createFunction(KEYSPACE,
+                                   "frozen<" + type + '>',
+                                   "CREATE OR REPLACE FUNCTION %s(val frozen<" + type + ">) " +
+                                   "CALLED ON NULL INPUT " +
+                                   "RETURNS text " +
+                                   "LANGUAGE java\n" +
+                                   "AS 'return \"foo bar\";';");
+        String fTup = createFunction(KEYSPACE,
+                                     "frozen<tuple<int, text>>",
+                                     "CREATE OR REPLACE FUNCTION %s(val frozen<tuple<int, text>>) " +
+                                     "CALLED ON NULL INPUT " +
+                                     "RETURNS text " +
+                                     "LANGUAGE java\n" +
+                                     "AS 'return \"foo bar\";';");
+
+        assertRows(execute("SELECT " + fI + "(i) FROM %s WHERE key=1"), row("foo bar"));
+        assertRows(execute("SELECT " + fB + "(b) FROM %s WHERE key=1"), row("foo bar"));
+        assertRows(execute("SELECT " + fF + "(f) FROM %s WHERE key=1"), row("foo bar"));
+        assertRows(execute("SELECT " + fD + "(d) FROM %s WHERE key=1"), row("foo bar"));
+        assertRows(execute("SELECT " + fX + "(x) FROM %s WHERE key=1"), row("foo bar"));
+        assertRows(execute("SELECT " + fT + "(t) FROM %s WHERE key=1"), row("foo bar"));
+        assertRows(execute("SELECT " + fU + "(u) FROM %s WHERE key=1"), row("foo bar"));
+        assertRows(execute("SELECT " + fTup + "(tup) FROM %s WHERE key=1"), row("foo bar"));
+    }
+
+    @Test
     public void testBrokenFunction() throws Throwable
     {
         createTable("CREATE TABLE %s (key int primary key, dval double)");
         execute("INSERT INTO %s (key, dval) VALUES (?, ?)", 1, 1d);
 
         String fName = createFunction(KEYSPACE_PER_TEST, "double",
-                                      "CREATE OR REPLACE FUNCTION %s(val double) RETURNS double LANGUAGE JAVA\n" +
+                                      "CREATE OR REPLACE FUNCTION %s(val double) " +
+                                      "RETURNS NULL ON NULL INPUT " +
+                                      "RETURNS double " +
+                                      "LANGUAGE JAVA\n" +
                                       "AS 'throw new RuntimeException();';");
 
         UDFunction f = (UDFunction) Functions.find(parseFunctionName(fName)).get(0);
 
-        Functions.replaceFunction(UDFunction.createBrokenFunction(f.name(), f.argNames(), f.argTypes(), f.returnType(),
+        Functions.replaceFunction(UDFunction.createBrokenFunction(f.name(), f.argNames(), f.argTypes(), f.returnType(), true,
                                                                   "java", f.body(), new InvalidRequestException("foo bar is broken")));
 
         assertInvalidThrowMessage("foo bar is broken", InvalidRequestException.class,
@@ -1770,7 +2140,10 @@ public class UFTest extends CQLTester
         execute("INSERT INTO %s (key, dval) VALUES (?, ?)", 1, 1d);
 
         String fName = createFunction(KEYSPACE_PER_TEST, "double",
-                                      "CREATE OR REPLACE FUNCTION %s(val double) RETURNS double LANGUAGE JAVA\n" +
+                                      "CREATE OR REPLACE FUNCTION %s(val double) " +
+                                      "RETURNS NULL ON NULL INPUT " +
+                                      "RETURNS double " +
+                                      "LANGUAGE JAVA\n" +
                                       "AS 'throw new RuntimeException()';");
 
         for (int version = Server.VERSION_2; version <= maxProtocolVersion; version++)


Mime
View raw message