Skip to content

Commit b741795

Browse files
CNDB-13464: Ordering on non-clustering column requires the column to be indexed with a non-analyzed index (#1656)
### What is the issue ... ORDER BY queries return wrong result if we have an analyzed index, as reported in CNDB-13464 ### What does this PR fix and why was it fixed ... ORDER BY should not work with an analyzed index at all. This PR fixes it and improves the error message.
1 parent 9cd33f3 commit b741795

File tree

3 files changed

+29
-5
lines changed

3 files changed

+29
-5
lines changed

src/java/org/apache/cassandra/cql3/restrictions/StatementRestrictions.java

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -84,7 +84,8 @@ public class StatementRestrictions
8484

8585
public static final String GEO_DISTANCE_REQUIRES_INDEX_MESSAGE = "GEO_DISTANCE requires the vector column to be indexed";
8686
public static final String BM25_ORDERING_REQUIRES_ANALYZED_INDEX_MESSAGE = "BM25 ordering on column %s requires an analyzed index";
87-
public static final String NON_CLUSTER_ORDERING_REQUIRES_INDEX_MESSAGE = "Ordering on non-clustering column %s requires the column to be indexed.";
87+
public static final String NON_CLUSTER_ORDERING_REQUIRES_INDEX_MESSAGE =
88+
"Ordering on non-clustering column %s requires the column to be indexed with a non-analyzed index.";
8889
public static final String NON_CLUSTER_ORDERING_REQUIRES_ALL_RESTRICTED_NON_PARTITION_KEY_COLUMNS_INDEXED_MESSAGE =
8990
"Ordering on non-clustering column requires each restricted column to be indexed except for fully-specified partition keys";
9091

src/java/org/apache/cassandra/index/sai/IndexContext.java

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -745,7 +745,8 @@ public boolean supports(Operator op)
745745
if (op == Operator.ORDER_BY_ASC || op == Operator.ORDER_BY_DESC)
746746
return !isCollection()
747747
&& column.isRegular()
748-
&& !(column.type instanceof InetAddressType // Possible, but need to add decoding logic based on
748+
&& !isAnalyzed
749+
&& !(column.type instanceof InetAddressType // Possible, but need to add decoding logic based on
749750
// SAI's TypeUtil.encode method.
750751
|| column.type instanceof DecimalType // Currently truncates to 24 bytes
751752
|| column.type instanceof IntegerType); // Currently truncates to 20 bytes

test/unit/org/apache/cassandra/index/sai/cql/GenericOrderByInvalidQueryTest.java

Lines changed: 25 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@
3737
import org.apache.cassandra.service.QueryState;
3838
import org.apache.cassandra.transport.messages.ResultMessage;
3939

40+
import static org.apache.cassandra.cql3.restrictions.StatementRestrictions.NON_CLUSTER_ORDERING_REQUIRES_INDEX_MESSAGE;
4041
import static org.assertj.core.api.Assertions.assertThatThrownBy;
4142
import static org.junit.Assert.assertEquals;
4243
import static org.junit.Assert.assertNull;
@@ -57,6 +58,27 @@ public void cannotOrderVarintColumn()
5758
executeOrderByAndAssertInvalidRequestException("varint");
5859
}
5960

61+
@Test
62+
public void cannotOrderWithAnalyzedIndex()
63+
{
64+
createTable("CREATE TABLE %s (pk int primary key, val text)");
65+
createIndex("CREATE CUSTOM INDEX test_v1_idx ON %s(val) USING 'StorageAttachedIndex'" +
66+
" WITH OPTIONS = {'index_analyzer': '{\"tokenizer\" : { \"name\" : \"whitespace\", \"args\" : {} }}'}");
67+
68+
execute("INSERT INTO %s (pk, val) VALUES (1, 'ciao amico')");
69+
execute("INSERT INTO %s (pk, val) VALUES (2, 'ciao amico')");
70+
assertRows(execute("SELECT * FROM %s"), row(1, "ciao amico"), row(2, "ciao amico"));
71+
72+
// Verify ORDER BY fails with analyzed index
73+
assertThatThrownBy(() -> execute("SELECT * FROM %s ORDER BY val LIMIT 10"))
74+
.isInstanceOf(InvalidRequestException.class)
75+
.hasMessage(String.format(NON_CLUSTER_ORDERING_REQUIRES_INDEX_MESSAGE, "val"));
76+
77+
// Verify ORDER BY works with non-analyzed index
78+
createIndex("CREATE CUSTOM INDEX ON %s(val) USING 'StorageAttachedIndex'");
79+
assertRows(execute("SELECT * FROM %s ORDER BY val LIMIT 10"), row(1, "ciao amico"), row(2, "ciao amico"));
80+
}
81+
6082
@Test
6183
public void cannotOrderDecimalColumn()
6284
{
@@ -77,10 +99,10 @@ public void cannotOrderTextColumnWithoutIndex()
7799
{
78100
createTable("CREATE TABLE %s (pk int, val text, PRIMARY KEY(pk))");
79101

80-
assertInvalidMessage(String.format(StatementRestrictions.NON_CLUSTER_ORDERING_REQUIRES_INDEX_MESSAGE, "val"),
102+
assertInvalidMessage(String.format(NON_CLUSTER_ORDERING_REQUIRES_INDEX_MESSAGE, "val"),
81103
"SELECT * FROM %s ORDER BY val ASC LIMIT 1");
82104
// Also confirm filtering does not make it work.
83-
assertInvalidMessage(String.format(StatementRestrictions.NON_CLUSTER_ORDERING_REQUIRES_INDEX_MESSAGE, "val"),
105+
assertInvalidMessage(String.format(NON_CLUSTER_ORDERING_REQUIRES_INDEX_MESSAGE, "val"),
84106
"SELECT * FROM %s ORDER BY val LIMIT 5 ALLOW FILTERING");
85107
}
86108

@@ -109,7 +131,7 @@ public void textOrderingMustHaveLimit()
109131
public void testInvalidColumnName()
110132
{
111133
String table = createTable(KEYSPACE, "CREATE TABLE %s (k int, c int, v int, primary key (k, c))");
112-
assertInvalidMessage(String.format("Undefined column name bad_col in table %s", KEYSPACE + "." + table),
134+
assertInvalidMessage(String.format("Undefined column name bad_col in table %s", KEYSPACE + '.' + table),
113135
"SELECT k from %s ORDER BY bad_col LIMIT 1");
114136
}
115137

0 commit comments

Comments
 (0)