18
18
19
19
package org .apache .cassandra .index .sai .plan ;
20
20
21
+ import java .nio .ByteBuffer ;
21
22
import java .util .ArrayList ;
22
23
import java .util .Collections ;
23
24
import java .util .HashSet ;
32
33
33
34
import io .netty .util .concurrent .FastThreadLocal ;
34
35
import org .apache .cassandra .db .Clustering ;
36
+ import org .apache .cassandra .db .ClusteringBound ;
37
+ import org .apache .cassandra .db .ClusteringComparator ;
35
38
import org .apache .cassandra .db .ColumnFamilyStore ;
36
39
import org .apache .cassandra .db .DataRange ;
37
40
import org .apache .cassandra .db .DecoratedKey ;
38
41
import org .apache .cassandra .db .PartitionPosition ;
39
42
import org .apache .cassandra .db .ReadCommand ;
40
43
import org .apache .cassandra .db .ReadExecutionController ;
41
44
import org .apache .cassandra .db .RegularAndStaticColumns ;
45
+ import org .apache .cassandra .db .Slices ;
46
+ import org .apache .cassandra .db .filter .ClusteringIndexFilter ;
47
+ import org .apache .cassandra .db .filter .ClusteringIndexNamesFilter ;
48
+ import org .apache .cassandra .db .filter .ClusteringIndexSliceFilter ;
42
49
import org .apache .cassandra .db .filter .RowFilter ;
43
50
import org .apache .cassandra .db .partitions .PartitionIterator ;
44
51
import org .apache .cassandra .db .partitions .UnfilteredPartitionIterator ;
@@ -138,6 +145,7 @@ private class ResultRetriever extends AbstractIterator<UnfilteredRowIterator> im
138
145
private final PrimaryKey firstPrimaryKey ;
139
146
private final PrimaryKey lastPrimaryKey ;
140
147
private final Iterator <DataRange > keyRanges ;
148
+ private final DataRange firstDataRange ;
141
149
private AbstractBounds <PartitionPosition > currentKeyRange ;
142
150
143
151
private final KeyRangeIterator resultKeyIterator ;
@@ -152,7 +160,8 @@ private class ResultRetriever extends AbstractIterator<UnfilteredRowIterator> im
152
160
private ResultRetriever (ReadExecutionController executionController , boolean topK )
153
161
{
154
162
this .keyRanges = queryController .dataRanges ().iterator ();
155
- this .currentKeyRange = keyRanges .next ().keyRange ();
163
+ this .firstDataRange = keyRanges .next ();
164
+ this .currentKeyRange = firstDataRange .keyRange ();
156
165
this .resultKeyIterator = Operation .buildIterator (queryController );
157
166
this .filterTree = Operation .buildFilter (queryController , queryController .usesStrictFiltering ());
158
167
this .executionController = executionController ;
@@ -175,7 +184,52 @@ public UnfilteredRowIterator computeNext()
175
184
// We can't put this code in the constructor because it may throw and the caller
176
185
// may not be prepared for that.
177
186
if (lastKey == null )
178
- resultKeyIterator .skipTo (firstPrimaryKey );
187
+ {
188
+ PrimaryKey skipTarget = firstPrimaryKey ;
189
+ ClusteringComparator comparator = command .metadata ().comparator ;
190
+
191
+ // If there are no clusterings, the first data range selects an entire partitions, or we have static
192
+ // expressions, don't bother trying to skip forward within the partition.
193
+ if (comparator .size () > 0 && !firstDataRange .selectsAllPartition () && !command .rowFilter ().hasStaticExpression ())
194
+ {
195
+ // Only attempt to skip if the first data range covers a single partition.
196
+ if (currentKeyRange .left .equals (currentKeyRange .right ) && currentKeyRange .left instanceof DecoratedKey )
197
+ {
198
+ DecoratedKey decoratedKey = (DecoratedKey ) currentKeyRange .left ;
199
+ ClusteringIndexFilter filter = firstDataRange .clusteringIndexFilter (decoratedKey );
200
+
201
+ if (filter instanceof ClusteringIndexSliceFilter )
202
+ {
203
+ Slices slices = ((ClusteringIndexSliceFilter ) filter ).requestedSlices ();
204
+
205
+ if (!slices .isEmpty ())
206
+ {
207
+ ClusteringBound <?> startBound = slices .get (0 ).start ();
208
+
209
+ if (!startBound .isEmpty ())
210
+ {
211
+ ByteBuffer [] rawValues = startBound .getBufferArray ();
212
+
213
+ if (rawValues .length == comparator .size ())
214
+ skipTarget = keyFactory .create (decoratedKey , Clustering .make (rawValues ));
215
+ }
216
+ }
217
+ }
218
+ else if (filter instanceof ClusteringIndexNamesFilter )
219
+ {
220
+ ClusteringIndexNamesFilter namesFilter = (ClusteringIndexNamesFilter ) filter ;
221
+
222
+ if (!namesFilter .requestedRows ().isEmpty ())
223
+ {
224
+ Clustering <?> skipClustering = namesFilter .requestedRows ().iterator ().next ();
225
+ skipTarget = keyFactory .create (decoratedKey , skipClustering );
226
+ }
227
+ }
228
+ }
229
+ }
230
+
231
+ resultKeyIterator .skipTo (skipTarget );
232
+ }
179
233
180
234
// Theoretically we wouldn't need this if the caller of computeNext always ran the
181
235
// returned iterators to the completion. Unfortunately, we have no control over the caller behavior here.
0 commit comments