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/9244531a
Tree: http://git-wip-us.apache.org/repos/asf/cassandra/tree/9244531a
Diff: http://git-wip-us.apache.org/repos/asf/cassandra/diff/9244531a

Branch: refs/heads/cassandra-3.9
Commit: 9244531ab7bb0f4d07cf9996a9bb89d5e4370f54
Parents: db61372 e06dae8
Author: Benjamin Lerer <b.le...@gmail.com>
Authored: Mon Jul 4 14:30:55 2016 +0200
Committer: Benjamin Lerer <b.le...@gmail.com>
Committed: Mon Jul 4 14:31:46 2016 +0200

----------------------------------------------------------------------
 CHANGES.txt                                     |  3 +
 .../restrictions/MultiColumnRestriction.java    |  2 +-
 .../restrictions/PrimaryKeyRestrictionSet.java  | 82 ++++----------------
 .../restrictions/StatementRestrictions.java     | 41 ++++++++--
 .../SelectMultiColumnRelationTest.java          | 33 +++++++-
 .../cql3/validation/operations/SelectTest.java  | 61 +++++++++++++++
 6 files changed, 144 insertions(+), 78 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/cassandra/blob/9244531a/CHANGES.txt
----------------------------------------------------------------------
diff --cc CHANGES.txt
index f12d704,451575c..2df77e1
--- a/CHANGES.txt
+++ b/CHANGES.txt
@@@ -1,17 -1,11 +1,20 @@@
 -2.2.8
 +3.0.9
 + * Avoid digest mismatch with empty but static rows (CASSANDRA-12090)
 + * Fix EOF exception when altering column type (CASSANDRA-11820)
 +Merged from 2.2:
   * MemoryUtil.getShort() should return an unsigned short also for 
architectures not supporting unaligned memory accesses (CASSANDRA-11973)
+ Merged from 2.1:
+  * Fix filtering on clustering columns when 2i is used (CASSANDRA-11907)
 - * Account for partition deletions in tombstone histogram (CASSANDRA-12112)
+ 
  
 -2.2.7
 +3.0.8
 + * Fix potential race in schema during new table creation (CASSANDRA-12083)
 + * cqlsh: fix error handling in rare COPY FROM failure scenario 
(CASSANDRA-12070)
 + * Disable autocompaction during drain (CASSANDRA-11878)
 + * Add a metrics timer to MemtablePool and use it to track time spent blocked 
on memory in MemtableAllocator (CASSANDRA-11327)
 + * Fix upgrading schema with super columns with non-text subcomparators 
(CASSANDRA-12023)
 + * Add TimeWindowCompactionStrategy (CASSANDRA-9666)
 +Merged from 2.2:
   * Allow nodetool info to run with readonly JMX access (CASSANDRA-11755)
   * Validate bloom_filter_fp_chance against lowest supported
     value when the table is created (CASSANDRA-11920)

http://git-wip-us.apache.org/repos/asf/cassandra/blob/9244531a/src/java/org/apache/cassandra/cql3/restrictions/MultiColumnRestriction.java
----------------------------------------------------------------------
diff --cc 
src/java/org/apache/cassandra/cql3/restrictions/MultiColumnRestriction.java
index 4329b6e,51e2ce4..9d33bb1
--- 
a/src/java/org/apache/cassandra/cql3/restrictions/MultiColumnRestriction.java
+++ 
b/src/java/org/apache/cassandra/cql3/restrictions/MultiColumnRestriction.java
@@@ -473,11 -471,11 +473,11 @@@ public abstract class MultiColumnRestri
          }
  
          @Override
 -        public final void addIndexExpressionTo(List<IndexExpression> 
expressions,
 -                                               SecondaryIndexManager 
indexManager,
 -                                               QueryOptions options) throws 
InvalidRequestException
 +        public final void addRowFilterTo(RowFilter filter,
 +                                         SecondaryIndexManager indexManager,
 +                                         QueryOptions options) throws 
InvalidRequestException
          {
-             throw invalidRequest("Slice restrictions are not supported on 
indexed columns");
+             throw invalidRequest("Multi-column slice restrictions cannot be 
used for filtering.");
          }
  
          @Override

http://git-wip-us.apache.org/repos/asf/cassandra/blob/9244531a/src/java/org/apache/cassandra/cql3/restrictions/PrimaryKeyRestrictionSet.java
----------------------------------------------------------------------
diff --cc 
src/java/org/apache/cassandra/cql3/restrictions/PrimaryKeyRestrictionSet.java
index 061284b,e1cbc29..a5f4a24
--- 
a/src/java/org/apache/cassandra/cql3/restrictions/PrimaryKeyRestrictionSet.java
+++ 
b/src/java/org/apache/cassandra/cql3/restrictions/PrimaryKeyRestrictionSet.java
@@@ -25,14 -26,11 +25,13 @@@ import org.apache.cassandra.config.Colu
  import org.apache.cassandra.cql3.QueryOptions;
  import org.apache.cassandra.cql3.functions.Function;
  import org.apache.cassandra.cql3.statements.Bound;
 -import org.apache.cassandra.db.IndexExpression;
 -import org.apache.cassandra.db.composites.*;
 -import org.apache.cassandra.db.composites.Composite.EOC;
 -import org.apache.cassandra.db.index.SecondaryIndexManager;
 +import org.apache.cassandra.db.*;
 +import org.apache.cassandra.db.filter.RowFilter;
  import org.apache.cassandra.exceptions.InvalidRequestException;
 +import org.apache.cassandra.index.SecondaryIndexManager;
 +import org.apache.cassandra.utils.btree.BTreeSet;
 +
- import static 
org.apache.cassandra.cql3.statements.RequestValidations.checkFalse;
 +import static 
org.apache.cassandra.cql3.statements.RequestValidations.invalidRequest;
  
  /**
   * A set of single column restrictions on a primary key part (partition key 
or clustering key).
@@@ -64,72 -62,18 +63,26 @@@ final class PrimaryKeyRestrictionSet ex
       */
      private boolean contains;
  
 -    public PrimaryKeyRestrictionSet(CType ctype)
 +    /**
 +     * <code>true</code> if the restrictions corresponding to a partition 
key, <code>false</code> if it's clustering columns.
 +     */
 +    private boolean isPartitionKey;
 +
-     /**
-      * If restrictions apply to clustering columns, we need to check whether 
they can be satisfied by an index lookup
-      * as this affects which other restrictions can legally be specified (if 
an index is present, we are more lenient
-      * about what additional filtering can be performed on the results of a 
lookup - see CASSANDRA-11510).
-      *
-      * We don't hold a reference to the SecondaryIndexManager itself as this 
is not strictly a singleton (although
-      * we often treat is as one), the field would also require annotation 
with @Unmetered to avoid blowing up the
-      * object size (used when calculating the size of prepared statements for 
caching). Instead, we refer to the
-      * CFMetaData and retrieve the index manager when necessary.
-      *
-      * There are a couple of scenarios where the CFM can be null (and we make 
sure and test for null when we use it):
-      *  * where an empty set of restrictions are created for use in 
processing query results - see
-      *    SelectStatement.forSelection
-      *  * where the restrictions apply to partition keys and not clustering 
columns e.g.
-      *    StatementRestrictions.partitionKeyRestrictions
-      *  * in unit tests (in particular PrimaryKeyRestrictionSetTest which is 
primarily concerned with the correct
-      *    generation of bounds when secondary indexes are not used).
-      */
-     private final CFMetaData cfm;
- 
 +    public PrimaryKeyRestrictionSet(ClusteringComparator comparator, boolean 
isPartitionKey)
      {
-        this(comparator, isPartitionKey, null);
-     }
- 
-     public PrimaryKeyRestrictionSet(ClusteringComparator comparator, boolean 
isPartitionKey, CFMetaData cfm)
-     {
 -        super(ctype);
 +        super(comparator);
 +
-         if (cfm != null)
-             assert !isPartitionKey;
- 
-         this.cfm = cfm;
          this.restrictions = new RestrictionSet();
          this.eq = true;
 +        this.isPartitionKey = isPartitionKey;
      }
  
      private PrimaryKeyRestrictionSet(PrimaryKeyRestrictionSet 
primaryKeyRestrictions,
                                       Restriction restriction) throws 
InvalidRequestException
      {
 -        super(primaryKeyRestrictions.ctype);
 +        super(primaryKeyRestrictions.comparator);
          this.restrictions = 
primaryKeyRestrictions.restrictions.addRestriction(restriction);
 +        this.isPartitionKey = primaryKeyRestrictions.isPartitionKey;
-         this.cfm = primaryKeyRestrictions.cfm;
- 
-         if (!primaryKeyRestrictions.isEmpty() && 
!hasSupportingIndex(restriction))
-         {
-             ColumnDefinition lastRestrictionStart = 
primaryKeyRestrictions.restrictions.lastRestriction().getFirstColumn();
-             ColumnDefinition newRestrictionStart = 
restriction.getFirstColumn();
- 
-             checkFalse(primaryKeyRestrictions.isSlice() && 
newRestrictionStart.position() > lastRestrictionStart.position(),
-                        "Clustering column \"%s\" cannot be restricted 
(preceding column \"%s\" is restricted by a non-EQ relation)",
-                        newRestrictionStart.name,
-                        lastRestrictionStart.name);
- 
-             if (newRestrictionStart.position() < 
lastRestrictionStart.position() && restriction.isSlice())
-                 throw invalidRequest("PRIMARY KEY column \"%s\" cannot be 
restricted (preceding column \"%s\" is restricted by a non-EQ relation)",
-                                      
restrictions.nextColumn(newRestrictionStart).name,
-                                      newRestrictionStart.name);
-         }
  
          if (restriction.isSlice() || primaryKeyRestrictions.isSlice())
              this.slice = true;
@@@ -141,22 -85,6 +94,15 @@@
              this.eq = true;
      }
  
 +    private List<ByteBuffer> toByteBuffers(SortedSet<? extends 
ClusteringPrefix> clusterings)
 +    {
 +        // It's currently a tad hard to follow that this is only called for 
partition key so we should fix that
 +        List<ByteBuffer> l = new ArrayList<>(clusterings.size());
 +        for (ClusteringPrefix clustering : clusterings)
 +            l.add(CFMetaData.serializePartitionKey(clustering));
 +        return l;
 +    }
 +
-     private boolean hasSupportingIndex(Restriction restriction)
-     {
-         return cfm != null
-                && 
restriction.hasSupportingIndex(Keyspace.openAndGetStore(cfm).indexManager);
- 
-     }
- 
      @Override
      public boolean isSlice()
      {
@@@ -319,16 -338,22 +265,15 @@@
  
          for (Restriction restriction : restrictions)
          {
--            ColumnDefinition columnDef = restriction.getFirstColumn();
 -
 -            // PrimaryKeyRestrictionSet contains only one kind of column, 
either partition key or clustering columns.
 -            // Therefore we only need to check the column kind once. All the 
other columns will be of the same kind.
 -            if (clusteringColumns == null)
 -                clusteringColumns = columnDef.isClusteringColumn() ? 
Boolean.TRUE : Boolean.FALSE;
--
              // We ignore all the clustering columns that can be handled by 
slices.
-             if (!isPartitionKey && !restriction.isContains()&& position == 
columnDef.position())
 -            if (!clusteringColumns || handleInFilter(restriction, position) 
|| restriction.hasSupportingIndex(indexManager))
++            if (isPartitionKey || handleInFilter(restriction, position) || 
restriction.hasSupportingIndex(indexManager))
              {
-                 position = restriction.getLastColumn().position() + 1;
-                 if (!restriction.hasSupportingIndex(indexManager))
-                     continue;
 -                restriction.addIndexExpressionTo(expressions, indexManager, 
options);
++                restriction.addRowFilterTo(filter, indexManager, options);
+                 continue;
              }
-             restriction.addRowFilterTo(filter, indexManager, options);
+ 
+             if (!restriction.isSlice())
+                 position = restriction.getLastColumn().position() + 1;
          }
      }
  

http://git-wip-us.apache.org/repos/asf/cassandra/blob/9244531a/src/java/org/apache/cassandra/cql3/restrictions/StatementRestrictions.java
----------------------------------------------------------------------
diff --cc 
src/java/org/apache/cassandra/cql3/restrictions/StatementRestrictions.java
index ae0c9c4,1547210..647d22f
--- a/src/java/org/apache/cassandra/cql3/restrictions/StatementRestrictions.java
+++ b/src/java/org/apache/cassandra/cql3/restrictions/StatementRestrictions.java
@@@ -27,22 -28,17 +27,20 @@@ import org.apache.cassandra.config.Colu
  import org.apache.cassandra.cql3.*;
  import org.apache.cassandra.cql3.functions.Function;
  import org.apache.cassandra.cql3.statements.Bound;
 +import org.apache.cassandra.cql3.statements.StatementType;
  import org.apache.cassandra.db.*;
 -import org.apache.cassandra.db.composites.Composite;
 -import org.apache.cassandra.db.index.SecondaryIndexManager;
 +import org.apache.cassandra.db.filter.RowFilter;
 +import org.apache.cassandra.db.marshal.AbstractType;
  import org.apache.cassandra.dht.*;
  import org.apache.cassandra.exceptions.InvalidRequestException;
 -import org.apache.cassandra.service.StorageService;
 +import org.apache.cassandra.index.Index;
 +import org.apache.cassandra.index.SecondaryIndexManager;
 +import org.apache.cassandra.net.MessagingService;
  import org.apache.cassandra.utils.ByteBufferUtil;
 +import org.apache.cassandra.utils.btree.BTreeSet;
  
 -import static org.apache.cassandra.config.ColumnDefinition.toIdentifiers;
  import static 
org.apache.cassandra.cql3.statements.RequestValidations.checkFalse;
  import static 
org.apache.cassandra.cql3.statements.RequestValidations.checkNotNull;
- import static 
org.apache.cassandra.cql3.statements.RequestValidations.checkTrue;
  import static 
org.apache.cassandra.cql3.statements.RequestValidations.invalidRequest;
  
  /**
@@@ -125,58 -110,28 +123,58 @@@ public final class StatementRestriction
                                   VariableSpecifications boundNames,
                                   boolean selectsOnlyStaticColumns,
                                   boolean selectACollection,
-                                  boolean useFiltering,
 -                                 boolean allowFiltering)
++                                 boolean allowFiltering,
 +                                 boolean forView) throws 
InvalidRequestException
      {
 +        this.type = type;
          this.cfm = cfm;
 -        this.partitionKeyRestrictions = new 
PrimaryKeyRestrictionSet(cfm.getKeyValidatorAsCType());
 -        this.clusteringColumnsRestrictions = new 
PrimaryKeyRestrictionSet(cfm.comparator);
 +        this.partitionKeyRestrictions = new 
PrimaryKeyRestrictionSet(cfm.getKeyValidatorAsClusteringComparator(), true);
-         this.clusteringColumnsRestrictions = new 
PrimaryKeyRestrictionSet(cfm.comparator, false, cfm);
++        this.clusteringColumnsRestrictions = new 
PrimaryKeyRestrictionSet(cfm.comparator, false);
          this.nonPrimaryKeyRestrictions = new RestrictionSet();
 +        this.notNullColumns = new HashSet<>();
  
          /*
 -         * WHERE clause. For a given entity, rules are: - EQ relation 
conflicts with anything else (including a 2nd EQ)
 -         * - Can't have more than one LT(E) relation (resp. GT(E) relation) - 
IN relation are restricted to row keys
 -         * (for now) and conflicts with anything else (we could allow two IN 
for the same entity but that doesn't seem
 -         * very useful) - The value_alias cannot be restricted in any way (we 
don't support wide rows with indexed value
 -         * in CQL so far)
 +         * WHERE clause. For a given entity, rules are:
 +         *   - EQ relation conflicts with anything else (including a 2nd EQ)
 +         *   - Can't have more than one LT(E) relation (resp. GT(E) relation)
 +         *   - IN relation are restricted to row keys (for now) and conflicts 
with anything else (we could
 +         *     allow two IN for the same entity but that doesn't seem very 
useful)
 +         *   - The value_alias cannot be restricted in any way (we don't 
support wide rows with indexed value
 +         *     in CQL so far)
           */
 -        for (Relation relation : whereClause)
 -            addRestriction(relation.toRestriction(cfm, boundNames));
 +        for (Relation relation : whereClause.relations)
 +        {
 +            if (relation.operator() == Operator.IS_NOT)
 +            {
 +                if (!forView)
 +                    throw new InvalidRequestException("Unsupported 
restriction: " + relation);
 +
 +                for (ColumnDefinition def : relation.toRestriction(cfm, 
boundNames).getColumnDefs())
 +                    this.notNullColumns.add(def);
 +            }
 +            else
 +            {
 +                addRestriction(relation.toRestriction(cfm, boundNames));
 +            }
 +        }
 +
 +        boolean hasQueriableClusteringColumnIndex = false;
 +        boolean hasQueriableIndex = false;
  
 -        SecondaryIndexManager secondaryIndexManager = 
Keyspace.open(cfm.ksName).getColumnFamilyStore(cfm.cfName).indexManager;
 -        boolean hasQueriableClusteringColumnIndex = 
clusteringColumnsRestrictions.hasSupportingIndex(secondaryIndexManager);
 -        boolean hasQueriableIndex = hasQueriableClusteringColumnIndex
 -                || 
partitionKeyRestrictions.hasSupportingIndex(secondaryIndexManager)
 -                || 
nonPrimaryKeyRestrictions.hasSupportingIndex(secondaryIndexManager);
 +        if (type.allowUseOfSecondaryIndices())
 +        {
 +            ColumnFamilyStore cfs = 
Keyspace.open(cfm.ksName).getColumnFamilyStore(cfm.cfName);
 +            SecondaryIndexManager secondaryIndexManager = cfs.indexManager;
 +
 +            if (whereClause.containsCustomExpressions())
 +                processCustomIndexExpressions(whereClause.expressions, 
boundNames, secondaryIndexManager);
 +
 +            hasQueriableClusteringColumnIndex = 
clusteringColumnsRestrictions.hasSupportingIndex(secondaryIndexManager);
 +            hasQueriableIndex = 
!indexRestrictions.getCustomIndexExpressions().isEmpty()
 +                    || hasQueriableClusteringColumnIndex
 +                    || 
partitionKeyRestrictions.hasSupportingIndex(secondaryIndexManager)
 +                    || 
nonPrimaryKeyRestrictions.hasSupportingIndex(secondaryIndexManager);
 +        }
  
          // At this point, the select statement if fully constructed, but we 
still have a few things to validate
          processPartitionKeyRestrictions(hasQueriableIndex);
@@@ -220,19 -159,17 +218,19 @@@
          // there is restrictions not covered by the PK.
          if (!nonPrimaryKeyRestrictions.isEmpty())
          {
 -            if (!hasQueriableIndex)
 +            if (!type.allowNonPrimaryKeyInWhereClause())
              {
 -                // Filtering for non-index query is only supported for thrift 
static CFs
 -                if (cfm.comparator.isDense() ||  cfm.comparator.isCompound())
 -                    throw invalidRequest("Predicates on non-primary-key 
columns (%s) are not yet supported for non secondary index queries",
 -                                         Joiner.on(", 
").join(toIdentifiers(nonPrimaryKeyRestrictions.getColumnDefs())));
 +                Collection<ColumnIdentifier> nonPrimaryKeyColumns =
 +                        
ColumnDefinition.toIdentifiers(nonPrimaryKeyRestrictions.getColumnDefs());
  
 -                if (!allowFiltering)
 -                    throw invalidRequest(REQUIRES_ALLOW_FILTERING_MESSAGE);
 +                throw invalidRequest("Non PRIMARY KEY columns found in where 
clause: %s ",
 +                                     Joiner.on(", 
").join(nonPrimaryKeyColumns));
              }
 -            usesSecondaryIndexing = true;
 +            if (hasQueriableIndex)
 +                usesSecondaryIndexing = true;
-             else if (!useFiltering)
++            else if (!allowFiltering)
 +                throw 
invalidRequest(StatementRestrictions.REQUIRES_ALLOW_FILTERING_MESSAGE);
 +
              indexRestrictions.add(nonPrimaryKeyRestrictions);
          }
  
@@@ -435,60 -299,78 +433,93 @@@
       * Processes the clustering column restrictions.
       *
       * @param hasQueriableIndex <code>true</code> if some of the queried data 
are indexed, <code>false</code> otherwise
 +     * @param selectsOnlyStaticColumns <code>true</code> if the selected or 
modified columns are all statics,
 +     * <code>false</code> otherwise.
       * @param selectACollection <code>true</code> if the query should return 
a collection column
 -     * @throws InvalidRequestException if the request is invalid
       */
      private void processClusteringColumnsRestrictions(boolean 
hasQueriableIndex,
 -                                                      boolean 
selectACollection) throws InvalidRequestException
 +                                                      boolean 
selectsOnlyStaticColumns,
 +                                                      boolean 
selectACollection,
 +                                                      boolean forView) throws 
InvalidRequestException
      {
+         validateClusteringRestrictions(hasQueriableIndex);
+ 
 -        checkFalse(clusteringColumnsRestrictions.isIN() && selectACollection,
 -                   "Cannot restrict clustering columns by IN relations when a 
collection is selected by the query");
 -        checkFalse(clusteringColumnsRestrictions.isContains() && 
!hasQueriableIndex,
 -                   "Cannot restrict clustering columns by a CONTAINS relation 
without a secondary index");
 +        checkFalse(!type.allowClusteringColumnSlices() && 
clusteringColumnsRestrictions.isSlice(),
 +                   "Slice restrictions are not supported on the clustering 
columns in %s statements", type);
  
 -        if (hasClusteringColumnsRestriction() && 
clusteringRestrictionsNeedFiltering())
 +        if (!type.allowClusteringColumnSlices()
 +               && (!cfm.isCompactTable() || (cfm.isCompactTable() && 
!hasClusteringColumnsRestriction())))
          {
 -            if (hasQueriableIndex)
 -            {
 -                usesSecondaryIndexing = true;
 -                return;
 -            }
 -
 -            List<ColumnDefinition> clusteringColumns = 
cfm.clusteringColumns();
 -            List<ColumnDefinition> restrictedColumns = new 
LinkedList<>(clusteringColumnsRestrictions.getColumnDefs());
 +            if (!selectsOnlyStaticColumns && 
hasUnrestrictedClusteringColumns())
 +                throw invalidRequest("Some clustering keys are missing: %s",
 +                                     Joiner.on(", 
").join(getUnrestrictedClusteringColumns()));
 +        }
 +        else
 +        {
 +            checkFalse(clusteringColumnsRestrictions.isIN() && 
selectACollection,
 +                       "Cannot restrict clustering columns by IN relations 
when a collection is selected by the query");
 +            checkFalse(clusteringColumnsRestrictions.isContains() && 
!hasQueriableIndex,
 +                       "Cannot restrict clustering columns by a CONTAINS 
relation without a secondary index");
  
 -            for (int i = 0, m = restrictedColumns.size(); i < m; i++)
 +            if (hasClusteringColumnsRestriction() && 
clusteringRestrictionsNeedFiltering())
              {
 -                ColumnDefinition clusteringColumn = clusteringColumns.get(i);
 -                ColumnDefinition restrictedColumn = restrictedColumns.get(i);
 +                if (hasQueriableIndex || forView)
 +                {
 +                    usesSecondaryIndexing = true;
 +                    return;
 +                }
  
 -                if (!clusteringColumn.equals(restrictedColumn))
 +                List<ColumnDefinition> clusteringColumns = 
cfm.clusteringColumns();
 +                List<ColumnDefinition> restrictedColumns = new 
LinkedList<>(clusteringColumnsRestrictions.getColumnDefs());
 +
 +                for (int i = 0, m = restrictedColumns.size(); i < m; i++)
                  {
 -                    throw invalidRequest(
 -                              "PRIMARY KEY column \"%s\" cannot be restricted 
as preceding column \"%s\" is not restricted",
 -                              restrictedColumn.name,
 -                              clusteringColumn.name);
 +                    ColumnDefinition clusteringColumn = 
clusteringColumns.get(i);
 +                    ColumnDefinition restrictedColumn = 
restrictedColumns.get(i);
 +
 +                    if (!clusteringColumn.equals(restrictedColumn))
 +                    {
 +                        throw invalidRequest(
 +                           "PRIMARY KEY column \"%s\" cannot be restricted as 
preceding column \"%s\" is not restricted",
 +                            restrictedColumn.name,
 +                            clusteringColumn.name);
 +                    }
                  }
              }
          }
      }
  
+     /**
+      * Validates whether or not restrictions are allowed for execution when 
secondary index is not used.
+      */
+     public final void validateClusteringRestrictions(boolean 
hasQueriableIndex)
+     {
+         assert clusteringColumnsRestrictions instanceof 
PrimaryKeyRestrictionSet;
+ 
+         // If there's a queriable index, filtering will take care of 
clustering restrictions
+         if (hasQueriableIndex)
+             return;
+ 
 -        Iterator<Restriction> iter = 
((PrimaryKeyRestrictionSet)clusteringColumnsRestrictions).iterator();
++        Iterator<Restriction> iter = ((PrimaryKeyRestrictionSet) 
clusteringColumnsRestrictions).iterator();
+         Restriction previousRestriction = null;
 -
+         while (iter.hasNext())
+         {
+             Restriction restriction = iter.next();
+ 
+             if (previousRestriction != null)
+             {
+                 ColumnDefinition lastRestrictionStart = 
previousRestriction.getFirstColumn();
+                 ColumnDefinition newRestrictionStart = 
restriction.getFirstColumn();
+ 
+                 if (previousRestriction.isSlice() && 
newRestrictionStart.position() > lastRestrictionStart.position())
+                     throw invalidRequest("Clustering column \"%s\" cannot be 
restricted (preceding column \"%s\" is restricted by a non-EQ relation)",
+                                          newRestrictionStart.name,
+                                          lastRestrictionStart.name);
+             }
+             previousRestriction = restriction;
+         }
+     }
+ 
      public final boolean clusteringRestrictionsNeedFiltering()
      {
          assert clusteringColumnsRestrictions instanceof 
PrimaryKeyRestrictionSet;

http://git-wip-us.apache.org/repos/asf/cassandra/blob/9244531a/test/unit/org/apache/cassandra/cql3/validation/operations/SelectMultiColumnRelationTest.java
----------------------------------------------------------------------
diff --cc 
test/unit/org/apache/cassandra/cql3/validation/operations/SelectMultiColumnRelationTest.java
index 0db0039,0975662..ce74fe2
--- 
a/test/unit/org/apache/cassandra/cql3/validation/operations/SelectMultiColumnRelationTest.java
+++ 
b/test/unit/org/apache/cassandra/cql3/validation/operations/SelectMultiColumnRelationTest.java
@@@ -885,19 -874,33 +885,46 @@@ public class SelectMultiColumnRelationT
      }
  
      @Test
+     public void testMultiColumnRestrictionsWithIndex() throws Throwable
+     {
+         createTable("CREATE TABLE %s (a int, b int, c int, d int, e int, v 
int, PRIMARY KEY (a, b, c, d, e))");
+         createIndex("CREATE INDEX ON %s (v)");
+         for (int i = 1; i <= 5; i++)
+         {
+             execute("INSERT INTO %s (a,b,c,d,e,v) VALUES (?,?,?,?,?,?)", 0, 
i, 0, 0, 0, 0);
+             execute("INSERT INTO %s (a,b,c,d,e,v) VALUES (?,?,?,?,?,?)", 0, 
i, i, 0, 0, 0);
+             execute("INSERT INTO %s (a,b,c,d,e,v) VALUES (?,?,?,?,?,?)", 0, 
i, i, i, 0, 0);
+             execute("INSERT INTO %s (a,b,c,d,e,v) VALUES (?,?,?,?,?,?)", 0, 
i, i, i, i, 0);
+             execute("INSERT INTO %s (a,b,c,d,e,v) VALUES (?,?,?,?,?,?)", 0, 
i, i, i, i, i);
+         }
+ 
+         String errorMsg = "Multi-column slice restrictions cannot be used for 
filtering.";
+         assertInvalidMessage(errorMsg,
+                              "SELECT * FROM %s WHERE a = 0 AND (c,d) < (2,2) 
AND v = 0 ALLOW FILTERING");
+         assertInvalidMessage(errorMsg,
+                              "SELECT * FROM %s WHERE a = 0 AND (d,e) < (2,2) 
AND b = 1 AND v = 0 ALLOW FILTERING");
+         assertInvalidMessage(errorMsg,
+                              "SELECT * FROM %s WHERE a = 0 AND b = 1 AND 
(d,e) < (2,2) AND v = 0 ALLOW FILTERING");
+         assertInvalidMessage(errorMsg,
+                              "SELECT * FROM %s WHERE a = 0 AND b > 1 AND 
(d,e) < (2,2) AND v = 0 ALLOW FILTERING");
+         assertInvalidMessage(errorMsg,
+                              "SELECT * FROM %s WHERE a = 0 AND (b,c) > (1,0) 
AND (d,e) < (2,2) AND v = 0 ALLOW FILTERING");
+     }
+ 
+     @Test
 +    public void testMultipleClusteringWithIndexAndValueOver64K() throws 
Throwable
 +    {
 +        createTable("CREATE TABLE %s (a int, b blob, c int, d int, PRIMARY 
KEY (a, b, c))");
 +        createIndex("CREATE INDEX ON %s (b)");
 +
 +        execute("INSERT INTO %s (a, b, c, d) VALUES (?, ?, ?, ?)", 0, 
ByteBufferUtil.bytes(1), 0, 0);
 +        execute("INSERT INTO %s (a, b, c, d) VALUES (?, ?, ?, ?)", 0, 
ByteBufferUtil.bytes(2), 1, 0);
 +
 +        assertInvalidMessage("Index expression values may not be larger than 
64K",
 +                             "SELECT * FROM %s WHERE (b, c) = (?, ?) AND d = 
?  ALLOW FILTERING", TOO_BIG, 1, 2);
 +    }
 +
 +    @Test
      public void testMultiplePartitionKeyAndMultiClusteringWithIndex() throws 
Throwable
      {
          createTable("CREATE TABLE %s (a int, b int, c int, d int, e int, f 
int, PRIMARY KEY ((a, b), c, d, e))");

http://git-wip-us.apache.org/repos/asf/cassandra/blob/9244531a/test/unit/org/apache/cassandra/cql3/validation/operations/SelectTest.java
----------------------------------------------------------------------
diff --cc 
test/unit/org/apache/cassandra/cql3/validation/operations/SelectTest.java
index 65bfb32,c8df4c3..1b6fe9b
--- a/test/unit/org/apache/cassandra/cql3/validation/operations/SelectTest.java
+++ b/test/unit/org/apache/cassandra/cql3/validation/operations/SelectTest.java
@@@ -1534,6 -1450,32 +1534,24 @@@ public class SelectTest extends CQLTest
      }
  
      @Test
 -    public void testFilteringOnStaticColumnWithoutIndices() throws Throwable
++    public void testIndexQueryWithCompositePartitionKey() throws Throwable
+     {
 -        createTable("CREATE TABLE %s (a int, b int, s int static, c int, 
PRIMARY KEY (a, b))");
++        createTable("CREATE TABLE %s (p1 int, p2 int, v int, PRIMARY KEY 
((p1, p2)))");
++        assertInvalidMessage("Partition key parts: p2 must be restricted as 
other parts are",
++                             "SELECT * FROM %s WHERE p1 = 1 AND v = 3 ALLOW 
FILTERING");
+ 
 -        // Checks filtering
 -        assertInvalidMessage("Predicates on non-primary-key columns (c, s) 
are not yet supported for non secondary index queries",
 -                             "SELECT * FROM %s WHERE c = 1 AND s = 2 ALLOW 
FILTERING");
 -        assertInvalidMessage("Predicates on non-primary-key columns (s) are 
not yet supported for non secondary index queries",
 -                             "SELECT * FROM %s WHERE a = 1 AND b = 1 AND s = 
2 ALLOW FILTERING");
 -        assertInvalidMessage("Predicates on non-primary-key columns (s) are 
not yet supported for non secondary index queries",
 -                             "SELECT * FROM %s WHERE s > 2 ALLOW FILTERING");
++        createIndex("CREATE INDEX ON %s(v)");
+ 
 -        // Checks filtering with null
 -        assertInvalidMessage("Predicates on non-primary-key columns (s) are 
not yet supported for non secondary index queries",
 -                             "SELECT * FROM %s WHERE s = null ALLOW 
FILTERING");
 -        assertInvalidMessage("Predicates on non-primary-key columns (s) are 
not yet supported for non secondary index queries",
 -                             "SELECT * FROM %s WHERE s > null ALLOW 
FILTERING");
++        execute("INSERT INTO %s(p1, p2, v) values (?, ?, ?)", 1, 1, 3);
++        execute("INSERT INTO %s(p1, p2, v) values (?, ?, ?)", 1, 2, 3);
++        execute("INSERT INTO %s(p1, p2, v) values (?, ?, ?)", 2, 1, 3);
+ 
 -        // Checks filtering with unset
 -        assertInvalidMessage("Predicates on non-primary-key columns (s) are 
not yet supported for non secondary index queries",
 -                             "SELECT * FROM %s WHERE s = ? ALLOW FILTERING", 
unset());
 -        assertInvalidMessage("Predicates on non-primary-key columns (s) are 
not yet supported for non secondary index queries",
 -                             "SELECT * FROM %s WHERE s > ? ALLOW FILTERING", 
unset());
++        assertRows(execute("SELECT * FROM %s WHERE p1 = 1 AND v = 3 ALLOW 
FILTERING"),
++                   row(1, 2, 3),
++                   row(1, 1, 3));
+     }
+ 
+     @Test
      public void testFilteringOnCompactTablesWithoutIndices() throws Throwable
      {
          //----------------------------------------------
@@@ -2515,4 -2250,93 +2533,47 @@@
                         row("a", 3, 5));
          }
      }
+ 
+     @Test
 -    public void testOverlyLargeSelectPK() throws Throwable
 -    {
 -        createTable("CREATE TABLE %s (a text, b text, PRIMARY KEY ((a), b))");
 -
 -        assertInvalidThrow(InvalidRequestException.class,
 -                           "SELECT * FROM %s WHERE a = ?", new 
String(TOO_BIG.array()));
 -    }
 -
 -    @Test
 -    public void testOverlyLargeSelectCK() throws Throwable
 -    {
 -        createTable("CREATE TABLE %s (a text, b text, PRIMARY KEY ((a), b))");
 -
 -        assertInvalidThrow(InvalidRequestException.class,
 -                           "SELECT * FROM %s WHERE a = 'foo' AND b = ?", new 
String(TOO_BIG.array()));
 -    }
 -
 -    @Test
 -    public void testOverlyLargeSelectKeyIn() throws Throwable
 -    {
 -        createTable("CREATE TABLE %s (a text, b text, c text, d text, PRIMARY 
KEY ((a, b, c), d))");
 -
 -        assertInvalidThrow(InvalidRequestException.class,
 -                           "SELECT * FROM %s WHERE a = 'foo' AND b= 'bar' AND 
c IN (?, ?)",
 -                           new String(TOO_BIG.array()), new 
String(TOO_BIG.array()));
 -    }
 -
 -    @Test
+     public void testFilteringWithSecondaryIndex() throws Throwable
+     {
+         createTable("CREATE TABLE %s (pk int, " +
+                     "c1 int, " +
+                     "c2 int, " +
+                     "c3 int, " +
+                     "v int, " +
+                     "PRIMARY KEY (pk, c1, c2, c3))");
+         createIndex("CREATE INDEX v_idx_1 ON %s (v);");
+ 
+         for (int i = 1; i <= 5; i++)
+         {
+             execute("INSERT INTO %s (pk, c1, c2, c3, v) VALUES (?, ?, ?, ?, 
?)", 1, 1, 1, 1, i);
+             execute("INSERT INTO %s (pk, c1, c2, c3, v) VALUES (?, ?, ?, ?, 
?)", 1, 1, 1, i, i);
+             execute("INSERT INTO %s (pk, c1, c2, c3, v) VALUES (?, ?, ?, ?, 
?)", 1, 1, i, i, i);
+             execute("INSERT INTO %s (pk, c1, c2, c3, v) VALUES (?, ?, ?, ?, 
?)", 1, i, i, i, i);
+         }
+ 
+         assertRows(execute("SELECT * FROM %s WHERE pk = 1 AND  c1 > 0 AND c1 
< 5 AND c2 = 1 AND v = 3 ALLOW FILTERING;"),
+                    row(1, 1, 1, 3, 3));
+ 
+         assertEmpty(execute("SELECT * FROM %s WHERE pk = 1 AND  c1 > 1 AND c1 
< 5 AND c2 = 1 AND v = 3 ALLOW FILTERING;"));
+ 
+         assertRows(execute("SELECT * FROM %s WHERE pk = 1 AND  c1 > 1 AND c2 
> 2 AND c3 > 2 AND v = 3 ALLOW FILTERING;"),
+                    row(1, 3, 3, 3, 3));
+ 
+         assertRows(execute("SELECT * FROM %s WHERE pk = 1 AND  c1 > 1 AND c2 
> 2 AND c3 = 3 AND v = 3 ALLOW FILTERING;"),
+                    row(1, 3, 3, 3, 3));
+ 
+         assertRows(execute("SELECT * FROM %s WHERE pk = 1 AND  c1 IN(0,1,2) 
AND c2 = 1 AND v = 3 ALLOW FILTERING;"),
+                    row(1, 1, 1, 3, 3));
+ 
+         assertRows(execute("SELECT * FROM %s WHERE pk = 1 AND  c1 IN(0,1,2) 
AND c2 = 1 AND v = 3"),
+                    row(1, 1, 1, 3, 3));
+ 
+         assertInvalidMessage("Clustering column \"c2\" cannot be restricted 
(preceding column \"c1\" is restricted by a non-EQ relation)",
+                              "SELECT * FROM %s WHERE pk = 1 AND  c1 > 0 AND 
c1 < 5 AND c2 = 1 ALLOW FILTERING;");
+ 
+         assertInvalidMessage("PRIMARY KEY column \"c2\" cannot be restricted 
as preceding column \"c1\" is not restricted",
+                              "SELECT * FROM %s WHERE pk = 1 AND  c2 = 1 ALLOW 
FILTERING;");
+     }
 -
 -    @Test
 -    public void testIndexQueryWithCompositePartitionKey() throws Throwable
 -    {
 -        createTable("CREATE TABLE %s (p1 int, p2 int, v int, PRIMARY KEY 
((p1, p2)))");
 -        assertInvalidMessage("Partition key parts: p2 must be restricted as 
other parts are",
 -                             "SELECT * FROM %s WHERE p1 = 1 AND v = 3 ALLOW 
FILTERING");
 -
 -        createIndex("CREATE INDEX ON %s(v)");
 -
 -        execute("INSERT INTO %s(p1, p2, v) values (?, ?, ?)", 1, 1, 3);
 -        execute("INSERT INTO %s(p1, p2, v) values (?, ?, ?)", 1, 2, 3);
 -        execute("INSERT INTO %s(p1, p2, v) values (?, ?, ?)", 2, 1, 3);
 -
 -        assertRows(execute("SELECT * FROM %s WHERE p1 = 1 AND v = 3 ALLOW 
FILTERING"),
 -                   row(1, 2, 3),
 -                   row(1, 1, 3));
 -    }
  }

Reply via email to