Skip to content

Commit 46d10ef

Browse files
authored
CNDB-15153: Improve logging of partition-restricted index queries (#1956)
Modify the behaviour of `PartitionRangeReadCommand.toCQLString` to print the single-key ranges produced by secondary indexes as an equality on the primary key, rather than as a token range.
1 parent 851accf commit 46d10ef

File tree

4 files changed

+110
-18
lines changed

4 files changed

+110
-18
lines changed

src/java/org/apache/cassandra/db/DataRange.java

Lines changed: 38 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -252,6 +252,21 @@ public DataRange forSubRange(AbstractBounds<PartitionPosition> range)
252252
return new DataRange(range, clusteringIndexFilter);
253253
}
254254

255+
/**
256+
* Whether this range queries a single partition. That happens for partition queries using secondary indexes, which
257+
* are internally mapped to range commands using a single-key data range.
258+
*
259+
* @return {@code true} if this range queries a single partition, {@code false} otherwise
260+
*/
261+
public boolean isSinglePartition()
262+
{
263+
return keyRange.inclusiveLeft() &&
264+
keyRange.inclusiveRight() &&
265+
keyRange.left instanceof DecoratedKey &&
266+
keyRange.right instanceof DecoratedKey &&
267+
keyRange.left.equals(keyRange.right);
268+
}
269+
255270
public String toString(TableMetadata metadata)
256271
{
257272
return String.format("range=%s pfilter=%s", keyRange.getString(metadata.partitionKeyType), clusteringIndexFilter.toString(metadata));
@@ -265,17 +280,33 @@ public String toCQLString(TableMetadata metadata)
265280
CqlBuilder builder = new CqlBuilder();
266281

267282
boolean needAnd = false;
268-
if (!startKey().isMinimum())
283+
284+
if (isSinglePartition())
269285
{
270-
appendClause(startKey(), builder, metadata, true, keyRange.isStartInclusive());
286+
/*
287+
* Single partition queries using an index are internally mapped to range commands where the start and end
288+
* key are the same. If that is the case, we want to print the query as an equality on the partition key
289+
* rather than a token range, as if it was a partition query, for better readability.
290+
*/
291+
builder.append(ColumnMetadata.toCQLString(metadata.partitionKeyColumns()));
292+
builder.append(" = ");
293+
appendKeyString(builder, metadata.partitionKeyType, ((DecoratedKey) startKey()).getKey());
271294
needAnd = true;
272295
}
273-
if (!stopKey().isMinimum())
296+
else
274297
{
275-
if (needAnd)
276-
builder.append(" AND ");
277-
appendClause(stopKey(), builder, metadata, false, keyRange.isEndInclusive());
278-
needAnd = true;
298+
if (!startKey().isMinimum())
299+
{
300+
appendClause(startKey(), builder, metadata, true, keyRange.isStartInclusive());
301+
needAnd = true;
302+
}
303+
if (!stopKey().isMinimum())
304+
{
305+
if (needAnd)
306+
builder.append(" AND ");
307+
appendClause(stopKey(), builder, metadata, false, keyRange.isEndInclusive());
308+
needAnd = true;
309+
}
279310
}
280311

281312
String filterString = clusteringIndexFilter.toCQLString(metadata);

src/java/org/apache/cassandra/db/filter/ClusteringIndexNamesFilter.java

Lines changed: 20 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -167,12 +167,28 @@ public String toCQLString(TableMetadata metadata)
167167
return "";
168168

169169
StringBuilder sb = new StringBuilder();
170-
sb.append('(').append(ColumnMetadata.toCQLString(metadata.clusteringColumns())).append(')');
171-
sb.append(clusterings.size() == 1 ? " = " : " IN (");
170+
171+
boolean multipleColumns = metadata.clusteringColumns().size() > 1;
172+
boolean multipleClusterings = clusterings.size() > 1;
173+
174+
if (multipleColumns)
175+
sb.append('(');
176+
sb.append(ColumnMetadata.toCQLString(metadata.clusteringColumns()));
177+
if (multipleColumns)
178+
sb.append(')');
179+
sb.append(multipleClusterings ? " IN (" : " = ");
172180
int i = 0;
173181
for (Clustering<?> clustering : clusterings)
174-
sb.append(i++ == 0 ? "" : ", ").append('(').append(clustering.toCQLString(metadata)).append(')');
175-
sb.append(clusterings.size() == 1 ? "" : ")");
182+
{
183+
sb.append(i++ == 0 ? "" : ", ");
184+
if (multipleColumns)
185+
sb.append('(');
186+
sb.append(clustering.toCQLString(metadata));
187+
if (multipleColumns)
188+
sb.append(')');
189+
}
190+
if (multipleClusterings)
191+
sb.append(')');
176192

177193
appendOrderByToCQLString(metadata, sb);
178194
return sb.toString();

test/unit/org/apache/cassandra/db/PartitionRangeReadCommandCQLTest.java

Lines changed: 49 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,9 @@
1717

1818
import org.junit.Test;
1919

20+
import org.apache.cassandra.config.DatabaseDescriptor;
21+
import org.apache.cassandra.db.marshal.Int32Type;
22+
import org.apache.cassandra.dht.IPartitioner;
2023
import org.assertj.core.api.Assertions;
2124

2225
public class PartitionRangeReadCommandCQLTest extends ReadCommandCQLTester<PartitionRangeReadCommand>
@@ -28,19 +31,62 @@ public void testToCQLString()
2831

2932
assertToCQLString("SELECT * FROM %s", "SELECT * FROM %s");
3033

31-
assertToCQLString("SELECT * FROM %s WHERE c = 0 ALLOW FILTERING", "SELECT * FROM %s WHERE (c) = (0)");
32-
assertToCQLString("SELECT * FROM %s WHERE (c) = (0) ALLOW FILTERING", "SELECT * FROM %s WHERE (c) = (0)");
34+
assertToCQLString("SELECT * FROM %s WHERE c = 0 ALLOW FILTERING", "SELECT * FROM %s WHERE c = 0");
35+
assertToCQLString("SELECT * FROM %s WHERE (c) = (0) ALLOW FILTERING", "SELECT * FROM %s WHERE c = 0");
3336
assertToCQLString("SELECT * FROM %s WHERE c > 0 ALLOW FILTERING", "SELECT * FROM %s WHERE c > 0");
3437
assertToCQLString("SELECT * FROM %s WHERE c < 0 ALLOW FILTERING", "SELECT * FROM %s WHERE c < 0");
3538
assertToCQLString("SELECT * FROM %s WHERE c >= 0 ALLOW FILTERING", "SELECT * FROM %s WHERE c >= 0");
3639
assertToCQLString("SELECT * FROM %s WHERE c <= 0 ALLOW FILTERING", "SELECT * FROM %s WHERE c <= 0");
3740

3841
assertToCQLString("SELECT * FROM %s WHERE v = 1 ALLOW FILTERING", "SELECT * FROM %s WHERE v = 1");
39-
assertToCQLString("SELECT * FROM %s WHERE c = 0 AND v = 1 ALLOW FILTERING", "SELECT * FROM %s WHERE v = 1 AND (c) = (0)");
42+
assertToCQLString("SELECT * FROM %s WHERE c = 0 AND v = 1 ALLOW FILTERING", "SELECT * FROM %s WHERE v = 1 AND c = 0");
4043
assertToCQLString("SELECT * FROM %s WHERE c > 0 AND v = 1 ALLOW FILTERING", "SELECT * FROM %s WHERE v = 1 AND c > 0");
4144
assertToCQLString("SELECT * FROM %s WHERE c < 0 AND v = 1 ALLOW FILTERING", "SELECT * FROM %s WHERE v = 1 AND c < 0");
4245
assertToCQLString("SELECT * FROM %s WHERE c >= 0 AND v = 1 ALLOW FILTERING", "SELECT * FROM %s WHERE v = 1 AND c >= 0");
4346
assertToCQLString("SELECT * FROM %s WHERE c <= 0 AND v = 1 ALLOW FILTERING", "SELECT * FROM %s WHERE v = 1 AND c <= 0");
47+
48+
// test with token restrictions
49+
IPartitioner partitioner = DatabaseDescriptor.getPartitioner();
50+
String token = partitioner.getToken(Int32Type.instance.decompose(0)).toString();
51+
assertToCQLString("SELECT * FROM %s WHERE token(k) > token(0)",
52+
"SELECT * FROM %s WHERE token(k) > " + token);
53+
assertToCQLString("SELECT * FROM %s WHERE token(k) >= token(0)",
54+
"SELECT * FROM %s WHERE token(k) >= " + token);
55+
assertToCQLString("SELECT * FROM %s WHERE token(k) >= token(0) AND token(k) <= token(0)",
56+
"SELECT * FROM %s WHERE token(k) >= " + token + " AND token(k) <= " + token);
57+
58+
// test with a secondary index (indexed queries are always mapped to range commands)
59+
createIndex("CREATE INDEX ON %s(v)");
60+
assertToCQLString("SELECT * FROM %s WHERE v = 0", "SELECT * FROM %s WHERE v = 0");
61+
assertToCQLString("SELECT * FROM %s WHERE v = 0 AND k = 0", "SELECT * FROM %s WHERE v = 0 AND k = 0");
62+
assertToCQLString("SELECT * FROM %s WHERE v = 0 AND k = 0 AND c = 0", "SELECT * FROM %s WHERE v = 0 AND k = 0 AND c = 0");
63+
assertToCQLString("SELECT * FROM %s WHERE v = 0 AND k = 0 AND c > 0", "SELECT * FROM %s WHERE v = 0 AND k = 0 AND c > 0");
64+
assertToCQLString("SELECT * FROM %s WHERE v = 0 AND k = 0 AND c < 0", "SELECT * FROM %s WHERE v = 0 AND k = 0 AND c < 0");
65+
assertToCQLString("SELECT * FROM %s WHERE v = 0 AND k = 0 AND c >= 0", "SELECT * FROM %s WHERE v = 0 AND k = 0 AND c >= 0");
66+
assertToCQLString("SELECT * FROM %s WHERE v = 0 AND k = 0 AND c <= 0", "SELECT * FROM %s WHERE v = 0 AND k = 0 AND c <= 0");
67+
assertToCQLString("SELECT * FROM %s WHERE v = 0 AND k = 0 AND c IN (0)", "SELECT * FROM %s WHERE v = 0 AND k = 0 AND c = 0");
68+
assertToCQLString("SELECT * FROM %s WHERE v = 0 AND k = 0 AND c IN (0, 1)", "SELECT * FROM %s WHERE v = 0 AND k = 0 AND c IN (0, 1)");
69+
assertToCQLString("SELECT * FROM %s WHERE v = 0 AND token(k) > token(0)",
70+
"SELECT * FROM %s WHERE v = 0 AND token(k) > " + token);
71+
assertToCQLString("SELECT * FROM %s WHERE v = 0 AND token(k) >= token(0)",
72+
"SELECT * FROM %s WHERE v = 0 AND token(k) >= " + token);
73+
assertToCQLString("SELECT * FROM %s WHERE v = 0 AND token(k) >= token(0) AND token(k) <= token(0)",
74+
"SELECT * FROM %s WHERE v = 0 AND token(k) >= " + token + " AND token(k) <= " + token);
75+
76+
// test with index and multi-column clustering
77+
createTable("CREATE TABLE %s (k int, c1 int, c2 int,v int, PRIMARY KEY (k, c1, c2))");
78+
createIndex("CREATE INDEX ON %s(v)");
79+
assertToCQLString("SELECT * FROM %s WHERE v = 0 AND k = 0", "SELECT * FROM %s WHERE v = 0 AND k = 0");
80+
assertToCQLString("SELECT * FROM %s WHERE v = 0 AND k = 0 AND c1 = 1", "SELECT * FROM %s WHERE v = 0 AND k = 0 AND c1 = 1");
81+
assertToCQLString("SELECT * FROM %s WHERE v = 0 AND k = 0 AND c1 > 1", "SELECT * FROM %s WHERE v = 0 AND k = 0 AND c1 > 1");
82+
assertToCQLString("SELECT * FROM %s WHERE v = 0 AND k = 0 AND c1 < 1", "SELECT * FROM %s WHERE v = 0 AND k = 0 AND c1 < 1");
83+
assertToCQLString("SELECT * FROM %s WHERE v = 0 AND k = 0 AND c1 >= 1", "SELECT * FROM %s WHERE v = 0 AND k = 0 AND c1 >= 1");
84+
assertToCQLString("SELECT * FROM %s WHERE v = 0 AND k = 0 AND c1 <= 1", "SELECT * FROM %s WHERE v = 0 AND k = 0 AND c1 <= 1");
85+
assertToCQLString("SELECT * FROM %s WHERE v = 0 AND k = 0 AND c1 = 1 AND c2 = 2", "SELECT * FROM %s WHERE v = 0 AND k = 0 AND (c1, c2) = (1, 2)");
86+
assertToCQLString("SELECT * FROM %s WHERE v = 0 AND k = 0 AND c1 = 1 AND c2 > 2", "SELECT * FROM %s WHERE v = 0 AND k = 0 AND c1 = 1 AND c2 > 2");
87+
assertToCQLString("SELECT * FROM %s WHERE v = 0 AND k = 0 AND c1 = 1 AND c2 < 2", "SELECT * FROM %s WHERE v = 0 AND k = 0 AND c1 = 1 AND c2 < 2");
88+
assertToCQLString("SELECT * FROM %s WHERE v = 0 AND k = 0 AND c1 = 1 AND c2 >= 2", "SELECT * FROM %s WHERE v = 0 AND k = 0 AND c1 = 1 AND c2 >= 2");
89+
assertToCQLString("SELECT * FROM %s WHERE v = 0 AND k = 0 AND c1 = 1 AND c2 <= 2", "SELECT * FROM %s WHERE v = 0 AND k = 0 AND c1 = 1 AND c2 <= 2");
4490
}
4591

4692
@Override
@@ -51,4 +97,3 @@ protected PartitionRangeReadCommand parseCommand(String query)
5197
return (PartitionRangeReadCommand) command;
5298
}
5399
}
54-

test/unit/org/apache/cassandra/db/SinglePartitionReadCommandCQLTest.java

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -46,15 +46,15 @@ public void testToCQLString()
4646

4747
assertToCQLString("SELECT * FROM %s WHERE k = 0", "SELECT * FROM %s WHERE k = 0");
4848

49-
assertToCQLString("SELECT * FROM %s WHERE k = 0 AND c = 0", "SELECT * FROM %s WHERE k = 0 AND (c) = (0)");
50-
assertToCQLString("SELECT * FROM %s WHERE k = 0 AND (c) = (0)", "SELECT * FROM %s WHERE k = 0 AND (c) = (0)");
49+
assertToCQLString("SELECT * FROM %s WHERE k = 0 AND c = 0", "SELECT * FROM %s WHERE k = 0 AND c = 0");
50+
assertToCQLString("SELECT * FROM %s WHERE k = 0 AND (c) = (0)", "SELECT * FROM %s WHERE k = 0 AND c = 0");
5151
assertToCQLString("SELECT * FROM %s WHERE k = 0 AND c > 0", "SELECT * FROM %s WHERE k = 0 AND c > 0");
5252
assertToCQLString("SELECT * FROM %s WHERE k = 0 AND c < 0", "SELECT * FROM %s WHERE k = 0 AND c < 0");
5353
assertToCQLString("SELECT * FROM %s WHERE k = 0 AND c >= 0", "SELECT * FROM %s WHERE k = 0 AND c >= 0");
5454
assertToCQLString("SELECT * FROM %s WHERE k = 0 AND c <= 0", "SELECT * FROM %s WHERE k = 0 AND c <= 0");
5555

5656
assertToCQLString("SELECT * FROM %s WHERE k = 0 AND v = 1 ALLOW FILTERING", "SELECT * FROM %s WHERE k = 0 AND v = 1");
57-
assertToCQLString("SELECT * FROM %s WHERE k = 0 AND c = 0 AND v = 1 ALLOW FILTERING", "SELECT * FROM %s WHERE k = 0 AND v = 1 AND (c) = (0)");
57+
assertToCQLString("SELECT * FROM %s WHERE k = 0 AND c = 0 AND v = 1 ALLOW FILTERING", "SELECT * FROM %s WHERE k = 0 AND v = 1 AND c = 0");
5858
assertToCQLString("SELECT * FROM %s WHERE k = 0 AND c > 0 AND v = 1 ALLOW FILTERING", "SELECT * FROM %s WHERE k = 0 AND v = 1 AND c > 0");
5959
assertToCQLString("SELECT * FROM %s WHERE k = 0 AND c < 0 AND v = 1 ALLOW FILTERING", "SELECT * FROM %s WHERE k = 0 AND v = 1 AND c < 0");
6060
assertToCQLString("SELECT * FROM %s WHERE k = 0 AND c >= 0 AND v = 1 ALLOW FILTERING", "SELECT * FROM %s WHERE k = 0 AND v = 1 AND c >= 0");

0 commit comments

Comments
 (0)