From 0b97c5d1f717be30a04c59c766465d9c62a4e9ee Mon Sep 17 00:00:00 2001 From: Robert Stupp Date: Wed, 7 Dec 2016 13:06:55 +0100 Subject: [PATCH 1/2] testall failure in org.apache.cassandra.cql3.validation.entities.UFTest.testAllNativeTypes patch by Robert Stupp; reviewed by Alex Petrov for CASSANDRA-12817 --- .../cql3/validation/entities/UFJavaTest.java | 1177 ++++++++++++ .../validation/entities/UFScriptTest.java | 504 ++++++ .../cql3/validation/entities/UFTest.java | 1582 +---------------- 3 files changed, 1683 insertions(+), 1580 deletions(-) create mode 100644 test/unit/org/apache/cassandra/cql3/validation/entities/UFJavaTest.java create mode 100644 test/unit/org/apache/cassandra/cql3/validation/entities/UFScriptTest.java diff --git a/test/unit/org/apache/cassandra/cql3/validation/entities/UFJavaTest.java b/test/unit/org/apache/cassandra/cql3/validation/entities/UFJavaTest.java new file mode 100644 index 0000000000..caef808ecd --- /dev/null +++ b/test/unit/org/apache/cassandra/cql3/validation/entities/UFJavaTest.java @@ -0,0 +1,1177 @@ +/* + * 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.validation.entities; + +import java.nio.ByteBuffer; +import java.util.Arrays; +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.BeforeClass; +import org.junit.Test; + +import com.datastax.driver.core.DataType; +import com.datastax.driver.core.Row; +import com.datastax.driver.core.TupleType; +import com.datastax.driver.core.TupleValue; +import com.datastax.driver.core.UDTValue; +import org.apache.cassandra.config.DatabaseDescriptor; +import org.apache.cassandra.cql3.CQL3Type; +import org.apache.cassandra.cql3.CQLTester; +import org.apache.cassandra.cql3.UntypedResultSet; +import org.apache.cassandra.cql3.functions.FunctionName; +import org.apache.cassandra.cql3.functions.Functions; +import org.apache.cassandra.dht.ByteOrderedPartitioner; +import org.apache.cassandra.exceptions.FunctionExecutionException; +import org.apache.cassandra.exceptions.InvalidRequestException; +import org.apache.cassandra.transport.Server; +import org.apache.cassandra.utils.ByteBufferUtil; + +public class UFJavaTest extends CQLTester +{ + @BeforeClass + public static void setUp() + { + DatabaseDescriptor.setPartitioner(ByteOrderedPartitioner.instance); + } + + + @Test + public void testJavaFunctionNoParameters() throws Throwable + { + createTable("CREATE TABLE %s (key int primary key, val double)"); + + String functionBody = "\n return 1L;\n"; + + String fName = createFunction(KEYSPACE, "", + "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=?", + KEYSPACE, parseFunctionName(fName).name), + row("java", functionBody)); + + execute("INSERT INTO %s (key, val) VALUES (?, ?)", 1, 1d); + execute("INSERT INTO %s (key, val) VALUES (?, ?)", 2, 2d); + execute("INSERT INTO %s (key, val) VALUES (?, ?)", 3, 3d); + assertRows(execute("SELECT key, val, " + fName + "() FROM %s"), + row(1, 1d, 1L), + row(2, 2d, 1L), + row(3, 3d, 1L) + ); + } + + @Test + public void testJavaFunctionInvalidBodies() throws Throwable + { + try + { + execute("CREATE OR REPLACE FUNCTION " + KEYSPACE + ".jfinv() " + + "RETURNS NULL ON NULL INPUT " + + "RETURNS bigint " + + "LANGUAGE JAVA\n" + + "AS '\n" + + "foobarbaz" + + "\n';"); + Assert.fail(); + } + catch (InvalidRequestException e) + { + Assert.assertTrue(e.getMessage(), e.getMessage().contains("Java source compilation failed")); + Assert.assertTrue(e.getMessage(), e.getMessage().contains("insert \";\" to complete BlockStatements")); + } + + try + { + execute("CREATE OR REPLACE FUNCTION " + KEYSPACE + ".jfinv() " + + "RETURNS NULL ON NULL INPUT " + + "RETURNS bigint " + + "LANGUAGE JAVA\n" + + "AS '\n" + + "foobarbaz;" + + "\n';"); + Assert.fail(); + } + catch (InvalidRequestException e) + { + Assert.assertTrue(e.getMessage(), e.getMessage().contains("Java source compilation failed")); + Assert.assertTrue(e.getMessage(), e.getMessage().contains("foobarbaz cannot be resolved to a type")); + } + } + + @Test + public void testJavaFunctionInvalidReturn() throws Throwable + { + assertInvalidMessage("system keyspace is not user-modifiable", + "CREATE OR REPLACE FUNCTION jfir(val double) " + + "RETURNS NULL ON NULL INPUT " + + "RETURNS double " + + "LANGUAGE JAVA\n" + + "AS 'return 1L;';"); + } + + @Test + public void testJavaFunctionArgumentTypeMismatch() throws Throwable + { + createTable("CREATE TABLE %s (key int primary key, val bigint)"); + + String fName = createFunction(KEYSPACE, "double", + "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, 1L); + execute("INSERT INTO %s (key, val) VALUES (?, ?)", 2, 2L); + execute("INSERT INTO %s (key, val) VALUES (?, ?)", 3, 3L); + assertInvalidMessage("val cannot be passed as argument 0 of function", + "SELECT key, val, " + fName + "(val) FROM %s"); + } + + @Test + public void testJavaFunction() throws Throwable + { + createTable("CREATE TABLE %s (key int primary key, val double)"); + + String functionBody = '\n' + + " // parameter val is of type java.lang.Double\n" + + " /* return type is of type java.lang.Double */\n" + + " if (val == null) {\n" + + " return null;\n" + + " }\n" + + " return Math.sin(val);\n"; + + String fName = createFunction(KEYSPACE, "double", + "CREATE OR REPLACE FUNCTION %s(val double) " + + "CALLED ON NULL INPUT " + + "RETURNS double " + + "LANGUAGE JAVA " + + "AS '" + functionBody + "';"); + + FunctionName fNameName = parseFunctionName(fName); + + assertRows(execute("SELECT language, body FROM system.schema_functions WHERE keyspace_name=? AND function_name=?", + fNameName.keyspace, fNameName.name), + row("java", functionBody)); + + execute("INSERT INTO %s (key, val) VALUES (?, ?)", 1, 1d); + execute("INSERT INTO %s (key, val) VALUES (?, ?)", 2, 2d); + execute("INSERT INTO %s (key, val) VALUES (?, ?)", 3, 3d); + assertRows(execute("SELECT key, val, " + fName + "(val) FROM %s"), + row(1, 1d, Math.sin(1d)), + row(2, 2d, Math.sin(2d)), + row(3, 3d, Math.sin(3d)) + ); + } + + @Test + public void testJavaFunctionCounter() throws Throwable + { + createTable("CREATE TABLE %s (key int primary key, val counter)"); + + String fName = createFunction(KEYSPACE, "counter", + "CREATE OR REPLACE FUNCTION %s(val counter) " + + "CALLED ON NULL INPUT " + + "RETURNS bigint " + + "LANGUAGE JAVA " + + "AS 'return val + 1;';"); + + execute("UPDATE %s SET val = val + 1 WHERE key = 1"); + assertRows(execute("SELECT key, val, " + fName + "(val) FROM %s"), + row(1, 1L, 2L)); + execute("UPDATE %s SET val = val + 1 WHERE key = 1"); + assertRows(execute("SELECT key, val, " + fName + "(val) FROM %s"), + row(1, 2L, 3L)); + execute("UPDATE %s SET val = val + 2 WHERE key = 1"); + assertRows(execute("SELECT key, val, " + fName + "(val) FROM %s"), + row(1, 4L, 5L)); + execute("UPDATE %s SET val = val - 2 WHERE key = 1"); + assertRows(execute("SELECT key, val, " + fName + "(val) FROM %s"), + row(1, 2L, 3L)); + } + + @Test + public void testJavaKeyspaceFunction() throws Throwable + { + createTable("CREATE TABLE %s (key int primary key, val double)"); + + String functionBody = '\n' + + " // parameter val is of type java.lang.Double\n" + + " /* return type is of type java.lang.Double */\n" + + " if (val == null) {\n" + + " return null;\n" + + " }\n" + + " return Math.sin( val );\n"; + + String fName = createFunction(KEYSPACE_PER_TEST, "double", + "CREATE OR REPLACE FUNCTION %s(val double) " + + "CALLED ON NULL INPUT " + + "RETURNS double " + + "LANGUAGE JAVA " + + "AS '" + functionBody + "';"); + + FunctionName fNameName = parseFunctionName(fName); + + assertRows(execute("SELECT language, body FROM system.schema_functions WHERE keyspace_name=? AND function_name=?", + fNameName.keyspace, fNameName.name), + row("java", functionBody)); + + execute("INSERT INTO %s (key, val) VALUES (?, ?)", 1, 1d); + execute("INSERT INTO %s (key, val) VALUES (?, ?)", 2, 2d); + execute("INSERT INTO %s (key, val) VALUES (?, ?)", 3, 3d); + assertRows(execute("SELECT key, val, " + fName + "(val) FROM %s"), + row(1, 1d, Math.sin(1d)), + row(2, 2d, Math.sin(2d)), + row(3, 3d, Math.sin(3d)) + ); + } + + @Test + public void testJavaRuntimeException() throws Throwable + { + createTable("CREATE TABLE %s (key int primary key, val double)"); + + String functionBody = '\n' + + " throw new RuntimeException(\"oh no!\");\n"; + + String fName = createFunction(KEYSPACE_PER_TEST, "double", + "CREATE OR REPLACE FUNCTION %s(val double) " + + "RETURNS NULL ON NULL INPUT " + + "RETURNS double " + + "LANGUAGE JAVA\n" + + "AS '" + functionBody + "';"); + + FunctionName fNameName = parseFunctionName(fName); + + assertRows(execute("SELECT language, body FROM system.schema_functions WHERE keyspace_name=? AND function_name=?", + fNameName.keyspace, fNameName.name), + row("java", functionBody)); + + execute("INSERT INTO %s (key, val) VALUES (?, ?)", 1, 1d); + execute("INSERT INTO %s (key, val) VALUES (?, ?)", 2, 2d); + execute("INSERT INTO %s (key, val) VALUES (?, ?)", 3, 3d); + + // function throws a RuntimeException which is wrapped by FunctionExecutionException + assertInvalidThrowMessage("java.lang.RuntimeException: oh no", FunctionExecutionException.class, + "SELECT key, val, " + fName + "(val) FROM %s"); + } + + @Test + public void testJavaDollarQuotedFunction() throws Throwable + { + String functionBody = '\n' + + " // parameter val is of type java.lang.Double\n" + + " /* return type is of type java.lang.Double */\n" + + " if (input == null) {\n" + + " return null;\n" + + " }\n" + + " return \"'\"+Math.sin(input)+'\\\'';\n"; + + String fName = createFunction(KEYSPACE_PER_TEST, "double", + "CREATE FUNCTION %s( input double ) " + + "CALLED ON NULL INPUT " + + "RETURNS text " + + "LANGUAGE java\n" + + "AS $$" + functionBody + "$$;"); + + FunctionName fNameName = parseFunctionName(fName); + + assertRows(execute("SELECT language, body FROM system.schema_functions WHERE keyspace_name=? AND function_name=?", + fNameName.keyspace, fNameName.name), + row("java", functionBody)); + } + + @Test + public void testJavaSimpleCollections() throws Throwable + { + createTable("CREATE TABLE %s (key int primary key, lst list, st set, mp map)"); + + String fList = createFunction(KEYSPACE_PER_TEST, "list", + "CREATE FUNCTION %s( lst list ) " + + "RETURNS NULL ON NULL INPUT " + + "RETURNS list " + + "LANGUAGE java\n" + + "AS $$return lst;$$;"); + String fSet = createFunction(KEYSPACE_PER_TEST, "set", + "CREATE FUNCTION %s( st set ) " + + "RETURNS NULL ON NULL INPUT " + + "RETURNS set " + + "LANGUAGE java\n" + + "AS $$return st;$$;"); + String fMap = createFunction(KEYSPACE_PER_TEST, "map", + "CREATE FUNCTION %s( mp map ) " + + "RETURNS NULL ON NULL INPUT " + + "RETURNS map " + + "LANGUAGE java\n" + + "AS $$return mp;$$;"); + + List list = Arrays.asList(1d, 2d, 3d); + Set set = new TreeSet<>(Arrays.asList("one", "three", "two")); + Map map = new TreeMap<>(); + map.put(1, true); + map.put(2, false); + map.put(3, true); + + execute("INSERT INTO %s (key, lst, st, mp) VALUES (1, ?, ?, ?)", list, set, map); + + assertRows(execute("SELECT " + fList + "(lst), " + fSet + "(st), " + fMap + "(mp) FROM %s WHERE key = 1"), + row(list, set, map)); + + // same test - but via native protocol + for (int version = Server.VERSION_2; version <= maxProtocolVersion; version++) + assertRowsNet(version, + executeNet(version, "SELECT " + fList + "(lst), " + fSet + "(st), " + fMap + "(mp) FROM %s WHERE key = 1"), + row(list, set, map)); + } + + @Test + public void testJavaTupleType() throws Throwable + { + createTable("CREATE TABLE %s (key int primary key, tup frozen>)"); + + String fName = createFunction(KEYSPACE, "tuple", + "CREATE FUNCTION %s( tup tuple ) " + + "RETURNS NULL ON NULL INPUT " + + "RETURNS tuple " + + "LANGUAGE java\n" + + "AS $$return tup;$$;"); + + Object t = tuple(1d, "foo", 2, true); + + execute("INSERT INTO %s (key, tup) VALUES (1, ?)", t); + + assertRows(execute("SELECT tup FROM %s WHERE key = 1"), + row(t)); + + assertRows(execute("SELECT " + fName + "(tup) FROM %s WHERE key = 1"), + row(t)); + } + + @Test + public void testJavaTupleTypeCollection() throws Throwable + { + String tupleTypeDef = "tuple, set, map>"; + + createTable("CREATE TABLE %s (key int primary key, tup frozen<" + tupleTypeDef + ">)"); + + 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 NULL ON NULL INPUT " + + "RETURNS list " + + "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 " + + "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 " + + "LANGUAGE java\n" + + "AS $$return " + + " tup.getMap(3, Integer.class, Boolean.class);$$;"); + + List list = Arrays.asList(1d, 2d, 3d); + Set set = new TreeSet<>(Arrays.asList("one", "three", "two")); + Map map = new TreeMap<>(); + map.put(1, true); + map.put(2, false); + map.put(3, true); + + Object t = tuple(1d, list, set, map); + + execute("INSERT INTO %s (key, tup) VALUES (1, ?)", t); + + assertRows(execute("SELECT " + fTup0 + "(tup) FROM %s WHERE key = 1"), + row(t)); + assertRows(execute("SELECT " + fTup1 + "(tup) FROM %s WHERE key = 1"), + row(1d)); + assertRows(execute("SELECT " + fTup2 + "(tup) FROM %s WHERE key = 1"), + row(list)); + assertRows(execute("SELECT " + fTup3 + "(tup) FROM %s WHERE key = 1"), + row(set)); + assertRows(execute("SELECT " + fTup4 + "(tup) FROM %s WHERE key = 1"), + row(map)); + + TupleType tType = TupleType.of(DataType.cdouble(), + DataType.list(DataType.cdouble()), + DataType.set(DataType.text()), + DataType.map(DataType.cint(), DataType.cboolean())); + TupleValue tup = tType.newValue(1d, list, set, map); + for (int version = Server.VERSION_2; version <= maxProtocolVersion; version++) + { + assertRowsNet(version, + executeNet(version, "SELECT " + fTup0 + "(tup) FROM %s WHERE key = 1"), + row(tup)); + assertRowsNet(version, + executeNet(version, "SELECT " + fTup1 + "(tup) FROM %s WHERE key = 1"), + row(1d)); + assertRowsNet(version, + executeNet(version, "SELECT " + fTup2 + "(tup) FROM %s WHERE key = 1"), + row(list)); + assertRowsNet(version, + executeNet(version, "SELECT " + fTup3 + "(tup) FROM %s WHERE key = 1"), + row(set)); + assertRowsNet(version, + executeNet(version, "SELECT " + fTup4 + "(tup) FROM %s WHERE key = 1"), + row(map)); + } + } + + @Test + public void testJavaUserTypeWithUse() throws Throwable + { + String type = createType("CREATE TYPE %s (txt text, i int)"); + createTable("CREATE TABLE %s (key int primary key, udt frozen<" + KEYSPACE + '.' + type + ">)"); + execute("INSERT INTO %s (key, udt) VALUES (1, {txt: 'one', i:1})"); + + for (int version = Server.VERSION_2; version <= maxProtocolVersion; version++) + { + executeNet(version, "USE " + KEYSPACE); + + executeNet(version, + "CREATE FUNCTION f_use1( udt " + type + " ) " + + "RETURNS NULL ON NULL INPUT " + + "RETURNS " + type + " " + + "LANGUAGE java " + + "AS $$return " + + " udt;$$;"); + try + { + List rowsNet = executeNet(version, "SELECT f_use1(udt) FROM %s WHERE key = 1").all(); + Assert.assertEquals(1, rowsNet.size()); + UDTValue udtVal = rowsNet.get(0).getUDTValue(0); + Assert.assertEquals("one", udtVal.getString("txt")); + Assert.assertEquals(1, udtVal.getInt("i")); + } + finally + { + executeNet(version, "DROP FUNCTION f_use1"); + } + } + } + + @Test + public void testJavaUserType() throws Throwable + { + String type = KEYSPACE + '.' + createType("CREATE TYPE %s (txt text, i int)"); + + createTable("CREATE TABLE %s (key int primary key, udt frozen<" + type + ">)"); + + String fUdt0 = createFunction(KEYSPACE, type, + "CREATE FUNCTION %s( udt " + type + " ) " + + "RETURNS NULL ON NULL INPUT " + + "RETURNS " + type + " " + + "LANGUAGE java " + + "AS $$return " + + " udt;$$;"); + String fUdt1 = createFunction(KEYSPACE, type, + "CREATE FUNCTION %s( udt " + type + ") " + + "RETURNS NULL ON NULL INPUT " + + "RETURNS text " + + "LANGUAGE java " + + "AS $$return " + + " udt.getString(\"txt\");$$;"); + String fUdt2 = createFunction(KEYSPACE, type, + "CREATE FUNCTION %s( udt " + type + ") " + + "CALLED ON NULL INPUT " + + "RETURNS int " + + "LANGUAGE java " + + "AS $$return " + + " Integer.valueOf(udt.getInt(\"i\"));$$;"); + + execute("INSERT INTO %s (key, udt) VALUES (1, {txt: 'one', i:1})"); + + UntypedResultSet rows = execute("SELECT " + fUdt0 + "(udt) FROM %s WHERE key = 1"); + Assert.assertEquals(1, rows.size()); + assertRows(execute("SELECT " + fUdt1 + "(udt) FROM %s WHERE key = 1"), + row("one")); + assertRows(execute("SELECT " + fUdt2 + "(udt) FROM %s WHERE key = 1"), + row(1)); + + for (int version = Server.VERSION_2; version <= maxProtocolVersion; version++) + { + List rowsNet = executeNet(version, "SELECT " + fUdt0 + "(udt) FROM %s WHERE key = 1").all(); + Assert.assertEquals(1, rowsNet.size()); + UDTValue udtVal = rowsNet.get(0).getUDTValue(0); + Assert.assertEquals("one", udtVal.getString("txt")); + Assert.assertEquals(1, udtVal.getInt("i")); + assertRowsNet(version, + executeNet(version, "SELECT " + fUdt1 + "(udt) FROM %s WHERE key = 1"), + row("one")); + assertRowsNet(version, + executeNet(version, "SELECT " + fUdt2 + "(udt) FROM %s WHERE key = 1"), + row(1)); + } + } + + @Test + public void testJavaUserTypeRenameField() throws Throwable + { + String type = KEYSPACE + '.' + createType("CREATE TYPE %s (txt text, i int)"); + + createTable("CREATE TABLE %s (key int primary key, udt frozen<" + type + ">)"); + + String fName = createFunction(KEYSPACE, type, + "CREATE FUNCTION %s( udt " + type + " ) " + + "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})"); + + assertRows(execute("SELECT " + fName + "(udt) FROM %s WHERE key = 1"), + row("one")); + + execute("ALTER TYPE " + type + " RENAME txt TO str"); + + assertInvalidMessage("txt is not a field defined in this UDT", + "SELECT " + fName + "(udt) FROM %s WHERE key = 1"); + + execute("ALTER TYPE " + type + " RENAME str TO txt"); + + assertRows(execute("SELECT " + fName + "(udt) FROM %s WHERE key = 1"), + row("one")); + } + + @Test + public void testJavaUserTypeAddFieldWithReplace() throws Throwable + { + String type = KEYSPACE + '.' + createType("CREATE TYPE %s (txt text, i int)"); + + createTable("CREATE TABLE %s (key int primary key, udt frozen<" + type + ">)"); + + String fName1replace = createFunction(KEYSPACE, type, + "CREATE FUNCTION %s( udt " + type + ") " + + "RETURNS NULL ON NULL INPUT " + + "RETURNS text " + + "LANGUAGE java\n" + + "AS $$return udt.getString(\"txt\");$$;"); + String fName2replace = createFunction(KEYSPACE, type, + "CREATE FUNCTION %s( udt " + type + " ) " + + "CALLED ON NULL INPUT " + + "RETURNS int " + + "LANGUAGE java\n" + + "AS $$return Integer.valueOf(udt.getInt(\"i\"));$$;"); + String fName3replace = createFunction(KEYSPACE, type, + "CREATE FUNCTION %s( udt " + type + " ) " + + "CALLED ON NULL INPUT " + + "RETURNS double " + + "LANGUAGE java\n" + + "AS $$return Double.valueOf(udt.getDouble(\"added\"));$$;"); + String fName4replace = createFunction(KEYSPACE, type, + "CREATE FUNCTION %s( udt " + type + " ) " + + "RETURNS NULL ON NULL INPUT " + + "RETURNS " + type + " " + + "LANGUAGE java\n" + + "AS $$return udt;$$;"); + + String fName1noReplace = createFunction(KEYSPACE, type, + "CREATE FUNCTION %s( udt " + type + " ) " + + "RETURNS NULL ON NULL INPUT " + + "RETURNS text " + + "LANGUAGE java\n" + + "AS $$return udt.getString(\"txt\");$$;"); + String fName2noReplace = createFunction(KEYSPACE, type, + "CREATE FUNCTION %s( udt " + type + " ) " + + "CALLED ON NULL INPUT " + + "RETURNS int " + + "LANGUAGE java\n" + + "AS $$return Integer.valueOf(udt.getInt(\"i\"));$$;"); + String fName3noReplace = createFunction(KEYSPACE, type, + "CREATE FUNCTION %s( udt " + type + " ) " + + "CALLED ON NULL INPUT " + + "RETURNS double " + + "LANGUAGE java\n" + + "AS $$return Double.valueOf(udt.getDouble(\"added\"));$$;"); + String fName4noReplace = createFunction(KEYSPACE, type, + "CREATE FUNCTION %s( udt " + type + " ) " + + "RETURNS NULL ON NULL INPUT " + + "RETURNS " + type + " " + + "LANGUAGE java\n" + + "AS $$return udt;$$;"); + + execute("INSERT INTO %s (key, udt) VALUES (1, {txt: 'one', i:1})"); + + assertRows(execute("SELECT " + fName1replace + "(udt) FROM %s WHERE key = 1"), + row("one")); + assertRows(execute("SELECT " + fName2replace + "(udt) FROM %s WHERE key = 1"), + row(1)); + + // add field + + execute("ALTER TYPE " + type + " ADD added double"); + + execute("INSERT INTO %s (key, udt) VALUES (2, {txt: 'two', i:2, added: 2})"); + + // note: type references of functions remain at the state _before_ the type mutation + // means we need to recreate the functions + + execute(String.format("CREATE OR REPLACE FUNCTION %s( udt %s ) " + + "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 %s ) " + + "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 %s ) " + + "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 %s ) " + + "RETURNS NULL ON NULL INPUT " + + "RETURNS %s " + + "LANGUAGE java\n" + + "AS $$return " + + " udt;$$;", + fName4replace, type, type)); + Assert.assertEquals(1, Functions.find(parseFunctionName(fName4replace)).size()); + + assertRows(execute("SELECT " + fName1replace + "(udt) FROM %s WHERE key = 2"), + row("two")); + assertRows(execute("SELECT " + fName2replace + "(udt) FROM %s WHERE key = 2"), + row(2)); + assertRows(execute("SELECT " + fName3replace + "(udt) FROM %s WHERE key = 2"), + row(2d)); + assertRows(execute("SELECT " + fName3replace + "(udt) FROM %s WHERE key = 1"), + row(0d)); + + // un-replaced functions will work since the user type has changed + // and the UDF has exchanged the user type reference + + assertRows(execute("SELECT " + fName1noReplace + "(udt) FROM %s WHERE key = 2"), + row("two")); + assertRows(execute("SELECT " + fName2noReplace + "(udt) FROM %s WHERE key = 2"), + row(2)); + assertRows(execute("SELECT " + fName3noReplace + "(udt) FROM %s WHERE key = 2"), + row(2d)); + assertRows(execute("SELECT " + fName3noReplace + "(udt) FROM %s WHERE key = 1"), + row(0d)); + + execute("DROP FUNCTION " + fName1replace); + execute("DROP FUNCTION " + fName2replace); + execute("DROP FUNCTION " + fName3replace); + execute("DROP FUNCTION " + fName4replace); + execute("DROP FUNCTION " + fName1noReplace); + execute("DROP FUNCTION " + fName2noReplace); + execute("DROP FUNCTION " + fName3noReplace); + execute("DROP FUNCTION " + fName4noReplace); + } + + @Test + public void testJavaUTCollections() throws Throwable + { + String type = KEYSPACE + '.' + createType("CREATE TYPE %s (txt text, i int)"); + + createTable(String.format("CREATE TABLE %%s " + + "(key int primary key, lst list>, st set>, mp map>)", + type, type, type)); + + String fName1 = createFunction(KEYSPACE, "list>", + "CREATE FUNCTION %s( lst list> ) " + + "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>", + "CREATE FUNCTION %s( st set> ) " + + "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>", + "CREATE FUNCTION %s( mp map> ) " + + "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\");$$;"); + + execute("INSERT INTO %s (key, lst, st, mp) values (1, " + + "[ {txt: 'one', i:1}, {txt: 'three', i:1}, {txt: 'one', i:1} ] , " + + "{ {txt: 'one', i:1}, {txt: 'three', i:3}, {txt: 'two', i:2} }, " + + "{ 1: {txt: 'one', i:1}, 2: {txt: 'one', i:3}, 3: {txt: 'two', i:2} })"); + + assertRows(execute("SELECT " + fName1 + "(lst), " + fName2 + "(st), " + fName3 + "(mp) FROM %s WHERE key = 1"), + row("three", "one", "two")); + + for (int version = Server.VERSION_2; version <= maxProtocolVersion; version++) + assertRowsNet(version, + executeNet(version, "SELECT " + fName1 + "(lst), " + fName2 + "(st), " + fName3 + "(mp) FROM %s WHERE key = 1"), + row("three", "one", "two")); + } + + @Test + public void testFunctionWithFrozenSetType() throws Throwable + { + createTable("CREATE TABLE %s (a int PRIMARY KEY, b frozen>)"); + createIndex("CREATE INDEX ON %s (FULL(b))"); + + execute("INSERT INTO %s (a, b) VALUES (?, ?)", 0, set()); + execute("INSERT INTO %s (a, b) VALUES (?, ?)", 1, set(1, 2, 3)); + execute("INSERT INTO %s (a, b) VALUES (?, ?)", 2, set(4, 5, 6)); + execute("INSERT INTO %s (a, b) VALUES (?, ?)", 3, set(7, 8, 9)); + + assertInvalidMessage("The function arguments should not be frozen", + "CREATE OR REPLACE FUNCTION " + KEYSPACE + ".frozenSetArg(values frozen>) " + + "CALLED ON NULL INPUT " + + "RETURNS int " + + "LANGUAGE java\n" + + "AS 'int sum = 0; for (Object value : values) {sum += value;} return sum;';"); + + assertInvalidMessage("The function return type should not be frozen", + "CREATE OR REPLACE FUNCTION " + KEYSPACE + ".frozenReturnType(values set) " + + "CALLED ON NULL INPUT " + + "RETURNS frozen> " + + "LANGUAGE java\n" + + "AS 'return values;';"); + + String functionName = createFunction(KEYSPACE, + "set", + "CREATE FUNCTION %s (values set) " + + "CALLED ON NULL INPUT " + + "RETURNS int " + + "LANGUAGE java\n" + + "AS 'int sum = 0; for (Object value : values) {sum += ((Integer) value);} return sum;';"); + + assertRows(execute("SELECT a, " + functionName + "(b) FROM %s"), + row(0, 0), + row(1, 6), + row(2, 15), + row(3, 24)); + + functionName = createFunction(KEYSPACE, + "set", + "CREATE FUNCTION %s (values set) " + + "CALLED ON NULL INPUT " + + "RETURNS set " + + "LANGUAGE java\n" + + "AS 'return values;';"); + + assertRows(execute("SELECT a FROM %s WHERE b = " + functionName + "(?)", set(1, 2, 3)), + row(1)); + + assertInvalidMessage("The function arguments should not be frozen", + "DROP FUNCTION " + functionName + "(frozen>);"); + } + + @Test + public void testFunctionWithFrozenListType() throws Throwable + { + createTable("CREATE TABLE %s (a int PRIMARY KEY, b frozen>)"); + createIndex("CREATE INDEX ON %s (FULL(b))"); + + execute("INSERT INTO %s (a, b) VALUES (?, ?)", 0, list()); + execute("INSERT INTO %s (a, b) VALUES (?, ?)", 1, list(1, 2, 3)); + execute("INSERT INTO %s (a, b) VALUES (?, ?)", 2, list(4, 5, 6)); + execute("INSERT INTO %s (a, b) VALUES (?, ?)", 3, list(7, 8, 9)); + + assertInvalidMessage("The function arguments should not be frozen", + "CREATE OR REPLACE FUNCTION " + KEYSPACE + ".withFrozenArg(values frozen>) " + + "CALLED ON NULL INPUT " + + "RETURNS int " + + "LANGUAGE java\n" + + "AS 'int sum = 0; for (Object value : values) {sum += value;} return sum;';"); + + assertInvalidMessage("The function return type should not be frozen", + "CREATE OR REPLACE FUNCTION " + KEYSPACE + ".frozenReturnType(values list) " + + "CALLED ON NULL INPUT " + + "RETURNS frozen> " + + "LANGUAGE java\n" + + "AS 'return values;';"); + + String functionName = createFunction(KEYSPACE, + "list", + "CREATE FUNCTION %s (values list) " + + "CALLED ON NULL INPUT " + + "RETURNS int " + + "LANGUAGE java\n" + + "AS 'int sum = 0; for (Object value : values) {sum += ((Integer) value);} return sum;';"); + + assertRows(execute("SELECT a, " + functionName + "(b) FROM %s"), + row(0, 0), + row(1, 6), + row(2, 15), + row(3, 24)); + + functionName = createFunction(KEYSPACE, + "list", + "CREATE FUNCTION %s (values list) " + + "CALLED ON NULL INPUT " + + "RETURNS list " + + "LANGUAGE java\n" + + "AS 'return values;';"); + + assertRows(execute("SELECT a FROM %s WHERE b = " + functionName + "(?)", set(1, 2, 3)), + row(1)); + + assertInvalidMessage("The function arguments should not be frozen", + "DROP FUNCTION " + functionName + "(frozen>);"); + } + + @Test + public void testFunctionWithFrozenMapType() throws Throwable + { + createTable("CREATE TABLE %s (a int PRIMARY KEY, b frozen>)"); + createIndex("CREATE INDEX ON %s (FULL(b))"); + + execute("INSERT INTO %s (a, b) VALUES (?, ?)", 0, map()); + execute("INSERT INTO %s (a, b) VALUES (?, ?)", 1, map(1, 1, 2, 2, 3, 3)); + execute("INSERT INTO %s (a, b) VALUES (?, ?)", 2, map(4, 4, 5, 5, 6, 6)); + execute("INSERT INTO %s (a, b) VALUES (?, ?)", 3, map(7, 7, 8, 8, 9, 9)); + + assertInvalidMessage("The function arguments should not be frozen", + "CREATE OR REPLACE FUNCTION " + KEYSPACE + ".withFrozenArg(values frozen>) " + + "CALLED ON NULL INPUT " + + "RETURNS int " + + "LANGUAGE java\n" + + "AS 'int sum = 0; for (Object value : values.values()) {sum += value;} return sum;';"); + + assertInvalidMessage("The function return type should not be frozen", + "CREATE OR REPLACE FUNCTION " + KEYSPACE + ".frozenReturnType(values map) " + + "CALLED ON NULL INPUT " + + "RETURNS frozen> " + + "LANGUAGE java\n" + + "AS 'return values;';"); + + String functionName = createFunction(KEYSPACE, + "map", + "CREATE FUNCTION %s (values map) " + + "CALLED ON NULL INPUT " + + "RETURNS int " + + "LANGUAGE java\n" + + "AS 'int sum = 0; for (Object value : values.values()) {sum += ((Integer) value);} return sum;';"); + + assertRows(execute("SELECT a, " + functionName + "(b) FROM %s"), + row(0, 0), + row(1, 6), + row(2, 15), + row(3, 24)); + + functionName = createFunction(KEYSPACE, + "map", + "CREATE FUNCTION %s (values map) " + + "CALLED ON NULL INPUT " + + "RETURNS map " + + "LANGUAGE java\n" + + "AS 'return values;';"); + + assertRows(execute("SELECT a FROM %s WHERE b = " + functionName + "(?)", map(1, 1, 2, 2, 3, 3)), + row(1)); + + assertInvalidMessage("The function arguments should not be frozen", + "DROP FUNCTION " + functionName + "(frozen>);"); + } + + @Test + public void testFunctionWithFrozenTupleType() throws Throwable + { + createTable("CREATE TABLE %s (a int PRIMARY KEY, b frozen>)"); + createIndex("CREATE INDEX ON %s (b)"); + + execute("INSERT INTO %s (a, b) VALUES (?, ?)", 0, tuple()); + execute("INSERT INTO %s (a, b) VALUES (?, ?)", 1, tuple(1, 2)); + execute("INSERT INTO %s (a, b) VALUES (?, ?)", 2, tuple(4, 5)); + execute("INSERT INTO %s (a, b) VALUES (?, ?)", 3, tuple(7, 8)); + + assertInvalidMessage("The function arguments should not be frozen", + "CREATE OR REPLACE FUNCTION " + KEYSPACE + ".withFrozenArg(values frozen>) " + + "CALLED ON NULL INPUT " + + "RETURNS text " + + "LANGUAGE java\n" + + "AS 'return values.toString();';"); + + assertInvalidMessage("The function return type should not be frozen", + "CREATE OR REPLACE FUNCTION " + KEYSPACE + ".frozenReturnType(values tuple) " + + "CALLED ON NULL INPUT " + + "RETURNS frozen> " + + "LANGUAGE java\n" + + "AS 'return values;';"); + + String functionName = createFunction(KEYSPACE, + "tuple", + "CREATE FUNCTION %s (values tuple) " + + "CALLED ON NULL INPUT " + + "RETURNS text " + + "LANGUAGE java\n" + + "AS 'return values.toString();';"); + + assertRows(execute("SELECT a, " + functionName + "(b) FROM %s"), + row(0, "(null, null)"), + row(1, "(1, 2)"), + row(2, "(4, 5)"), + row(3, "(7, 8)")); + + functionName = createFunction(KEYSPACE, + "tuple", + "CREATE FUNCTION %s (values tuple) " + + "CALLED ON NULL INPUT " + + "RETURNS tuple " + + "LANGUAGE java\n" + + "AS 'return values;';"); + + assertRows(execute("SELECT a FROM %s WHERE b = " + functionName + "(?)", tuple(1, 2)), + row(1)); + + assertInvalidMessage("The function arguments should not be frozen", + "DROP FUNCTION " + functionName + "(frozen>);"); + } + + @Test + public void testFunctionWithFrozenUDType() throws Throwable + { + String myType = createType("CREATE TYPE %s (f int)"); + createTable("CREATE TABLE %s (a int PRIMARY KEY, b frozen<" + myType + ">)"); + createIndex("CREATE INDEX ON %s (b)"); + + execute("INSERT INTO %s (a, b) VALUES (?, {f : ?})", 0, 0); + execute("INSERT INTO %s (a, b) VALUES (?, {f : ?})", 1, 1); + execute("INSERT INTO %s (a, b) VALUES (?, {f : ?})", 2, 4); + execute("INSERT INTO %s (a, b) VALUES (?, {f : ?})", 3, 7); + + assertInvalidMessage("The function arguments should not be frozen", + "CREATE OR REPLACE FUNCTION " + KEYSPACE + ".withFrozenArg(values frozen<" + myType + ">) " + + "CALLED ON NULL INPUT " + + "RETURNS text " + + "LANGUAGE java\n" + + "AS 'return values.toString();';"); + + assertInvalidMessage("The function return type should not be frozen", + "CREATE OR REPLACE FUNCTION " + KEYSPACE + ".frozenReturnType(values " + myType + ") " + + "CALLED ON NULL INPUT " + + "RETURNS frozen<" + myType + "> " + + "LANGUAGE java\n" + + "AS 'return values;';"); + + String functionName = createFunction(KEYSPACE, + myType, + "CREATE FUNCTION %s (values " + myType + ") " + + "CALLED ON NULL INPUT " + + "RETURNS text " + + "LANGUAGE java\n" + + "AS 'return values.toString();';"); + + assertRows(execute("SELECT a, " + functionName + "(b) FROM %s"), + row(0, "{f:0}"), + row(1, "{f:1}"), + row(2, "{f:4}"), + row(3, "{f:7}")); + + functionName = createFunction(KEYSPACE, + myType, + "CREATE FUNCTION %s (values " + myType + ") " + + "CALLED ON NULL INPUT " + + "RETURNS " + myType + " " + + "LANGUAGE java\n" + + "AS 'return values;';"); + + assertRows(execute("SELECT a FROM %s WHERE b = " + functionName + "({f: ?})", 1), + row(1)); + + 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, "int", + "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})); + } + + @Test + public void testAllNativeTypes() throws Throwable + { + StringBuilder sig = new StringBuilder(); + StringBuilder args = new StringBuilder(); + for (CQL3Type.Native type : CQL3Type.Native.values()) + { + if (sig.length() > 0) + sig.append(','); + sig.append(type.toString()); + + if (args.length() > 0) + args.append(','); + args.append("arg").append(type.toString()).append(' ').append(type.toString()); + } + createFunction(KEYSPACE, sig.toString(), + "CREATE OR REPLACE FUNCTION %s(" + args + ") " + + "RETURNS NULL ON NULL INPUT " + + "RETURNS int " + + "LANGUAGE JAVA\n" + + "AS 'return 0;'"); + + for (CQL3Type.Native type : CQL3Type.Native.values()) + { + createFunction(KEYSPACE_PER_TEST, type.toString(), + "CREATE OR REPLACE FUNCTION %s(val " + type.toString() + ") " + + "RETURNS NULL ON NULL INPUT " + + "RETURNS int " + + "LANGUAGE JAVA\n" + + "AS 'return 0;'"); + } + } +} diff --git a/test/unit/org/apache/cassandra/cql3/validation/entities/UFScriptTest.java b/test/unit/org/apache/cassandra/cql3/validation/entities/UFScriptTest.java new file mode 100644 index 0000000000..d3050a5bd7 --- /dev/null +++ b/test/unit/org/apache/cassandra/cql3/validation/entities/UFScriptTest.java @@ -0,0 +1,504 @@ +/* + * 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.validation.entities; + +import java.math.BigDecimal; +import java.math.BigInteger; +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 java.util.UUID; + +import org.junit.Assert; +import org.junit.BeforeClass; +import org.junit.Test; + +import com.datastax.driver.core.DataType; +import com.datastax.driver.core.TupleType; +import com.datastax.driver.core.TupleValue; +import org.apache.cassandra.config.DatabaseDescriptor; +import org.apache.cassandra.cql3.CQLTester; +import org.apache.cassandra.cql3.UntypedResultSet; +import org.apache.cassandra.cql3.functions.FunctionName; +import org.apache.cassandra.dht.ByteOrderedPartitioner; +import org.apache.cassandra.exceptions.FunctionExecutionException; +import org.apache.cassandra.transport.Server; +import org.apache.cassandra.utils.UUIDGen; + +public class UFScriptTest extends CQLTester +{ + @BeforeClass + public static void setUp() + { + DatabaseDescriptor.setPartitioner(ByteOrderedPartitioner.instance); + } + + @Test + public void testJavascriptSimpleCollections() throws Throwable + { + createTable("CREATE TABLE %s (key int primary key, lst list, st set, mp map)"); + + String fName1 = createFunction(KEYSPACE_PER_TEST, "list", + "CREATE FUNCTION %s( lst list ) " + + "RETURNS NULL ON NULL INPUT " + + "RETURNS list " + + "LANGUAGE javascript\n" + + "AS 'lst;';"); + String fName2 = createFunction(KEYSPACE_PER_TEST, "set", + "CREATE FUNCTION %s( st set ) " + + "RETURNS NULL ON NULL INPUT " + + "RETURNS set " + + "LANGUAGE javascript\n" + + "AS 'st;';"); + String fName3 = createFunction(KEYSPACE_PER_TEST, "map", + "CREATE FUNCTION %s( mp map ) " + + "RETURNS NULL ON NULL INPUT " + + "RETURNS map " + + "LANGUAGE javascript\n" + + "AS 'mp;';"); + + List list = Arrays.asList(1d, 2d, 3d); + Set set = new TreeSet<>(Arrays.asList("one", "three", "two")); + Map map = new TreeMap<>(); + map.put(1, true); + map.put(2, false); + map.put(3, true); + + execute("INSERT INTO %s (key, lst, st, mp) VALUES (1, ?, ?, ?)", list, set, map); + + assertRows(execute("SELECT lst, st, mp FROM %s WHERE key = 1"), + row(list, set, map)); + + assertRows(execute("SELECT " + fName1 + "(lst), " + fName2 + "(st), " + fName3 + "(mp) FROM %s WHERE key = 1"), + row(list, set, map)); + + for (int version = Server.VERSION_2; version <= maxProtocolVersion; version++) + assertRowsNet(version, + executeNet(version, "SELECT " + fName1 + "(lst), " + fName2 + "(st), " + fName3 + "(mp) FROM %s WHERE key = 1"), + row(list, set, map)); + } + + @Test + public void testJavascriptTupleType() throws Throwable + { + createTable("CREATE TABLE %s (key int primary key, tup frozen>)"); + + String fName = createFunction(KEYSPACE_PER_TEST, "tuple", + "CREATE FUNCTION %s( tup tuple ) " + + "RETURNS NULL ON NULL INPUT " + + "RETURNS tuple " + + "LANGUAGE javascript\n" + + "AS $$tup;$$;"); + + Object t = tuple(1d, "foo", 2, true); + + execute("INSERT INTO %s (key, tup) VALUES (1, ?)", t); + + assertRows(execute("SELECT tup FROM %s WHERE key = 1"), + row(t)); + + assertRows(execute("SELECT " + fName + "(tup) FROM %s WHERE key = 1"), + row(t)); + } + + @Test + public void testJavascriptTupleTypeCollection() throws Throwable + { + String tupleTypeDef = "tuple, set, map>"; + createTable("CREATE TABLE %s (key int primary key, tup frozen<" + tupleTypeDef + ">)"); + + String fTup1 = createFunction(KEYSPACE_PER_TEST, tupleTypeDef, + "CREATE FUNCTION %s( tup " + tupleTypeDef + " ) " + + "RETURNS NULL ON NULL INPUT " + + "RETURNS tuple, set, map> " + + "LANGUAGE javascript\n" + + "AS $$" + + " tup;$$;"); + String fTup2 = createFunction(KEYSPACE_PER_TEST, tupleTypeDef, + "CREATE FUNCTION %s( tup " + tupleTypeDef + " ) " + + "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 " + + "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 " + + "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 " + + "LANGUAGE javascript\n" + + "AS $$" + + " tup.getMap(3, java.lang.Class.forName(\"java.lang.Integer\"), java.lang.Class.forName(\"java.lang.Boolean\"));$$;"); + + List list = Arrays.asList(1d, 2d, 3d); + Set set = new TreeSet<>(Arrays.asList("one", "three", "two")); + Map map = new TreeMap<>(); + map.put(1, true); + map.put(2, false); + map.put(3, true); + + Object t = tuple(1d, list, set, map); + + execute("INSERT INTO %s (key, tup) VALUES (1, ?)", t); + + assertRows(execute("SELECT " + fTup1 + "(tup) FROM %s WHERE key = 1"), + row(t)); + assertRows(execute("SELECT " + fTup2 + "(tup) FROM %s WHERE key = 1"), + row(1d)); + assertRows(execute("SELECT " + fTup3 + "(tup) FROM %s WHERE key = 1"), + row(list)); + assertRows(execute("SELECT " + fTup4 + "(tup) FROM %s WHERE key = 1"), + row(set)); + assertRows(execute("SELECT " + fTup5 + "(tup) FROM %s WHERE key = 1"), + row(map)); + + // same test - but via native protocol + TupleType tType = TupleType.of(DataType.cdouble(), + DataType.list(DataType.cdouble()), + DataType.set(DataType.text()), + DataType.map(DataType.cint(), DataType.cboolean())); + TupleValue tup = tType.newValue(1d, list, set, map); + for (int version = Server.VERSION_2; version <= maxProtocolVersion; version++) + { + assertRowsNet(version, + executeNet(version, "SELECT " + fTup1 + "(tup) FROM %s WHERE key = 1"), + row(tup)); + assertRowsNet(version, + executeNet(version, "SELECT " + fTup2 + "(tup) FROM %s WHERE key = 1"), + row(1d)); + assertRowsNet(version, + executeNet(version, "SELECT " + fTup3 + "(tup) FROM %s WHERE key = 1"), + row(list)); + assertRowsNet(version, + executeNet(version, "SELECT " + fTup4 + "(tup) FROM %s WHERE key = 1"), + row(set)); + assertRowsNet(version, + executeNet(version, "SELECT " + fTup5 + "(tup) FROM %s WHERE key = 1"), + row(map)); + } + } + + @Test + public void testJavascriptUserType() throws Throwable + { + String type = createType("CREATE TYPE %s (txt text, i int)"); + + createTable("CREATE TABLE %s (key int primary key, udt frozen<" + type + ">)"); + + String fUdt1 = createFunction(KEYSPACE, type, + "CREATE FUNCTION %s( udt " + type + " ) " + + "RETURNS NULL ON NULL INPUT " + + "RETURNS " + type + " " + + "LANGUAGE javascript\n" + + "AS $$" + + " udt;$$;"); + String fUdt2 = createFunction(KEYSPACE, type, + "CREATE FUNCTION %s( udt " + type + " ) " + + "RETURNS NULL ON NULL INPUT " + + "RETURNS text " + + "LANGUAGE javascript\n" + + "AS $$" + + " udt.getString(\"txt\");$$;"); + String fUdt3 = createFunction(KEYSPACE, type, + "CREATE FUNCTION %s( udt " + 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})"); + + UntypedResultSet rows = execute("SELECT " + fUdt1 + "(udt) FROM %s WHERE key = 1"); + Assert.assertEquals(1, rows.size()); + assertRows(execute("SELECT " + fUdt2 + "(udt) FROM %s WHERE key = 1"), + row("one")); + assertRows(execute("SELECT " + fUdt3 + "(udt) FROM %s WHERE key = 1"), + row(1)); + } + + @Test + public void testJavascriptUTCollections() throws Throwable + { + String type = createType("CREATE TYPE %s (txt text, i int)"); + + createTable(String.format("CREATE TABLE %%s " + + "(key int primary key, lst list>, st set>, mp map>)", + type, type, type)); + + String fName = createFunction(KEYSPACE, "list>", + "CREATE FUNCTION %s( lst list> ) " + + "RETURNS NULL ON NULL INPUT " + + "RETURNS text " + + "LANGUAGE javascript\n" + + "AS $$" + + " lst.get(1).getString(\"txt\");$$;"); + createFunctionOverload(fName, "set>", + "CREATE FUNCTION %s( st set> ) " + + "RETURNS NULL ON NULL INPUT " + + "RETURNS text " + + "LANGUAGE javascript\n" + + "AS $$" + + " st.iterator().next().getString(\"txt\");$$;"); + createFunctionOverload(fName, "map>", + "CREATE FUNCTION %s( mp map> ) " + + "RETURNS NULL ON NULL INPUT " + + "RETURNS text " + + "LANGUAGE javascript\n" + + "AS $$" + + " mp.get(java.lang.Integer.valueOf(3)).getString(\"txt\");$$;"); + + execute("INSERT INTO %s (key, lst, st, mp) values (1, " + + // list> + "[ {txt: 'one', i:1}, {txt: 'three', i:1}, {txt: 'one', i:1} ] , " + + // set> + "{ {txt: 'one', i:1}, {txt: 'three', i:3}, {txt: 'two', i:2} }, " + + // map> + "{ 1: {txt: 'one', i:1}, 2: {txt: 'one', i:3}, 3: {txt: 'two', i:2} })"); + + assertRows(execute("SELECT " + fName + "(lst) FROM %s WHERE key = 1"), + row("three")); + assertRows(execute("SELECT " + fName + "(st) FROM %s WHERE key = 1"), + row("one")); + assertRows(execute("SELECT " + fName + "(mp) FROM %s WHERE key = 1"), + row("two")); + + String cqlSelect = "SELECT " + fName + "(lst), " + fName + "(st), " + fName + "(mp) FROM %s WHERE key = 1"; + assertRows(execute(cqlSelect), + row("three", "one", "two")); + + // same test - but via native protocol + for (int version = Server.VERSION_2; version <= maxProtocolVersion; version++) + assertRowsNet(version, + executeNet(version, cqlSelect), + row("three", "one", "two")); + } + + @Test + public void testJavascriptFunction() throws Throwable + { + createTable("CREATE TABLE %s (key int primary key, val double)"); + + String functionBody = '\n' + + " Math.sin(val);\n"; + + String fName = createFunction(KEYSPACE, "double", + "CREATE OR REPLACE FUNCTION %s(val double) " + + "RETURNS NULL ON NULL INPUT " + + "RETURNS double " + + "LANGUAGE javascript\n" + + "AS '" + functionBody + "';"); + + FunctionName fNameName = parseFunctionName(fName); + + assertRows(execute("SELECT language, body FROM system.schema_functions WHERE keyspace_name=? AND function_name=?", + fNameName.keyspace, fNameName.name), + row("javascript", functionBody)); + + execute("INSERT INTO %s (key, val) VALUES (?, ?)", 1, 1d); + execute("INSERT INTO %s (key, val) VALUES (?, ?)", 2, 2d); + execute("INSERT INTO %s (key, val) VALUES (?, ?)", 3, 3d); + assertRows(execute("SELECT key, val, " + fName + "(val) FROM %s"), + row(1, 1d, Math.sin(1d)), + row(2, 2d, Math.sin(2d)), + row(3, 3d, Math.sin(3d)) + ); + } + + @Test + public void testJavascriptBadReturnType() throws Throwable + { + createTable("CREATE TABLE %s (key int primary key, val double)"); + + String fName = createFunction(KEYSPACE, "double", + "CREATE OR REPLACE FUNCTION %s(val double) " + + "RETURNS NULL ON NULL INPUT " + + "RETURNS double " + + "LANGUAGE javascript\n" + + "AS '\"string\";';"); + + execute("INSERT INTO %s (key, val) VALUES (?, ?)", 1, 1d); + // throws IRE with ClassCastException + assertInvalidMessage("Invalid value for CQL type double", "SELECT key, val, " + fName + "(val) FROM %s"); + } + + @Test + public void testJavascriptThrow() throws Throwable + { + createTable("CREATE TABLE %s (key int primary key, val double)"); + + 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\";';"); + + execute("INSERT INTO %s (key, val) VALUES (?, ?)", 1, 1d); + // throws IRE with ScriptException + assertInvalidThrowMessage("fool", FunctionExecutionException.class, + "SELECT key, val, " + fName + "(val) FROM %s"); + } + + @Test + public void testJavascriptCompileFailure() throws Throwable + { + assertInvalidMessage("Failed to compile function 'cql_test_keyspace.scrinv'", + "CREATE OR REPLACE FUNCTION " + KEYSPACE + ".scrinv(val double) " + + "RETURNS NULL ON NULL INPUT " + + "RETURNS double " + + "LANGUAGE javascript\n" + + "AS 'foo bar';"); + } + + @Test + public void testScriptInvalidLanguage() throws Throwable + { + assertInvalidMessage("Invalid language 'artificial_intelligence' for function 'cql_test_keyspace.scrinv'", + "CREATE OR REPLACE FUNCTION " + KEYSPACE + ".scrinv(val double) " + + "RETURNS NULL ON NULL INPUT " + + "RETURNS double " + + "LANGUAGE artificial_intelligence\n" + + "AS 'question for 42?';"); + } + + @Test + public void testScriptReturnTypeCasting() throws Throwable + { + createTable("CREATE TABLE %s (key int primary key, val double)"); + execute("INSERT INTO %s (key, val) VALUES (?, ?)", 1, 1d); + + Object[][] variations = { + new Object[] { "true", "boolean", true }, + new Object[] { "false", "boolean", false }, + new Object[] { "100", "tinyint", (byte)100 }, + new Object[] { "100.", "tinyint", (byte)100 }, + new Object[] { "100", "smallint", (short)100 }, + new Object[] { "100.", "smallint", (short)100 }, + new Object[] { "100", "int", 100 }, + new Object[] { "100.", "int", 100 }, + new Object[] { "100", "double", 100d }, + new Object[] { "100.", "double", 100d }, + new Object[] { "100", "bigint", 100L }, + new Object[] { "100.", "bigint", 100L }, + new Object[] { "100", "varint", BigInteger.valueOf(100L) }, + new Object[] { "100.", "varint", BigInteger.valueOf(100L) }, + new Object[] { "parseInt(\"100\");", "decimal", BigDecimal.valueOf(100d) }, + new Object[] { "100.", "decimal", BigDecimal.valueOf(100d) }, + }; + + for (Object[] variation : variations) + { + Object functionBody = variation[0]; + Object returnType = variation[1]; + Object expectedResult = variation[2]; + + String fName = createFunction(KEYSPACE, "double", + "CREATE OR REPLACE FUNCTION %s(val double) " + + "RETURNS NULL ON NULL INPUT " + + "RETURNS " +returnType + ' ' + + "LANGUAGE javascript " + + "AS '" + functionBody + ";';"); + assertRows(execute("SELECT key, val, " + fName + "(val) FROM %s"), + row(1, 1d, expectedResult)); + } + } + + @Test + public void testScriptParamReturnTypes() throws Throwable + { + UUID ruuid = UUID.randomUUID(); + UUID tuuid = UUIDGen.getTimeUUID(); + + createTable("CREATE TABLE %s (key int primary key, " + + "tival tinyint, sival smallint, ival int, lval bigint, fval float, dval double, vval varint, ddval decimal, " + + "timval time, dtval date, tsval timestamp, uval uuid, tuval timeuuid)"); + execute("INSERT INTO %s (key, tival, sival, ival, lval, fval, dval, vval, ddval, timval, dtval, tsval, uval, tuval) VALUES " + + "(?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)", 1, + (byte)1, (short)1, 1, 1L, 1f, 1d, BigInteger.valueOf(1L), BigDecimal.valueOf(1d), 1L, Integer.MAX_VALUE, new Date(1), ruuid, tuuid); + + Object[][] variations = { + new Object[] { "tinyint", "tival", (byte)1, (byte)2 }, + new Object[] { "smallint", "sival", (short)1, (short)2 }, + new Object[] { "int", "ival", 1, 2 }, + new Object[] { "bigint", "lval", 1L, 2L }, + new Object[] { "float", "fval", 1f, 2f }, + new Object[] { "double", "dval", 1d, 2d }, + new Object[] { "varint", "vval", BigInteger.valueOf(1L), BigInteger.valueOf(2L) }, + new Object[] { "decimal", "ddval", BigDecimal.valueOf(1d), BigDecimal.valueOf(2d) }, + new Object[] { "time", "timval", 1L, 2L }, + }; + + for (Object[] variation : variations) + { + Object type = variation[0]; + Object col = variation[1]; + Object expected1 = variation[2]; + 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;';"); + assertRows(execute("SELECT key, " + col + ", " + fName + '(' + col + ") FROM %s"), + row(1, expected1, expected2)); + } + + variations = new Object[][] { + new Object[] { "timestamp","tsval", new Date(1), new Date(1) }, + new Object[] { "uuid", "uval", ruuid, ruuid }, + new Object[] { "timeuuid", "tuval", tuuid, tuuid }, + new Object[] { "date", "dtval", Integer.MAX_VALUE, Integer.MAX_VALUE }, + }; + + for (Object[] variation : variations) + { + Object type = variation[0]; + Object col = variation[1]; + Object expected1 = variation[2]; + 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;';"); + assertRows(execute("SELECT key, " + col + ", " + fName + '(' + col + ") FROM %s"), + row(1, expected1, expected2)); + } + } +} diff --git a/test/unit/org/apache/cassandra/cql3/validation/entities/UFTest.java b/test/unit/org/apache/cassandra/cql3/validation/entities/UFTest.java index bcfe871993..d4d2a10a77 100644 --- a/test/unit/org/apache/cassandra/cql3/validation/entities/UFTest.java +++ b/test/unit/org/apache/cassandra/cql3/validation/entities/UFTest.java @@ -17,9 +17,6 @@ */ package org.apache.cassandra.cql3.validation.entities; -import java.math.BigDecimal; -import java.math.BigInteger; -import java.nio.ByteBuffer; import java.util.ArrayList; import java.util.Arrays; import java.util.Date; @@ -34,14 +31,9 @@ import org.junit.Assert; import org.junit.BeforeClass; import org.junit.Test; -import com.datastax.driver.core.DataType; import com.datastax.driver.core.Row; -import com.datastax.driver.core.TupleType; -import com.datastax.driver.core.TupleValue; -import com.datastax.driver.core.UDTValue; import com.datastax.driver.core.exceptions.InvalidQueryException; import org.apache.cassandra.config.DatabaseDescriptor; -import org.apache.cassandra.cql3.CQL3Type; import org.apache.cassandra.cql3.CQLTester; import org.apache.cassandra.cql3.QueryProcessor; import org.apache.cassandra.cql3.UntypedResultSet; @@ -50,13 +42,11 @@ import org.apache.cassandra.cql3.functions.Functions; import org.apache.cassandra.cql3.functions.UDFunction; import org.apache.cassandra.db.marshal.CollectionType; import org.apache.cassandra.dht.ByteOrderedPartitioner; -import org.apache.cassandra.exceptions.FunctionExecutionException; import org.apache.cassandra.exceptions.InvalidRequestException; 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; import org.apache.cassandra.utils.UUIDGen; public class UFTest extends CQLTester @@ -295,7 +285,7 @@ public class UFTest extends CQLTester return prepared; } - public void checkDelayedValuesCorrectlyIdentifyFunctionsInUse(boolean dropKeyspace) throws Throwable + private void checkDelayedValuesCorrectlyIdentifyFunctionsInUse(boolean dropKeyspace) throws Throwable { // prepare a statement which doesn't use any function for a control createTable("CREATE TABLE %s (" + @@ -583,164 +573,6 @@ public class UFTest extends CQLTester ); } - @Test - public void testJavaFunctionNoParameters() throws Throwable - { - createTable("CREATE TABLE %s (key int primary key, val double)"); - - String functionBody = "\n return 1L;\n"; - - String fName = createFunction(KEYSPACE, "", - "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=?", - KEYSPACE, parseFunctionName(fName).name), - row("java", functionBody)); - - execute("INSERT INTO %s (key, val) VALUES (?, ?)", 1, 1d); - execute("INSERT INTO %s (key, val) VALUES (?, ?)", 2, 2d); - execute("INSERT INTO %s (key, val) VALUES (?, ?)", 3, 3d); - assertRows(execute("SELECT key, val, " + fName + "() FROM %s"), - row(1, 1d, 1L), - row(2, 2d, 1L), - row(3, 3d, 1L) - ); - } - - @Test - public void testJavaFunctionInvalidBodies() throws Throwable - { - try - { - execute("CREATE OR REPLACE FUNCTION " + KEYSPACE + ".jfinv() " + - "RETURNS NULL ON NULL INPUT " + - "RETURNS bigint " + - "LANGUAGE JAVA\n" + - "AS '\n" + - "foobarbaz" + - "\n';"); - Assert.fail(); - } - catch (InvalidRequestException e) - { - Assert.assertTrue(e.getMessage(), e.getMessage().contains("Java source compilation failed")); - Assert.assertTrue(e.getMessage(), e.getMessage().contains("insert \";\" to complete BlockStatements")); - } - - try - { - execute("CREATE OR REPLACE FUNCTION " + KEYSPACE + ".jfinv() " + - "RETURNS NULL ON NULL INPUT " + - "RETURNS bigint " + - "LANGUAGE JAVA\n" + - "AS '\n" + - "foobarbaz;" + - "\n';"); - Assert.fail(); - } - catch (InvalidRequestException e) - { - Assert.assertTrue(e.getMessage(), e.getMessage().contains("Java source compilation failed")); - Assert.assertTrue(e.getMessage(), e.getMessage().contains("foobarbaz cannot be resolved to a type")); - } - } - - @Test - public void testJavaFunctionInvalidReturn() throws Throwable - { - assertInvalidMessage("system keyspace is not user-modifiable", - "CREATE OR REPLACE FUNCTION jfir(val double) " + - "RETURNS NULL ON NULL INPUT " + - "RETURNS double " + - "LANGUAGE JAVA\n" + - "AS 'return 1L;';"); - } - - @Test - public void testJavaFunctionArgumentTypeMismatch() throws Throwable - { - createTable("CREATE TABLE %s (key int primary key, val bigint)"); - - String fName = createFunction(KEYSPACE, "double", - "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, 1L); - execute("INSERT INTO %s (key, val) VALUES (?, ?)", 2, 2L); - execute("INSERT INTO %s (key, val) VALUES (?, ?)", 3, 3L); - assertInvalidMessage("val cannot be passed as argument 0 of function", - "SELECT key, val, " + fName + "(val) FROM %s"); - } - - @Test - public void testJavaFunction() throws Throwable - { - createTable("CREATE TABLE %s (key int primary key, val double)"); - - String functionBody = '\n' + - " // parameter val is of type java.lang.Double\n" + - " /* return type is of type java.lang.Double */\n" + - " if (val == null) {\n" + - " return null;\n" + - " }\n" + - " return Math.sin(val);\n"; - - String fName = createFunction(KEYSPACE, "double", - "CREATE OR REPLACE FUNCTION %s(val double) " + - "CALLED ON NULL INPUT " + - "RETURNS double " + - "LANGUAGE JAVA " + - "AS '" + functionBody + "';"); - - FunctionName fNameName = parseFunctionName(fName); - - assertRows(execute("SELECT language, body FROM system.schema_functions WHERE keyspace_name=? AND function_name=?", - fNameName.keyspace, fNameName.name), - row("java", functionBody)); - - execute("INSERT INTO %s (key, val) VALUES (?, ?)", 1, 1d); - execute("INSERT INTO %s (key, val) VALUES (?, ?)", 2, 2d); - execute("INSERT INTO %s (key, val) VALUES (?, ?)", 3, 3d); - assertRows(execute("SELECT key, val, " + fName + "(val) FROM %s"), - row(1, 1d, Math.sin(1d)), - row(2, 2d, Math.sin(2d)), - row(3, 3d, Math.sin(3d)) - ); - } - - @Test - public void testJavaFunctionCounter() throws Throwable - { - createTable("CREATE TABLE %s (key int primary key, val counter)"); - - String fName = createFunction(KEYSPACE, "counter", - "CREATE OR REPLACE FUNCTION %s(val counter) " + - "CALLED ON NULL INPUT " + - "RETURNS bigint " + - "LANGUAGE JAVA " + - "AS 'return val + 1;';"); - - execute("UPDATE %s SET val = val + 1 WHERE key = 1"); - assertRows(execute("SELECT key, val, " + fName + "(val) FROM %s"), - row(1, 1L, 2L)); - execute("UPDATE %s SET val = val + 1 WHERE key = 1"); - assertRows(execute("SELECT key, val, " + fName + "(val) FROM %s"), - row(1, 2L, 3L)); - execute("UPDATE %s SET val = val + 2 WHERE key = 1"); - assertRows(execute("SELECT key, val, " + fName + "(val) FROM %s"), - row(1, 4L, 5L)); - execute("UPDATE %s SET val = val - 2 WHERE key = 1"); - assertRows(execute("SELECT key, val, " + fName + "(val) FROM %s"), - row(1, 2L, 3L)); - } - @Test public void testFunctionInTargetKeyspace() throws Throwable { @@ -868,140 +700,6 @@ public class UFTest extends CQLTester "AS 'return null;';"); } - @Test - public void testJavaKeyspaceFunction() throws Throwable - { - createTable("CREATE TABLE %s (key int primary key, val double)"); - - String functionBody = '\n' + - " // parameter val is of type java.lang.Double\n" + - " /* return type is of type java.lang.Double */\n" + - " if (val == null) {\n" + - " return null;\n" + - " }\n" + - " return Math.sin( val );\n"; - - String fName = createFunction(KEYSPACE_PER_TEST, "double", - "CREATE OR REPLACE FUNCTION %s(val double) " + - "CALLED ON NULL INPUT " + - "RETURNS double " + - "LANGUAGE JAVA " + - "AS '" + functionBody + "';"); - - FunctionName fNameName = parseFunctionName(fName); - - assertRows(execute("SELECT language, body FROM system.schema_functions WHERE keyspace_name=? AND function_name=?", - fNameName.keyspace, fNameName.name), - row("java", functionBody)); - - execute("INSERT INTO %s (key, val) VALUES (?, ?)", 1, 1d); - execute("INSERT INTO %s (key, val) VALUES (?, ?)", 2, 2d); - execute("INSERT INTO %s (key, val) VALUES (?, ?)", 3, 3d); - assertRows(execute("SELECT key, val, " + fName + "(val) FROM %s"), - row(1, 1d, Math.sin(1d)), - row(2, 2d, Math.sin(2d)), - row(3, 3d, Math.sin(3d)) - ); - } - - @Test - public void testJavaRuntimeException() throws Throwable - { - createTable("CREATE TABLE %s (key int primary key, val double)"); - - String functionBody = '\n' + - " throw new RuntimeException(\"oh no!\");\n"; - - String fName = createFunction(KEYSPACE_PER_TEST, "double", - "CREATE OR REPLACE FUNCTION %s(val double) " + - "RETURNS NULL ON NULL INPUT " + - "RETURNS double " + - "LANGUAGE JAVA\n" + - "AS '" + functionBody + "';"); - - FunctionName fNameName = parseFunctionName(fName); - - assertRows(execute("SELECT language, body FROM system.schema_functions WHERE keyspace_name=? AND function_name=?", - fNameName.keyspace, fNameName.name), - row("java", functionBody)); - - execute("INSERT INTO %s (key, val) VALUES (?, ?)", 1, 1d); - execute("INSERT INTO %s (key, val) VALUES (?, ?)", 2, 2d); - execute("INSERT INTO %s (key, val) VALUES (?, ?)", 3, 3d); - - // function throws a RuntimeException which is wrapped by FunctionExecutionException - assertInvalidThrowMessage("java.lang.RuntimeException: oh no", FunctionExecutionException.class, - "SELECT key, val, " + fName + "(val) FROM %s"); - } - - @Test - public void testJavaDollarQuotedFunction() throws Throwable - { - String functionBody = '\n' + - " // parameter val is of type java.lang.Double\n" + - " /* return type is of type java.lang.Double */\n" + - " if (input == null) {\n" + - " return null;\n" + - " }\n" + - " return \"'\"+Math.sin(input)+'\\\'';\n"; - - String fName = createFunction(KEYSPACE_PER_TEST, "double", - "CREATE FUNCTION %s( input double ) " + - "CALLED ON NULL INPUT " + - "RETURNS text " + - "LANGUAGE java\n" + - "AS $$" + functionBody + "$$;"); - - FunctionName fNameName = parseFunctionName(fName); - - assertRows(execute("SELECT language, body FROM system.schema_functions WHERE keyspace_name=? AND function_name=?", - fNameName.keyspace, fNameName.name), - row("java", functionBody)); - } - - @Test - public void testJavaSimpleCollections() throws Throwable - { - createTable("CREATE TABLE %s (key int primary key, lst list, st set, mp map)"); - - String fList = createFunction(KEYSPACE_PER_TEST, "list", - "CREATE FUNCTION %s( lst list ) " + - "RETURNS NULL ON NULL INPUT " + - "RETURNS list " + - "LANGUAGE java\n" + - "AS $$return lst;$$;"); - String fSet = createFunction(KEYSPACE_PER_TEST, "set", - "CREATE FUNCTION %s( st set ) " + - "RETURNS NULL ON NULL INPUT " + - "RETURNS set " + - "LANGUAGE java\n" + - "AS $$return st;$$;"); - String fMap = createFunction(KEYSPACE_PER_TEST, "map", - "CREATE FUNCTION %s( mp map ) " + - "RETURNS NULL ON NULL INPUT " + - "RETURNS map " + - "LANGUAGE java\n" + - "AS $$return mp;$$;"); - - List list = Arrays.asList(1d, 2d, 3d); - Set set = new TreeSet<>(Arrays.asList("one", "three", "two")); - Map map = new TreeMap<>(); - map.put(1, true); - map.put(2, false); - map.put(3, true); - - execute("INSERT INTO %s (key, lst, st, mp) VALUES (1, ?, ?, ?)", list, set, map); - - assertRows(execute("SELECT " + fList + "(lst), " + fSet + "(st), " + fMap + "(mp) FROM %s WHERE key = 1"), - row(list, set, map)); - - // same test - but via native protocol - for (int version = Server.VERSION_2; version <= maxProtocolVersion; version++) - assertRowsNet(version, - executeNet(version, "SELECT " + fList + "(lst), " + fSet + "(st), " + fMap + "(mp) FROM %s WHERE key = 1"), - row(list, set, map)); - } - @Test public void testWrongKeyspace() throws Throwable { @@ -1134,206 +832,6 @@ public class UFTest extends CQLTester } } - @Test - public void testJavaTupleType() throws Throwable - { - createTable("CREATE TABLE %s (key int primary key, tup frozen>)"); - - String fName = createFunction(KEYSPACE, "tuple", - "CREATE FUNCTION %s( tup tuple ) " + - "RETURNS NULL ON NULL INPUT " + - "RETURNS tuple " + - "LANGUAGE java\n" + - "AS $$return tup;$$;"); - - Object t = tuple(1d, "foo", 2, true); - - execute("INSERT INTO %s (key, tup) VALUES (1, ?)", t); - - assertRows(execute("SELECT tup FROM %s WHERE key = 1"), - row(t)); - - assertRows(execute("SELECT " + fName + "(tup) FROM %s WHERE key = 1"), - row(t)); - } - - @Test - public void testJavaTupleTypeCollection() throws Throwable - { - String tupleTypeDef = "tuple, set, map>"; - - createTable("CREATE TABLE %s (key int primary key, tup frozen<" + tupleTypeDef + ">)"); - - 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 NULL ON NULL INPUT " + - "RETURNS list " + - "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 " + - "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 " + - "LANGUAGE java\n" + - "AS $$return " + - " tup.getMap(3, Integer.class, Boolean.class);$$;"); - - List list = Arrays.asList(1d, 2d, 3d); - Set set = new TreeSet<>(Arrays.asList("one", "three", "two")); - Map map = new TreeMap<>(); - map.put(1, true); - map.put(2, false); - map.put(3, true); - - Object t = tuple(1d, list, set, map); - - execute("INSERT INTO %s (key, tup) VALUES (1, ?)", t); - - assertRows(execute("SELECT " + fTup0 + "(tup) FROM %s WHERE key = 1"), - row(t)); - assertRows(execute("SELECT " + fTup1 + "(tup) FROM %s WHERE key = 1"), - row(1d)); - assertRows(execute("SELECT " + fTup2 + "(tup) FROM %s WHERE key = 1"), - row(list)); - assertRows(execute("SELECT " + fTup3 + "(tup) FROM %s WHERE key = 1"), - row(set)); - assertRows(execute("SELECT " + fTup4 + "(tup) FROM %s WHERE key = 1"), - row(map)); - - TupleType tType = TupleType.of(DataType.cdouble(), - DataType.list(DataType.cdouble()), - DataType.set(DataType.text()), - DataType.map(DataType.cint(), DataType.cboolean())); - TupleValue tup = tType.newValue(1d, list, set, map); - for (int version = Server.VERSION_2; version <= maxProtocolVersion; version++) - { - assertRowsNet(version, - executeNet(version, "SELECT " + fTup0 + "(tup) FROM %s WHERE key = 1"), - row(tup)); - assertRowsNet(version, - executeNet(version, "SELECT " + fTup1 + "(tup) FROM %s WHERE key = 1"), - row(1d)); - assertRowsNet(version, - executeNet(version, "SELECT " + fTup2 + "(tup) FROM %s WHERE key = 1"), - row(list)); - assertRowsNet(version, - executeNet(version, "SELECT " + fTup3 + "(tup) FROM %s WHERE key = 1"), - row(set)); - assertRowsNet(version, - executeNet(version, "SELECT " + fTup4 + "(tup) FROM %s WHERE key = 1"), - row(map)); - } - } - - @Test - public void testJavaUserTypeWithUse() throws Throwable - { - String type = createType("CREATE TYPE %s (txt text, i int)"); - createTable("CREATE TABLE %s (key int primary key, udt frozen<" + KEYSPACE + '.' + type + ">)"); - execute("INSERT INTO %s (key, udt) VALUES (1, {txt: 'one', i:1})"); - - for (int version = Server.VERSION_2; version <= maxProtocolVersion; version++) - { - executeNet(version, "USE " + KEYSPACE); - - executeNet(version, - "CREATE FUNCTION f_use1( udt " + type + " ) " + - "RETURNS NULL ON NULL INPUT " + - "RETURNS " + type + " " + - "LANGUAGE java " + - "AS $$return " + - " udt;$$;"); - try - { - List rowsNet = executeNet(version, "SELECT f_use1(udt) FROM %s WHERE key = 1").all(); - Assert.assertEquals(1, rowsNet.size()); - UDTValue udtVal = rowsNet.get(0).getUDTValue(0); - Assert.assertEquals("one", udtVal.getString("txt")); - Assert.assertEquals(1, udtVal.getInt("i")); - } - finally - { - executeNet(version, "DROP FUNCTION f_use1"); - } - } - } - - @Test - public void testJavaUserType() throws Throwable - { - String type = KEYSPACE + '.' + createType("CREATE TYPE %s (txt text, i int)"); - - createTable("CREATE TABLE %s (key int primary key, udt frozen<" + type + ">)"); - - String fUdt0 = createFunction(KEYSPACE, type, - "CREATE FUNCTION %s( udt " + type + " ) " + - "RETURNS NULL ON NULL INPUT " + - "RETURNS " + type + " " + - "LANGUAGE java " + - "AS $$return " + - " udt;$$;"); - String fUdt1 = createFunction(KEYSPACE, type, - "CREATE FUNCTION %s( udt " + type + ") " + - "RETURNS NULL ON NULL INPUT " + - "RETURNS text " + - "LANGUAGE java " + - "AS $$return " + - " udt.getString(\"txt\");$$;"); - String fUdt2 = createFunction(KEYSPACE, type, - "CREATE FUNCTION %s( udt " + type + ") " + - "CALLED ON NULL INPUT " + - "RETURNS int " + - "LANGUAGE java " + - "AS $$return " + - " Integer.valueOf(udt.getInt(\"i\"));$$;"); - - execute("INSERT INTO %s (key, udt) VALUES (1, {txt: 'one', i:1})"); - - UntypedResultSet rows = execute("SELECT " + fUdt0 + "(udt) FROM %s WHERE key = 1"); - Assert.assertEquals(1, rows.size()); - assertRows(execute("SELECT " + fUdt1 + "(udt) FROM %s WHERE key = 1"), - row("one")); - assertRows(execute("SELECT " + fUdt2 + "(udt) FROM %s WHERE key = 1"), - row(1)); - - for (int version = Server.VERSION_2; version <= maxProtocolVersion; version++) - { - List rowsNet = executeNet(version, "SELECT " + fUdt0 + "(udt) FROM %s WHERE key = 1").all(); - Assert.assertEquals(1, rowsNet.size()); - UDTValue udtVal = rowsNet.get(0).getUDTValue(0); - Assert.assertEquals("one", udtVal.getString("txt")); - Assert.assertEquals(1, udtVal.getInt("i")); - assertRowsNet(version, - executeNet(version, "SELECT " + fUdt1 + "(udt) FROM %s WHERE key = 1"), - row("one")); - assertRowsNet(version, - executeNet(version, "SELECT " + fUdt2 + "(udt) FROM %s WHERE key = 1"), - row(1)); - } - } - @Test public void testUserTypeDrop() throws Throwable { @@ -1371,541 +869,6 @@ public class UFTest extends CQLTester Assert.assertEquals(1, Functions.find(fNameName).size()); } - @Test - public void testJavaUserTypeRenameField() throws Throwable - { - String type = KEYSPACE + '.' + createType("CREATE TYPE %s (txt text, i int)"); - - createTable("CREATE TABLE %s (key int primary key, udt frozen<" + type + ">)"); - - String fName = createFunction(KEYSPACE, type, - "CREATE FUNCTION %s( udt " + type + " ) " + - "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})"); - - assertRows(execute("SELECT " + fName + "(udt) FROM %s WHERE key = 1"), - row("one")); - - execute("ALTER TYPE " + type + " RENAME txt TO str"); - - assertInvalidMessage("txt is not a field defined in this UDT", - "SELECT " + fName + "(udt) FROM %s WHERE key = 1"); - - execute("ALTER TYPE " + type + " RENAME str TO txt"); - - assertRows(execute("SELECT " + fName + "(udt) FROM %s WHERE key = 1"), - row("one")); - } - - @Test - public void testJavaUserTypeAddFieldWithReplace() throws Throwable - { - String type = KEYSPACE + '.' + createType("CREATE TYPE %s (txt text, i int)"); - - createTable("CREATE TABLE %s (key int primary key, udt frozen<" + type + ">)"); - - String fName1replace = createFunction(KEYSPACE, type, - "CREATE FUNCTION %s( udt " + type + ") " + - "RETURNS NULL ON NULL INPUT " + - "RETURNS text " + - "LANGUAGE java\n" + - "AS $$return udt.getString(\"txt\");$$;"); - String fName2replace = createFunction(KEYSPACE, type, - "CREATE FUNCTION %s( udt " + type + " ) " + - "CALLED ON NULL INPUT " + - "RETURNS int " + - "LANGUAGE java\n" + - "AS $$return Integer.valueOf(udt.getInt(\"i\"));$$;"); - String fName3replace = createFunction(KEYSPACE, type, - "CREATE FUNCTION %s( udt " + type + " ) " + - "CALLED ON NULL INPUT " + - "RETURNS double " + - "LANGUAGE java\n" + - "AS $$return Double.valueOf(udt.getDouble(\"added\"));$$;"); - String fName4replace = createFunction(KEYSPACE, type, - "CREATE FUNCTION %s( udt " + type + " ) " + - "RETURNS NULL ON NULL INPUT " + - "RETURNS " + type + " " + - "LANGUAGE java\n" + - "AS $$return udt;$$;"); - - String fName1noReplace = createFunction(KEYSPACE, type, - "CREATE FUNCTION %s( udt " + type + " ) " + - "RETURNS NULL ON NULL INPUT " + - "RETURNS text " + - "LANGUAGE java\n" + - "AS $$return udt.getString(\"txt\");$$;"); - String fName2noReplace = createFunction(KEYSPACE, type, - "CREATE FUNCTION %s( udt " + type + " ) " + - "CALLED ON NULL INPUT " + - "RETURNS int " + - "LANGUAGE java\n" + - "AS $$return Integer.valueOf(udt.getInt(\"i\"));$$;"); - String fName3noReplace = createFunction(KEYSPACE, type, - "CREATE FUNCTION %s( udt " + type + " ) " + - "CALLED ON NULL INPUT " + - "RETURNS double " + - "LANGUAGE java\n" + - "AS $$return Double.valueOf(udt.getDouble(\"added\"));$$;"); - String fName4noReplace = createFunction(KEYSPACE, type, - "CREATE FUNCTION %s( udt " + type + " ) " + - "RETURNS NULL ON NULL INPUT " + - "RETURNS " + type + " " + - "LANGUAGE java\n" + - "AS $$return udt;$$;"); - - execute("INSERT INTO %s (key, udt) VALUES (1, {txt: 'one', i:1})"); - - assertRows(execute("SELECT " + fName1replace + "(udt) FROM %s WHERE key = 1"), - row("one")); - assertRows(execute("SELECT " + fName2replace + "(udt) FROM %s WHERE key = 1"), - row(1)); - - // add field - - execute("ALTER TYPE " + type + " ADD added double"); - - execute("INSERT INTO %s (key, udt) VALUES (2, {txt: 'two', i:2, added: 2})"); - - // note: type references of functions remain at the state _before_ the type mutation - // means we need to recreate the functions - - execute(String.format("CREATE OR REPLACE FUNCTION %s( udt %s ) " + - "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 %s ) " + - "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 %s ) " + - "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 %s ) " + - "RETURNS NULL ON NULL INPUT " + - "RETURNS %s " + - "LANGUAGE java\n" + - "AS $$return " + - " udt;$$;", - fName4replace, type, type)); - Assert.assertEquals(1, Functions.find(parseFunctionName(fName4replace)).size()); - - assertRows(execute("SELECT " + fName1replace + "(udt) FROM %s WHERE key = 2"), - row("two")); - assertRows(execute("SELECT " + fName2replace + "(udt) FROM %s WHERE key = 2"), - row(2)); - assertRows(execute("SELECT " + fName3replace + "(udt) FROM %s WHERE key = 2"), - row(2d)); - assertRows(execute("SELECT " + fName3replace + "(udt) FROM %s WHERE key = 1"), - row(0d)); - - // un-replaced functions will work since the user type has changed - // and the UDF has exchanged the user type reference - - assertRows(execute("SELECT " + fName1noReplace + "(udt) FROM %s WHERE key = 2"), - row("two")); - assertRows(execute("SELECT " + fName2noReplace + "(udt) FROM %s WHERE key = 2"), - row(2)); - assertRows(execute("SELECT " + fName3noReplace + "(udt) FROM %s WHERE key = 2"), - row(2d)); - assertRows(execute("SELECT " + fName3noReplace + "(udt) FROM %s WHERE key = 1"), - row(0d)); - - execute("DROP FUNCTION " + fName1replace); - execute("DROP FUNCTION " + fName2replace); - execute("DROP FUNCTION " + fName3replace); - execute("DROP FUNCTION " + fName4replace); - execute("DROP FUNCTION " + fName1noReplace); - execute("DROP FUNCTION " + fName2noReplace); - execute("DROP FUNCTION " + fName3noReplace); - execute("DROP FUNCTION " + fName4noReplace); - } - - @Test - public void testJavaUTCollections() throws Throwable - { - String type = KEYSPACE + '.' + createType("CREATE TYPE %s (txt text, i int)"); - - createTable(String.format("CREATE TABLE %%s " + - "(key int primary key, lst list>, st set>, mp map>)", - type, type, type)); - - String fName1 = createFunction(KEYSPACE, "list>", - "CREATE FUNCTION %s( lst list> ) " + - "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>", - "CREATE FUNCTION %s( st set> ) " + - "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>", - "CREATE FUNCTION %s( mp map> ) " + - "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\");$$;"); - - execute("INSERT INTO %s (key, lst, st, mp) values (1, " + - "[ {txt: 'one', i:1}, {txt: 'three', i:1}, {txt: 'one', i:1} ] , " + - "{ {txt: 'one', i:1}, {txt: 'three', i:3}, {txt: 'two', i:2} }, " + - "{ 1: {txt: 'one', i:1}, 2: {txt: 'one', i:3}, 3: {txt: 'two', i:2} })"); - - assertRows(execute("SELECT " + fName1 + "(lst), " + fName2 + "(st), " + fName3 + "(mp) FROM %s WHERE key = 1"), - row("three", "one", "two")); - - for (int version = Server.VERSION_2; version <= maxProtocolVersion; version++) - assertRowsNet(version, - executeNet(version, "SELECT " + fName1 + "(lst), " + fName2 + "(st), " + fName3 + "(mp) FROM %s WHERE key = 1"), - row("three", "one", "two")); - } - - @Test - public void testJavascriptSimpleCollections() throws Throwable - { - createTable("CREATE TABLE %s (key int primary key, lst list, st set, mp map)"); - - String fName1 = createFunction(KEYSPACE_PER_TEST, "list", - "CREATE FUNCTION %s( lst list ) " + - "RETURNS NULL ON NULL INPUT " + - "RETURNS list " + - "LANGUAGE javascript\n" + - "AS 'lst;';"); - String fName2 = createFunction(KEYSPACE_PER_TEST, "set", - "CREATE FUNCTION %s( st set ) " + - "RETURNS NULL ON NULL INPUT " + - "RETURNS set " + - "LANGUAGE javascript\n" + - "AS 'st;';"); - String fName3 = createFunction(KEYSPACE_PER_TEST, "map", - "CREATE FUNCTION %s( mp map ) " + - "RETURNS NULL ON NULL INPUT " + - "RETURNS map " + - "LANGUAGE javascript\n" + - "AS 'mp;';"); - - List list = Arrays.asList(1d, 2d, 3d); - Set set = new TreeSet<>(Arrays.asList("one", "three", "two")); - Map map = new TreeMap<>(); - map.put(1, true); - map.put(2, false); - map.put(3, true); - - execute("INSERT INTO %s (key, lst, st, mp) VALUES (1, ?, ?, ?)", list, set, map); - - assertRows(execute("SELECT lst, st, mp FROM %s WHERE key = 1"), - row(list, set, map)); - - assertRows(execute("SELECT " + fName1 + "(lst), " + fName2 + "(st), " + fName3 + "(mp) FROM %s WHERE key = 1"), - row(list, set, map)); - - for (int version = Server.VERSION_2; version <= maxProtocolVersion; version++) - assertRowsNet(version, - executeNet(version, "SELECT " + fName1 + "(lst), " + fName2 + "(st), " + fName3 + "(mp) FROM %s WHERE key = 1"), - row(list, set, map)); - } - - @Test - public void testJavascriptTupleType() throws Throwable - { - createTable("CREATE TABLE %s (key int primary key, tup frozen>)"); - - String fName = createFunction(KEYSPACE_PER_TEST, "tuple", - "CREATE FUNCTION %s( tup tuple ) " + - "RETURNS NULL ON NULL INPUT " + - "RETURNS tuple " + - "LANGUAGE javascript\n" + - "AS $$tup;$$;"); - - Object t = tuple(1d, "foo", 2, true); - - execute("INSERT INTO %s (key, tup) VALUES (1, ?)", t); - - assertRows(execute("SELECT tup FROM %s WHERE key = 1"), - row(t)); - - assertRows(execute("SELECT " + fName + "(tup) FROM %s WHERE key = 1"), - row(t)); - } - - @Test - public void testJavascriptTupleTypeCollection() throws Throwable - { - String tupleTypeDef = "tuple, set, map>"; - createTable("CREATE TABLE %s (key int primary key, tup frozen<" + tupleTypeDef + ">)"); - - String fTup1 = createFunction(KEYSPACE_PER_TEST, tupleTypeDef, - "CREATE FUNCTION %s( tup " + tupleTypeDef + " ) " + - "RETURNS NULL ON NULL INPUT " + - "RETURNS tuple, set, map> " + - "LANGUAGE javascript\n" + - "AS $$" + - " tup;$$;"); - String fTup2 = createFunction(KEYSPACE_PER_TEST, tupleTypeDef, - "CREATE FUNCTION %s( tup " + tupleTypeDef + " ) " + - "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 " + - "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 " + - "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 " + - "LANGUAGE javascript\n" + - "AS $$" + - " tup.getMap(3, java.lang.Class.forName(\"java.lang.Integer\"), java.lang.Class.forName(\"java.lang.Boolean\"));$$;"); - - List list = Arrays.asList(1d, 2d, 3d); - Set set = new TreeSet<>(Arrays.asList("one", "three", "two")); - Map map = new TreeMap<>(); - map.put(1, true); - map.put(2, false); - map.put(3, true); - - Object t = tuple(1d, list, set, map); - - execute("INSERT INTO %s (key, tup) VALUES (1, ?)", t); - - assertRows(execute("SELECT " + fTup1 + "(tup) FROM %s WHERE key = 1"), - row(t)); - assertRows(execute("SELECT " + fTup2 + "(tup) FROM %s WHERE key = 1"), - row(1d)); - assertRows(execute("SELECT " + fTup3 + "(tup) FROM %s WHERE key = 1"), - row(list)); - assertRows(execute("SELECT " + fTup4 + "(tup) FROM %s WHERE key = 1"), - row(set)); - assertRows(execute("SELECT " + fTup5 + "(tup) FROM %s WHERE key = 1"), - row(map)); - - // same test - but via native protocol - TupleType tType = TupleType.of(DataType.cdouble(), - DataType.list(DataType.cdouble()), - DataType.set(DataType.text()), - DataType.map(DataType.cint(), DataType.cboolean())); - TupleValue tup = tType.newValue(1d, list, set, map); - for (int version = Server.VERSION_2; version <= maxProtocolVersion; version++) - { - assertRowsNet(version, - executeNet(version, "SELECT " + fTup1 + "(tup) FROM %s WHERE key = 1"), - row(tup)); - assertRowsNet(version, - executeNet(version, "SELECT " + fTup2 + "(tup) FROM %s WHERE key = 1"), - row(1d)); - assertRowsNet(version, - executeNet(version, "SELECT " + fTup3 + "(tup) FROM %s WHERE key = 1"), - row(list)); - assertRowsNet(version, - executeNet(version, "SELECT " + fTup4 + "(tup) FROM %s WHERE key = 1"), - row(set)); - assertRowsNet(version, - executeNet(version, "SELECT " + fTup5 + "(tup) FROM %s WHERE key = 1"), - row(map)); - } - } - - @Test - public void testJavascriptUserType() throws Throwable - { - String type = createType("CREATE TYPE %s (txt text, i int)"); - - createTable("CREATE TABLE %s (key int primary key, udt frozen<" + type + ">)"); - - String fUdt1 = createFunction(KEYSPACE, type, - "CREATE FUNCTION %s( udt " + type + " ) " + - "RETURNS NULL ON NULL INPUT " + - "RETURNS " + type + " " + - "LANGUAGE javascript\n" + - "AS $$" + - " udt;$$;"); - String fUdt2 = createFunction(KEYSPACE, type, - "CREATE FUNCTION %s( udt " + type + " ) " + - "RETURNS NULL ON NULL INPUT " + - "RETURNS text " + - "LANGUAGE javascript\n" + - "AS $$" + - " udt.getString(\"txt\");$$;"); - String fUdt3 = createFunction(KEYSPACE, type, - "CREATE FUNCTION %s( udt " + 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})"); - - UntypedResultSet rows = execute("SELECT " + fUdt1 + "(udt) FROM %s WHERE key = 1"); - Assert.assertEquals(1, rows.size()); - assertRows(execute("SELECT " + fUdt2 + "(udt) FROM %s WHERE key = 1"), - row("one")); - assertRows(execute("SELECT " + fUdt3 + "(udt) FROM %s WHERE key = 1"), - row(1)); - } - - @Test - public void testJavascriptUTCollections() throws Throwable - { - String type = createType("CREATE TYPE %s (txt text, i int)"); - - createTable(String.format("CREATE TABLE %%s " + - "(key int primary key, lst list>, st set>, mp map>)", - type, type, type)); - - String fName = createFunction(KEYSPACE, "list>", - "CREATE FUNCTION %s( lst list> ) " + - "RETURNS NULL ON NULL INPUT " + - "RETURNS text " + - "LANGUAGE javascript\n" + - "AS $$" + - " lst.get(1).getString(\"txt\");$$;"); - createFunctionOverload(fName, "set>", - "CREATE FUNCTION %s( st set> ) " + - "RETURNS NULL ON NULL INPUT " + - "RETURNS text " + - "LANGUAGE javascript\n" + - "AS $$" + - " st.iterator().next().getString(\"txt\");$$;"); - createFunctionOverload(fName, "map>", - "CREATE FUNCTION %s( mp map> ) " + - "RETURNS NULL ON NULL INPUT " + - "RETURNS text " + - "LANGUAGE javascript\n" + - "AS $$" + - " mp.get(java.lang.Integer.valueOf(3)).getString(\"txt\");$$;"); - - execute("INSERT INTO %s (key, lst, st, mp) values (1, " + - // list> - "[ {txt: 'one', i:1}, {txt: 'three', i:1}, {txt: 'one', i:1} ] , " + - // set> - "{ {txt: 'one', i:1}, {txt: 'three', i:3}, {txt: 'two', i:2} }, " + - // map> - "{ 1: {txt: 'one', i:1}, 2: {txt: 'one', i:3}, 3: {txt: 'two', i:2} })"); - - assertRows(execute("SELECT " + fName + "(lst) FROM %s WHERE key = 1"), - row("three")); - assertRows(execute("SELECT " + fName + "(st) FROM %s WHERE key = 1"), - row("one")); - assertRows(execute("SELECT " + fName + "(mp) FROM %s WHERE key = 1"), - row("two")); - - String cqlSelect = "SELECT " + fName + "(lst), " + fName + "(st), " + fName + "(mp) FROM %s WHERE key = 1"; - assertRows(execute(cqlSelect), - row("three", "one", "two")); - - // same test - but via native protocol - for (int version = Server.VERSION_2; version <= maxProtocolVersion; version++) - assertRowsNet(version, - executeNet(version, cqlSelect), - row("three", "one", "two")); - } - - @Test - public void testJavascriptFunction() throws Throwable - { - createTable("CREATE TABLE %s (key int primary key, val double)"); - - String functionBody = '\n' + - " Math.sin(val);\n"; - - String fName = createFunction(KEYSPACE, "double", - "CREATE OR REPLACE FUNCTION %s(val double) " + - "RETURNS NULL ON NULL INPUT " + - "RETURNS double " + - "LANGUAGE javascript\n" + - "AS '" + functionBody + "';"); - - FunctionName fNameName = parseFunctionName(fName); - - assertRows(execute("SELECT language, body FROM system.schema_functions WHERE keyspace_name=? AND function_name=?", - fNameName.keyspace, fNameName.name), - row("javascript", functionBody)); - - execute("INSERT INTO %s (key, val) VALUES (?, ?)", 1, 1d); - execute("INSERT INTO %s (key, val) VALUES (?, ?)", 2, 2d); - execute("INSERT INTO %s (key, val) VALUES (?, ?)", 3, 3d); - assertRows(execute("SELECT key, val, " + fName + "(val) FROM %s"), - row(1, 1d, Math.sin(1d)), - row(2, 2d, Math.sin(2d)), - row(3, 3d, Math.sin(3d)) - ); - } - - @Test - public void testJavascriptBadReturnType() throws Throwable - { - createTable("CREATE TABLE %s (key int primary key, val double)"); - - String fName = createFunction(KEYSPACE, "double", - "CREATE OR REPLACE FUNCTION %s(val double) " + - "RETURNS NULL ON NULL INPUT " + - "RETURNS double " + - "LANGUAGE javascript\n" + - "AS '\"string\";';"); - - execute("INSERT INTO %s (key, val) VALUES (?, ?)", 1, 1d); - // throws IRE with ClassCastException - assertInvalidMessage("Invalid value for CQL type double", "SELECT key, val, " + fName + "(val) FROM %s"); - } - - @Test - public void testJavascriptThrow() throws Throwable - { - createTable("CREATE TABLE %s (key int primary key, val double)"); - - 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\";';"); - - execute("INSERT INTO %s (key, val) VALUES (?, ?)", 1, 1d); - // throws IRE with ScriptException - assertInvalidThrowMessage("fool", FunctionExecutionException.class, - "SELECT key, val, " + fName + "(val) FROM %s"); - } - @Test public void testDuplicateArgNames() throws Throwable { @@ -1917,136 +880,7 @@ public class UFTest extends CQLTester "AS '\"foo bar\";';"); } - @Test - public void testJavascriptCompileFailure() throws Throwable - { - assertInvalidMessage("Failed to compile function 'cql_test_keyspace.scrinv'", - "CREATE OR REPLACE FUNCTION " + KEYSPACE + ".scrinv(val double) " + - "RETURNS NULL ON NULL INPUT " + - "RETURNS double " + - "LANGUAGE javascript\n" + - "AS 'foo bar';"); - } - - @Test - public void testScriptInvalidLanguage() throws Throwable - { - assertInvalidMessage("Invalid language 'artificial_intelligence' for function 'cql_test_keyspace.scrinv'", - "CREATE OR REPLACE FUNCTION " + KEYSPACE + ".scrinv(val double) " + - "RETURNS NULL ON NULL INPUT " + - "RETURNS double " + - "LANGUAGE artificial_intelligence\n" + - "AS 'question for 42?';"); - } - - @Test - public void testScriptReturnTypeCasting() throws Throwable - { - createTable("CREATE TABLE %s (key int primary key, val double)"); - execute("INSERT INTO %s (key, val) VALUES (?, ?)", 1, 1d); - - Object[][] variations = { - new Object[] { "true", "boolean", true }, - new Object[] { "false", "boolean", false }, - new Object[] { "100", "tinyint", (byte)100 }, - new Object[] { "100.", "tinyint", (byte)100 }, - new Object[] { "100", "smallint", (short)100 }, - new Object[] { "100.", "smallint", (short)100 }, - new Object[] { "100", "int", 100 }, - new Object[] { "100.", "int", 100 }, - new Object[] { "100", "double", 100d }, - new Object[] { "100.", "double", 100d }, - new Object[] { "100", "bigint", 100L }, - new Object[] { "100.", "bigint", 100L }, - new Object[] { "100", "varint", BigInteger.valueOf(100L) }, - new Object[] { "100.", "varint", BigInteger.valueOf(100L) }, - new Object[] { "parseInt(\"100\");", "decimal", BigDecimal.valueOf(100d) }, - new Object[] { "100.", "decimal", BigDecimal.valueOf(100d) }, - }; - - for (Object[] variation : variations) - { - Object functionBody = variation[0]; - Object returnType = variation[1]; - Object expectedResult = variation[2]; - - String fName = createFunction(KEYSPACE, "double", - "CREATE OR REPLACE FUNCTION %s(val double) " + - "RETURNS NULL ON NULL INPUT " + - "RETURNS " +returnType + ' ' + - "LANGUAGE javascript " + - "AS '" + functionBody + ";';"); - assertRows(execute("SELECT key, val, " + fName + "(val) FROM %s"), - row(1, 1d, expectedResult)); - } - } - - @Test - public void testScriptParamReturnTypes() throws Throwable - { - UUID ruuid = UUID.randomUUID(); - UUID tuuid = UUIDGen.getTimeUUID(); - - createTable("CREATE TABLE %s (key int primary key, " + - "tival tinyint, sival smallint, ival int, lval bigint, fval float, dval double, vval varint, ddval decimal, " + - "timval time, dtval date, tsval timestamp, uval uuid, tuval timeuuid)"); - execute("INSERT INTO %s (key, tival, sival, ival, lval, fval, dval, vval, ddval, timval, dtval, tsval, uval, tuval) VALUES " + - "(?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)", 1, - (byte)1, (short)1, 1, 1L, 1f, 1d, BigInteger.valueOf(1L), BigDecimal.valueOf(1d), 1L, Integer.MAX_VALUE, new Date(1), ruuid, tuuid); - - Object[][] variations = { - new Object[] { "tinyint", "tival", (byte)1, (byte)2 }, - new Object[] { "smallint", "sival", (short)1, (short)2 }, - new Object[] { "int", "ival", 1, 2 }, - new Object[] { "bigint", "lval", 1L, 2L }, - new Object[] { "float", "fval", 1f, 2f }, - new Object[] { "double", "dval", 1d, 2d }, - new Object[] { "varint", "vval", BigInteger.valueOf(1L), BigInteger.valueOf(2L) }, - new Object[] { "decimal", "ddval", BigDecimal.valueOf(1d), BigDecimal.valueOf(2d) }, - new Object[] { "time", "timval", 1L, 2L }, - }; - - for (Object[] variation : variations) - { - Object type = variation[0]; - Object col = variation[1]; - Object expected1 = variation[2]; - 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;';"); - assertRows(execute("SELECT key, " + col + ", " + fName + '(' + col + ") FROM %s"), - row(1, expected1, expected2)); - } - - variations = new Object[][] { - new Object[] { "timestamp","tsval", new Date(1), new Date(1) }, - new Object[] { "uuid", "uval", ruuid, ruuid }, - new Object[] { "timeuuid", "tuval", tuuid, tuuid }, - new Object[] { "date", "dtval", Integer.MAX_VALUE, Integer.MAX_VALUE }, - }; - - for (Object[] variation : variations) - { - Object type = variation[0]; - Object col = variation[1]; - Object expected1 = variation[2]; - 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;';"); - assertRows(execute("SELECT key, " + col + ", " + fName + '(' + col + ") FROM %s"), - row(1, expected1, expected2)); - } - } - - static class TypesTestDef + private static class TypesTestDef { final String udfType; final String tableType; @@ -2274,416 +1108,4 @@ public class UFTest extends CQLTester } } } - - @Test - public void testFunctionWithFrozenSetType() throws Throwable - { - createTable("CREATE TABLE %s (a int PRIMARY KEY, b frozen>)"); - createIndex("CREATE INDEX ON %s (FULL(b))"); - - execute("INSERT INTO %s (a, b) VALUES (?, ?)", 0, set()); - execute("INSERT INTO %s (a, b) VALUES (?, ?)", 1, set(1, 2, 3)); - execute("INSERT INTO %s (a, b) VALUES (?, ?)", 2, set(4, 5, 6)); - execute("INSERT INTO %s (a, b) VALUES (?, ?)", 3, set(7, 8, 9)); - - assertInvalidMessage("The function arguments should not be frozen", - "CREATE OR REPLACE FUNCTION " + KEYSPACE + ".frozenSetArg(values frozen>) " + - "CALLED ON NULL INPUT " + - "RETURNS int " + - "LANGUAGE java\n" + - "AS 'int sum = 0; for (Object value : values) {sum += value;} return sum;';"); - - assertInvalidMessage("The function return type should not be frozen", - "CREATE OR REPLACE FUNCTION " + KEYSPACE + ".frozenReturnType(values set) " + - "CALLED ON NULL INPUT " + - "RETURNS frozen> " + - "LANGUAGE java\n" + - "AS 'return values;';"); - - String functionName = createFunction(KEYSPACE, - "set", - "CREATE FUNCTION %s (values set) " + - "CALLED ON NULL INPUT " + - "RETURNS int " + - "LANGUAGE java\n" + - "AS 'int sum = 0; for (Object value : values) {sum += ((Integer) value);} return sum;';"); - - assertRows(execute("SELECT a, " + functionName + "(b) FROM %s"), - row(0, 0), - row(1, 6), - row(2, 15), - row(3, 24)); - - functionName = createFunction(KEYSPACE, - "set", - "CREATE FUNCTION %s (values set) " + - "CALLED ON NULL INPUT " + - "RETURNS set " + - "LANGUAGE java\n" + - "AS 'return values;';"); - - assertRows(execute("SELECT a FROM %s WHERE b = " + functionName + "(?)", set(1, 2, 3)), - row(1)); - - assertInvalidMessage("The function arguments should not be frozen", - "DROP FUNCTION " + functionName + "(frozen>);"); - } - - @Test - public void testFunctionWithFrozenListType() throws Throwable - { - createTable("CREATE TABLE %s (a int PRIMARY KEY, b frozen>)"); - createIndex("CREATE INDEX ON %s (FULL(b))"); - - execute("INSERT INTO %s (a, b) VALUES (?, ?)", 0, list()); - execute("INSERT INTO %s (a, b) VALUES (?, ?)", 1, list(1, 2, 3)); - execute("INSERT INTO %s (a, b) VALUES (?, ?)", 2, list(4, 5, 6)); - execute("INSERT INTO %s (a, b) VALUES (?, ?)", 3, list(7, 8, 9)); - - assertInvalidMessage("The function arguments should not be frozen", - "CREATE OR REPLACE FUNCTION " + KEYSPACE + ".withFrozenArg(values frozen>) " + - "CALLED ON NULL INPUT " + - "RETURNS int " + - "LANGUAGE java\n" + - "AS 'int sum = 0; for (Object value : values) {sum += value;} return sum;';"); - - assertInvalidMessage("The function return type should not be frozen", - "CREATE OR REPLACE FUNCTION " + KEYSPACE + ".frozenReturnType(values list) " + - "CALLED ON NULL INPUT " + - "RETURNS frozen> " + - "LANGUAGE java\n" + - "AS 'return values;';"); - - String functionName = createFunction(KEYSPACE, - "list", - "CREATE FUNCTION %s (values list) " + - "CALLED ON NULL INPUT " + - "RETURNS int " + - "LANGUAGE java\n" + - "AS 'int sum = 0; for (Object value : values) {sum += ((Integer) value);} return sum;';"); - - assertRows(execute("SELECT a, " + functionName + "(b) FROM %s"), - row(0, 0), - row(1, 6), - row(2, 15), - row(3, 24)); - - functionName = createFunction(KEYSPACE, - "list", - "CREATE FUNCTION %s (values list) " + - "CALLED ON NULL INPUT " + - "RETURNS list " + - "LANGUAGE java\n" + - "AS 'return values;';"); - - assertRows(execute("SELECT a FROM %s WHERE b = " + functionName + "(?)", set(1, 2, 3)), - row(1)); - - assertInvalidMessage("The function arguments should not be frozen", - "DROP FUNCTION " + functionName + "(frozen>);"); - } - - @Test - public void testFunctionWithFrozenMapType() throws Throwable - { - createTable("CREATE TABLE %s (a int PRIMARY KEY, b frozen>)"); - createIndex("CREATE INDEX ON %s (FULL(b))"); - - execute("INSERT INTO %s (a, b) VALUES (?, ?)", 0, map()); - execute("INSERT INTO %s (a, b) VALUES (?, ?)", 1, map(1, 1, 2, 2, 3, 3)); - execute("INSERT INTO %s (a, b) VALUES (?, ?)", 2, map(4, 4, 5, 5, 6, 6)); - execute("INSERT INTO %s (a, b) VALUES (?, ?)", 3, map(7, 7, 8, 8, 9, 9)); - - assertInvalidMessage("The function arguments should not be frozen", - "CREATE OR REPLACE FUNCTION " + KEYSPACE + ".withFrozenArg(values frozen>) " + - "CALLED ON NULL INPUT " + - "RETURNS int " + - "LANGUAGE java\n" + - "AS 'int sum = 0; for (Object value : values.values()) {sum += value;} return sum;';"); - - assertInvalidMessage("The function return type should not be frozen", - "CREATE OR REPLACE FUNCTION " + KEYSPACE + ".frozenReturnType(values map) " + - "CALLED ON NULL INPUT " + - "RETURNS frozen> " + - "LANGUAGE java\n" + - "AS 'return values;';"); - - String functionName = createFunction(KEYSPACE, - "map", - "CREATE FUNCTION %s (values map) " + - "CALLED ON NULL INPUT " + - "RETURNS int " + - "LANGUAGE java\n" + - "AS 'int sum = 0; for (Object value : values.values()) {sum += ((Integer) value);} return sum;';"); - - assertRows(execute("SELECT a, " + functionName + "(b) FROM %s"), - row(0, 0), - row(1, 6), - row(2, 15), - row(3, 24)); - - functionName = createFunction(KEYSPACE, - "map", - "CREATE FUNCTION %s (values map) " + - "CALLED ON NULL INPUT " + - "RETURNS map " + - "LANGUAGE java\n" + - "AS 'return values;';"); - - assertRows(execute("SELECT a FROM %s WHERE b = " + functionName + "(?)", map(1, 1, 2, 2, 3, 3)), - row(1)); - - assertInvalidMessage("The function arguments should not be frozen", - "DROP FUNCTION " + functionName + "(frozen>);"); - } - - @Test - public void testFunctionWithFrozenTupleType() throws Throwable - { - createTable("CREATE TABLE %s (a int PRIMARY KEY, b frozen>)"); - createIndex("CREATE INDEX ON %s (b)"); - - execute("INSERT INTO %s (a, b) VALUES (?, ?)", 0, tuple()); - execute("INSERT INTO %s (a, b) VALUES (?, ?)", 1, tuple(1, 2)); - execute("INSERT INTO %s (a, b) VALUES (?, ?)", 2, tuple(4, 5)); - execute("INSERT INTO %s (a, b) VALUES (?, ?)", 3, tuple(7, 8)); - - assertInvalidMessage("The function arguments should not be frozen", - "CREATE OR REPLACE FUNCTION " + KEYSPACE + ".withFrozenArg(values frozen>) " + - "CALLED ON NULL INPUT " + - "RETURNS text " + - "LANGUAGE java\n" + - "AS 'return values.toString();';"); - - assertInvalidMessage("The function return type should not be frozen", - "CREATE OR REPLACE FUNCTION " + KEYSPACE + ".frozenReturnType(values tuple) " + - "CALLED ON NULL INPUT " + - "RETURNS frozen> " + - "LANGUAGE java\n" + - "AS 'return values;';"); - - String functionName = createFunction(KEYSPACE, - "tuple", - "CREATE FUNCTION %s (values tuple) " + - "CALLED ON NULL INPUT " + - "RETURNS text " + - "LANGUAGE java\n" + - "AS 'return values.toString();';"); - - assertRows(execute("SELECT a, " + functionName + "(b) FROM %s"), - row(0, "(null, null)"), - row(1, "(1, 2)"), - row(2, "(4, 5)"), - row(3, "(7, 8)")); - - functionName = createFunction(KEYSPACE, - "tuple", - "CREATE FUNCTION %s (values tuple) " + - "CALLED ON NULL INPUT " + - "RETURNS tuple " + - "LANGUAGE java\n" + - "AS 'return values;';"); - - assertRows(execute("SELECT a FROM %s WHERE b = " + functionName + "(?)", tuple(1, 2)), - row(1)); - - assertInvalidMessage("The function arguments should not be frozen", - "DROP FUNCTION " + functionName + "(frozen>);"); - } - - @Test - public void testFunctionWithFrozenUDType() throws Throwable - { - String myType = createType("CREATE TYPE %s (f int)"); - createTable("CREATE TABLE %s (a int PRIMARY KEY, b frozen<" + myType + ">)"); - createIndex("CREATE INDEX ON %s (b)"); - - execute("INSERT INTO %s (a, b) VALUES (?, {f : ?})", 0, 0); - execute("INSERT INTO %s (a, b) VALUES (?, {f : ?})", 1, 1); - execute("INSERT INTO %s (a, b) VALUES (?, {f : ?})", 2, 4); - execute("INSERT INTO %s (a, b) VALUES (?, {f : ?})", 3, 7); - - assertInvalidMessage("The function arguments should not be frozen", - "CREATE OR REPLACE FUNCTION " + KEYSPACE + ".withFrozenArg(values frozen<" + myType + ">) " + - "CALLED ON NULL INPUT " + - "RETURNS text " + - "LANGUAGE java\n" + - "AS 'return values.toString();';"); - - assertInvalidMessage("The function return type should not be frozen", - "CREATE OR REPLACE FUNCTION " + KEYSPACE + ".frozenReturnType(values " + myType + ") " + - "CALLED ON NULL INPUT " + - "RETURNS frozen<" + myType + "> " + - "LANGUAGE java\n" + - "AS 'return values;';"); - - String functionName = createFunction(KEYSPACE, - myType, - "CREATE FUNCTION %s (values " + myType + ") " + - "CALLED ON NULL INPUT " + - "RETURNS text " + - "LANGUAGE java\n" + - "AS 'return values.toString();';"); - - assertRows(execute("SELECT a, " + functionName + "(b) FROM %s"), - row(0, "{f:0}"), - row(1, "{f:1}"), - row(2, "{f:4}"), - row(3, "{f:7}")); - - functionName = createFunction(KEYSPACE, - myType, - "CREATE FUNCTION %s (values " + myType + ") " + - "CALLED ON NULL INPUT " + - "RETURNS " + myType + " " + - "LANGUAGE java\n" + - "AS 'return values;';"); - - assertRows(execute("SELECT a FROM %s WHERE b = " + functionName + "({f: ?})", 1), - row(1)); - - 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, "int", - "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})); - } - - @Test - public void testAllNativeTypes() throws Throwable - { - StringBuilder sig = new StringBuilder(); - StringBuilder args = new StringBuilder(); - for (CQL3Type.Native type : CQL3Type.Native.values()) - { - if (sig.length() > 0) - sig.append(','); - sig.append(type.toString()); - - if (args.length() > 0) - args.append(','); - args.append("arg").append(type.toString()).append(' ').append(type.toString()); - } - createFunction(KEYSPACE, sig.toString(), - "CREATE OR REPLACE FUNCTION %s(" + args + ") " + - "RETURNS NULL ON NULL INPUT " + - "RETURNS int " + - "LANGUAGE JAVA\n" + - "AS 'return 0;'"); - - for (CQL3Type.Native type : CQL3Type.Native.values()) - { - createFunction(KEYSPACE_PER_TEST, type.toString(), - "CREATE OR REPLACE FUNCTION %s(val " + type.toString() + ") " + - "RETURNS NULL ON NULL INPUT " + - "RETURNS int " + - "LANGUAGE JAVA\n" + - "AS 'return 0;'"); - } - } } From d9b06e8af41c42244f76058641aeecda53a9bf75 Mon Sep 17 00:00:00 2001 From: Alex Petrov Date: Wed, 7 Dec 2016 16:04:51 +0100 Subject: [PATCH 2/2] Make distinction between unset row and non-existing partition for LWTs Patch by Alex Petrov; reviewed by Sylvain Lebresne for CASSANDRA-12964. --- .../cql3/statements/CQL3CasRequest.java | 10 +++--- .../cassandra/db/filter/ColumnFilter.java | 9 ++++++ .../InsertUpdateIfConditionTest.java | 32 ++++++++++++++++++- 3 files changed, 46 insertions(+), 5 deletions(-) diff --git a/src/java/org/apache/cassandra/cql3/statements/CQL3CasRequest.java b/src/java/org/apache/cassandra/cql3/statements/CQL3CasRequest.java index d9e8796356..db8653d3e4 100644 --- a/src/java/org/apache/cassandra/cql3/statements/CQL3CasRequest.java +++ b/src/java/org/apache/cassandra/cql3/statements/CQL3CasRequest.java @@ -20,8 +20,7 @@ package org.apache.cassandra.cql3.statements; import java.nio.ByteBuffer; import java.util.*; -import com.google.common.collect.HashMultimap; -import com.google.common.collect.Multimap; +import com.google.common.collect.*; import org.apache.cassandra.config.CFMetaData; import org.apache.cassandra.cql3.*; @@ -179,19 +178,22 @@ public class CQL3CasRequest implements CASRequest { assert staticConditions != null || !conditions.isEmpty(); + // Fetch all columns, but query only the selected ones + ColumnFilter columnFilter = ColumnFilter.selection(cfm, columnsToRead()); + // With only a static condition, we still want to make the distinction between a non-existing partition and one // that exists (has some live data) but has not static content. So we query the first live row of the partition. if (conditions.isEmpty()) return SinglePartitionReadCommand.create(cfm, nowInSec, - ColumnFilter.selection(columnsToRead()), + columnFilter, RowFilter.NONE, DataLimits.cqlLimits(1), key, new ClusteringIndexSliceFilter(Slices.ALL, false)); ClusteringIndexNamesFilter filter = new ClusteringIndexNamesFilter(conditions.navigableKeySet(), false); - return SinglePartitionReadCommand.create(cfm, nowInSec, key, ColumnFilter.selection(columnsToRead()), filter); + return SinglePartitionReadCommand.create(cfm, nowInSec, key, columnFilter, filter); } /** diff --git a/src/java/org/apache/cassandra/db/filter/ColumnFilter.java b/src/java/org/apache/cassandra/db/filter/ColumnFilter.java index 8d4f8b8781..2377ad04a9 100644 --- a/src/java/org/apache/cassandra/db/filter/ColumnFilter.java +++ b/src/java/org/apache/cassandra/db/filter/ColumnFilter.java @@ -95,6 +95,15 @@ public class ColumnFilter return new ColumnFilter(false, null, columns, null); } + /** + * A filter that fetches all columns for the provided table, but returns + * only the queried ones. + */ + public static ColumnFilter selection(CFMetaData metadata, PartitionColumns queried) + { + return new ColumnFilter(true, metadata, queried, null); + } + /** * The columns that needs to be fetched internally for this selection. *

diff --git a/test/unit/org/apache/cassandra/cql3/validation/operations/InsertUpdateIfConditionTest.java b/test/unit/org/apache/cassandra/cql3/validation/operations/InsertUpdateIfConditionTest.java index 40db977fe8..ec81cf23aa 100644 --- a/test/unit/org/apache/cassandra/cql3/validation/operations/InsertUpdateIfConditionTest.java +++ b/test/unit/org/apache/cassandra/cql3/validation/operations/InsertUpdateIfConditionTest.java @@ -186,6 +186,10 @@ public class InsertUpdateIfConditionTest extends CQLTester execute("INSERT INTO %s (k, s, i, v) VALUES ('k', 's', 0, 'v')"); assertRows(execute("DELETE v FROM %s WHERE k='k' AND i=0 IF EXISTS"), row(true)); assertRows(execute("DELETE FROM %s WHERE k='k' AND i=0 IF EXISTS"), row(true)); + assertRows(execute("SELECT * FROM %s"), row("k", null, "s", null)); + assertRows(execute("DELETE v FROM %s WHERE k='k' AND i=0 IF s = 'z'"), row(false, "s")); + assertRows(execute("DELETE v FROM %s WHERE k='k' AND i=0 IF v = 'z'"), row(false)); + assertRows(execute("DELETE v FROM %s WHERE k='k' AND i=0 IF v = 'z' AND s = 'z'"), row(false, null, "s")); assertRows(execute("DELETE v FROM %s WHERE k='k' AND i=0 IF EXISTS"), row(false)); assertRows(execute("DELETE FROM %s WHERE k='k' AND i=0 IF EXISTS"), row(false)); @@ -220,6 +224,28 @@ public class InsertUpdateIfConditionTest extends CQLTester assertRows(execute("DELETE FROM %s WHERE k = 1 AND i = 2 IF s = 1"), row(true)); assertEmpty(execute("SELECT * FROM %s WHERE k = 1 AND i = 2")); assertRows(execute("SELECT * FROM %s WHERE k = 1"), row(1, null, 1, null)); + + createTable("CREATE TABLE %s (k int, i int, v1 int, v2 int, s int static, PRIMARY KEY (k, i))"); + execute("INSERT INTO %s (k, i, v1, v2, s) VALUES (?, ?, ?, ?, ?)", + 1, 1, 1, 1, 1); + assertRows(execute("DELETE v1 FROM %s WHERE k = 1 AND i = 1 IF EXISTS"), + row(true)); + assertRows(execute("DELETE v2 FROM %s WHERE k = 1 AND i = 1 IF EXISTS"), + row(true)); + assertRows(execute("DELETE FROM %s WHERE k = 1 AND i = 1 IF EXISTS"), + row(true)); + assertRows(execute("select * from %s"), + row(1, null, 1, null, null)); + assertRows(execute("DELETE v1 FROM %s WHERE k = 1 AND i = 1 IF EXISTS"), + row(false)); + assertRows(execute("DELETE v1 FROM %s WHERE k = 1 AND i = 1 IF s = 5"), + row(false, 1)); + assertRows(execute("DELETE v1 FROM %s WHERE k = 1 AND i = 1 IF v1 = 1 AND v2 = 1"), + row(false)); + assertRows(execute("DELETE v1 FROM %s WHERE k = 1 AND i = 1 IF v1 = 1 AND v2 = 1 AND s = 1"), + row(false, null, null, 1)); + assertRows(execute("DELETE v1 FROM %s WHERE k = 1 AND i = 5 IF s = 1"), + row(true)); } /** @@ -249,13 +275,17 @@ public class InsertUpdateIfConditionTest extends CQLTester assertRows(execute("UPDATE %s SET v='bar', version=2 WHERE id=0 AND k='k2' IF version = 1"), row(true)); assertRows(execute("SELECT * FROM %s"), row(0, "k1", 2, "foo"), row(0, "k2", 2, "bar")); + // Batch output is slightly different from non-batch CAS, since a full PK is included to disambiguate + // cases when conditions span across multiple rows. + assertRows(execute("UPDATE %1$s SET version=3 WHERE id=0 IF version=1; "), + row(false, 2)); // Testing batches assertRows(execute("BEGIN BATCH " + "UPDATE %1$s SET v='foobar' WHERE id=0 AND k='k1'; " + "UPDATE %1$s SET v='barfoo' WHERE id=0 AND k='k2'; " + "UPDATE %1$s SET version=3 WHERE id=0 IF version=1; " + "APPLY BATCH "), - row(false, 0, null, 2)); + row(false, 0, "k1", 2)); assertRows(execute("BEGIN BATCH " + "UPDATE %1$s SET v = 'foobar' WHERE id = 0 AND k = 'k1'; " +