You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@cassandra.apache.org by al...@apache.org on 2013/10/28 11:39:15 UTC
git commit: Provide hooks around CQL2/CQL3 statement execution
Updated Branches:
refs/heads/cassandra-2.0 d280e970e -> f1c052434
Provide hooks around CQL2/CQL3 statement execution
patch by Sam Tunnicliffe; reviewed by Aleksey Yeschenko for
CASSANDRA-6252
Project: http://git-wip-us.apache.org/repos/asf/cassandra/repo
Commit: http://git-wip-us.apache.org/repos/asf/cassandra/commit/f1c05243
Tree: http://git-wip-us.apache.org/repos/asf/cassandra/tree/f1c05243
Diff: http://git-wip-us.apache.org/repos/asf/cassandra/diff/f1c05243
Branch: refs/heads/cassandra-2.0
Commit: f1c0524347190dc28db21c3b27e2c05fb36dae43
Parents: d280e97
Author: Aleksey Yeschenko <al...@apache.org>
Authored: Mon Oct 28 13:38:22 2013 +0300
Committer: Aleksey Yeschenko <al...@apache.org>
Committed: Mon Oct 28 13:38:22 2013 +0300
----------------------------------------------------------------------
CHANGES.txt | 1 +
.../apache/cassandra/cql/QueryProcessor.java | 52 ++++++++++-
.../cassandra/cql/hooks/ExecutionContext.java | 48 ++++++++++
.../cql/hooks/PostPreparationHook.java | 38 ++++++++
.../cassandra/cql/hooks/PreExecutionHook.java | 46 ++++++++++
.../cassandra/cql/hooks/PreparationContext.java | 39 ++++++++
.../apache/cassandra/cql3/QueryProcessor.java | 97 ++++++++++++++++++--
.../cql3/hooks/BatchExecutionContext.java | 52 +++++++++++
.../cassandra/cql3/hooks/ExecutionContext.java | 47 ++++++++++
.../cassandra/cql3/hooks/PostExecutionHook.java | 52 +++++++++++
.../cql3/hooks/PostPreparationHook.java | 38 ++++++++
.../cassandra/cql3/hooks/PreExecutionHook.java | 62 +++++++++++++
.../cql3/hooks/PreparationContext.java | 41 +++++++++
.../transport/messages/BatchMessage.java | 2 +-
14 files changed, 603 insertions(+), 12 deletions(-)
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/cassandra/blob/f1c05243/CHANGES.txt
----------------------------------------------------------------------
diff --git a/CHANGES.txt b/CHANGES.txt
index 7e6ba95..55da5d3 100644
--- a/CHANGES.txt
+++ b/CHANGES.txt
@@ -3,6 +3,7 @@
* cqlsh: fix LIST USERS output (CASSANDRA-6242)
* Add IRequestSink interface (CASSANDRA-6248)
* Update memtable size while flushing (CASSANDRA-6249)
+ * Provide hooks around CQL2/CQL3 statement execution (CASSANDRA-6252)
2.0.2
http://git-wip-us.apache.org/repos/asf/cassandra/blob/f1c05243/src/java/org/apache/cassandra/cql/QueryProcessor.java
----------------------------------------------------------------------
diff --git a/src/java/org/apache/cassandra/cql/QueryProcessor.java b/src/java/org/apache/cassandra/cql/QueryProcessor.java
index ef804c2..f54f5ef 100644
--- a/src/java/org/apache/cassandra/cql/QueryProcessor.java
+++ b/src/java/org/apache/cassandra/cql/QueryProcessor.java
@@ -20,15 +20,19 @@ package org.apache.cassandra.cql;
import java.io.IOException;
import java.nio.ByteBuffer;
import java.util.*;
+import java.util.concurrent.CopyOnWriteArrayList;
import java.util.concurrent.TimeoutException;
-import org.apache.cassandra.serializers.MarshalException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.apache.cassandra.auth.Permission;
import org.apache.cassandra.config.*;
import org.apache.cassandra.cli.CliUtils;
+import org.apache.cassandra.cql.hooks.ExecutionContext;
+import org.apache.cassandra.cql.hooks.PostPreparationHook;
+import org.apache.cassandra.cql.hooks.PreExecutionHook;
+import org.apache.cassandra.cql.hooks.PreparationContext;
import org.apache.cassandra.db.CounterColumn;
import org.apache.cassandra.db.*;
import org.apache.cassandra.db.context.CounterContext;
@@ -39,6 +43,7 @@ import org.apache.cassandra.db.marshal.AsciiType;
import org.apache.cassandra.db.marshal.TypeParser;
import org.apache.cassandra.dht.*;
import org.apache.cassandra.exceptions.*;
+import org.apache.cassandra.serializers.MarshalException;
import org.apache.cassandra.service.StorageProxy;
import org.apache.cassandra.service.StorageService;
import org.apache.cassandra.service.MigrationManager;
@@ -70,6 +75,29 @@ public class QueryProcessor
public static final String DEFAULT_KEY_NAME = CFMetaData.DEFAULT_KEY_ALIAS.toUpperCase();
+ private static final List<PreExecutionHook> preExecutionHooks = new CopyOnWriteArrayList<>();
+ private static final List<PostPreparationHook> postPreparationHooks = new CopyOnWriteArrayList<>();
+
+ public static void addPreExecutionHook(PreExecutionHook hook)
+ {
+ preExecutionHooks.add(hook);
+ }
+
+ public static void removePreExecutionHook(PreExecutionHook hook)
+ {
+ preExecutionHooks.remove(hook);
+ }
+
+ public static void addPostPreparationHook(PostPreparationHook hook)
+ {
+ postPreparationHooks.add(hook);
+ }
+
+ public static void removePostPreparationHook(PostPreparationHook hook)
+ {
+ postPreparationHooks.remove(hook);
+ }
+
private static List<org.apache.cassandra.db.Row> getSlice(CFMetaData metadata, SelectStatement select, List<ByteBuffer> variables, long now)
throws InvalidRequestException, ReadTimeoutException, UnavailableException, IsBootstrappingException
{
@@ -337,10 +365,12 @@ public class QueryProcessor
throw new InvalidRequestException("range finish must come after start in traversal order");
}
- public static CqlResult processStatement(CQLStatement statement,ThriftClientState clientState, List<ByteBuffer> variables )
+ public static CqlResult processStatement(CQLStatement statement, ExecutionContext context)
throws RequestExecutionException, RequestValidationException
{
String keyspace = null;
+ ThriftClientState clientState = context.clientState;
+ List<ByteBuffer> variables = context.variables;
// Some statements won't have (or don't need) a keyspace (think USE, or CREATE).
if (statement.type != StatementType.SELECT && StatementType.REQUIRES_KEYSPACE.contains(statement.type))
@@ -348,6 +378,10 @@ public class QueryProcessor
CqlResult result = new CqlResult();
+ if (!preExecutionHooks.isEmpty())
+ for (PreExecutionHook hook : preExecutionHooks)
+ statement = hook.processStatement(statement, context);
+
if (logger.isDebugEnabled()) logger.debug("CQL statement type: {}", statement.type.toString());
CFMetaData metadata;
switch (statement.type)
@@ -761,11 +795,12 @@ public class QueryProcessor
throws RequestValidationException, RequestExecutionException
{
logger.trace("CQL QUERY: {}", queryString);
- return processStatement(getStatement(queryString), clientState, new ArrayList<ByteBuffer>(0));
+ return processStatement(getStatement(queryString),
+ new ExecutionContext(clientState, queryString, Collections.<ByteBuffer>emptyList()));
}
public static CqlPreparedResult prepare(String queryString, ThriftClientState clientState)
- throws SyntaxException
+ throws RequestValidationException
{
logger.trace("CQL QUERY: {}", queryString);
@@ -778,6 +813,13 @@ public class QueryProcessor
statementId,
statement.boundTerms));
+ if (!postPreparationHooks.isEmpty())
+ {
+ PreparationContext context = new PreparationContext(clientState, queryString, statement);
+ for (PostPreparationHook hook : postPreparationHooks)
+ hook.processStatement(statement, context);
+ }
+
return new CqlPreparedResult(statementId, statement.boundTerms);
}
@@ -799,7 +841,7 @@ public class QueryProcessor
logger.trace("[{}] '{}'", i+1, variables.get(i));
}
- return processStatement(statement, clientState, variables);
+ return processStatement(statement, new ExecutionContext(clientState, null, variables));
}
private static final int makeStatementId(String cql)
http://git-wip-us.apache.org/repos/asf/cassandra/blob/f1c05243/src/java/org/apache/cassandra/cql/hooks/ExecutionContext.java
----------------------------------------------------------------------
diff --git a/src/java/org/apache/cassandra/cql/hooks/ExecutionContext.java b/src/java/org/apache/cassandra/cql/hooks/ExecutionContext.java
new file mode 100644
index 0000000..deb785c
--- /dev/null
+++ b/src/java/org/apache/cassandra/cql/hooks/ExecutionContext.java
@@ -0,0 +1,48 @@
+/*
+ * 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.cql.hooks;
+
+import java.nio.ByteBuffer;
+import java.util.List;
+
+import com.google.common.base.Optional;
+
+import org.apache.cassandra.thrift.ThriftClientState;
+
+/**
+ * Contextual information about the execution of a CQLStatement.
+ * Used by {@link org.apache.cassandra.cql.hooks.PreExecutionHook}
+ *
+ * The CQL string representing the statement being executed is optional
+ * and is not present for prepared statements. Contexts created for the
+ * execution of regular (i.e. non-prepared) statements will always
+ * contain a CQL string.
+ */
+public class ExecutionContext
+{
+ public final ThriftClientState clientState;
+ public final Optional<String> queryString;
+ public final List<ByteBuffer> variables;
+
+ public ExecutionContext(ThriftClientState clientState, String queryString, List<ByteBuffer> variables)
+ {
+ this.clientState = clientState;
+ this.queryString = Optional.fromNullable(queryString);
+ this.variables = variables;
+ }
+}
http://git-wip-us.apache.org/repos/asf/cassandra/blob/f1c05243/src/java/org/apache/cassandra/cql/hooks/PostPreparationHook.java
----------------------------------------------------------------------
diff --git a/src/java/org/apache/cassandra/cql/hooks/PostPreparationHook.java b/src/java/org/apache/cassandra/cql/hooks/PostPreparationHook.java
new file mode 100644
index 0000000..1de9c70
--- /dev/null
+++ b/src/java/org/apache/cassandra/cql/hooks/PostPreparationHook.java
@@ -0,0 +1,38 @@
+/*
+ * 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.cql.hooks;
+
+import org.apache.cassandra.cql.CQLStatement;
+import org.apache.cassandra.exceptions.RequestValidationException;
+
+/**
+ * Run directly after a CQL Statement is prepared in
+ * {@link org.apache.cassandra.cql.QueryProcessor}.
+ */
+public interface PostPreparationHook
+{
+ /**
+ * Called in QueryProcessor, once a CQL statement has been prepared.
+ *
+ * @param statement the statement to perform additional processing on
+ * @param context preparation context containing additional info
+ * about the operation and statement
+ * @throws RequestValidationException
+ */
+ void processStatement(CQLStatement statement, PreparationContext context) throws RequestValidationException;
+}
http://git-wip-us.apache.org/repos/asf/cassandra/blob/f1c05243/src/java/org/apache/cassandra/cql/hooks/PreExecutionHook.java
----------------------------------------------------------------------
diff --git a/src/java/org/apache/cassandra/cql/hooks/PreExecutionHook.java b/src/java/org/apache/cassandra/cql/hooks/PreExecutionHook.java
new file mode 100644
index 0000000..29ed38e
--- /dev/null
+++ b/src/java/org/apache/cassandra/cql/hooks/PreExecutionHook.java
@@ -0,0 +1,46 @@
+/*
+ * 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.cql.hooks;
+
+import org.apache.cassandra.cql.CQLStatement;
+import org.apache.cassandra.exceptions.RequestExecutionException;
+import org.apache.cassandra.exceptions.RequestValidationException;
+
+/**
+ * Run before the CQL Statement is executed in
+ * {@link org.apache.cassandra.cql.QueryProcessor}. The CQLStatement
+ * returned from the processStatement method is what is actually executed
+ * by the QueryProcessor.
+ */
+public interface PreExecutionHook
+{
+ /**
+ * Perform pre-processing on a CQL statement prior to it being
+ * executed by the QueryProcessor. If required, implementations
+ * may modify the statement as the returned instance is what
+ * is actually executed.
+ *
+ * @param statement the statement to perform pre-processing on
+ * @param context execution context containing additional info
+ * about the operation and statement
+ * @return the actual statement that will be executed, possibly
+ * a modification of the initial statement
+ * @throws RequestExecutionException, RequestValidationException
+ */
+ CQLStatement processStatement(CQLStatement statement, ExecutionContext context) throws RequestExecutionException, RequestValidationException;
+}
http://git-wip-us.apache.org/repos/asf/cassandra/blob/f1c05243/src/java/org/apache/cassandra/cql/hooks/PreparationContext.java
----------------------------------------------------------------------
diff --git a/src/java/org/apache/cassandra/cql/hooks/PreparationContext.java b/src/java/org/apache/cassandra/cql/hooks/PreparationContext.java
new file mode 100644
index 0000000..00cce78
--- /dev/null
+++ b/src/java/org/apache/cassandra/cql/hooks/PreparationContext.java
@@ -0,0 +1,39 @@
+/*
+ * 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.cql.hooks;
+
+import org.apache.cassandra.cql.CQLStatement;
+import org.apache.cassandra.thrift.ThriftClientState;
+
+/**
+ * Contextual information about the preparation of a CQLStatement.
+ * Used by {@link org.apache.cassandra.cql.hooks.PostPreparationHook}
+ */
+public class PreparationContext
+{
+ public final ThriftClientState clientState;
+ public final String queryString;
+ public final CQLStatement statement;
+
+ public PreparationContext(ThriftClientState clientState, String queryString, CQLStatement statement)
+ {
+ this.clientState = clientState;
+ this.queryString = queryString;
+ this.statement = statement;
+ }
+}
http://git-wip-us.apache.org/repos/asf/cassandra/blob/f1c05243/src/java/org/apache/cassandra/cql3/QueryProcessor.java
----------------------------------------------------------------------
diff --git a/src/java/org/apache/cassandra/cql3/QueryProcessor.java b/src/java/org/apache/cassandra/cql3/QueryProcessor.java
index 7ddff96..ec8b379 100644
--- a/src/java/org/apache/cassandra/cql3/QueryProcessor.java
+++ b/src/java/org/apache/cassandra/cql3/QueryProcessor.java
@@ -19,6 +19,7 @@ package org.apache.cassandra.cql3;
import java.nio.ByteBuffer;
import java.util.*;
+import java.util.concurrent.CopyOnWriteArrayList;
import com.google.common.primitives.Ints;
@@ -29,6 +30,7 @@ import org.github.jamm.MemoryMeter;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
+import org.apache.cassandra.cql3.hooks.*;
import org.apache.cassandra.cql3.statements.*;
import org.apache.cassandra.transport.messages.ResultMessage;
import org.apache.cassandra.db.*;
@@ -76,6 +78,39 @@ public class QueryProcessor
.weigher(thriftMemoryUsageWeigher)
.build();
+ private static final List<PreExecutionHook> preExecutionHooks = new CopyOnWriteArrayList<>();
+ private static final List<PostExecutionHook> postExecutionHooks = new CopyOnWriteArrayList<>();
+ private static final List<PostPreparationHook> postPreparationHooks = new CopyOnWriteArrayList<>();
+
+ public static void addPreExecutionHook(PreExecutionHook hook)
+ {
+ preExecutionHooks.add(hook);
+ }
+
+ public static void removePreExecutionHook(PreExecutionHook hook)
+ {
+ preExecutionHooks.remove(hook);
+ }
+
+ public static void addPostExecutionHook(PostExecutionHook hook)
+ {
+ postExecutionHooks.add(hook);
+ }
+
+ public static void removePostExecutionHook(PostExecutionHook hook)
+ {
+ postExecutionHooks.remove(hook);
+ }
+
+ public static void addPostPreparationHook(PostPreparationHook hook)
+ {
+ postPreparationHooks.add(hook);
+ }
+
+ public static void removePostPreparationHook(PostPreparationHook hook)
+ {
+ postPreparationHooks.remove(hook);
+ }
public static CQLStatement getPrepared(MD5Digest id)
{
@@ -119,17 +154,38 @@ public class QueryProcessor
throw new InvalidRequestException("Invalid empty value for clustering column of COMPACT TABLE");
}
- private static ResultMessage processStatement(CQLStatement statement, QueryState queryState, QueryOptions options)
+ private static ResultMessage processStatement(CQLStatement statement,
+ QueryState queryState,
+ QueryOptions options,
+ String queryString)
throws RequestExecutionException, RequestValidationException
{
logger.trace("Process {} @CL.{}", statement, options.getConsistency());
ClientState clientState = queryState.getClientState();
statement.checkAccess(clientState);
statement.validate(clientState);
- ResultMessage result = statement.execute(queryState, options);
+
+ ResultMessage result = preExecutionHooks.isEmpty() && postExecutionHooks.isEmpty()
+ ? statement.execute(queryState, options)
+ : executeWithHooks(statement, new ExecutionContext(queryState, queryString, options));
+
return result == null ? new ResultMessage.Void() : result;
}
+ private static ResultMessage executeWithHooks(CQLStatement statement, ExecutionContext context)
+ throws RequestExecutionException, RequestValidationException
+ {
+ for (PreExecutionHook hook : preExecutionHooks)
+ statement = hook.processStatement(statement, context);
+
+ ResultMessage result = statement.execute(context.queryState, context.queryOptions);
+
+ for (PostExecutionHook hook : postExecutionHooks)
+ hook.processStatement(statement, context);
+
+ return result;
+ }
+
public static ResultMessage process(String queryString, ConsistencyLevel cl, QueryState queryState)
throws RequestExecutionException, RequestValidationException
{
@@ -142,7 +198,8 @@ public class QueryProcessor
CQLStatement prepared = getStatement(queryString, queryState.getClientState()).statement;
if (prepared.getBoundsTerms() != options.getValues().size())
throw new InvalidRequestException("Invalid amount of bind variables");
- return processStatement(prepared, queryState, options);
+
+ return processStatement(prepared, queryState, options, queryString);
}
public static CQLStatement parseStatement(String queryStr, QueryState queryState) throws RequestValidationException
@@ -211,6 +268,13 @@ public class QueryProcessor
ParsedStatement.Prepared prepared = getStatement(queryString, clientState);
ResultMessage.Prepared msg = storePreparedStatement(queryString, clientState.getRawKeyspace(), prepared, forThrift);
+ if (!postPreparationHooks.isEmpty())
+ {
+ PreparationContext context = new PreparationContext(clientState, queryString, prepared.boundNames);
+ for (PostPreparationHook hook : postPreparationHooks)
+ hook.processStatement(prepared.statement, context);
+ }
+
assert prepared.statement.getBoundsTerms() == prepared.boundNames.size();
return msg;
}
@@ -267,19 +331,40 @@ public class QueryProcessor
logger.trace("[{}] '{}'", i+1, variables.get(i));
}
- return processStatement(statement, queryState, options);
+ return processStatement(statement, queryState, options, null);
}
- public static ResultMessage processBatch(BatchStatement batch, ConsistencyLevel cl, QueryState queryState, List<List<ByteBuffer>> variables)
+ public static ResultMessage processBatch(BatchStatement batch,
+ ConsistencyLevel cl,
+ QueryState queryState,
+ List<List<ByteBuffer>> variables,
+ List<Object> queryOrIdList)
throws RequestExecutionException, RequestValidationException
{
ClientState clientState = queryState.getClientState();
batch.checkAccess(clientState);
batch.validate(clientState);
- batch.executeWithPerStatementVariables(cl, queryState, variables);
+
+ if (preExecutionHooks.isEmpty() && postExecutionHooks.isEmpty())
+ batch.executeWithPerStatementVariables(cl, queryState, variables);
+ else
+ executeBatchWithHooks(batch, cl, new BatchExecutionContext(queryState, queryOrIdList, variables));
+
return new ResultMessage.Void();
}
+ private static void executeBatchWithHooks(BatchStatement batch, ConsistencyLevel cl, BatchExecutionContext context)
+ throws RequestExecutionException, RequestValidationException
+ {
+ for (PreExecutionHook hook : preExecutionHooks)
+ batch = hook.processBatch(batch, context);
+
+ batch.executeWithPerStatementVariables(cl, context.queryState, context.variables);
+
+ for (PostExecutionHook hook : postExecutionHooks)
+ hook.processBatch(batch, context);
+ }
+
private static ParsedStatement.Prepared getStatement(String queryStr, ClientState clientState)
throws RequestValidationException
{
http://git-wip-us.apache.org/repos/asf/cassandra/blob/f1c05243/src/java/org/apache/cassandra/cql3/hooks/BatchExecutionContext.java
----------------------------------------------------------------------
diff --git a/src/java/org/apache/cassandra/cql3/hooks/BatchExecutionContext.java b/src/java/org/apache/cassandra/cql3/hooks/BatchExecutionContext.java
new file mode 100644
index 0000000..8c81bea
--- /dev/null
+++ b/src/java/org/apache/cassandra/cql3/hooks/BatchExecutionContext.java
@@ -0,0 +1,52 @@
+/*
+ * 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.hooks;
+
+import java.nio.ByteBuffer;
+import java.util.List;
+
+import org.apache.cassandra.service.QueryState;
+
+/**
+ * Contextual information about the execution of a CQL Batch.
+ * Used by {@link org.apache.cassandra.cql3.hooks.PreExecutionHook} and
+ * {@link org.apache.cassandra.cql3.hooks.PostExecutionHook}
+ *
+ * The {@code queryOrIdList} field, provides a list of objects which
+ * may be used to identify the individual statements in the batch.
+ * Currently, these objects will be one of two types (and the list may
+ * contain a mixture of the two). A {@code String} indicates the statement is
+ * a regular (i.e. non-prepared) statement, and is in fact the CQL
+ * string for the statement. An {@code MD5Digest} object indicates a prepared
+ * statement & may be used to retrieve the corresponding CQLStatement
+ * using {@link org.apache.cassandra.cql3.QueryProcessor#getPrepared(org.apache.cassandra.utils.MD5Digest) QueryProcessor.getPrepared()}
+ *
+ */
+public class BatchExecutionContext
+{
+ public final QueryState queryState;
+ public final List<Object> queryOrIdList;
+ public final List<List<ByteBuffer>> variables;
+
+ public BatchExecutionContext(QueryState queryState, List<Object> queryOrIdList, List<List<ByteBuffer>> variables)
+ {
+ this.queryState = queryState;
+ this.queryOrIdList = queryOrIdList;
+ this.variables = variables;
+ }
+}
http://git-wip-us.apache.org/repos/asf/cassandra/blob/f1c05243/src/java/org/apache/cassandra/cql3/hooks/ExecutionContext.java
----------------------------------------------------------------------
diff --git a/src/java/org/apache/cassandra/cql3/hooks/ExecutionContext.java b/src/java/org/apache/cassandra/cql3/hooks/ExecutionContext.java
new file mode 100644
index 0000000..56d56c8
--- /dev/null
+++ b/src/java/org/apache/cassandra/cql3/hooks/ExecutionContext.java
@@ -0,0 +1,47 @@
+/*
+ * 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.hooks;
+
+import com.google.common.base.Optional;
+
+import org.apache.cassandra.cql3.QueryOptions;
+import org.apache.cassandra.service.QueryState;
+
+/**
+ * Contextual information about the execution of a CQLStatement.
+ * Used by {@link org.apache.cassandra.cql3.hooks.PreExecutionHook} and
+ * {@link org.apache.cassandra.cql3.hooks.PostExecutionHook}
+ *
+ * The CQL string representing the statement being executed is optional
+ * and is not present for prepared statements. Contexts created for the
+ * execution of regular (i.e. non-prepared) statements will always
+ * contain a CQL string.
+ */
+public class ExecutionContext
+{
+ public final QueryState queryState;
+ public final Optional<String> queryString;
+ public final QueryOptions queryOptions;
+
+ public ExecutionContext(QueryState queryState, String queryString, QueryOptions queryOptions)
+ {
+ this.queryState = queryState;
+ this.queryString = Optional.fromNullable(queryString);
+ this.queryOptions = queryOptions;
+ }
+}
http://git-wip-us.apache.org/repos/asf/cassandra/blob/f1c05243/src/java/org/apache/cassandra/cql3/hooks/PostExecutionHook.java
----------------------------------------------------------------------
diff --git a/src/java/org/apache/cassandra/cql3/hooks/PostExecutionHook.java b/src/java/org/apache/cassandra/cql3/hooks/PostExecutionHook.java
new file mode 100644
index 0000000..96c742f
--- /dev/null
+++ b/src/java/org/apache/cassandra/cql3/hooks/PostExecutionHook.java
@@ -0,0 +1,52 @@
+/*
+ * 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.hooks;
+
+import org.apache.cassandra.cql3.CQLStatement;
+import org.apache.cassandra.cql3.statements.BatchStatement;
+import org.apache.cassandra.exceptions.RequestExecutionException;
+import org.apache.cassandra.exceptions.RequestValidationException;
+
+/**
+ * Run after the CQL Statement is executed in
+ * {@link org.apache.cassandra.cql3.QueryProcessor}.
+ */
+public interface PostExecutionHook
+{
+ /**
+ * Perform post-processing on a CQL statement directly after
+ * it being executed by the QueryProcessor.
+ *
+ * @param statement the statement to perform post-processing on
+ * @param context execution context containing additional info
+ * about the operation and statement
+ * @throws RequestExecutionException, RequestValidationException
+ */
+ void processStatement(CQLStatement statement, ExecutionContext context) throws RequestExecutionException, RequestValidationException;
+
+ /**
+ * Perform post-processing on a CQL batch directly after
+ * it being executed by the QueryProcessor.
+ *
+ * @param batch the CQL batch to perform post-processing on
+ * @param context execution context containing additional info
+ * about the operation and batch
+ * @throws RequestExecutionException, RequestValidationException
+ */
+ void processBatch(BatchStatement batch, BatchExecutionContext context) throws RequestExecutionException, RequestValidationException;
+}
http://git-wip-us.apache.org/repos/asf/cassandra/blob/f1c05243/src/java/org/apache/cassandra/cql3/hooks/PostPreparationHook.java
----------------------------------------------------------------------
diff --git a/src/java/org/apache/cassandra/cql3/hooks/PostPreparationHook.java b/src/java/org/apache/cassandra/cql3/hooks/PostPreparationHook.java
new file mode 100644
index 0000000..c2cf88a
--- /dev/null
+++ b/src/java/org/apache/cassandra/cql3/hooks/PostPreparationHook.java
@@ -0,0 +1,38 @@
+/*
+ * 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.hooks;
+
+import org.apache.cassandra.cql3.CQLStatement;
+import org.apache.cassandra.exceptions.RequestValidationException;
+
+/**
+ * Run directly after a CQL Statement is prepared in
+ * {@link org.apache.cassandra.cql3.QueryProcessor}.
+ */
+public interface PostPreparationHook
+{
+ /**
+ * Called in QueryProcessor, once a CQL statement has been prepared.
+ *
+ * @param statement the statement to perform additional processing on
+ * @param context preparation context containing additional info
+ * about the operation and statement
+ * @throws RequestValidationException
+ */
+ void processStatement(CQLStatement statement, PreparationContext context) throws RequestValidationException;
+}
http://git-wip-us.apache.org/repos/asf/cassandra/blob/f1c05243/src/java/org/apache/cassandra/cql3/hooks/PreExecutionHook.java
----------------------------------------------------------------------
diff --git a/src/java/org/apache/cassandra/cql3/hooks/PreExecutionHook.java b/src/java/org/apache/cassandra/cql3/hooks/PreExecutionHook.java
new file mode 100644
index 0000000..3a8182f
--- /dev/null
+++ b/src/java/org/apache/cassandra/cql3/hooks/PreExecutionHook.java
@@ -0,0 +1,62 @@
+/*
+ * 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.hooks;
+
+import org.apache.cassandra.cql3.CQLStatement;
+import org.apache.cassandra.cql3.statements.BatchStatement;
+import org.apache.cassandra.exceptions.RequestExecutionException;
+import org.apache.cassandra.exceptions.RequestValidationException;
+
+/**
+ * Run before the CQL Statement is executed in
+ * {@link org.apache.cassandra.cql3.QueryProcessor}. The CQLStatement
+ * returned from the process* methods is what is actually executed
+ * by the QueryProcessor.
+ */
+public interface PreExecutionHook
+{
+ /**
+ * Perform pre-processing on a CQL statement prior to it being
+ * executed by the QueryProcessor. If required, implementations
+ * may modify the statement as the returned instance is what
+ * is actually executed.
+ *
+ * @param statement the statement to perform pre-processing on
+ * @param context execution context containing additional info
+ * about the operation and statement
+ * @return the actual statement that will be executed, potentially
+ * a modification of the initial statement
+ * @throws RequestExecutionException, RequestValidationException
+ */
+ CQLStatement processStatement(CQLStatement statement, ExecutionContext context) throws RequestExecutionException, RequestValidationException;
+
+ /**
+ * Perform pre-processing on a CQL batch prior to it being
+ * executed by the QueryProcessor. If required, implementations
+ * may modify the batch & its component statements as the returned
+ * instance is what is actually executed.
+ *
+ * @param batch the CQL batch to perform pre-processing on
+ * @param context execution context containing additional info
+ * about the operation and batch
+ * @return the actual batch that will be executed, potentially
+ * a modification of the initial batch
+ * @throws RequestExecutionException, RequestValidationException
+ */
+ BatchStatement processBatch(BatchStatement batch, BatchExecutionContext context) throws RequestExecutionException, RequestValidationException;
+}
http://git-wip-us.apache.org/repos/asf/cassandra/blob/f1c05243/src/java/org/apache/cassandra/cql3/hooks/PreparationContext.java
----------------------------------------------------------------------
diff --git a/src/java/org/apache/cassandra/cql3/hooks/PreparationContext.java b/src/java/org/apache/cassandra/cql3/hooks/PreparationContext.java
new file mode 100644
index 0000000..fda0b7d
--- /dev/null
+++ b/src/java/org/apache/cassandra/cql3/hooks/PreparationContext.java
@@ -0,0 +1,41 @@
+/*
+ * 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.hooks;
+
+import java.util.List;
+
+import org.apache.cassandra.cql3.ColumnSpecification;
+import org.apache.cassandra.service.ClientState;
+
+/**
+ * Contextual information about the preparation of a CQLStatement.
+ * Used by {@link org.apache.cassandra.cql3.hooks.PostPreparationHook}
+ */
+public class PreparationContext
+{
+ public final ClientState clientState;
+ public final String queryString;
+ public final List<ColumnSpecification> boundNames;
+
+ public PreparationContext(ClientState clientState, String queryString, List<ColumnSpecification> boundNames)
+ {
+ this.clientState = clientState;
+ this.queryString = queryString;
+ this.boundNames = boundNames;
+ }
+}
http://git-wip-us.apache.org/repos/asf/cassandra/blob/f1c05243/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 0f63018..bd95ef3 100644
--- a/src/java/org/apache/cassandra/transport/messages/BatchMessage.java
+++ b/src/java/org/apache/cassandra/transport/messages/BatchMessage.java
@@ -204,7 +204,7 @@ public class BatchMessage extends Message.Request
// Note: It's ok at this point to pass a bogus value for the number of bound terms in the BatchState ctor
// (and no value would be really correct, so we prefer passing a clearly wrong one).
BatchStatement batch = new BatchStatement(-1, type, statements, Attributes.none());
- Message.Response response = QueryProcessor.processBatch(batch, consistency, state, values);
+ Message.Response response = QueryProcessor.processBatch(batch, consistency, state, values, queryOrIdList);
if (tracingId != null)
response.setTracingId(tracingId);