From 9b539f966f56a98775eacadd6495b4482b0e87a9 Mon Sep 17 00:00:00 2001 From: Jonathan Ellis Date: Sun, 6 Jul 2014 23:55:32 -0500 Subject: [PATCH] Fix prepared marker for collections inside UDT patch by slebresne; reviewed by thobbs for CASSANDRA-7472 --- CHANGES.txt | 1 + .../apache/cassandra/cql3/QueryOptions.java | 12 ----------- .../org/apache/cassandra/cql3/Tuples.java | 20 +++++++++++++------ .../org/apache/cassandra/cql3/UserTypes.java | 16 ++++++++++----- .../cassandra/db/marshal/CollectionType.java | 3 +++ .../serializers/CollectionSerializer.java | 5 +++++ 6 files changed, 34 insertions(+), 23 deletions(-) diff --git a/CHANGES.txt b/CHANGES.txt index 01cc224ba5..9fd3b85b43 100644 --- a/CHANGES.txt +++ b/CHANGES.txt @@ -1,4 +1,5 @@ 2.1.0 + * Fix prepared marker for collections inside UDT (CASSANDRA-7472) * Remove left-over populate_io_cache_on_flush and replicate_on_write uses (CASSANDRA-7493) * (Windows) handle spaces in path names (CASSANDRA-7451) diff --git a/src/java/org/apache/cassandra/cql3/QueryOptions.java b/src/java/org/apache/cassandra/cql3/QueryOptions.java index 369dce4a0c..c946e8b3dd 100644 --- a/src/java/org/apache/cassandra/cql3/QueryOptions.java +++ b/src/java/org/apache/cassandra/cql3/QueryOptions.java @@ -110,8 +110,6 @@ public abstract class QueryOptions */ public abstract int getProtocolVersion(); - public abstract QueryOptions withProtocolVersion(int version); - // Mainly for the sake of BatchQueryOptions abstract SpecificOptions getSpecificOptions(); @@ -139,11 +137,6 @@ public abstract class QueryOptions this.protocolVersion = protocolVersion; } - public QueryOptions withProtocolVersion(int version) - { - return new DefaultQueryOptions(consistency, values, skipMetadata, options, version); - } - public ConsistencyLevel getConsistency() { return consistency; @@ -205,11 +198,6 @@ public abstract class QueryOptions wrapped.prepare(specs); return this; } - - public QueryOptions withProtocolVersion(int version) - { - return new DefaultQueryOptions(getConsistency(), getValues(), skipMetadata(), getSpecificOptions(), version); - } } static class OptionsWithNames extends QueryOptionsWrapper diff --git a/src/java/org/apache/cassandra/cql3/Tuples.java b/src/java/org/apache/cassandra/cql3/Tuples.java index e1ce551d2a..f0d7a13651 100644 --- a/src/java/org/apache/cassandra/cql3/Tuples.java +++ b/src/java/org/apache/cassandra/cql3/Tuples.java @@ -71,7 +71,7 @@ public class Tuples values.add(value); } - DelayedValue value = new DelayedValue(values); + DelayedValue value = new DelayedValue((TupleType)receiver.type, values); return allTerminal ? value.bind(QueryOptions.DEFAULT) : value; } @@ -81,6 +81,7 @@ public class Tuples throw new InvalidRequestException(String.format("Expected %d elements in value tuple, but got %d: %s", receivers.size(), elements.size(), this)); List values = new ArrayList<>(elements.size()); + List> types = new ArrayList<>(elements.size()); boolean allTerminal = true; for (int i = 0; i < elements.size(); i++) { @@ -89,8 +90,9 @@ public class Tuples allTerminal = false; values.add(t); + types.add(receivers.get(i).type); } - DelayedValue value = new DelayedValue(values); + DelayedValue value = new DelayedValue(new TupleType(types), values); return allTerminal ? value.bind(QueryOptions.DEFAULT) : value; } @@ -166,10 +168,12 @@ public class Tuples */ public static class DelayedValue extends Term.NonTerminal { + public final TupleType type; public final List elements; - public DelayedValue(List elements) + public DelayedValue(TupleType type, List elements) { + this.type = type; this.elements = elements; } @@ -190,13 +194,17 @@ public class Tuples private ByteBuffer[] bindInternal(QueryOptions options) throws InvalidRequestException { - // Inside tuples, we must force the serialization of collections whatever the protocol version is in - // use since we're going to store directly that serialized value. - options = options.withProtocolVersion(3); + int version = options.getProtocolVersion(); ByteBuffer[] buffers = new ByteBuffer[elements.size()]; for (int i = 0; i < elements.size(); i++) + { buffers[i] = elements.get(i).bindAndGet(options); + // Inside tuples, we must force the serialization of collections to v3 whatever protocol + // version is in use since we're going to store directly that serialized value. + if (version < 3 && type.type(i).isCollection()) + buffers[i] = ((CollectionType)type.type(i)).getSerializer().reserializeToV3(buffers[i]); + } return buffers; } diff --git a/src/java/org/apache/cassandra/cql3/UserTypes.java b/src/java/org/apache/cassandra/cql3/UserTypes.java index c5469d29d6..876de2a795 100644 --- a/src/java/org/apache/cassandra/cql3/UserTypes.java +++ b/src/java/org/apache/cassandra/cql3/UserTypes.java @@ -20,6 +20,7 @@ package org.apache.cassandra.cql3; import java.nio.ByteBuffer; import java.util.*; +import org.apache.cassandra.db.marshal.CollectionType; import org.apache.cassandra.db.marshal.UserType; import org.apache.cassandra.db.marshal.UTF8Type; import org.apache.cassandra.exceptions.InvalidRequestException; @@ -33,10 +34,11 @@ public abstract class UserTypes public static ColumnSpecification fieldSpecOf(ColumnSpecification column, int field) { + UserType ut = (UserType)column.type; return new ColumnSpecification(column.ksName, column.cfName, - new ColumnIdentifier(column.name + "." + field, true), - ((UserType)column.type).fieldType(field)); + new ColumnIdentifier(column.name + "." + UTF8Type.instance.compose(ut.fieldName(field)), true), + ut.fieldType(field)); } public static class Literal implements Term.Raw @@ -150,13 +152,17 @@ public abstract class UserTypes private ByteBuffer[] bindInternal(QueryOptions options) throws InvalidRequestException { - // Inside UDT values, we must force the serialization of collections whatever the protocol version is in - // use since we're going to store directly that serialized value. - options = options.withProtocolVersion(3); + int version = options.getProtocolVersion(); ByteBuffer[] buffers = new ByteBuffer[values.size()]; for (int i = 0; i < type.size(); i++) + { buffers[i] = values.get(i).bindAndGet(options); + // Inside UDT values, we must force the serialization of collections to v3 whatever protocol + // version is in use since we're going to store directly that serialized value. + if (version < 3 && type.fieldType(i).isCollection()) + buffers[i] = ((CollectionType)type.fieldType(i)).getSerializer().reserializeToV3(buffers[i]); + } return buffers; } diff --git a/src/java/org/apache/cassandra/db/marshal/CollectionType.java b/src/java/org/apache/cassandra/db/marshal/CollectionType.java index b1d8da16ab..be344f9d5a 100644 --- a/src/java/org/apache/cassandra/db/marshal/CollectionType.java +++ b/src/java/org/apache/cassandra/db/marshal/CollectionType.java @@ -60,6 +60,9 @@ public abstract class CollectionType extends AbstractType public abstract List serializedValues(List cells); + @Override + public abstract CollectionSerializer getSerializer(); + @Override public String toString() { diff --git a/src/java/org/apache/cassandra/serializers/CollectionSerializer.java b/src/java/org/apache/cassandra/serializers/CollectionSerializer.java index 7cddb127e6..2a5e809fe4 100644 --- a/src/java/org/apache/cassandra/serializers/CollectionSerializer.java +++ b/src/java/org/apache/cassandra/serializers/CollectionSerializer.java @@ -48,6 +48,11 @@ public abstract class CollectionSerializer implements TypeSerializer return deserializeForNativeProtocol(bytes, 3); } + public ByteBuffer reserializeToV3(ByteBuffer bytes) + { + return serialize(deserializeForNativeProtocol(bytes, 2)); + } + public void validate(ByteBuffer bytes) throws MarshalException { // Same thing than above