diff --git a/CHANGES.txt b/CHANGES.txt
index 443c8bca6e..329e55cfbc 100644
--- a/CHANGES.txt
+++ b/CHANGES.txt
@@ -43,6 +43,7 @@
* (cqlsh) Show static columns in a different color (CASSANDRA-11059)
* Allow to remove TTLs on table with default_time_to_live (CASSANDRA-11207)
Merged from 3.0:
+ * Allow only DISTINCT queries with partition keys or static columns restrictions (CASSANDRA-11339)
* LogAwareFileLister should only use OLD sstable files in current folder to determine disk consistency (CASSANDRA-11470)
* Notify indexers of expired rows during compaction (CASSANDRA-11329)
* Properly respond with ProtocolError when a v1/v2 native protocol
diff --git a/src/java/org/apache/cassandra/cql3/restrictions/StatementRestrictions.java b/src/java/org/apache/cassandra/cql3/restrictions/StatementRestrictions.java
index ba1e7c0cd3..4b8218954c 100644
--- a/src/java/org/apache/cassandra/cql3/restrictions/StatementRestrictions.java
+++ b/src/java/org/apache/cassandra/cql3/restrictions/StatementRestrictions.java
@@ -419,6 +419,15 @@ public final class StatementRestrictions
return !partitionKeyRestrictions.isEmpty();
}
+ /**
+ * Checks if the restrictions contain any non-primary key restrictions
+ * @return true if the restrictions contain any non-primary key restrictions, false otherwise.
+ */
+ public boolean hasNonPrimaryKeyRestrictions()
+ {
+ return !nonPrimaryKeyRestrictions.isEmpty();
+ }
+
/**
* Returns the partition key components that are not restricted.
* @return the partition key components that are not restricted.
diff --git a/src/java/org/apache/cassandra/cql3/statements/SelectStatement.java b/src/java/org/apache/cassandra/cql3/statements/SelectStatement.java
index 2f64b2543b..e46cb06060 100644
--- a/src/java/org/apache/cassandra/cql3/statements/SelectStatement.java
+++ b/src/java/org/apache/cassandra/cql3/statements/SelectStatement.java
@@ -940,6 +940,10 @@ public class SelectStatement implements CQLStatement
StatementRestrictions restrictions)
throws InvalidRequestException
{
+ checkFalse(restrictions.hasClusteringColumnsRestriction() ||
+ (restrictions.hasNonPrimaryKeyRestrictions() && !restrictions.nonPKRestrictedColumns(true).stream().allMatch(ColumnDefinition::isStatic)),
+ "SELECT DISTINCT with WHERE clause only supports restriction by partition key and/or static columns.");
+
Collection requestedColumns = selection.getColumns();
for (ColumnDefinition def : requestedColumns)
checkFalse(!def.isPartitionKey() && !def.isStatic(),
diff --git a/test/unit/org/apache/cassandra/cql3/validation/operations/SelectTest.java b/test/unit/org/apache/cassandra/cql3/validation/operations/SelectTest.java
index 08c2732511..75334f0ab9 100644
--- a/test/unit/org/apache/cassandra/cql3/validation/operations/SelectTest.java
+++ b/test/unit/org/apache/cassandra/cql3/validation/operations/SelectTest.java
@@ -1253,6 +1253,78 @@ public class SelectTest extends CQLTester
Assert.assertEquals(9, rows.length);
}
+ @Test
+ public void testSelectDistinctWithWhereClause() throws Throwable {
+ createTable("CREATE TABLE %s (k int, a int, b int, PRIMARY KEY (k, a))");
+ createIndex("CREATE INDEX ON %s (b)");
+
+ for (int i = 0; i < 10; i++)
+ {
+ execute("INSERT INTO %s (k, a, b) VALUES (?, ?, ?)", i, i, i);
+ execute("INSERT INTO %s (k, a, b) VALUES (?, ?, ?)", i, i * 10, i * 10);
+ }
+
+ String distinctQueryErrorMsg = "SELECT DISTINCT with WHERE clause only supports restriction by partition key and/or static columns.";
+ assertInvalidMessage(distinctQueryErrorMsg,
+ "SELECT DISTINCT k FROM %s WHERE a >= 80 ALLOW FILTERING");
+
+ assertInvalidMessage(distinctQueryErrorMsg,
+ "SELECT DISTINCT k FROM %s WHERE k IN (1, 2, 3) AND a = 10");
+
+ assertInvalidMessage(distinctQueryErrorMsg,
+ "SELECT DISTINCT k FROM %s WHERE b = 5");
+
+ assertRows(execute("SELECT DISTINCT k FROM %s WHERE k = 1"),
+ row(1));
+ assertRows(execute("SELECT DISTINCT k FROM %s WHERE k IN (5, 6, 7)"),
+ row(5),
+ row(6),
+ row(7));
+
+ // With static columns
+ createTable("CREATE TABLE %s (k int, a int, s int static, b int, PRIMARY KEY (k, a))");
+ createIndex("CREATE INDEX ON %s (b)");
+ for (int i = 0; i < 10; i++)
+ {
+ execute("INSERT INTO %s (k, a, b, s) VALUES (?, ?, ?, ?)", i, i, i, i);
+ execute("INSERT INTO %s (k, a, b, s) VALUES (?, ?, ?, ?)", i, i * 10, i * 10, i * 10);
+ }
+
+ assertRows(execute("SELECT DISTINCT s FROM %s WHERE k = 5"),
+ row(50));
+ assertRows(execute("SELECT DISTINCT s FROM %s WHERE k IN (5, 6, 7)"),
+ row(50),
+ row(60),
+ row(70));
+ }
+
+ @Test
+ public void testSelectDistinctWithWhereClauseOnStaticColumn() throws Throwable
+ {
+ createTable("CREATE TABLE %s (k int, a int, s int static, s1 int static, b int, PRIMARY KEY (k, a))");
+
+ for (int i = 0; i < 10; i++)
+ {
+ execute("INSERT INTO %s (k, a, b, s, s1) VALUES (?, ?, ?, ?, ?)", i, i, i, i, i);
+ execute("INSERT INTO %s (k, a, b, s, s1) VALUES (?, ?, ?, ?, ?)", i, i * 10, i * 10, i * 10, i * 10);
+ }
+
+ execute("INSERT INTO %s (k, a, b, s, s1) VALUES (?, ?, ?, ?, ?)", 2, 10, 10, 10, 10);
+
+ assertRows(execute("SELECT DISTINCT k, s, s1 FROM %s WHERE s = 90 AND s1 = 90 ALLOW FILTERING"),
+ row(9, 90, 90));
+
+ assertRows(execute("SELECT DISTINCT k, s, s1 FROM %s WHERE s = 90 AND s1 = 90 ALLOW FILTERING"),
+ row(9, 90, 90));
+
+ assertRows(execute("SELECT DISTINCT k, s, s1 FROM %s WHERE s = 10 AND s1 = 10 ALLOW FILTERING"),
+ row(1, 10, 10),
+ row(2, 10, 10));
+
+ assertRows(execute("SELECT DISTINCT k, s, s1 FROM %s WHERE k = 1 AND s = 10 AND s1 = 10 ALLOW FILTERING"),
+ row(1, 10, 10));
+ }
+
/**
* Migrated from cql_tests.py:TestCQL.bug_6327_test()
*/