Preserve the names of query parameters in QueryOptions patch by Benjamin Lerer; reviewed by Sam Tunnicliffe for CASSANDRA-9632
Project: http://git-wip-us.apache.org/repos/asf/cassandra/repo Commit: http://git-wip-us.apache.org/repos/asf/cassandra/commit/416ba6e8 Tree: http://git-wip-us.apache.org/repos/asf/cassandra/tree/416ba6e8 Diff: http://git-wip-us.apache.org/repos/asf/cassandra/diff/416ba6e8 Branch: refs/heads/cassandra-2.2 Commit: 416ba6e8b2729c671d66be43cbcbc7c76d1c8a91 Parents: 1a2a1d2 Author: blerer <[email protected]> Authored: Thu Sep 17 21:06:21 2015 +0200 Committer: blerer <[email protected]> Committed: Thu Sep 17 21:06:21 2015 +0200 ---------------------------------------------------------------------- NEWS.txt | 6 ++ .../cassandra/cql3/BatchQueryOptions.java | 28 ++++++++ .../cassandra/cql3/ColumnSpecification.java | 10 +++ .../org/apache/cassandra/cql3/QueryOptions.java | 70 +++++++++++++++++++- .../transport/messages/BatchMessage.java | 2 +- .../transport/messages/ExecuteMessage.java | 5 +- 6 files changed, 117 insertions(+), 4 deletions(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/cassandra/blob/416ba6e8/NEWS.txt ---------------------------------------------------------------------- diff --git a/NEWS.txt b/NEWS.txt index 198e8e9..49744cf 100644 --- a/NEWS.txt +++ b/NEWS.txt @@ -33,6 +33,12 @@ Changed Defaults - Parallel repairs are the default since 2.2.0, run sequential repairs by providing the '-seq' parameter to nodetool repair. +New features +------------ + - Custom QueryHandlers can retrieve the column specifications for the bound + variables from QueryOptions by using the hasColumnSpecifications() + and getColumnSpecifications() methods. + 2.2.1 ===== http://git-wip-us.apache.org/repos/asf/cassandra/blob/416ba6e8/src/java/org/apache/cassandra/cql3/BatchQueryOptions.java ---------------------------------------------------------------------- diff --git a/src/java/org/apache/cassandra/cql3/BatchQueryOptions.java b/src/java/org/apache/cassandra/cql3/BatchQueryOptions.java index 2fcee5b..db7fa39 100644 --- a/src/java/org/apache/cassandra/cql3/BatchQueryOptions.java +++ b/src/java/org/apache/cassandra/cql3/BatchQueryOptions.java @@ -22,6 +22,8 @@ import java.util.ArrayList; import java.util.Collections; import java.util.List; +import org.apache.cassandra.utils.MD5Digest; + import org.apache.cassandra.db.ConsistencyLevel; import org.apache.cassandra.service.QueryState; @@ -50,6 +52,11 @@ public abstract class BatchQueryOptions public abstract QueryOptions forStatement(int i); + public void prepareStatement(int i, List<ColumnSpecification> boundNames) + { + forStatement(i).prepare(boundNames); + } + public ConsistencyLevel getConsistency() { return wrapped.getConsistency(); @@ -107,5 +114,26 @@ public abstract class BatchQueryOptions { return perStatementOptions.get(i); } + + @Override + public void prepareStatement(int i, List<ColumnSpecification> boundNames) + { + if (isPreparedStatement(i)) + { + QueryOptions options = perStatementOptions.get(i); + options.prepare(boundNames); + options = QueryOptions.addColumnSpecifications(options, boundNames); + perStatementOptions.set(i, options); + } + else + { + super.prepareStatement(i, boundNames); + } + } + + private boolean isPreparedStatement(int i) + { + return getQueryOrIdList().get(i) instanceof MD5Digest; + } } } http://git-wip-us.apache.org/repos/asf/cassandra/blob/416ba6e8/src/java/org/apache/cassandra/cql3/ColumnSpecification.java ---------------------------------------------------------------------- diff --git a/src/java/org/apache/cassandra/cql3/ColumnSpecification.java b/src/java/org/apache/cassandra/cql3/ColumnSpecification.java index e12a57e..e64f5f9 100644 --- a/src/java/org/apache/cassandra/cql3/ColumnSpecification.java +++ b/src/java/org/apache/cassandra/cql3/ColumnSpecification.java @@ -18,6 +18,7 @@ package org.apache.cassandra.cql3; import com.google.common.base.Objects; + import org.apache.cassandra.db.marshal.AbstractType; import org.apache.cassandra.db.marshal.ReversedType; @@ -91,4 +92,13 @@ public class ColumnSpecification { return Objects.hashCode(ksName, cfName, name, type); } + + @Override + public String toString() + { + return Objects.toStringHelper(this) + .add("name", name) + .add("type", type) + .toString(); + } } http://git-wip-us.apache.org/repos/asf/cassandra/blob/416ba6e8/src/java/org/apache/cassandra/cql3/QueryOptions.java ---------------------------------------------------------------------- diff --git a/src/java/org/apache/cassandra/cql3/QueryOptions.java b/src/java/org/apache/cassandra/cql3/QueryOptions.java index fb46b9b..7fc0997 100644 --- a/src/java/org/apache/cassandra/cql3/QueryOptions.java +++ b/src/java/org/apache/cassandra/cql3/QueryOptions.java @@ -23,8 +23,9 @@ import java.util.Collections; import java.util.EnumSet; import java.util.List; -import io.netty.buffer.ByteBuf; +import com.google.common.collect.ImmutableList; +import io.netty.buffer.ByteBuf; import org.apache.cassandra.db.ConsistencyLevel; import org.apache.cassandra.service.QueryState; import org.apache.cassandra.service.pager.PagingState; @@ -82,10 +83,43 @@ public abstract class QueryOptions return new DefaultQueryOptions(consistency, values, skipMetadata, new SpecificOptions(pageSize, pagingState, serialConsistency, -1L), 0); } + public static QueryOptions addColumnSpecifications(QueryOptions options, List<ColumnSpecification> columnSpecs) + { + return new OptionsWithColumnSpecifications(options, columnSpecs); + } + public abstract ConsistencyLevel getConsistency(); public abstract List<ByteBuffer> getValues(); public abstract boolean skipMetadata(); + /** + * Tells whether or not this <code>QueryOptions</code> contains the column specifications for the bound variables. + * <p>The column specifications will be present only for prepared statements.</p> + * @return <code>true</code> this <code>QueryOptions</code> contains the column specifications for the bound + * variables, <code>false</code> otherwise. + */ + public boolean hasColumnSpecifications() + { + return false; + } + + /** + * Returns the column specifications for the bound variables (<i>optional operation</i>). + * + * <p>The column specifications will be present only for prepared statements.</p> + * + * <p>Invoke the {@link hasColumnSpecifications} method before invoking this method in order to ensure that this + * <code>QueryOptions</code> contains the column specifications.</p> + * + * @return the option names + * @throws UnsupportedOperationException If this <code>QueryOptions</code> does not contains the column + * specifications. + */ + public ImmutableList<ColumnSpecification> getColumnSpecifications() + { + throw new UnsupportedOperationException(); + } + /** The pageSize for this query. Will be <= 0 if not relevant for the query. */ public int getPageSize() { @@ -169,7 +203,7 @@ public abstract class QueryOptions } } - static abstract class QueryOptionsWrapper extends QueryOptions + static class QueryOptionsWrapper extends QueryOptions { protected final QueryOptions wrapped; @@ -178,6 +212,11 @@ public abstract class QueryOptions this.wrapped = wrapped; } + public List<ByteBuffer> getValues() + { + return this.wrapped.getValues(); + } + public ConsistencyLevel getConsistency() { return wrapped.getConsistency(); @@ -206,6 +245,32 @@ public abstract class QueryOptions } } + /** + * <code>QueryOptions</code> decorator that provides access to the column specifications. + */ + static class OptionsWithColumnSpecifications extends QueryOptionsWrapper + { + private final ImmutableList<ColumnSpecification> columnSpecs; + + OptionsWithColumnSpecifications(QueryOptions wrapped, List<ColumnSpecification> columnSpecs) + { + super(wrapped); + this.columnSpecs = ImmutableList.copyOf(columnSpecs); + } + + @Override + public boolean hasColumnSpecifications() + { + return true; + } + + @Override + public ImmutableList<ColumnSpecification> getColumnSpecifications() + { + return columnSpecs; + } + } + static class OptionsWithNames extends QueryOptionsWrapper { private final List<String> names; @@ -238,6 +303,7 @@ public abstract class QueryOptions return this; } + @Override public List<ByteBuffer> getValues() { assert orderedValues != null; // We should have called prepare first! http://git-wip-us.apache.org/repos/asf/cassandra/blob/416ba6e8/src/java/org/apache/cassandra/transport/messages/BatchMessage.java ---------------------------------------------------------------------- diff --git a/src/java/org/apache/cassandra/transport/messages/BatchMessage.java b/src/java/org/apache/cassandra/transport/messages/BatchMessage.java index 2db380b..d86bb1a 100644 --- a/src/java/org/apache/cassandra/transport/messages/BatchMessage.java +++ b/src/java/org/apache/cassandra/transport/messages/BatchMessage.java @@ -208,7 +208,7 @@ public class BatchMessage extends Message.Request for (int i = 0; i < prepared.size(); i++) { ParsedStatement.Prepared p = prepared.get(i); - batchOptions.forStatement(i).prepare(p.boundNames); + batchOptions.prepareStatement(i, p.boundNames); if (!(p.statement instanceof ModificationStatement)) throw new InvalidRequestException("Invalid statement in batch: only UPDATE, INSERT and DELETE statements are allowed."); http://git-wip-us.apache.org/repos/asf/cassandra/blob/416ba6e8/src/java/org/apache/cassandra/transport/messages/ExecuteMessage.java ---------------------------------------------------------------------- diff --git a/src/java/org/apache/cassandra/transport/messages/ExecuteMessage.java b/src/java/org/apache/cassandra/transport/messages/ExecuteMessage.java index 718595c..11a227c 100644 --- a/src/java/org/apache/cassandra/transport/messages/ExecuteMessage.java +++ b/src/java/org/apache/cassandra/transport/messages/ExecuteMessage.java @@ -136,7 +136,10 @@ public class ExecuteMessage extends Message.Request Tracing.instance.begin("Execute CQL3 prepared query", state.getClientAddress(), builder.build()); } - Message.Response response = handler.processPrepared(statement, state, options, getCustomPayload()); + // Some custom QueryHandlers are interested by the bound names. We provide them this information + // by wrapping the QueryOptions. + QueryOptions queryOptions = QueryOptions.addColumnSpecifications(options, prepared.boundNames); + Message.Response response = handler.processPrepared(statement, state, queryOptions, getCustomPayload()); if (options.skipMetadata() && response instanceof ResultMessage.Rows) ((ResultMessage.Rows)response).result.metadata.setSkipMetadata();
