You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@cassandra.apache.org by jb...@apache.org on 2014/08/04 14:35:15 UTC
[1/3] git commit: Add support for custom 2i validation patch by Sergio Bossa and Andrés de la Peña for CASSANDRA-7575
Repository: cassandra
Updated Branches:
refs/heads/cassandra-2.1 f302eb784 -> 5009ee31b
refs/heads/trunk f47863e13 -> f1b0c26a1
Add support for custom 2i validation
patch by Sergio Bossa and Andrés de la Peña for CASSANDRA-7575
Project: http://git-wip-us.apache.org/repos/asf/cassandra/repo
Commit: http://git-wip-us.apache.org/repos/asf/cassandra/commit/5009ee31
Tree: http://git-wip-us.apache.org/repos/asf/cassandra/tree/5009ee31
Diff: http://git-wip-us.apache.org/repos/asf/cassandra/diff/5009ee31
Branch: refs/heads/cassandra-2.1
Commit: 5009ee31b933fcc5843417fd65ab9ff91bb74e73
Parents: f302eb7
Author: Jonathan Ellis <jb...@apache.org>
Authored: Mon Aug 4 07:32:56 2014 -0500
Committer: Jonathan Ellis <jb...@apache.org>
Committed: Mon Aug 4 07:32:56 2014 -0500
----------------------------------------------------------------------
CHANGES.txt | 1 +
.../cql3/statements/SelectStatement.java | 15 ++++-
.../db/index/SecondaryIndexManager.java | 47 ++++++++++++++++
.../db/index/SecondaryIndexSearcher.java | 12 ++++
.../db/index/PerRowSecondaryIndexTest.java | 59 +++++++++++++++++---
5 files changed, 124 insertions(+), 10 deletions(-)
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/cassandra/blob/5009ee31/CHANGES.txt
----------------------------------------------------------------------
diff --git a/CHANGES.txt b/CHANGES.txt
index e2cc92b..6ba5e7a 100644
--- a/CHANGES.txt
+++ b/CHANGES.txt
@@ -1,4 +1,5 @@
2.1.1
+ * Add support for custom 2i validation (CASSANDRA-7575)
* Pig support for hadoop CqlInputFormat (CASSANDRA-6454)
* Add listen_interface and rpc_interface options (CASSANDRA-7417)
* Improve schema merge performance (CASSANDRA-7444)
http://git-wip-us.apache.org/repos/asf/cassandra/blob/5009ee31/src/java/org/apache/cassandra/cql3/statements/SelectStatement.java
----------------------------------------------------------------------
diff --git a/src/java/org/apache/cassandra/cql3/statements/SelectStatement.java b/src/java/org/apache/cassandra/cql3/statements/SelectStatement.java
index e4ef0a8..45dd77e 100644
--- a/src/java/org/apache/cassandra/cql3/statements/SelectStatement.java
+++ b/src/java/org/apache/cassandra/cql3/statements/SelectStatement.java
@@ -36,6 +36,7 @@ import org.apache.cassandra.config.CFMetaData;
import org.apache.cassandra.config.ColumnDefinition;
import org.apache.cassandra.db.*;
import org.apache.cassandra.db.filter.*;
+import org.apache.cassandra.db.index.SecondaryIndexManager;
import org.apache.cassandra.db.marshal.*;
import org.apache.cassandra.dht.*;
import org.apache.cassandra.exceptions.*;
@@ -357,7 +358,7 @@ public class SelectStatement implements CQLStatement, MeasurableForPreparedCache
if (filter == null)
return null;
- List<IndexExpression> expressions = getIndexExpressions(options);
+ List<IndexExpression> expressions = getValidatedIndexExpressions(options);
// The LIMIT provided by the user is the number of CQL row he wants returned.
// We want to have getRangeSlice to count the number of columns, not the number of keys.
AbstractBounds<RowPosition> keyBounds = getKeyBounds(options);
@@ -1012,7 +1013,7 @@ public class SelectStatement implements CQLStatement, MeasurableForPreparedCache
return buildBound(b, cfm.clusteringColumns(), columnRestrictions, isReversed, cfm.comparator, options);
}
- public List<IndexExpression> getIndexExpressions(QueryOptions options) throws InvalidRequestException
+ public List<IndexExpression> getValidatedIndexExpressions(QueryOptions options) throws InvalidRequestException
{
if (!usesSecondaryIndexing || restrictedColumns.isEmpty())
return Collections.emptyList();
@@ -1081,6 +1082,14 @@ public class SelectStatement implements CQLStatement, MeasurableForPreparedCache
expressions.add(new IndexExpression(def.name.bytes, IndexExpression.Operator.EQ, value));
}
}
+
+ if (usesSecondaryIndexing)
+ {
+ ColumnFamilyStore cfs = Keyspace.open(keyspace()).getColumnFamilyStore(columnFamily());
+ SecondaryIndexManager secondaryIndexManager = cfs.indexManager;
+ secondaryIndexManager.validateIndexSearchersForQuery(expressions);
+ }
+
return expressions;
}
@@ -1858,7 +1867,7 @@ public class SelectStatement implements CQLStatement, MeasurableForPreparedCache
// the static parts. But 1) we don't have an easy way to do that with 2i and 2) since we don't support index on static columns
// so far, 2i means that you've restricted a non static column, so the query is somewhat non-sensical.
if (stmt.selectsOnlyStaticColumns)
- throw new InvalidRequestException("Queries using 2ndary indexes don't support selecting only static columns");
+ throw new InvalidRequestException("Queries using 2ndary indexes don't support selecting only static columns");
}
private void verifyOrderingIsAllowed(SelectStatement stmt) throws InvalidRequestException
http://git-wip-us.apache.org/repos/asf/cassandra/blob/5009ee31/src/java/org/apache/cassandra/db/index/SecondaryIndexManager.java
----------------------------------------------------------------------
diff --git a/src/java/org/apache/cassandra/db/index/SecondaryIndexManager.java b/src/java/org/apache/cassandra/db/index/SecondaryIndexManager.java
index edb9126..669f651 100644
--- a/src/java/org/apache/cassandra/db/index/SecondaryIndexManager.java
+++ b/src/java/org/apache/cassandra/db/index/SecondaryIndexManager.java
@@ -50,6 +50,7 @@ import org.apache.cassandra.db.compaction.CompactionManager;
import org.apache.cassandra.db.composites.CellName;
import org.apache.cassandra.db.filter.ExtendedFilter;
import org.apache.cassandra.exceptions.ConfigurationException;
+import org.apache.cassandra.exceptions.InvalidRequestException;
import org.apache.cassandra.io.sstable.ReducingKeyIterator;
import org.apache.cassandra.io.sstable.SSTableReader;
import org.apache.cassandra.utils.FBUtilities;
@@ -563,6 +564,52 @@ public class SecondaryIndexManager
}
/**
+ * Validates an union of expression index types. It will throw a {@link RuntimeException} if
+ * any of the expressions in the provided clause is not valid for its index implementation.
+ * @param clause the query clause
+ * @throws org.apache.cassandra.exceptions.InvalidRequestException in case of validation errors
+ */
+ public void validateIndexSearchersForQuery(List<IndexExpression> clause) throws InvalidRequestException
+ {
+ // Group by index type
+ Map<String, Set<IndexExpression>> expressionsByIndexType = new HashMap<>();
+ Map<String, Set<ByteBuffer>> columnsByIndexType = new HashMap<>();
+ for (IndexExpression indexExpression : clause)
+ {
+ SecondaryIndex index = getIndexForColumn(indexExpression.column);
+
+ if (index == null)
+ continue;
+
+ String canonicalIndexName = index.getClass().getCanonicalName();
+ Set<IndexExpression> expressions = expressionsByIndexType.get(canonicalIndexName);
+ Set<ByteBuffer> columns = columnsByIndexType.get(canonicalIndexName);
+ if (expressions == null)
+ {
+ expressions = new HashSet<>();
+ columns = new HashSet<>();
+ expressionsByIndexType.put(canonicalIndexName, expressions);
+ columnsByIndexType.put(canonicalIndexName, columns);
+ }
+
+ expressions.add(indexExpression);
+ columns.add(indexExpression.column);
+ }
+
+ // Validate
+ for (Map.Entry<String, Set<IndexExpression>> expressions : expressionsByIndexType.entrySet())
+ {
+ Set<ByteBuffer> columns = columnsByIndexType.get(expressions.getKey());
+ SecondaryIndex secondaryIndex = getIndexForColumn(columns.iterator().next());
+ SecondaryIndexSearcher searcher = secondaryIndex.createSecondaryIndexSearcher(columns);
+ for (IndexExpression expression : expressions.getValue())
+ {
+ searcher.validate(expression);
+ }
+ }
+ }
+
+ /**
* Performs a search across a number of column indexes
*
* @param filter the column range to restrict to
http://git-wip-us.apache.org/repos/asf/cassandra/blob/5009ee31/src/java/org/apache/cassandra/db/index/SecondaryIndexSearcher.java
----------------------------------------------------------------------
diff --git a/src/java/org/apache/cassandra/db/index/SecondaryIndexSearcher.java b/src/java/org/apache/cassandra/db/index/SecondaryIndexSearcher.java
index 395708a..1239c29 100644
--- a/src/java/org/apache/cassandra/db/index/SecondaryIndexSearcher.java
+++ b/src/java/org/apache/cassandra/db/index/SecondaryIndexSearcher.java
@@ -22,6 +22,7 @@ import java.util.*;
import org.apache.cassandra.db.*;
import org.apache.cassandra.db.filter.ExtendedFilter;
+import org.apache.cassandra.exceptions.InvalidRequestException;
import org.apache.cassandra.tracing.Tracing;
import org.apache.cassandra.utils.FBUtilities;
@@ -62,6 +63,17 @@ public abstract class SecondaryIndexSearcher
}
return false;
}
+
+ /**
+ * Validates the specified {@link IndexExpression}. It will throw an {@link org.apache.cassandra.exceptions.InvalidRequestException}
+ * if the provided clause is not valid for the index implementation.
+ *
+ * @param indexExpression An {@link IndexExpression} to be validated
+ * @throws org.apache.cassandra.exceptions.InvalidRequestException in case of validation errors
+ */
+ public void validate(IndexExpression indexExpression) throws InvalidRequestException
+ {
+ }
protected IndexExpression highestSelectivityPredicate(List<IndexExpression> clause)
{
http://git-wip-us.apache.org/repos/asf/cassandra/blob/5009ee31/test/unit/org/apache/cassandra/db/index/PerRowSecondaryIndexTest.java
----------------------------------------------------------------------
diff --git a/test/unit/org/apache/cassandra/db/index/PerRowSecondaryIndexTest.java b/test/unit/org/apache/cassandra/db/index/PerRowSecondaryIndexTest.java
index 158dd2c..c6a80ea 100644
--- a/test/unit/org/apache/cassandra/db/index/PerRowSecondaryIndexTest.java
+++ b/test/unit/org/apache/cassandra/db/index/PerRowSecondaryIndexTest.java
@@ -18,8 +18,11 @@
package org.apache.cassandra.db.index;
+import java.io.IOException;
import java.nio.ByteBuffer;
import java.util.Arrays;
+import java.util.Collections;
+import java.util.List;
import java.util.Set;
import org.junit.Before;
@@ -28,14 +31,16 @@ import org.junit.Test;
import org.apache.cassandra.SchemaLoader;
import org.apache.cassandra.Util;
import org.apache.cassandra.config.DatabaseDescriptor;
-import org.apache.cassandra.db.Cell;
-import org.apache.cassandra.db.ColumnFamily;
-import org.apache.cassandra.db.ColumnFamilyStore;
-import org.apache.cassandra.db.DecoratedKey;
-import org.apache.cassandra.db.Mutation;
+import org.apache.cassandra.cql3.QueryProcessor;
+import org.apache.cassandra.cql3.UntypedResultSet;
+import org.apache.cassandra.db.*;
+import org.apache.cassandra.db.columniterator.IdentityQueryFilter;
import org.apache.cassandra.db.composites.CellName;
+import org.apache.cassandra.db.filter.ExtendedFilter;
import org.apache.cassandra.db.filter.QueryFilter;
+import org.apache.cassandra.db.marshal.UTF8Type;
import org.apache.cassandra.exceptions.ConfigurationException;
+import org.apache.cassandra.exceptions.InvalidRequestException;
import org.apache.cassandra.utils.ByteBufferUtil;
import org.apache.cassandra.utils.concurrent.OpOrder;
@@ -114,6 +119,30 @@ public class PerRowSecondaryIndexTest extends SchemaLoader
assertTrue(Arrays.equals("k3".getBytes(), PerRowSecondaryIndexTest.TestIndex.LAST_INDEXED_KEY.array()));
}
+
+ @Test
+ public void testInvalidSearch() throws IOException
+ {
+ Mutation rm;
+ rm = new Mutation("PerRowSecondaryIndex", ByteBufferUtil.bytes("k4"));
+ rm.add("Indexed1", Util.cellname("indexed"), ByteBufferUtil.bytes("foo"), 1);
+ rm.apply();
+
+ // test we can search:
+ UntypedResultSet result = QueryProcessor.executeInternal("SELECT * FROM \"PerRowSecondaryIndex\".\"Indexed1\" WHERE indexed = 'foo'");
+ assertEquals(1, result.size());
+
+ // test we can't search if the searcher doesn't validate the expression:
+ try
+ {
+ QueryProcessor.executeInternal("SELECT * FROM \"PerRowSecondaryIndex\".\"Indexed1\" WHERE indexed = 'invalid'");
+ fail("Query should have been invalid!");
+ }
+ catch (Exception e)
+ {
+ assertTrue(e instanceof InvalidRequestException || (e.getCause() != null && (e.getCause() instanceof InvalidRequestException)));
+ }
+ }
public static class TestIndex extends PerRowSecondaryIndex
{
@@ -165,7 +194,23 @@ public class PerRowSecondaryIndexTest extends SchemaLoader
@Override
protected SecondaryIndexSearcher createSecondaryIndexSearcher(Set<ByteBuffer> columns)
{
- return null;
+ return new SecondaryIndexSearcher(baseCfs.indexManager, columns)
+ {
+
+ @Override
+ public List<Row> search(ExtendedFilter filter)
+ {
+ return Arrays.asList(new Row(LAST_INDEXED_KEY, LAST_INDEXED_ROW));
+ }
+
+ @Override
+ public void validate(IndexExpression indexExpression) throws InvalidRequestException
+ {
+ if (indexExpression.value.equals(ByteBufferUtil.bytes("invalid")))
+ throw new InvalidRequestException("Invalid search!");
+ }
+
+ };
}
@Override
@@ -176,7 +221,7 @@ public class PerRowSecondaryIndexTest extends SchemaLoader
@Override
public ColumnFamilyStore getIndexCfs()
{
- return null;
+ return baseCfs;
}
@Override
[3/3] git commit: merge from 2.1
Posted by jb...@apache.org.
merge from 2.1
Project: http://git-wip-us.apache.org/repos/asf/cassandra/repo
Commit: http://git-wip-us.apache.org/repos/asf/cassandra/commit/f1b0c26a
Tree: http://git-wip-us.apache.org/repos/asf/cassandra/tree/f1b0c26a
Diff: http://git-wip-us.apache.org/repos/asf/cassandra/diff/f1b0c26a
Branch: refs/heads/trunk
Commit: f1b0c26a1537c8b9c48abc96ba0c972f4ddf5221
Parents: f47863e 5009ee3
Author: Jonathan Ellis <jb...@apache.org>
Authored: Mon Aug 4 07:35:07 2014 -0500
Committer: Jonathan Ellis <jb...@apache.org>
Committed: Mon Aug 4 07:35:07 2014 -0500
----------------------------------------------------------------------
CHANGES.txt | 1 +
.../cql3/statements/SelectStatement.java | 15 ++++-
.../db/index/SecondaryIndexManager.java | 47 ++++++++++++++++
.../db/index/SecondaryIndexSearcher.java | 12 ++++
.../db/index/PerRowSecondaryIndexTest.java | 58 +++++++++++++++++++-
5 files changed, 127 insertions(+), 6 deletions(-)
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/cassandra/blob/f1b0c26a/CHANGES.txt
----------------------------------------------------------------------
diff --cc CHANGES.txt
index f175b34,6ba5e7a..99a8fed
--- a/CHANGES.txt
+++ b/CHANGES.txt
@@@ -1,22 -1,6 +1,23 @@@
+3.0
+ * Permit configurable timestamps with cassandra-stress (CASSANDRA-7416)
+ * Move sstable RandomAccessReader to nio2, which allows using the
+ FILE_SHARE_DELETE flag on Windows (CASSANDRA-4050)
+ * Remove CQL2 (CASSANDRA-5918)
+ * Add Thrift get_multi_slice call (CASSANDRA-6757)
+ * Optimize fetching multiple cells by name (CASSANDRA-6933)
+ * Allow compilation in java 8 (CASSANDRA-7028)
+ * Make incremental repair default (CASSANDRA-7250)
+ * Enable code coverage thru JaCoCo (CASSANDRA-7226)
+ * Switch external naming of 'column families' to 'tables' (CASSANDRA-4369)
+ * Shorten SSTable path (CASSANDRA-6962)
+ * Use unsafe mutations for most unit tests (CASSANDRA-6969)
+ * Fix race condition during calculation of pending ranges (CASSANDRA-7390)
+
+
2.1.1
+ * Add support for custom 2i validation (CASSANDRA-7575)
* Pig support for hadoop CqlInputFormat (CASSANDRA-6454)
+ * Add duration mode to cassandra-stress (CASSANDRA-7468)
* Add listen_interface and rpc_interface options (CASSANDRA-7417)
* Improve schema merge performance (CASSANDRA-7444)
* Adjust MT depth based on # of partition validating (CASSANDRA-5263)
http://git-wip-us.apache.org/repos/asf/cassandra/blob/f1b0c26a/test/unit/org/apache/cassandra/db/index/PerRowSecondaryIndexTest.java
----------------------------------------------------------------------
diff --cc test/unit/org/apache/cassandra/db/index/PerRowSecondaryIndexTest.java
index 3097099,c6a80ea..92fbf3a
--- a/test/unit/org/apache/cassandra/db/index/PerRowSecondaryIndexTest.java
+++ b/test/unit/org/apache/cassandra/db/index/PerRowSecondaryIndexTest.java
@@@ -18,8 -18,11 +18,10 @@@
package org.apache.cassandra.db.index;
+ import java.io.IOException;
import java.nio.ByteBuffer;
import java.util.Arrays;
-import java.util.Collections;
+ import java.util.List;
import java.util.Set;
import org.junit.Before;
@@@ -29,22 -31,22 +31,32 @@@ import org.junit.Test
import org.apache.cassandra.SchemaLoader;
import org.apache.cassandra.Util;
import org.apache.cassandra.config.DatabaseDescriptor;
+import org.apache.cassandra.config.KSMetaData;
+ import org.apache.cassandra.cql3.QueryProcessor;
+ import org.apache.cassandra.cql3.UntypedResultSet;
-import org.apache.cassandra.db.*;
-import org.apache.cassandra.db.columniterator.IdentityQueryFilter;
+import org.apache.cassandra.db.Cell;
+import org.apache.cassandra.db.ColumnFamily;
+import org.apache.cassandra.db.ColumnFamilyStore;
+import org.apache.cassandra.db.DecoratedKey;
++import org.apache.cassandra.db.IndexExpression;
+import org.apache.cassandra.db.Mutation;
++import org.apache.cassandra.db.Row;
import org.apache.cassandra.db.composites.CellName;
+ import org.apache.cassandra.db.filter.ExtendedFilter;
import org.apache.cassandra.db.filter.QueryFilter;
-import org.apache.cassandra.db.marshal.UTF8Type;
import org.apache.cassandra.exceptions.ConfigurationException;
+ import org.apache.cassandra.exceptions.InvalidRequestException;
+import org.apache.cassandra.locator.SimpleStrategy;
import org.apache.cassandra.utils.ByteBufferUtil;
import org.apache.cassandra.utils.concurrent.OpOrder;
--import static org.junit.Assert.*;
++import static org.junit.Assert.assertEquals;
++import static org.junit.Assert.assertFalse;
++import static org.junit.Assert.assertNotNull;
++import static org.junit.Assert.assertTrue;
++import static org.junit.Assert.fail;
-public class PerRowSecondaryIndexTest extends SchemaLoader
+public class PerRowSecondaryIndexTest
{
// test that when index(key) is called on a PRSI index,
[2/3] git commit: Add support for custom 2i validation patch by Sergio Bossa and Andrés de la Peña for CASSANDRA-7575
Posted by jb...@apache.org.
Add support for custom 2i validation
patch by Sergio Bossa and Andrés de la Peña for CASSANDRA-7575
Project: http://git-wip-us.apache.org/repos/asf/cassandra/repo
Commit: http://git-wip-us.apache.org/repos/asf/cassandra/commit/5009ee31
Tree: http://git-wip-us.apache.org/repos/asf/cassandra/tree/5009ee31
Diff: http://git-wip-us.apache.org/repos/asf/cassandra/diff/5009ee31
Branch: refs/heads/trunk
Commit: 5009ee31b933fcc5843417fd65ab9ff91bb74e73
Parents: f302eb7
Author: Jonathan Ellis <jb...@apache.org>
Authored: Mon Aug 4 07:32:56 2014 -0500
Committer: Jonathan Ellis <jb...@apache.org>
Committed: Mon Aug 4 07:32:56 2014 -0500
----------------------------------------------------------------------
CHANGES.txt | 1 +
.../cql3/statements/SelectStatement.java | 15 ++++-
.../db/index/SecondaryIndexManager.java | 47 ++++++++++++++++
.../db/index/SecondaryIndexSearcher.java | 12 ++++
.../db/index/PerRowSecondaryIndexTest.java | 59 +++++++++++++++++---
5 files changed, 124 insertions(+), 10 deletions(-)
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/cassandra/blob/5009ee31/CHANGES.txt
----------------------------------------------------------------------
diff --git a/CHANGES.txt b/CHANGES.txt
index e2cc92b..6ba5e7a 100644
--- a/CHANGES.txt
+++ b/CHANGES.txt
@@ -1,4 +1,5 @@
2.1.1
+ * Add support for custom 2i validation (CASSANDRA-7575)
* Pig support for hadoop CqlInputFormat (CASSANDRA-6454)
* Add listen_interface and rpc_interface options (CASSANDRA-7417)
* Improve schema merge performance (CASSANDRA-7444)
http://git-wip-us.apache.org/repos/asf/cassandra/blob/5009ee31/src/java/org/apache/cassandra/cql3/statements/SelectStatement.java
----------------------------------------------------------------------
diff --git a/src/java/org/apache/cassandra/cql3/statements/SelectStatement.java b/src/java/org/apache/cassandra/cql3/statements/SelectStatement.java
index e4ef0a8..45dd77e 100644
--- a/src/java/org/apache/cassandra/cql3/statements/SelectStatement.java
+++ b/src/java/org/apache/cassandra/cql3/statements/SelectStatement.java
@@ -36,6 +36,7 @@ import org.apache.cassandra.config.CFMetaData;
import org.apache.cassandra.config.ColumnDefinition;
import org.apache.cassandra.db.*;
import org.apache.cassandra.db.filter.*;
+import org.apache.cassandra.db.index.SecondaryIndexManager;
import org.apache.cassandra.db.marshal.*;
import org.apache.cassandra.dht.*;
import org.apache.cassandra.exceptions.*;
@@ -357,7 +358,7 @@ public class SelectStatement implements CQLStatement, MeasurableForPreparedCache
if (filter == null)
return null;
- List<IndexExpression> expressions = getIndexExpressions(options);
+ List<IndexExpression> expressions = getValidatedIndexExpressions(options);
// The LIMIT provided by the user is the number of CQL row he wants returned.
// We want to have getRangeSlice to count the number of columns, not the number of keys.
AbstractBounds<RowPosition> keyBounds = getKeyBounds(options);
@@ -1012,7 +1013,7 @@ public class SelectStatement implements CQLStatement, MeasurableForPreparedCache
return buildBound(b, cfm.clusteringColumns(), columnRestrictions, isReversed, cfm.comparator, options);
}
- public List<IndexExpression> getIndexExpressions(QueryOptions options) throws InvalidRequestException
+ public List<IndexExpression> getValidatedIndexExpressions(QueryOptions options) throws InvalidRequestException
{
if (!usesSecondaryIndexing || restrictedColumns.isEmpty())
return Collections.emptyList();
@@ -1081,6 +1082,14 @@ public class SelectStatement implements CQLStatement, MeasurableForPreparedCache
expressions.add(new IndexExpression(def.name.bytes, IndexExpression.Operator.EQ, value));
}
}
+
+ if (usesSecondaryIndexing)
+ {
+ ColumnFamilyStore cfs = Keyspace.open(keyspace()).getColumnFamilyStore(columnFamily());
+ SecondaryIndexManager secondaryIndexManager = cfs.indexManager;
+ secondaryIndexManager.validateIndexSearchersForQuery(expressions);
+ }
+
return expressions;
}
@@ -1858,7 +1867,7 @@ public class SelectStatement implements CQLStatement, MeasurableForPreparedCache
// the static parts. But 1) we don't have an easy way to do that with 2i and 2) since we don't support index on static columns
// so far, 2i means that you've restricted a non static column, so the query is somewhat non-sensical.
if (stmt.selectsOnlyStaticColumns)
- throw new InvalidRequestException("Queries using 2ndary indexes don't support selecting only static columns");
+ throw new InvalidRequestException("Queries using 2ndary indexes don't support selecting only static columns");
}
private void verifyOrderingIsAllowed(SelectStatement stmt) throws InvalidRequestException
http://git-wip-us.apache.org/repos/asf/cassandra/blob/5009ee31/src/java/org/apache/cassandra/db/index/SecondaryIndexManager.java
----------------------------------------------------------------------
diff --git a/src/java/org/apache/cassandra/db/index/SecondaryIndexManager.java b/src/java/org/apache/cassandra/db/index/SecondaryIndexManager.java
index edb9126..669f651 100644
--- a/src/java/org/apache/cassandra/db/index/SecondaryIndexManager.java
+++ b/src/java/org/apache/cassandra/db/index/SecondaryIndexManager.java
@@ -50,6 +50,7 @@ import org.apache.cassandra.db.compaction.CompactionManager;
import org.apache.cassandra.db.composites.CellName;
import org.apache.cassandra.db.filter.ExtendedFilter;
import org.apache.cassandra.exceptions.ConfigurationException;
+import org.apache.cassandra.exceptions.InvalidRequestException;
import org.apache.cassandra.io.sstable.ReducingKeyIterator;
import org.apache.cassandra.io.sstable.SSTableReader;
import org.apache.cassandra.utils.FBUtilities;
@@ -563,6 +564,52 @@ public class SecondaryIndexManager
}
/**
+ * Validates an union of expression index types. It will throw a {@link RuntimeException} if
+ * any of the expressions in the provided clause is not valid for its index implementation.
+ * @param clause the query clause
+ * @throws org.apache.cassandra.exceptions.InvalidRequestException in case of validation errors
+ */
+ public void validateIndexSearchersForQuery(List<IndexExpression> clause) throws InvalidRequestException
+ {
+ // Group by index type
+ Map<String, Set<IndexExpression>> expressionsByIndexType = new HashMap<>();
+ Map<String, Set<ByteBuffer>> columnsByIndexType = new HashMap<>();
+ for (IndexExpression indexExpression : clause)
+ {
+ SecondaryIndex index = getIndexForColumn(indexExpression.column);
+
+ if (index == null)
+ continue;
+
+ String canonicalIndexName = index.getClass().getCanonicalName();
+ Set<IndexExpression> expressions = expressionsByIndexType.get(canonicalIndexName);
+ Set<ByteBuffer> columns = columnsByIndexType.get(canonicalIndexName);
+ if (expressions == null)
+ {
+ expressions = new HashSet<>();
+ columns = new HashSet<>();
+ expressionsByIndexType.put(canonicalIndexName, expressions);
+ columnsByIndexType.put(canonicalIndexName, columns);
+ }
+
+ expressions.add(indexExpression);
+ columns.add(indexExpression.column);
+ }
+
+ // Validate
+ for (Map.Entry<String, Set<IndexExpression>> expressions : expressionsByIndexType.entrySet())
+ {
+ Set<ByteBuffer> columns = columnsByIndexType.get(expressions.getKey());
+ SecondaryIndex secondaryIndex = getIndexForColumn(columns.iterator().next());
+ SecondaryIndexSearcher searcher = secondaryIndex.createSecondaryIndexSearcher(columns);
+ for (IndexExpression expression : expressions.getValue())
+ {
+ searcher.validate(expression);
+ }
+ }
+ }
+
+ /**
* Performs a search across a number of column indexes
*
* @param filter the column range to restrict to
http://git-wip-us.apache.org/repos/asf/cassandra/blob/5009ee31/src/java/org/apache/cassandra/db/index/SecondaryIndexSearcher.java
----------------------------------------------------------------------
diff --git a/src/java/org/apache/cassandra/db/index/SecondaryIndexSearcher.java b/src/java/org/apache/cassandra/db/index/SecondaryIndexSearcher.java
index 395708a..1239c29 100644
--- a/src/java/org/apache/cassandra/db/index/SecondaryIndexSearcher.java
+++ b/src/java/org/apache/cassandra/db/index/SecondaryIndexSearcher.java
@@ -22,6 +22,7 @@ import java.util.*;
import org.apache.cassandra.db.*;
import org.apache.cassandra.db.filter.ExtendedFilter;
+import org.apache.cassandra.exceptions.InvalidRequestException;
import org.apache.cassandra.tracing.Tracing;
import org.apache.cassandra.utils.FBUtilities;
@@ -62,6 +63,17 @@ public abstract class SecondaryIndexSearcher
}
return false;
}
+
+ /**
+ * Validates the specified {@link IndexExpression}. It will throw an {@link org.apache.cassandra.exceptions.InvalidRequestException}
+ * if the provided clause is not valid for the index implementation.
+ *
+ * @param indexExpression An {@link IndexExpression} to be validated
+ * @throws org.apache.cassandra.exceptions.InvalidRequestException in case of validation errors
+ */
+ public void validate(IndexExpression indexExpression) throws InvalidRequestException
+ {
+ }
protected IndexExpression highestSelectivityPredicate(List<IndexExpression> clause)
{
http://git-wip-us.apache.org/repos/asf/cassandra/blob/5009ee31/test/unit/org/apache/cassandra/db/index/PerRowSecondaryIndexTest.java
----------------------------------------------------------------------
diff --git a/test/unit/org/apache/cassandra/db/index/PerRowSecondaryIndexTest.java b/test/unit/org/apache/cassandra/db/index/PerRowSecondaryIndexTest.java
index 158dd2c..c6a80ea 100644
--- a/test/unit/org/apache/cassandra/db/index/PerRowSecondaryIndexTest.java
+++ b/test/unit/org/apache/cassandra/db/index/PerRowSecondaryIndexTest.java
@@ -18,8 +18,11 @@
package org.apache.cassandra.db.index;
+import java.io.IOException;
import java.nio.ByteBuffer;
import java.util.Arrays;
+import java.util.Collections;
+import java.util.List;
import java.util.Set;
import org.junit.Before;
@@ -28,14 +31,16 @@ import org.junit.Test;
import org.apache.cassandra.SchemaLoader;
import org.apache.cassandra.Util;
import org.apache.cassandra.config.DatabaseDescriptor;
-import org.apache.cassandra.db.Cell;
-import org.apache.cassandra.db.ColumnFamily;
-import org.apache.cassandra.db.ColumnFamilyStore;
-import org.apache.cassandra.db.DecoratedKey;
-import org.apache.cassandra.db.Mutation;
+import org.apache.cassandra.cql3.QueryProcessor;
+import org.apache.cassandra.cql3.UntypedResultSet;
+import org.apache.cassandra.db.*;
+import org.apache.cassandra.db.columniterator.IdentityQueryFilter;
import org.apache.cassandra.db.composites.CellName;
+import org.apache.cassandra.db.filter.ExtendedFilter;
import org.apache.cassandra.db.filter.QueryFilter;
+import org.apache.cassandra.db.marshal.UTF8Type;
import org.apache.cassandra.exceptions.ConfigurationException;
+import org.apache.cassandra.exceptions.InvalidRequestException;
import org.apache.cassandra.utils.ByteBufferUtil;
import org.apache.cassandra.utils.concurrent.OpOrder;
@@ -114,6 +119,30 @@ public class PerRowSecondaryIndexTest extends SchemaLoader
assertTrue(Arrays.equals("k3".getBytes(), PerRowSecondaryIndexTest.TestIndex.LAST_INDEXED_KEY.array()));
}
+
+ @Test
+ public void testInvalidSearch() throws IOException
+ {
+ Mutation rm;
+ rm = new Mutation("PerRowSecondaryIndex", ByteBufferUtil.bytes("k4"));
+ rm.add("Indexed1", Util.cellname("indexed"), ByteBufferUtil.bytes("foo"), 1);
+ rm.apply();
+
+ // test we can search:
+ UntypedResultSet result = QueryProcessor.executeInternal("SELECT * FROM \"PerRowSecondaryIndex\".\"Indexed1\" WHERE indexed = 'foo'");
+ assertEquals(1, result.size());
+
+ // test we can't search if the searcher doesn't validate the expression:
+ try
+ {
+ QueryProcessor.executeInternal("SELECT * FROM \"PerRowSecondaryIndex\".\"Indexed1\" WHERE indexed = 'invalid'");
+ fail("Query should have been invalid!");
+ }
+ catch (Exception e)
+ {
+ assertTrue(e instanceof InvalidRequestException || (e.getCause() != null && (e.getCause() instanceof InvalidRequestException)));
+ }
+ }
public static class TestIndex extends PerRowSecondaryIndex
{
@@ -165,7 +194,23 @@ public class PerRowSecondaryIndexTest extends SchemaLoader
@Override
protected SecondaryIndexSearcher createSecondaryIndexSearcher(Set<ByteBuffer> columns)
{
- return null;
+ return new SecondaryIndexSearcher(baseCfs.indexManager, columns)
+ {
+
+ @Override
+ public List<Row> search(ExtendedFilter filter)
+ {
+ return Arrays.asList(new Row(LAST_INDEXED_KEY, LAST_INDEXED_ROW));
+ }
+
+ @Override
+ public void validate(IndexExpression indexExpression) throws InvalidRequestException
+ {
+ if (indexExpression.value.equals(ByteBufferUtil.bytes("invalid")))
+ throw new InvalidRequestException("Invalid search!");
+ }
+
+ };
}
@Override
@@ -176,7 +221,7 @@ public class PerRowSecondaryIndexTest extends SchemaLoader
@Override
public ColumnFamilyStore getIndexCfs()
{
- return null;
+ return baseCfs;
}
@Override