Merge branch cassandra-2.2 into cassandra-3.0

Project: http://git-wip-us.apache.org/repos/asf/cassandra/repo
Commit: http://git-wip-us.apache.org/repos/asf/cassandra/commit/1aa97e30
Tree: http://git-wip-us.apache.org/repos/asf/cassandra/tree/1aa97e30
Diff: http://git-wip-us.apache.org/repos/asf/cassandra/diff/1aa97e30

Branch: refs/heads/trunk
Commit: 1aa97e30ca075ad3c350d1b49aee94dffc62db19
Parents: 62f94b7 90fc896
Author: Benjamin Lerer <b.le...@gmail.com>
Authored: Thu Feb 11 10:40:50 2016 +0100
Committer: Benjamin Lerer <b.le...@gmail.com>
Committed: Thu Feb 11 10:42:17 2016 +0100

----------------------------------------------------------------------
 CHANGES.txt                                     |    1 +
 .../cql3/restrictions/AbstractRestriction.java  |   14 +
 .../ForwardingPrimaryKeyRestrictions.java       |    3 +-
 .../restrictions/MultiColumnRestriction.java    |   76 +-
 .../restrictions/PrimaryKeyRestrictionSet.java  |   22 +-
 .../cql3/restrictions/Restriction.java          |    4 +-
 .../cql3/restrictions/RestrictionSet.java       |    4 +-
 .../restrictions/SingleColumnRestriction.java   |    9 +-
 .../cql3/restrictions/TokenRestriction.java     |    2 +-
 .../org/apache/cassandra/db/MultiCBuilder.java  |   67 +-
 .../PrimaryKeyRestrictionSetTest.java           | 1174 +++++++++++++++---
 .../SelectMultiColumnRelationTest.java          |  859 ++++++++++++-
 12 files changed, 2025 insertions(+), 210 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/cassandra/blob/1aa97e30/CHANGES.txt
----------------------------------------------------------------------
diff --cc CHANGES.txt
index 188cdd6,5674b9d..bca5fcd
--- a/CHANGES.txt
+++ b/CHANGES.txt
@@@ -1,12 -1,5 +1,13 @@@
 -2.2.6
 +3.0.4
 + * AssertionError when listing sstable files on inconsistent disk state 
(CASSANDRA-11156)
 + * Fix wrong rack counting and invalid conditions check for TokenAllocation
 +   (CASSANDRA-11139)
 + * Avoid creating empty hint files (CASSANDRA-11090)
 + * Fix leak detection strong reference loop using weak reference 
(CASSANDRA-11120)
 + * Configurie BatchlogManager to stop delayed tasks on shutdown 
(CASSANDRA-11062)
 + * Hadoop integration is incompatible with Cassandra Driver 3.0.0 
(CASSANDRA-11001)
 +Merged from 2.2.6:
+  * Fix SELECT on tuple relations for mixed ASC/DESC clustering order 
(CASSANDRA-7281)
   * (cqlsh) Support utf-8/cp65001 encoding on Windows (CASSANDRA-11030)
   * Fix paging on DISTINCT queries repeats result when first row in partition 
changes (CASSANDRA-10010)
  Merged from 2.1:

http://git-wip-us.apache.org/repos/asf/cassandra/blob/1aa97e30/src/java/org/apache/cassandra/cql3/restrictions/AbstractRestriction.java
----------------------------------------------------------------------
diff --cc 
src/java/org/apache/cassandra/cql3/restrictions/AbstractRestriction.java
index 023c2ac,dac7203..df04331
--- a/src/java/org/apache/cassandra/cql3/restrictions/AbstractRestriction.java
+++ b/src/java/org/apache/cassandra/cql3/restrictions/AbstractRestriction.java
@@@ -17,10 -17,19 +17,12 @@@
   */
  package org.apache.cassandra.cql3.restrictions;
  
 -import java.nio.ByteBuffer;
 -
 -import org.apache.cassandra.config.ColumnDefinition;
 -
 -import org.apache.cassandra.cql3.ColumnSpecification;
  import org.apache.cassandra.cql3.QueryOptions;
  import org.apache.cassandra.cql3.statements.Bound;
 -import org.apache.cassandra.db.composites.CompositesBuilder;
 -import org.apache.cassandra.exceptions.InvalidRequestException;
 -import static 
org.apache.cassandra.cql3.statements.RequestValidations.checkBindValueSet;
 -import static 
org.apache.cassandra.cql3.statements.RequestValidations.checkFalse;
 -import static 
org.apache.cassandra.cql3.statements.RequestValidations.checkNotNull;
 +import org.apache.cassandra.db.MultiCBuilder;
 +
++import org.apache.cassandra.config.ColumnDefinition;
+ 
  /**
   * Base class for <code>Restriction</code>s
   */
@@@ -85,4 -88,26 +87,16 @@@ abstract class AbstractRestriction  imp
      {
          return true;
      }
+ 
 -    protected static ByteBuffer validateIndexedValue(ColumnSpecification 
columnSpec,
 -                                                     ByteBuffer value)
 -                                                     throws 
InvalidRequestException
 -    {
 -        checkNotNull(value, "Unsupported null value for indexed column %s", 
columnSpec.name);
 -        checkBindValueSet(value, "Unsupported unset value for indexed column 
%s", columnSpec.name);
 -        checkFalse(value.remaining() > 0xFFFF, "Index expression values may 
not be larger than 64K");
 -        return value;
 -    }
 -
+     /**
+      * Reverses the specified bound if the column type is a reversed one.
+      *
+      * @param columnDefinition the column definition
+      * @param bound the bound
+      * @return the bound reversed if the column type was a reversed one or 
the original bound
+      */
+     protected static Bound reverseBoundIfNeeded(ColumnDefinition 
columnDefinition, Bound bound)
+     {
+         return columnDefinition.isReversedType() ? bound.reverse() : bound;
+     }
  }

http://git-wip-us.apache.org/repos/asf/cassandra/blob/1aa97e30/src/java/org/apache/cassandra/cql3/restrictions/ForwardingPrimaryKeyRestrictions.java
----------------------------------------------------------------------
diff --cc 
src/java/org/apache/cassandra/cql3/restrictions/ForwardingPrimaryKeyRestrictions.java
index 18e7105,71855a0..723ebc3
--- 
a/src/java/org/apache/cassandra/cql3/restrictions/ForwardingPrimaryKeyRestrictions.java
+++ 
b/src/java/org/apache/cassandra/cql3/restrictions/ForwardingPrimaryKeyRestrictions.java
@@@ -18,9 -18,7 +18,8 @@@
  package org.apache.cassandra.cql3.restrictions;
  
  import java.nio.ByteBuffer;
- import java.util.Collection;
  import java.util.List;
 +import java.util.NavigableSet;
  
  import org.apache.cassandra.config.ColumnDefinition;
  import org.apache.cassandra.cql3.QueryOptions;

http://git-wip-us.apache.org/repos/asf/cassandra/blob/1aa97e30/src/java/org/apache/cassandra/cql3/restrictions/MultiColumnRestriction.java
----------------------------------------------------------------------
diff --cc 
src/java/org/apache/cassandra/cql3/restrictions/MultiColumnRestriction.java
index 069a01b,96e6f2b..ec02d06
--- 
a/src/java/org/apache/cassandra/cql3/restrictions/MultiColumnRestriction.java
+++ 
b/src/java/org/apache/cassandra/cql3/restrictions/MultiColumnRestriction.java
@@@ -357,16 -357,64 +357,64 @@@ public abstract class MultiColumnRestri
          }
  
          @Override
 -        public CompositesBuilder appendBoundTo(CompositesBuilder builder, 
Bound bound, QueryOptions options)
 +        public MultiCBuilder appendBoundTo(MultiCBuilder builder, Bound 
bound, QueryOptions options)
          {
-             List<ByteBuffer> vals = componentBounds(bound, options);
+             boolean reversed = getFirstColumn().isReversedType();
  
-             for (int i = 0, m = vals.size(); i < m; i++)
+             EnumMap<Bound, List<ByteBuffer>> componentBounds = new 
EnumMap<Bound, List<ByteBuffer>>(Bound.class);
+             componentBounds.put(Bound.START, componentBounds(Bound.START, 
options));
+             componentBounds.put(Bound.END, componentBounds(Bound.END, 
options));
+ 
+             List<List<ByteBuffer>> toAdd = new ArrayList<>();
+             List<ByteBuffer> values = new ArrayList<>();
+ 
+             for (int i = 0, m = columnDefs.size(); i < m; i++)
              {
-                 ByteBuffer v = checkNotNull(vals.get(i), "Invalid null value 
in condition for column %s", columnDefs.get(i).name);
-                 builder.addElementToAll(v);
+                 ColumnDefinition column = columnDefs.get(i);
+                 Bound b = reverseBoundIfNeeded(column, bound);
+ 
+                 // For mixed order columns, we need to create additional 
slices when 2 columns are in reverse order
+                 if (reversed != column.isReversedType())
+                 {
+                     reversed = column.isReversedType();
+                     // As we are switching direction we need to add the 
current composite
+                     toAdd.add(values);
+ 
+                     // The new bound side has no value for this component.  
just stop
+                     if (!hasComponent(b, i, componentBounds))
+                         continue;
+ 
+                     // The other side has still some components. We need to 
end the slice that we have just open.
+                     if (hasComponent(b.reverse(), i, componentBounds))
+                         toAdd.add(values);
+ 
+                     // We need to rebuild where we are in this bound side
+                     values = new ArrayList<ByteBuffer>();
+ 
+                     List<ByteBuffer> vals = componentBounds.get(b);
+ 
+                     int n = Math.min(i, vals.size());
+                     for (int j = 0; j < n; j++)
+                     {
+                         ByteBuffer v = checkNotNull(vals.get(j),
+                                                     "Invalid null value in 
condition for column %s",
+                                                     columnDefs.get(j).name);
+                         values.add(v);
+                     }
+                 }
+ 
+                 if (!hasComponent(b, i, componentBounds))
+                     continue;
+ 
+                 ByteBuffer v = checkNotNull(componentBounds.get(b).get(i), 
"Invalid null value in condition for column %s", columnDefs.get(i).name);
+                 values.add(v);
              }
-             return builder;
+             toAdd.add(values);
+ 
+             if (bound.isEnd())
+                 Collections.reverse(toAdd);
+ 
+             return builder.addAllElementsToAll(toAdd);
          }
  
          @Override
@@@ -458,60 -506,10 +509,65 @@@
  
              return 
Collections.singletonList(terminal.get(options.getProtocolVersion()));
          }
+ 
+         private boolean hasComponent(Bound b, int index, EnumMap<Bound, 
List<ByteBuffer>> componentBounds)
+         {
+             return componentBounds.get(b).size() > index;
+         }
      }
 +
 +    public static class NotNullRestriction extends MultiColumnRestriction
 +    {
 +        public NotNullRestriction(List<ColumnDefinition> columnDefs)
 +        {
 +            super(columnDefs);
 +            assert columnDefs.size() == 1;
 +        }
 +
 +        @Override
 +        public Iterable<Function> getFunctions()
 +        {
 +            return Collections.emptyList();
 +        }
 +
 +        @Override
 +        public boolean isNotNull()
 +        {
 +            return true;
 +        }
 +
 +        @Override
 +        public String toString()
 +        {
 +            return "IS NOT NULL";
 +        }
 +
 +        @Override
 +        public Restriction doMergeWith(Restriction otherRestriction) throws 
InvalidRequestException
 +        {
 +            throw invalidRequest("%s cannot be restricted by a relation if it 
includes an IS NOT NULL clause",
 +                                 getColumnsInCommons(otherRestriction));
 +        }
 +
 +        @Override
 +        protected boolean isSupportedBy(Index index)
 +        {
 +            for(ColumnDefinition column : columnDefs)
 +                if (index.supportsExpression(column, Operator.IS_NOT))
 +                    return true;
 +            return false;
 +        }
 +
 +        @Override
 +        public MultiCBuilder appendTo(MultiCBuilder builder, QueryOptions 
options)
 +        {
 +            throw new UnsupportedOperationException("Cannot use IS NOT NULL 
restriction for slicing");
 +        }
 +
 +        @Override
 +        public final void addRowFilterTo(RowFilter filter, 
SecondaryIndexManager indexMananger, QueryOptions options) throws 
InvalidRequestException
 +        {
 +            throw new UnsupportedOperationException("Secondary indexes do not 
support IS NOT NULL restrictions");
 +        }
 +    }
  }

http://git-wip-us.apache.org/repos/asf/cassandra/blob/1aa97e30/src/java/org/apache/cassandra/cql3/restrictions/PrimaryKeyRestrictionSet.java
----------------------------------------------------------------------
diff --cc 
src/java/org/apache/cassandra/cql3/restrictions/PrimaryKeyRestrictionSet.java
index 107cbd1,936dbd6..8121858
--- 
a/src/java/org/apache/cassandra/cql3/restrictions/PrimaryKeyRestrictionSet.java
+++ 
b/src/java/org/apache/cassandra/cql3/restrictions/PrimaryKeyRestrictionSet.java
@@@ -216,22 -200,14 +213,17 @@@ final class PrimaryKeyRestrictionSet ex
  
              if (r.isSlice())
              {
-                 if (!r.hasBound(b))
-                 {
-                     // There wasn't any non EQ relation on that key, we 
select all records having the preceding component as prefix.
-                     // For composites, if there was preceding component and 
we're computing the end, we must change the last component
-                     // End-Of-Component, otherwise we would be selecting only 
one record.
-                     return builder.buildBound(bound.isStart(), true);
-                 }
- 
-                 r.appendBoundTo(builder, b, options);
-                 return builder.buildBound(bound.isStart(), r.isInclusive(b));
+                 r.appendBoundTo(builder, bound, options);
 -                return filterAndSort(setEocs(r, bound, builder.build()));
++                return builder.buildBoundForSlice(bound.isStart(),
++                                                  r.isInclusive(bound),
++                                                  
r.isInclusive(bound.reverse()),
++                                                  r.getColumnDefs());
              }
  
-             r.appendBoundTo(builder, b, options);
+             r.appendBoundTo(builder, bound, options);
  
              if (builder.hasMissingElements())
 -                return Collections.emptyList();
 +                return BTreeSet.empty(comparator);
  
              keyPosition = r.getLastColumn().position() + 1;
          }

http://git-wip-us.apache.org/repos/asf/cassandra/blob/1aa97e30/src/java/org/apache/cassandra/cql3/restrictions/Restriction.java
----------------------------------------------------------------------

http://git-wip-us.apache.org/repos/asf/cassandra/blob/1aa97e30/src/java/org/apache/cassandra/cql3/restrictions/RestrictionSet.java
----------------------------------------------------------------------

http://git-wip-us.apache.org/repos/asf/cassandra/blob/1aa97e30/src/java/org/apache/cassandra/cql3/restrictions/SingleColumnRestriction.java
----------------------------------------------------------------------
diff --cc 
src/java/org/apache/cassandra/cql3/restrictions/SingleColumnRestriction.java
index d851253,735a2e2..3a61616
--- 
a/src/java/org/apache/cassandra/cql3/restrictions/SingleColumnRestriction.java
+++ 
b/src/java/org/apache/cassandra/cql3/restrictions/SingleColumnRestriction.java
@@@ -350,9 -349,14 +350,14 @@@ public abstract class SingleColumnRestr
          }
  
          @Override
 -        public CompositesBuilder appendBoundTo(CompositesBuilder builder, 
Bound bound, QueryOptions options)
 +        public MultiCBuilder appendBoundTo(MultiCBuilder builder, Bound 
bound, QueryOptions options)
          {
-             ByteBuffer value = slice.bound(bound).bindAndGet(options);
+             Bound b = reverseBoundIfNeeded(getFirstColumn(), bound);
+ 
+             if (!hasBound(b))
+                 return builder;
+ 
+             ByteBuffer value = slice.bound(b).bindAndGet(options);
              checkBindValueSet(value, "Invalid unset value for column %s", 
columnDef.name);
              return builder.addElementToAll(value);
  

http://git-wip-us.apache.org/repos/asf/cassandra/blob/1aa97e30/src/java/org/apache/cassandra/cql3/restrictions/TokenRestriction.java
----------------------------------------------------------------------

http://git-wip-us.apache.org/repos/asf/cassandra/blob/1aa97e30/src/java/org/apache/cassandra/db/MultiCBuilder.java
----------------------------------------------------------------------
diff --cc src/java/org/apache/cassandra/db/MultiCBuilder.java
index be654fa,0000000..7c77ab0
mode 100644,000000..100644
--- a/src/java/org/apache/cassandra/db/MultiCBuilder.java
+++ b/src/java/org/apache/cassandra/db/MultiCBuilder.java
@@@ -1,324 -1,0 +1,379 @@@
 +/*
 + * 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.db;
 +
 +import java.nio.ByteBuffer;
- import java.util.*;
++import java.util.ArrayList;
++import java.util.List;
++import java.util.NavigableSet;
 +
++import org.apache.cassandra.config.ColumnDefinition;
 +import org.apache.cassandra.utils.ByteBufferUtil;
 +import org.apache.cassandra.utils.btree.BTreeSet;
 +
 +/**
 + * Builder that allow to build multiple Clustering/Slice.Bound at the same 
time.
 + */
 +public class MultiCBuilder
 +{
 +    /**
 +     * The table comparator.
 +     */
 +    private final ClusteringComparator comparator;
 +
 +    /**
 +     * The elements of the clusterings
 +     */
 +    private final List<List<ByteBuffer>> elementsList = new ArrayList<>();
 +
 +    /**
 +     * The number of elements that have been added.
 +     */
 +    private int size;
 +
 +    /**
 +     * <code>true</code> if the clusterings have been build, 
<code>false</code> otherwise.
 +     */
 +    private boolean built;
 +
 +    /**
 +     * <code>true</code> if the clusterings contains some <code>null</code> 
elements.
 +     */
 +    private boolean containsNull;
 +
 +    /**
 +     * <code>true</code> if the composites contains some <code>unset</code> 
elements.
 +     */
 +    private boolean containsUnset;
 +
 +    /**
 +     * <code>true</code> if some empty collection have been added.
 +     */
 +    private boolean hasMissingElements;
 +
 +    private MultiCBuilder(ClusteringComparator comparator)
 +    {
 +        this.comparator = comparator;
 +    }
 +
 +    /**
 +     * Creates a new empty {@code MultiCBuilder}.
 +     */
 +    public static MultiCBuilder create(ClusteringComparator comparator)
 +    {
 +        return new MultiCBuilder(comparator);
 +    }
 +
 +    /**
 +     * Checks if this builder is empty.
 +     *
 +     * @return <code>true</code> if this builder is empty, <code>false</code> 
otherwise.
 +     */
 +    private boolean isEmpty()
 +    {
 +        return elementsList.isEmpty();
 +    }
 +
 +    /**
 +     * Adds the specified element to all the clusterings.
 +     * <p>
 +     * If this builder contains 2 clustering: A-B and A-C a call to this 
method to add D will result in the clusterings:
 +     * A-B-D and A-C-D.
 +     * </p>
 +     *
 +     * @param value the value of the next element
 +     * @return this <code>MulitCBuilder</code>
 +     */
 +    public MultiCBuilder addElementToAll(ByteBuffer value)
 +    {
 +        checkUpdateable();
 +
 +        if (isEmpty())
 +            elementsList.add(new ArrayList<ByteBuffer>());
 +
 +        for (int i = 0, m = elementsList.size(); i < m; i++)
 +        {
 +            if (value == null)
 +                containsNull = true;
 +            if (value == ByteBufferUtil.UNSET_BYTE_BUFFER)
 +                containsUnset = true;
 +
 +            elementsList.get(i).add(value);
 +        }
 +        size++;
 +        return this;
 +    }
 +
 +    /**
 +     * Adds individually each of the specified elements to the end of all of 
the existing clusterings.
 +     * <p>
 +     * If this builder contains 2 clusterings: A-B and A-C a call to this 
method to add D and E will result in the 4
 +     * clusterings: A-B-D, A-B-E, A-C-D and A-C-E.
 +     * </p>
 +     *
 +     * @param values the elements to add
 +     * @return this <code>CompositeBuilder</code>
 +     */
 +    public MultiCBuilder addEachElementToAll(List<ByteBuffer> values)
 +    {
 +        checkUpdateable();
 +
 +        if (isEmpty())
 +            elementsList.add(new ArrayList<ByteBuffer>());
 +
 +        if (values.isEmpty())
 +        {
 +            hasMissingElements = true;
 +        }
 +        else
 +        {
 +            for (int i = 0, m = elementsList.size(); i < m; i++)
 +            {
 +                List<ByteBuffer> oldComposite = elementsList.remove(0);
 +
 +                for (int j = 0, n = values.size(); j < n; j++)
 +                {
 +                    List<ByteBuffer> newComposite = new 
ArrayList<>(oldComposite);
 +                    elementsList.add(newComposite);
 +
 +                    ByteBuffer value = values.get(j);
 +
 +                    if (value == null)
 +                        containsNull = true;
 +                    if (value == ByteBufferUtil.UNSET_BYTE_BUFFER)
 +                        containsUnset = true;
 +
 +                    newComposite.add(values.get(j));
 +                }
 +            }
 +        }
 +        size++;
 +        return this;
 +    }
 +
 +    /**
 +     * Adds individually each of the specified list of elements to the end of 
all of the existing composites.
 +     * <p>
 +     * If this builder contains 2 composites: A-B and A-C a call to this 
method to add [[D, E], [F, G]] will result in the 4
 +     * composites: A-B-D-E, A-B-F-G, A-C-D-E and A-C-F-G.
 +     * </p>
 +     *
 +     * @param values the elements to add
 +     * @return this <code>CompositeBuilder</code>
 +     */
 +    public MultiCBuilder addAllElementsToAll(List<List<ByteBuffer>> values)
 +    {
 +        checkUpdateable();
 +
 +        if (isEmpty())
 +            elementsList.add(new ArrayList<ByteBuffer>());
 +
 +        if (values.isEmpty())
 +        {
 +            hasMissingElements = true;
 +        }
 +        else
 +        {
 +            for (int i = 0, m = elementsList.size(); i < m; i++)
 +            {
 +                List<ByteBuffer> oldComposite = elementsList.remove(0);
 +
 +                for (int j = 0, n = values.size(); j < n; j++)
 +                {
 +                    List<ByteBuffer> newComposite = new 
ArrayList<>(oldComposite);
 +                    elementsList.add(newComposite);
 +
 +                    List<ByteBuffer> value = values.get(j);
 +
-                     if (value.isEmpty())
-                         hasMissingElements = true;
- 
 +                    if (value.contains(null))
 +                        containsNull = true;
 +                    if (value.contains(ByteBufferUtil.UNSET_BYTE_BUFFER))
 +                        containsUnset = true;
 +
 +                    newComposite.addAll(value);
 +                }
 +            }
 +            size += values.get(0).size();
 +        }
 +        return this;
 +    }
 +
 +    /**
 +     * Returns the number of elements that can be added to the clusterings.
 +     *
 +     * @return the number of elements that can be added to the clusterings.
 +     */
 +    public int remainingCount()
 +    {
 +        return comparator.size() - size;
 +    }
 +
 +    /**
 +     * Checks if the clusterings contains null elements.
 +     *
 +     * @return <code>true</code> if the clusterings contains 
<code>null</code> elements, <code>false</code> otherwise.
 +     */
 +    public boolean containsNull()
 +    {
 +        return containsNull;
 +    }
 +
 +    /**
 +     * Checks if the clusterings contains unset elements.
 +     *
 +     * @return <code>true</code> if the clusterings contains 
<code>unset</code> elements, <code>false</code> otherwise.
 +     */
 +    public boolean containsUnset()
 +    {
 +        return containsUnset;
 +    }
 +
 +    /**
 +     * Checks if some empty list of values have been added
 +     * @return <code>true</code> if the clusterings have some missing 
elements, <code>false</code> otherwise.
 +     */
 +    public boolean hasMissingElements()
 +    {
 +        return hasMissingElements;
 +    }
 +
 +    /**
 +     * Builds the <code>clusterings</code>.
 +     *
 +     * @return the clusterings
 +     */
 +    public NavigableSet<Clustering> build()
 +    {
 +        built = true;
 +
 +        if (hasMissingElements)
 +            return BTreeSet.empty(comparator);
 +
 +        CBuilder builder = CBuilder.create(comparator);
 +
 +        if (elementsList.isEmpty())
 +            return BTreeSet.of(builder.comparator(), builder.build());
 +
 +        BTreeSet.Builder<Clustering> set = 
BTreeSet.builder(builder.comparator());
 +        for (int i = 0, m = elementsList.size(); i < m; i++)
 +        {
 +            List<ByteBuffer> elements = elementsList.get(i);
 +            set.add(builder.buildWith(elements));
 +        }
 +        return set.build();
 +    }
 +
 +    /**
-      * Builds the <code>clusterings</code> with the specified EOC.
++     * Builds the <code>Slice.Bound</code>s for slice restrictions.
 +     *
-      * @return the clusterings
++     * @param isStart specify if the bound is a start one
++     * @param isInclusive specify if the bound is inclusive or not
++     * @param isOtherBoundInclusive specify if the other bound is inclusive 
or not
++     * @param columnDefs the columns of the slice restriction
++     * @return the <code>Slice.Bound</code>s
 +     */
++    public NavigableSet<Slice.Bound> buildBoundForSlice(boolean isStart,
++                                                        boolean isInclusive,
++                                                        boolean 
isOtherBoundInclusive,
++                                                        
List<ColumnDefinition> columnDefs)
++    {
++        built = true;
++
++        if (hasMissingElements)
++            return BTreeSet.empty(comparator);
++
++        CBuilder builder = CBuilder.create(comparator);
++
++        if (elementsList.isEmpty())
++            return BTreeSet.of(comparator, builder.buildBound(isStart, 
isInclusive));
++
++        // Use a TreeSet to sort and eliminate duplicates
++        BTreeSet.Builder<Slice.Bound> set = BTreeSet.builder(comparator);
++
++        // The first column of the slice might not be the first clustering 
column (e.g. clustering_0 = ? AND (clustering_1, clustering_2) >= (?, ?)
++        int offset = columnDefs.get(0).position();
++
++        for (int i = 0, m = elementsList.size(); i < m; i++)
++        {
++            List<ByteBuffer> elements = elementsList.get(i);
++
++            // Handle the no bound case
++            if (elements.size() == offset)
++            {
++                set.add(builder.buildBoundWith(elements, isStart, true));
++                continue;
++            }
++
++            // In the case of mixed order columns, we will have some extra 
slices where the columns change directions.
++            // For example: if we have clustering_0 DESC and clustering_1 ASC 
a slice like (clustering_0, clustering_1) > (1, 2)
++            // will produce 2 slices: [BOTTOM, 1) and (1.2, 1]
++            // So, the END bound will return 2 bounds with the same values 1
++            ColumnDefinition lastColumn = columnDefs.get(columnDefs.size() - 
1);
++            if (elements.size() <= lastColumn.position() && i < m - 1 && 
elements.equals(elementsList.get(i + 1)))
++            {
++                set.add(builder.buildBoundWith(elements, isStart, false));
++                set.add(builder.buildBoundWith(elementsList.get(i++), 
isStart, true));
++                continue;
++            }
++
++            // Handle the normal bounds
++            ColumnDefinition column = columnDefs.get(elements.size() - 1 - 
offset);
++            set.add(builder.buildBoundWith(elements, isStart, 
column.isReversedType() ? isOtherBoundInclusive : isInclusive));
++        }
++        return set.build();
++    }
++
 +    public NavigableSet<Slice.Bound> buildBound(boolean isStart, boolean 
isInclusive)
 +    {
 +        built = true;
 +
 +        if (hasMissingElements)
 +            return BTreeSet.empty(comparator);
 +
 +        CBuilder builder = CBuilder.create(comparator);
 +
 +        if (elementsList.isEmpty())
 +            return BTreeSet.of(comparator, builder.buildBound(isStart, 
isInclusive));
 +
 +        // Use a TreeSet to sort and eliminate duplicates
 +        BTreeSet.Builder<Slice.Bound> set = BTreeSet.builder(comparator);
 +
 +        for (int i = 0, m = elementsList.size(); i < m; i++)
 +        {
 +            List<ByteBuffer> elements = elementsList.get(i);
 +            set.add(builder.buildBoundWith(elements, isStart, isInclusive));
 +        }
 +        return set.build();
 +    }
 +
 +    /**
 +     * Checks if some elements can still be added to the clusterings.
 +     *
 +     * @return <code>true</code> if it is possible to add more elements to 
the clusterings, <code>false</code> otherwise.
 +     */
 +    public boolean hasRemaining()
 +    {
 +        return remainingCount() > 0;
 +    }
 +
 +    private void checkUpdateable()
 +    {
 +        if (!hasRemaining() || built)
 +            throw new IllegalStateException("this builder cannot be updated 
anymore");
 +    }
 +}

Reply via email to