/* * Copyright (C) 2012-2015 DataStax Inc. * * Licensed 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 com.datastax.driver.core; import com.beust.jcommander.internal.Lists; import com.datastax.driver.core.utils.CassandraVersion; import com.google.common.base.Joiner; import org.testng.annotations.Test; import java.nio.ByteBuffer; import java.util.*; import static org.assertj.core.api.Assertions.assertThat; import static org.testng.Assert.assertEquals; import static org.testng.Assert.fail; @CassandraVersion("2.1.0") public class TupleTest extends CCMTestsSupport { private ProtocolVersion protocolVersion; private Map<DataType, Object> samples; @Override public void onTestContextInitialized() { protocolVersion = ccm().getProtocolVersion(); samples = PrimitiveTypeSamples.samples(protocolVersion); execute("CREATE TABLE t (k int PRIMARY KEY, v frozen<tuple<int, text, float>>)"); } @Test(groups = "short") public void simpleValueTest() throws Exception { TupleType t = cluster().getMetadata().newTupleType(DataType.cint(), DataType.text(), DataType.cfloat()); TupleValue v = t.newValue(); v.setInt(0, 1); v.setString(1, "a"); v.setFloat(2, 1.0f); assertEquals(v.getType().getComponentTypes().size(), 3); assertEquals(v.getType().getComponentTypes().get(0), DataType.cint()); assertEquals(v.getType().getComponentTypes().get(1), DataType.text()); assertEquals(v.getType().getComponentTypes().get(2), DataType.cfloat()); assertEquals(v.getInt(0), 1); assertEquals(v.getString(1), "a"); assertEquals(v.getFloat(2), 1.0f); assertEquals(TypeCodec.tuple(t).format(v), "(1,'a',1.0)"); } @Test(groups = "short") public void simpleWriteReadTest() throws Exception { session().execute("USE " + keyspace); PreparedStatement ins = session().prepare("INSERT INTO t(k, v) VALUES (?, ?)"); PreparedStatement sel = session().prepare("SELECT * FROM t WHERE k=?"); TupleType t = cluster().getMetadata().newTupleType(DataType.cint(), DataType.text(), DataType.cfloat()); int k = 1; TupleValue v = t.newValue(1, "a", 1.0f); session().execute(ins.bind(k, v)); TupleValue v2 = session().execute(sel.bind(k)).one().getTupleValue("v"); assertEquals(v2, v); // Test simple statement interpolation k = 2; v = t.newValue(2, "b", 2.0f); session().execute("INSERT INTO t(k, v) VALUES (?, ?)", k, v); v2 = session().execute(sel.bind(k)).one().getTupleValue("v"); assertEquals(v2, v); } /** * Basic test of tuple functionality. * Original code found in python-driver:integration.standard.test_types.py:test_tuple_type * * @throws Exception */ @Test(groups = "short") public void tupleTypeTest() throws Exception { session().execute("CREATE KEYSPACE test_tuple_type " + "WITH replication = { 'class' : 'SimpleStrategy', 'replication_factor': '1'}"); session().execute("USE test_tuple_type"); session().execute("CREATE TABLE mytable (a int PRIMARY KEY, b frozen<tuple<ascii, int, boolean>>)"); TupleType t = cluster().getMetadata().newTupleType(DataType.ascii(), DataType.cint(), DataType.cboolean()); // test non-prepared statement TupleValue complete = t.newValue("foo", 123, true); session().execute("INSERT INTO mytable (a, b) VALUES (0, ?)", complete); TupleValue r = session().execute("SELECT b FROM mytable WHERE a=0").one().getTupleValue("b"); assertEquals(r, complete); // test incomplete tuples try { t.newValue("bar", 456); fail(); } catch (IllegalArgumentException e) { //ok } // test incomplete tuples with new TupleType TupleType t1 = cluster().getMetadata().newTupleType(DataType.ascii(), DataType.cint()); TupleValue partial = t1.newValue("bar", 456); TupleValue partionResult = t.newValue("bar", 456, null); session().execute("INSERT INTO mytable (a, b) VALUES (0, ?)", partial); r = session().execute("SELECT b FROM mytable WHERE a=0").one().getTupleValue("b"); assertEquals(r, partionResult); // test single value tuples try { t.newValue("zoo"); fail(); } catch (IllegalArgumentException e) { //ok } // test single value tuples with new TupleType TupleType t2 = cluster().getMetadata().newTupleType(DataType.ascii()); TupleValue subpartial = t2.newValue("zoo"); TupleValue subpartialResult = t.newValue("zoo", null, null); session().execute("INSERT INTO mytable (a, b) VALUES (0, ?)", subpartial); r = session().execute("SELECT b FROM mytable WHERE a=0").one().getTupleValue("b"); assertEquals(r, subpartialResult); // test prepared statements PreparedStatement prepared = session().prepare("INSERT INTO mytable (a, b) VALUES (?, ?)"); session().execute(prepared.bind(3, complete)); session().execute(prepared.bind(4, partial)); session().execute(prepared.bind(5, subpartial)); prepared = session().prepare("SELECT b FROM mytable WHERE a=?"); assertEquals(session().execute(prepared.bind(3)).one().getTupleValue("b"), complete); assertEquals(session().execute(prepared.bind(4)).one().getTupleValue("b"), partionResult); assertEquals(session().execute(prepared.bind(5)).one().getTupleValue("b"), subpartialResult); } /** * Test tuple types of lengths of 1, 2, 3, and 384 to ensure edge cases work * as expected. * Original code found in python-driver:integration.standard.test_types.py:test_tuple_type_varying_lengths * * @throws Exception */ @Test(groups = "short") public void tupleTestTypeVaryingLengths() throws Exception { session().execute("CREATE KEYSPACE test_tuple_type_varying_lengths " + "WITH replication = { 'class' : 'SimpleStrategy', 'replication_factor': '1'}"); session().execute("USE test_tuple_type_varying_lengths"); // programmatically create the table with tuples of said sizes int[] lengths = {1, 2, 3, 384}; ArrayList<String> valueSchema = new ArrayList<String>(); for (int i : lengths) { ArrayList<String> ints = new ArrayList<String>(); for (int j = 0; j < i; ++j) { ints.add("int"); } valueSchema.add(String.format(" v_%d frozen<tuple<%s>>", i, Joiner.on(',').join(ints))); } session().execute(String.format("CREATE TABLE mytable (k int PRIMARY KEY, %s)", Joiner.on(',').join(valueSchema))); // insert tuples into same key using different columns // and verify the results for (int i : lengths) { // create tuple ArrayList<DataType> dataTypes = new ArrayList<DataType>(); ArrayList<Integer> values = new ArrayList<Integer>(); for (int j = 0; j < i; ++j) { dataTypes.add(DataType.cint()); values.add(j); } TupleType t = new TupleType(dataTypes, protocolVersion, cluster().getConfiguration().getCodecRegistry()); TupleValue createdTuple = t.newValue(values.toArray()); // write tuple session().execute(String.format("INSERT INTO mytable (k, v_%s) VALUES (0, ?)", i), createdTuple); // read tuple TupleValue r = session().execute(String.format("SELECT v_%s FROM mytable WHERE k=0", i)).one().getTupleValue(String.format("v_%s", i)); assertEquals(r, createdTuple); } } /** * Ensure tuple subtypes are appropriately handled. * Original code found in python-driver:integration.standard.test_types.py:test_tuple_subtypes * * @throws Exception */ @Test(groups = "short") public void tupleSubtypesTest() throws Exception { List<DataType> DATA_TYPE_PRIMITIVES = Lists.newArrayList(samples.keySet()); session().execute("CREATE KEYSPACE test_tuple_subtypes " + "WITH replication = { 'class' : 'SimpleStrategy', 'replication_factor': '1'}"); session().execute("USE test_tuple_subtypes"); // programmatically create the table with a tuple of all datatypes session().execute(String.format("CREATE TABLE mytable (k int PRIMARY KEY, v frozen<tuple<%s>>)", Joiner.on(',').join(DATA_TYPE_PRIMITIVES))); // insert tuples into same key using different columns // and verify the results int i = 1; for (DataType ignored : DATA_TYPE_PRIMITIVES) { // create tuples to be written and ensure they match with the expected response // responses have trailing None values for every element that has not been written ArrayList<DataType> dataTypes = new ArrayList<DataType>(); ArrayList<DataType> completeDataTypes = new ArrayList<DataType>(); ArrayList<Object> createdValues = new ArrayList<Object>(); ArrayList<Object> completeValues = new ArrayList<Object>(); // create written portion of the arrays for (int j = 0; j < i; ++j) { dataTypes.add(DATA_TYPE_PRIMITIVES.get(j)); completeDataTypes.add(DATA_TYPE_PRIMITIVES.get(j)); createdValues.add(samples.get(DATA_TYPE_PRIMITIVES.get(j))); completeValues.add(samples.get(DATA_TYPE_PRIMITIVES.get(j))); } // complete portion of the arrays needed for trailing nulls for (int j = 0; j < DATA_TYPE_PRIMITIVES.size() - i; ++j) { completeDataTypes.add(DATA_TYPE_PRIMITIVES.get(i + j)); completeValues.add(null); } // actually create the tuples TupleType t = new TupleType(dataTypes, protocolVersion, cluster().getConfiguration().getCodecRegistry()); TupleType t2 = new TupleType(completeDataTypes, protocolVersion, cluster().getConfiguration().getCodecRegistry()); TupleValue createdTuple = t.newValue(createdValues.toArray()); TupleValue completeTuple = t2.newValue(completeValues.toArray()); // write tuple session().execute(String.format("INSERT INTO mytable (k, v) VALUES (%s, ?)", i), createdTuple); // read tuple TupleValue r = session().execute("SELECT v FROM mytable WHERE k=?", i).one().getTupleValue("v"); assertEquals(r, completeTuple); ++i; } } /** * Ensure tuple subtypes are appropriately handled for maps, sets, and lists. * Original code found in python-driver:integration.standard.test_types.py:test_tuple_non_primitive_subtypes * * @throws Exception */ @Test(groups = "short") public void tupleNonPrimitiveSubTypesTest() throws Exception { List<DataType> DATA_TYPE_PRIMITIVES = Lists.newArrayList(samples.keySet()); session().execute("CREATE KEYSPACE test_tuple_non_primitive_subtypes " + "WITH replication = { 'class' : 'SimpleStrategy', 'replication_factor': '1'}"); session().execute("USE test_tuple_non_primitive_subtypes"); ArrayList<String> values = new ArrayList<String>(); //create list values for (DataType datatype : DATA_TYPE_PRIMITIVES) { values.add(String.format("v_%s frozen<tuple<list<%s>>>", values.size(), datatype)); } // create set values for (DataType datatype : DATA_TYPE_PRIMITIVES) { // Duration not supported in Set. if (datatype != DataType.duration()) values.add(String.format("v_%s frozen<tuple<set<%s>>>", values.size(), datatype)); } // create map values for (DataType datatype : DATA_TYPE_PRIMITIVES) { // Duration not supported as Map key. if (datatype != DataType.duration()) values.add(String.format("v_%s frozen<tuple<map<%s, %s>>>", values.size(), datatype, datatype)); } // create table session().execute(String.format("CREATE TABLE mytable (k int PRIMARY KEY, %s)", Joiner.on(',').join(values))); int i = 0; // test tuple<list<datatype>> for (DataType datatype : DATA_TYPE_PRIMITIVES) { // create tuple ArrayList<DataType> dataTypes = new ArrayList<DataType>(); ArrayList<Object> createdValues = new ArrayList<Object>(); dataTypes.add(DataType.list(datatype)); createdValues.add(Collections.singletonList(samples.get(datatype))); TupleType t = new TupleType(dataTypes, protocolVersion, cluster().getConfiguration().getCodecRegistry()); TupleValue createdTuple = t.newValue(createdValues.toArray()); // write tuple session().execute(String.format("INSERT INTO mytable (k, v_%s) VALUES (0, ?)", i), createdTuple); // read tuple TupleValue r = session().execute(String.format("SELECT v_%s FROM mytable WHERE k=0", i)) .one().getTupleValue(String.format("v_%s", i)); assertEquals(r, createdTuple); ++i; } // test tuple<set<datatype>> for (DataType datatype : DATA_TYPE_PRIMITIVES) { if (datatype == DataType.duration()) continue; // create tuple ArrayList<DataType> dataTypes = new ArrayList<DataType>(); ArrayList<Object> createdValues = new ArrayList<Object>(); dataTypes.add(DataType.set(datatype)); createdValues.add(new HashSet<Object>(Collections.singletonList(samples.get(datatype)))); TupleType t = new TupleType(dataTypes, protocolVersion, cluster().getConfiguration().getCodecRegistry()); TupleValue createdTuple = t.newValue(createdValues.toArray()); // write tuple session().execute(String.format("INSERT INTO mytable (k, v_%s) VALUES (0, ?)", i), createdTuple); // read tuple TupleValue r = session().execute(String.format("SELECT v_%s FROM mytable WHERE k=0", i)) .one().getTupleValue(String.format("v_%s", i)); assertEquals(r, createdTuple); ++i; } // test tuple<map<datatype, datatype>> for (DataType datatype : DATA_TYPE_PRIMITIVES) { if (datatype == DataType.duration()) continue; // create tuple ArrayList<DataType> dataTypes = new ArrayList<DataType>(); ArrayList<Object> createdValues = new ArrayList<Object>(); HashMap<Object, Object> hm = new HashMap<Object, Object>(); hm.put(samples.get(datatype), samples.get(datatype)); dataTypes.add(DataType.map(datatype, datatype)); createdValues.add(hm); TupleType t = new TupleType(dataTypes, protocolVersion, cluster().getConfiguration().getCodecRegistry()); TupleValue createdTuple = t.newValue(createdValues.toArray()); // write tuple session().execute(String.format("INSERT INTO mytable (k, v_%s) VALUES (0, ?)", i), createdTuple); // read tuple TupleValue r = session().execute(String.format("SELECT v_%s FROM mytable WHERE k=0", i)) .one().getTupleValue(String.format("v_%s", i)); assertEquals(r, createdTuple); ++i; } } /** * Validates that tuple values generated from an attached type (cluster-provided TupleType) and * a detached type (using TupleType.of) are the same. * * @since 2.2.0 */ @Test(groups = "short") public void detachedTupleTypeTest() { TupleType detachedType = TupleType.of(protocolVersion, CodecRegistry.DEFAULT_INSTANCE, DataType.cint(), DataType.text(), DataType.cfloat()); TupleValue detachedValue = detachedType.newValue(1, "hello", 2.0f); TupleType attachedType = cluster().getMetadata().newTupleType(DataType.cint(), DataType.text(), DataType.cfloat()); TupleValue attachedValue = attachedType.newValue(1, "hello", 2.0f); assertThat(detachedValue).isEqualTo(attachedValue); } /** * Helper method for creating nested tuple schema */ private String nestedTuplesSchemaHelper(int depth) { if (depth == 0) return "int"; else return String.format("frozen<tuple<%s>>", nestedTuplesSchemaHelper(depth - 1)); } /** * Helper method for creating nested tuples */ private TupleValue nestedTuplesCreatorHelper(int depth) { if (depth == 1) { TupleType baseTuple = cluster().getMetadata().newTupleType(DataType.cint()); return baseTuple.newValue(303); } else { TupleValue innerTuple = nestedTuplesCreatorHelper(depth - 1); TupleType t = cluster().getMetadata().newTupleType(innerTuple.getType()); return t.newValue(innerTuple); } } /** * Ensure nested are appropriately handled. * Original code found in python-driver:integration.standard.test_types.py:test_nested_tuples * * @throws Exception */ @Test(groups = "short") public void nestedTuplesTest() throws Exception { session().execute("CREATE KEYSPACE test_nested_tuples " + "WITH replication = { 'class' : 'SimpleStrategy', 'replication_factor': '1'}"); session().execute("USE test_nested_tuples"); // create a table with multiple sizes of nested tuples session().execute(String.format("CREATE TABLE mytable (" + "k int PRIMARY KEY, " + "v_1 %s, " + "v_2 %s, " + "v_3 %s, " + "v_32 %s)", nestedTuplesSchemaHelper(1), nestedTuplesSchemaHelper(2), nestedTuplesSchemaHelper(3), nestedTuplesSchemaHelper(32))); for (int i : Arrays.asList(1, 2, 3, 32)) { // create tuple TupleValue createdTuple = nestedTuplesCreatorHelper(i); // write tuple session().execute(String.format("INSERT INTO mytable (k, v_%s) VALUES (?, ?)", i), i, createdTuple); // verify tuple was written and read correctly TupleValue r = session().execute(String.format("SELECT v_%s FROM mytable WHERE k=?", i), i) .one().getTupleValue(String.format("v_%s", i)); assertEquals(r, createdTuple); } } /** * Test for inserting null Tuple values into UDT's * Original code found in python-driver:integration.standard.test_types.py:test_tuples_with_nulls * * @throws Exception */ @Test(groups = "short") public void testTuplesWithNulls() throws Exception { // create keyspace session().execute("CREATE KEYSPACE testTuplesWithNulls " + "WITH replication = { 'class' : 'SimpleStrategy', 'replication_factor': '1'}"); session().execute("USE testTuplesWithNulls"); // create UDT session().execute("CREATE TYPE user (a text, b frozen<tuple<text, int, uuid, blob>>)"); session().execute("CREATE TABLE mytable (a int PRIMARY KEY, b frozen<user>)"); // insert UDT data UserType userTypeDef = cluster().getMetadata().getKeyspace("testTuplesWithNulls").getUserType("user"); UDTValue userType = userTypeDef.newValue(); TupleType t = cluster().getMetadata().newTupleType(DataType.text(), DataType.cint(), DataType.uuid(), DataType.blob()); TupleValue v = t.newValue(null, null, null, null); userType.setTupleValue("b", v); PreparedStatement ins = session().prepare("INSERT INTO mytable (a, b) VALUES (?, ?)"); session().execute(ins.bind(0, userType)); // retrieve and verify data ResultSet rs = session().execute("SELECT * FROM mytable"); List<Row> rows = rs.all(); assertEquals(1, rows.size()); Row row = rows.get(0); assertEquals(row.getInt("a"), 0); assertEquals(row.getUDTValue("b"), userType); // test empty strings v = t.newValue("", null, null, ByteBuffer.allocate(0)); userType.setTupleValue("b", v); session().execute(ins.bind(0, userType)); // retrieve and verify data rs = session().execute("SELECT * FROM mytable"); rows = rs.all(); assertEquals(1, rows.size()); row = rows.get(0); assertEquals(row.getInt("a"), 0); assertEquals(row.getUDTValue("b"), userType); } }