/*
* 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.datastax.driver.core.utils.CassandraVersion;
import org.testng.annotations.Test;
import java.util.UUID;
import static com.datastax.driver.core.DataType.cfloat;
import static org.assertj.core.api.Assertions.assertThat;
@CassandraVersion("2.1.0")
public class TypeCodecTupleIntegrationTest extends CCMTestsSupport {
private final String insertQuery = "INSERT INTO users (id, name, location) VALUES (?, ?, ?)";
private final String selectQuery = "SELECT id, name, location FROM users WHERE id = ?";
private final UUID uuid = UUID.randomUUID();
private TupleType locationType;
private TupleValue locationValue;
private TupleValue partialLocationValueInserted;
private Location location;
private Location partialLocation;
private TupleValue partialLocationValueRetrieved;
@Override
public void onTestContextInitialized() {
execute(
"CREATE TABLE IF NOT EXISTS \"users\" (id uuid PRIMARY KEY, name text, location frozen<tuple<float,float>>)"
);
}
@Test(groups = "short")
public void should_handle_tuples_with_default_codecs() {
setUpTupleTypes(cluster());
// simple statement
session().execute(insertQuery, uuid, "John Doe", locationValue);
ResultSet rows = session().execute(selectQuery, uuid);
Row row = rows.one();
assertRow(row);
// prepared + values
PreparedStatement ps = session().prepare(insertQuery);
session().execute(ps.bind(uuid, "John Doe", locationValue));
rows = session().execute(selectQuery, uuid);
row = rows.one();
assertRow(row);
// bound with setTupleValue
session().execute(ps.bind().setUUID(0, uuid).setString(1, "John Doe").setTupleValue("location", locationValue));
rows = session().execute(selectQuery, uuid);
row = rows.one();
assertRow(row);
}
@Test(groups = "short")
public void should_handle_partial_tuples_with_default_codecs() {
setUpTupleTypes(cluster());
// simple statement
session().execute(insertQuery, uuid, "John Doe", partialLocationValueInserted);
ResultSet rows = session().execute(selectQuery, uuid);
Row row = rows.one();
assertPartialRow(row);
// prepared + values
PreparedStatement ps = session().prepare(insertQuery);
session().execute(ps.bind(uuid, "John Doe", partialLocationValueInserted));
rows = session().execute(selectQuery, uuid);
row = rows.one();
assertPartialRow(row);
// bound with setTupleValue
session().execute(ps.bind().setUUID(0, uuid).setString(1, "John Doe").setTupleValue("location", partialLocationValueInserted));
rows = session().execute(selectQuery, uuid);
row = rows.one();
assertPartialRow(row);
}
@Test(groups = "short")
public void should_handle_tuples_with_custom_codecs() {
CodecRegistry codecRegistry = new CodecRegistry();
Cluster cluster = register(Cluster.builder()
.addContactPoints(getContactPoints())
.withPort(ccm().getBinaryPort())
.withCodecRegistry(codecRegistry)
.build());
Session session = cluster.connect(keyspace);
setUpTupleTypes(cluster);
codecRegistry.register(new LocationCodec(TypeCodec.tuple(locationType)));
session.execute(insertQuery, uuid, "John Doe", locationValue);
ResultSet rows = session.execute(selectQuery, uuid);
Row row = rows.one();
assertThat(row.getUUID(0)).isEqualTo(uuid);
assertThat(row.getObject(0)).isEqualTo(uuid);
assertThat(row.get(0, UUID.class)).isEqualTo(uuid);
assertThat(row.getString(1)).isEqualTo("John Doe");
assertThat(row.getObject(1)).isEqualTo("John Doe");
assertThat(row.get(1, String.class)).isEqualTo("John Doe");
assertThat(row.getTupleValue(2)).isEqualTo(locationValue);
// edge case: getObject should use default codecs;
// but tuple and udt codecs are registered on the fly;
// so if we have another manually-registered codec
// that one will be picked up
assertThat(row.getObject(2)).isEqualTo(location);
assertThat(row.get(2, TupleValue.class)).isEqualTo(locationValue);
assertThat(row.get(2, Location.class)).isEqualTo(location);
}
@Test(groups = "short")
public void should_handle_partial_tuples_with_custom_codecs() {
CodecRegistry codecRegistry = new CodecRegistry();
Cluster cluster = register(Cluster.builder()
.addContactPoints(getContactPoints())
.withPort(ccm().getBinaryPort())
.withCodecRegistry(codecRegistry)
.build());
Session session = cluster.connect(keyspace);
setUpTupleTypes(cluster);
codecRegistry.register(new LocationCodec(TypeCodec.tuple(locationType)));
session.execute(insertQuery, uuid, "John Doe", partialLocationValueInserted);
ResultSet rows = session.execute(selectQuery, uuid);
Row row = rows.one();
assertThat(row.getUUID(0)).isEqualTo(uuid);
assertThat(row.getObject(0)).isEqualTo(uuid);
assertThat(row.get(0, UUID.class)).isEqualTo(uuid);
assertThat(row.getString(1)).isEqualTo("John Doe");
assertThat(row.getObject(1)).isEqualTo("John Doe");
assertThat(row.get(1, String.class)).isEqualTo("John Doe");
assertThat(row.getTupleValue(2)).isEqualTo(locationType.newValue(37.387224f, null));
// corner case: getObject should use default codecs;
// but tuple and udt codecs are registered on the fly;
// so if we have another manually-registered codec
// that one will be picked up :(
assertThat(row.getObject(2)).isEqualTo(partialLocation);
assertThat(row.get(2, TupleValue.class)).isEqualTo(locationType.newValue(37.387224f, null));
assertThat(row.get(2, Location.class)).isEqualTo(partialLocation);
}
private void assertRow(Row row) {
assertThat(row.getUUID(0)).isEqualTo(uuid);
assertThat(row.getObject(0)).isEqualTo(uuid);
assertThat(row.get(0, UUID.class)).isEqualTo(uuid);
assertThat(row.getString(1)).isEqualTo("John Doe");
assertThat(row.getObject(1)).isEqualTo("John Doe");
assertThat(row.get(1, String.class)).isEqualTo("John Doe");
assertThat(row.getTupleValue(2)).isEqualTo(locationValue);
assertThat(row.getObject(2)).isEqualTo(locationValue);
assertThat(row.get(2, TupleValue.class)).isEqualTo(locationValue);
}
private void assertPartialRow(Row row) {
assertThat(row.getUUID(0)).isEqualTo(uuid);
assertThat(row.getObject(0)).isEqualTo(uuid);
assertThat(row.get(0, UUID.class)).isEqualTo(uuid);
assertThat(row.getString(1)).isEqualTo("John Doe");
assertThat(row.getObject(1)).isEqualTo("John Doe");
assertThat(row.get(1, String.class)).isEqualTo("John Doe");
assertThat(row.getTupleValue(2)).isEqualTo(partialLocationValueRetrieved);
assertThat(row.getObject(2)).isEqualTo(partialLocationValueRetrieved);
assertThat(row.get(2, TupleValue.class)).isEqualTo(partialLocationValueRetrieved);
}
private void setUpTupleTypes(Cluster cluster) {
locationType = cluster.getMetadata().newTupleType(cfloat(), cfloat());
locationValue = locationType.newValue()
.setFloat(0, 37.387224f)
.setFloat(1, -121.9733837f);
// insert a tuple of a different dimension
partialLocationValueInserted = cluster.getMetadata().newTupleType(cfloat()).newValue().setFloat(0, 37.387224f);
// retrieve the partial tuple with null missing values
partialLocationValueRetrieved = locationType.newValue(37.387224f, null);
location = new Location(37.387224f, -121.9733837f);
partialLocation = new Location(37.387224f, 0.0f);
}
static class LocationCodec extends MappingCodec<Location, TupleValue> {
private final TupleType tupleType;
public LocationCodec(TypeCodec<TupleValue> innerCodec) {
super(innerCodec, Location.class);
tupleType = (TupleType) innerCodec.getCqlType();
}
@Override
protected Location deserialize(TupleValue value) {
return value == null ? null : new Location(value.getFloat(0), value.getFloat(1));
}
@Override
protected TupleValue serialize(Location value) {
return value == null ? null : tupleType.newValue().setFloat(0, value.latitude).setFloat(1, value.longitude);
}
}
static class Location {
float latitude;
float longitude;
public Location(float latitude, float longitude) {
this.latitude = latitude;
this.longitude = longitude;
}
@Override
public boolean equals(Object o) {
if (this == o)
return true;
if (o == null || getClass() != o.getClass())
return false;
Location location = (Location) o;
return Float.compare(location.latitude, latitude) == 0 && Float.compare(location.longitude, longitude) == 0;
}
@Override
public int hashCode() {
int result = (latitude != +0.0f ? Float.floatToIntBits(latitude) : 0);
result = 31 * result + (longitude != +0.0f ? Float.floatToIntBits(longitude) : 0);
return result;
}
@Override
public String toString() {
return "[" + latitude + ", " + longitude + "]";
}
}
}