Separate static conditions in CQL3CasRequest and use name queries when we can
Project: http://git-wip-us.apache.org/repos/asf/cassandra/repo Commit: http://git-wip-us.apache.org/repos/asf/cassandra/commit/578450b9 Tree: http://git-wip-us.apache.org/repos/asf/cassandra/tree/578450b9 Diff: http://git-wip-us.apache.org/repos/asf/cassandra/diff/578450b9 Branch: refs/heads/12060-3.0-v2 Commit: 578450b9bb6b285241448686f402fd262fe56f10 Parents: 28d4e8e Author: Sylvain Lebresne <[email protected]> Authored: Fri Sep 2 10:58:59 2016 +0200 Committer: Sylvain Lebresne <[email protected]> Committed: Mon Sep 12 11:50:21 2016 +0200 ---------------------------------------------------------------------- .../cql3/statements/CQL3CasRequest.java | 101 ++++++++++++------- 1 file changed, 62 insertions(+), 39 deletions(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/cassandra/blob/578450b9/src/java/org/apache/cassandra/cql3/statements/CQL3CasRequest.java ---------------------------------------------------------------------- diff --git a/src/java/org/apache/cassandra/cql3/statements/CQL3CasRequest.java b/src/java/org/apache/cassandra/cql3/statements/CQL3CasRequest.java index f556580..6ce05f3 100644 --- a/src/java/org/apache/cassandra/cql3/statements/CQL3CasRequest.java +++ b/src/java/org/apache/cassandra/cql3/statements/CQL3CasRequest.java @@ -26,10 +26,7 @@ import com.google.common.collect.Multimap; import org.apache.cassandra.config.CFMetaData; import org.apache.cassandra.cql3.*; import org.apache.cassandra.db.*; -import org.apache.cassandra.db.filter.ClusteringIndexSliceFilter; -import org.apache.cassandra.db.filter.ColumnFilter; -import org.apache.cassandra.db.filter.DataLimits; -import org.apache.cassandra.db.filter.RowFilter; +import org.apache.cassandra.db.filter.*; import org.apache.cassandra.db.partitions.FilteredPartition; import org.apache.cassandra.db.partitions.Partition; import org.apache.cassandra.db.partitions.PartitionUpdate; @@ -50,10 +47,13 @@ public class CQL3CasRequest implements CASRequest private final boolean updatesStaticRow; private boolean hasExists; // whether we have an exist or if not exist condition + // Conditions on the static row. We keep it separate from 'conditions' as most things related to the static row are + // special cases anyway. + private RowCondition staticConditions; // We index RowCondition by the clustering of the row they applied to for 2 reasons: - // 1) this allows to keep things sorted to build the ColumnSlice array below + // 1) this allows to keep things sorted to build the read command below // 2) this allows to detect when contradictory conditions are set (not exists with some other conditions on the same row) - private final SortedMap<Clustering, RowCondition> conditions; + private final TreeMap<Clustering, RowCondition> conditions; private final List<RowUpdate> updates = new ArrayList<>(); @@ -80,34 +80,37 @@ public class CQL3CasRequest implements CASRequest public void addNotExist(Clustering clustering) throws InvalidRequestException { - RowCondition previous = conditions.put(clustering, new NotExistCondition(clustering)); - if (previous != null && !(previous instanceof NotExistCondition)) - { - // these should be prevented by the parser, but it doesn't hurt to check - if (previous instanceof ExistCondition) - throw new InvalidRequestException("Cannot mix IF EXISTS and IF NOT EXISTS conditions for the same row"); - else - throw new InvalidRequestException("Cannot mix IF conditions and IF NOT EXISTS for the same row"); - } - hasExists = true; + addExistsCondition(clustering, new NotExistCondition(clustering), true); } public void addExist(Clustering clustering) throws InvalidRequestException { - RowCondition previous = conditions.put(clustering, new ExistCondition(clustering)); - // this should be prevented by the parser, but it doesn't hurt to check - if (previous instanceof NotExistCondition) - throw new InvalidRequestException("Cannot mix IF EXISTS and IF NOT EXISTS conditions for the same row"); + addExistsCondition(clustering, new ExistCondition(clustering), false); + } + + private void addExistsCondition(Clustering clustering, RowCondition condition, boolean isNotExist) + { + assert condition instanceof ExistCondition || condition instanceof NotExistCondition; + RowCondition previous = getConditionsForRow(clustering); + if (previous != null && !(previous.getClass().equals(condition.getClass()))) + { + // these should be prevented by the parser, but it doesn't hurt to check + throw (previous instanceof NotExistCondition || previous instanceof ExistCondition) + ? new InvalidRequestException("Cannot mix IF EXISTS and IF NOT EXISTS conditions for the same row") + : new InvalidRequestException("Cannot mix IF conditions and IF " + (isNotExist ? "NOT " : "") + "EXISTS for the same row"); + } + + setConditionsForRow(clustering, condition); hasExists = true; } public void addConditions(Clustering clustering, Collection<ColumnCondition> conds, QueryOptions options) throws InvalidRequestException { - RowCondition condition = conditions.get(clustering); + RowCondition condition = getConditionsForRow(clustering); if (condition == null) { condition = new ColumnsConditions(clustering); - conditions.put(clustering, condition); + setConditionsForRow(clustering, condition); } else if (!(condition instanceof ColumnsConditions)) { @@ -116,6 +119,25 @@ public class CQL3CasRequest implements CASRequest ((ColumnsConditions)condition).addConditions(conds, options); } + private RowCondition getConditionsForRow(Clustering clustering) + { + return clustering == Clustering.STATIC_CLUSTERING ? staticConditions : conditions.get(clustering); + } + + private void setConditionsForRow(Clustering clustering, RowCondition condition) + { + if (clustering == Clustering.STATIC_CLUSTERING) + { + assert staticConditions == null; + staticConditions = condition; + } + else + { + RowCondition previous = conditions.put(clustering, condition); + assert previous == null; + } + } + private PartitionColumns columnsToRead() { // If all our conditions are columns conditions (IF x = ?), then it's enough to query @@ -137,27 +159,28 @@ public class CQL3CasRequest implements CASRequest public SinglePartitionReadCommand readCommand(int nowInSec) { - assert !conditions.isEmpty(); - Slices.Builder builder = new Slices.Builder(cfm.comparator, conditions.size()); - // We always read CQL rows entirely as on CAS failure we want to be able to distinguish between "row exists - // but all values for which there were conditions are null" and "row doesn't exists", and we can't rely on the - // row marker for that (see #6623) - for (Clustering clustering : conditions.keySet()) - { - if (clustering != Clustering.STATIC_CLUSTERING) - builder.add(Slice.make(clustering)); - // In order to make distinction between non-existing partition and partition without statics on static condition, - // we have to read at least one row (see #12060). - else if (conditions.size() == 1) - builder.add(Slice.ALL); - } - - ClusteringIndexSliceFilter filter = new ClusteringIndexSliceFilter(builder.build(), false); - return SinglePartitionReadCommand.create(false, cfm, nowInSec, ColumnFilter.selection(columnsToRead()), RowFilter.NONE, DataLimits.cqlLimits(conditions.size()), key, filter); + assert staticConditions != null || !conditions.isEmpty(); + + // With only a static condition, we still want to make the distinction between a non-existing partition and one + // that exists (has some live data) but has not static content. So we query the first live row of the partition. + if (conditions.isEmpty()) + return SinglePartitionReadCommand.create(cfm, + nowInSec, + ColumnFilter.selection(columnsToRead()), + RowFilter.NONE, + DataLimits.cqlLimits(1), + key, + new ClusteringIndexSliceFilter(Slices.ALL, false)); + + ClusteringIndexNamesFilter filter = new ClusteringIndexNamesFilter(conditions.navigableKeySet(), false); + return SinglePartitionReadCommand.create(cfm, nowInSec, key, ColumnFilter.selection(columnsToRead()), filter); } public boolean appliesTo(FilteredPartition current) throws InvalidRequestException { + if (staticConditions != null && !staticConditions.appliesTo(current)) + return false; + for (RowCondition condition : conditions.values()) { if (!condition.appliesTo(current))
