Merge lp:~mmcm/akiban-server/lookahead-quantum-index-scan into lp:~akiban-technologies/akiban-server/trunk
- lookahead-quantum-index-scan
- Merge into trunk
Status: | Superseded |
---|---|
Proposed branch: | lp:~mmcm/akiban-server/lookahead-quantum-index-scan |
Merge into: | lp:~akiban-technologies/akiban-server/trunk |
Prerequisite: | lp:~mmcm/akiban-server/map-query-bindings |
Diff against target: |
1229 lines (+641/-49) 23 files modified
src/main/java/com/akiban/qp/operator/API.java (+13/-2) src/main/java/com/akiban/qp/operator/IndexScan_Default.java (+220/-10) src/main/java/com/akiban/qp/operator/Map_NestedLoops.java (+14/-4) src/main/java/com/akiban/server/explain/Label.java (+1/-0) src/main/java/com/akiban/sql/optimizer/rule/OperatorAssembler.java (+7/-3) src/main/java/com/akiban/sql/optimizer/rule/PipelineConfiguration.java (+67/-0) src/main/java/com/akiban/sql/optimizer/rule/SchemaRulesContext.java (+11/-0) src/main/java/com/akiban/sql/server/ServerOperatorCompiler.java (+1/-0) src/main/java/com/akiban/sql/server/ServerSession.java (+4/-0) src/main/java/com/akiban/sql/server/ServerSessionBase.java (+10/-0) src/main/java/com/akiban/util/SparseArray.java (+2/-1) src/main/resources/com/akiban/server/service/config/configuration-defaults.properties (+1/-0) src/test/java/com/akiban/server/test/ApiTestBase.java (+4/-0) src/test/java/com/akiban/server/test/costmodel/MapCT.java (+2/-1) src/test/java/com/akiban/server/test/it/qp/AncestorLookup_NestedIT.java (+8/-8) src/test/java/com/akiban/server/test/it/qp/BranchLookup_NestedIT.java (+8/-8) src/test/java/com/akiban/server/test/it/qp/IndexScanLookaheadIT.java (+227/-0) src/test/java/com/akiban/server/test/it/qp/Map_NestedLoopsIT.java (+10/-10) src/test/java/com/akiban/server/test/it/qp/Map_NestedLoopsPipelineIT.java (+26/-0) src/test/java/com/akiban/server/test/it/qp/Sort_InsertionLimitedIT.java (+1/-1) src/test/java/com/akiban/server/test/it/qp/UnionAll_DefaultIT.java (+1/-1) src/test/java/com/akiban/sql/optimizer/OperatorCompilerTest.java (+2/-0) src/test/java/com/akiban/sql/optimizer/rule/RulesTestContext.java (+1/-0) |
To merge this branch: | bzr merge lp:~mmcm/akiban-server/lookahead-quantum-index-scan |
Related bugs: |
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
Akiban Technologies | Pending | ||
Review via email: mp+174297@code.launchpad.net |
This proposal has been superseded by a proposal from 2013-07-13.
Commit message
Description of the change
Pipelining option for IndexScan_Default.
When quantum is set > 1, open() tries to keep this many Cursors in play. It does this by looking ahead in the bindings stream and opening new cursors for those that match the depth at which it is being opened. It queues these bindings (whether a cursor was opened or not) so that it can later honor nextBindings().
Most of the trickiness is in implementing the cursor lifecycle. For instance, although all these cursors are open ahead of where downstream is, and one of them gets associated when moving to its bindings, the IS cursor itself is not yet active until open().
Nathan Williams (nwilliams) wrote : | # |
- 2732. By Mike McMahon
-
Merge from origin.
- 2733. By Mike McMahon
-
Update test.
- 2734. By Mike McMahon
-
Merge from origin.
- 2735. By Mike McMahon
-
Use new configuration technique.
- 2736. By Mike McMahon
-
Javadoc.
Unmerged revisions
Preview Diff
1 | === modified file 'src/main/java/com/akiban/qp/operator/API.java' |
2 | --- src/main/java/com/akiban/qp/operator/API.java 2013-07-10 18:42:14 +0000 |
3 | +++ src/main/java/com/akiban/qp/operator/API.java 2013-07-13 21:10:31 +0000 |
4 | @@ -385,7 +385,16 @@ |
5 | Ordering ordering, |
6 | IndexScanSelector indexScanSelector) |
7 | { |
8 | - return new IndexScan_Default(indexType, indexKeyRange, ordering, indexScanSelector, USE_PVALUES); |
9 | + return indexScan_Default(indexType, indexKeyRange, ordering, indexScanSelector, 1); |
10 | + } |
11 | + |
12 | + public static Operator indexScan_Default(IndexRowType indexType, |
13 | + IndexKeyRange indexKeyRange, |
14 | + Ordering ordering, |
15 | + IndexScanSelector indexScanSelector, |
16 | + int lookaheadQuantum) |
17 | + { |
18 | + return new IndexScan_Default(indexType, indexKeyRange, ordering, indexScanSelector, lookaheadQuantum, USE_PVALUES); |
19 | } |
20 | |
21 | // Select |
22 | @@ -497,9 +506,11 @@ |
23 | public static Operator map_NestedLoops(Operator outerInput, |
24 | Operator innerInput, |
25 | int inputBindingPosition, |
26 | + boolean pipeline, |
27 | int depth) |
28 | { |
29 | - return new Map_NestedLoops(outerInput, innerInput, inputBindingPosition, depth); |
30 | + return new Map_NestedLoops(outerInput, innerInput, inputBindingPosition, |
31 | + pipeline, depth); |
32 | } |
33 | |
34 | // IfEmpty |
35 | |
36 | === modified file 'src/main/java/com/akiban/qp/operator/IndexScan_Default.java' |
37 | --- src/main/java/com/akiban/qp/operator/IndexScan_Default.java 2013-07-09 17:26:33 +0000 |
38 | +++ src/main/java/com/akiban/qp/operator/IndexScan_Default.java 2013-07-13 21:10:31 +0000 |
39 | @@ -31,7 +31,9 @@ |
40 | import org.slf4j.Logger; |
41 | import org.slf4j.LoggerFactory; |
42 | |
43 | +import java.util.ArrayDeque; |
44 | import java.util.List; |
45 | +import java.util.Queue; |
46 | |
47 | /** |
48 | |
49 | @@ -47,20 +49,19 @@ |
50 | |
51 | <li><b>IndexRowType indexType:</b> The index's type. |
52 | |
53 | - <li><b>boolean reverse:</b> Indicates whether keys should be visited |
54 | - in ascending order (reverse = false) or descending order (reverse = |
55 | - true). |
56 | - |
57 | <li><b>IndexKeyRange indexKeyRange:</b> Describes the range of keys |
58 | to be visited. The values specified by the indexKeyRange should |
59 | restrict one or more of the leading fields of the index. If null, |
60 | then the entire index will be scanned. |
61 | |
62 | - <li><b>UserTableRowType innerJoinUntilRowType</b>: On a table index, |
63 | - this must be the UserTableRowType of the Index's table (but it's |
64 | - ignored). On a group index, this is the table until which the group |
65 | - index is interpreted with INNER JOIN semantics. The specified row |
66 | - type must be within the group index's branch segment. |
67 | + <li><b>Ordering ordering:</b> Indicates whether keys should be visited |
68 | + in ascending order or descending order. |
69 | + |
70 | + <li><b>IndexScanSelector scanSelector:</b> On a group index, specify which |
71 | + tables must be present for OUTER JOIN semantics. |
72 | + |
73 | + <li><b>int lookaheadQuantum:</b> Number of cursors to try to keep open by looking |
74 | + ahead in bindings stream. |
75 | |
76 | </ul> |
77 | |
78 | @@ -165,7 +166,12 @@ |
79 | @Override |
80 | protected Cursor cursor(QueryContext context, QueryBindingsCursor bindingsCursor) |
81 | { |
82 | - return new Execution(context, bindingsCursor); |
83 | + if (lookaheadQuantum <= 1) { |
84 | + return new Execution(context, bindingsCursor); |
85 | + } |
86 | + else { |
87 | + return new LookaheadExecution(context, bindingsCursor, lookaheadQuantum); |
88 | + } |
89 | } |
90 | |
91 | // IndexScan_Default interface |
92 | @@ -174,6 +180,7 @@ |
93 | IndexKeyRange indexKeyRange, |
94 | API.Ordering ordering, |
95 | IndexScanSelector scanSelector, |
96 | + int lookaheadQuantum, |
97 | boolean usePValues) |
98 | { |
99 | ArgumentValidation.notNull("indexType", indexType); |
100 | @@ -182,6 +189,7 @@ |
101 | this.ordering = ordering; |
102 | this.indexKeyRange = indexKeyRange; |
103 | this.scanSelector = scanSelector; |
104 | + this.lookaheadQuantum = lookaheadQuantum; |
105 | this.usePValues = usePValues; |
106 | } |
107 | |
108 | @@ -198,6 +206,7 @@ |
109 | private final API.Ordering ordering; |
110 | private final IndexKeyRange indexKeyRange; |
111 | private final IndexScanSelector scanSelector; |
112 | + private final int lookaheadQuantum; |
113 | private final boolean usePValues; |
114 | |
115 | @Override |
116 | @@ -372,4 +381,205 @@ |
117 | |
118 | private final RowCursor cursor; |
119 | } |
120 | + |
121 | + static final class BindingsAndCursor { |
122 | + QueryBindings bindings; |
123 | + RowCursor cursor; |
124 | + |
125 | + BindingsAndCursor(QueryBindings bindings, RowCursor cursor) { |
126 | + this.bindings = bindings; |
127 | + this.cursor = cursor; |
128 | + } |
129 | + } |
130 | + |
131 | + private class LookaheadExecution extends OperatorCursor |
132 | + { |
133 | + // Cursor interface |
134 | + |
135 | + @Override |
136 | + public void open() { |
137 | + TAP_OPEN.in(); |
138 | + try { |
139 | + CursorLifecycle.checkIdle(this); |
140 | + if (currentCursor != null) { |
141 | + currentCursor.open(); |
142 | + } |
143 | + else if (pendingCursor != null) { |
144 | + currentCursor = pendingCursor; |
145 | + pendingCursor = null; |
146 | + } |
147 | + else { |
148 | + // At the very beginning, the pipeline isn't started. |
149 | + currentCursor = openACursor(currentBindings); |
150 | + } |
151 | + while (!cursorPool.isEmpty() && !bindingsExhausted) { |
152 | + QueryBindings bindings = bindingsCursor.nextBindings(); |
153 | + if (bindings == null) { |
154 | + bindingsExhausted = true; |
155 | + break; |
156 | + } |
157 | + RowCursor cursor = null; |
158 | + if (bindings.getDepth() == currentBindings.getDepth()) { |
159 | + cursor = openACursor(bindings); |
160 | + LOG.debug("IndexScan: lookahead {}", bindings); |
161 | + } |
162 | + pendingBindings.add(new BindingsAndCursor(bindings, cursor)); |
163 | + } |
164 | + } finally { |
165 | + TAP_OPEN.out(); |
166 | + } |
167 | + } |
168 | + |
169 | + @Override |
170 | + public Row next() { |
171 | + if (TAP_NEXT_ENABLED) { |
172 | + TAP_NEXT.in(); |
173 | + } |
174 | + try { |
175 | + checkQueryCancelation(); |
176 | + Row row = currentCursor.next(); |
177 | + if (row == null) { |
178 | + currentCursor.close(); |
179 | + } |
180 | + if (LOG_EXECUTION) { |
181 | + LOG.debug("IndexScan: yield {}", row); |
182 | + } |
183 | + return row; |
184 | + } finally { |
185 | + if (TAP_NEXT_ENABLED) { |
186 | + TAP_NEXT.out(); |
187 | + } |
188 | + } |
189 | + } |
190 | + |
191 | + @Override |
192 | + public void jump(Row row, ColumnSelector columnSelector) { |
193 | + currentCursor.jump(row, columnSelector); |
194 | + } |
195 | + |
196 | + @Override |
197 | + public void close() { |
198 | + if (currentCursor != null) { |
199 | + currentCursor.close(); |
200 | + } |
201 | + } |
202 | + |
203 | + @Override |
204 | + public void destroy() { |
205 | + CursorLifecycle.checkIdleOrActive(this); |
206 | + if (currentCursor != null) { |
207 | + currentCursor.destroy(); |
208 | + currentCursor = null; |
209 | + } |
210 | + if (pendingCursor != null) { |
211 | + pendingCursor.destroy(); |
212 | + pendingCursor = null; |
213 | + } |
214 | + recyclePending(); |
215 | + while (true) { |
216 | + RowCursor cursor = cursorPool.poll(); |
217 | + if (cursor == null) break; |
218 | + cursor.destroy(); |
219 | + } |
220 | + destroyed = true; |
221 | + } |
222 | + |
223 | + @Override |
224 | + public boolean isIdle() { |
225 | + return (currentCursor != null) ? currentCursor.isIdle() : !destroyed; |
226 | + } |
227 | + |
228 | + @Override |
229 | + public boolean isActive() { |
230 | + return ((currentCursor != null) && currentCursor.isActive()); |
231 | + } |
232 | + |
233 | + @Override |
234 | + public boolean isDestroyed() { |
235 | + return destroyed; |
236 | + } |
237 | + |
238 | + @Override |
239 | + public void openBindings() { |
240 | + recyclePending(); |
241 | + bindingsCursor.openBindings(); |
242 | + bindingsExhausted = false; |
243 | + currentCursor = pendingCursor = null; |
244 | + } |
245 | + |
246 | + @Override |
247 | + public QueryBindings nextBindings() { |
248 | + if (currentCursor != null) { |
249 | + cursorPool.add(currentCursor); |
250 | + currentCursor = null; |
251 | + } |
252 | + if (pendingCursor != null) { |
253 | + pendingCursor.close(); // Abandoning lookahead. |
254 | + cursorPool.add(pendingCursor); |
255 | + pendingCursor = null; |
256 | + } |
257 | + BindingsAndCursor bandc = pendingBindings.poll(); |
258 | + if (bandc != null) { |
259 | + currentBindings = bandc.bindings; |
260 | + pendingCursor = bandc.cursor; |
261 | + return currentBindings; |
262 | + } |
263 | + currentBindings = bindingsCursor.nextBindings(); |
264 | + if (currentBindings == null) { |
265 | + bindingsExhausted = true; |
266 | + } |
267 | + return currentBindings; |
268 | + } |
269 | + |
270 | + @Override |
271 | + public void closeBindings() { |
272 | + bindingsCursor.closeBindings(); |
273 | + recyclePending(); |
274 | + } |
275 | + |
276 | + // LookaheadExecution interface |
277 | + |
278 | + LookaheadExecution(QueryContext context, QueryBindingsCursor bindingsCursor, |
279 | + int quantum) { |
280 | + super(context); |
281 | + this.bindingsCursor = bindingsCursor; |
282 | + this.pendingBindings = new ArrayDeque<>(quantum+1); |
283 | + this.cursorPool = new ArrayDeque<>(quantum); |
284 | + UserTable table = (UserTable)index.rootMostTable(); |
285 | + StoreAdapter adapter = adapter(table); |
286 | + for (int i = 0; i < quantum; i++) { |
287 | + RowCursor cursor = adapter.newIndexCursor(context, index, indexKeyRange, ordering, scanSelector, usePValues); |
288 | + cursorPool.add(cursor); |
289 | + } |
290 | + } |
291 | + |
292 | + // For use by this class |
293 | + |
294 | + private void recyclePending() { |
295 | + while (true) { |
296 | + BindingsAndCursor bandc = pendingBindings.poll(); |
297 | + if (bandc == null) break; |
298 | + if (bandc.cursor != null) { |
299 | + bandc.cursor.close(); |
300 | + cursorPool.add(bandc.cursor); |
301 | + } |
302 | + } |
303 | + } |
304 | + |
305 | + private RowCursor openACursor(QueryBindings bindings) { |
306 | + RowCursor cursor = cursorPool.remove(); |
307 | + ((BindingsAwareCursor)cursor).rebind(bindings); |
308 | + cursor.open(); |
309 | + return cursor; |
310 | + } |
311 | + |
312 | + // Object state |
313 | + |
314 | + private final QueryBindingsCursor bindingsCursor; |
315 | + private final Queue<BindingsAndCursor> pendingBindings; |
316 | + private final Queue<RowCursor> cursorPool; |
317 | + private QueryBindings currentBindings; |
318 | + private RowCursor pendingCursor, currentCursor; |
319 | + private boolean bindingsExhausted, destroyed; |
320 | + } |
321 | } |
322 | |
323 | === modified file 'src/main/java/com/akiban/qp/operator/Map_NestedLoops.java' |
324 | --- src/main/java/com/akiban/qp/operator/Map_NestedLoops.java 2013-07-10 21:30:34 +0000 |
325 | +++ src/main/java/com/akiban/qp/operator/Map_NestedLoops.java 2013-07-13 21:10:31 +0000 |
326 | @@ -51,6 +51,8 @@ |
327 | |
328 | <li><b>int inputBindingPosition:</b> Position of inner loop row in query context. |
329 | |
330 | + <li><b>boolean pipeline:</b> Whether to use bracketing cursors instead of rebinding. |
331 | + |
332 | <li><b>int depth:</b> Number of nested Maps, including this one. |
333 | |
334 | </ul> |
335 | @@ -90,7 +92,7 @@ |
336 | @Override |
337 | protected Cursor cursor(QueryContext context, QueryBindingsCursor bindingsCursor) |
338 | { |
339 | - if (false) |
340 | + if (!pipeline) |
341 | return new Execution(context, bindingsCursor); // Old-style |
342 | else { |
343 | Cursor outerCursor = outerInputOperator.cursor(context, bindingsCursor); |
344 | @@ -127,6 +129,7 @@ |
345 | public Map_NestedLoops(Operator outerInputOperator, |
346 | Operator innerInputOperator, |
347 | int inputBindingPosition, |
348 | + boolean pipeline, |
349 | int depth) |
350 | { |
351 | ArgumentValidation.notNull("outerInputOperator", outerInputOperator); |
352 | @@ -136,6 +139,7 @@ |
353 | this.outerInputOperator = outerInputOperator; |
354 | this.innerInputOperator = innerInputOperator; |
355 | this.inputBindingPosition = inputBindingPosition; |
356 | + this.pipeline = pipeline; |
357 | this.depth = depth; |
358 | } |
359 | |
360 | @@ -150,12 +154,14 @@ |
361 | private final Operator outerInputOperator; |
362 | private final Operator innerInputOperator; |
363 | private final int inputBindingPosition, depth; |
364 | + private final boolean pipeline; |
365 | |
366 | @Override |
367 | public CompoundExplainer getExplainer(ExplainContext context) |
368 | { |
369 | CompoundExplainer ex = new NestedLoopsExplainer(getName(), innerInputOperator, outerInputOperator, null, null, context); |
370 | ex.addAttribute(Label.BINDING_POSITION, PrimitiveExplainer.getInstance(inputBindingPosition)); |
371 | + ex.addAttribute(Label.PIPELINE, PrimitiveExplainer.getInstance(pipeline)); |
372 | ex.addAttribute(Label.DEPTH, PrimitiveExplainer.getInstance(depth)); |
373 | if (context.hasExtraInfo(this)) |
374 | ex.get().putAll(context.getExtraInfo(this).get()); |
375 | @@ -346,9 +352,13 @@ |
376 | pendingBindings = null; |
377 | return bindings; |
378 | } |
379 | - bindings = input.nextBindings(); |
380 | - assert ((bindings == null) || (bindings.getDepth() < depth)); |
381 | - return bindings; |
382 | + while (true) { |
383 | + // Skip over any that we would elide. |
384 | + bindings = input.nextBindings(); |
385 | + if ((bindings == null) || (bindings.getDepth() < depth)) |
386 | + return bindings; |
387 | + assert (bindings.getDepth() == depth); |
388 | + } |
389 | } |
390 | |
391 | @Override |
392 | |
393 | === modified file 'src/main/java/com/akiban/server/explain/Label.java' |
394 | --- src/main/java/com/akiban/server/explain/Label.java 2013-07-10 18:42:14 +0000 |
395 | +++ src/main/java/com/akiban/server/explain/Label.java 2013-07-13 21:10:31 +0000 |
396 | @@ -48,6 +48,7 @@ |
397 | INFIX_REPRESENTATION(Category.DESCRIPTION), |
398 | ASSOCIATIVE(Category.DESCRIPTION), |
399 | INDEX(Category.DESCRIPTION), |
400 | + PIPELINE(Category.DESCRIPTION), |
401 | DEPTH(Category.DESCRIPTION), |
402 | |
403 | // IDENTIFIER |
404 | |
405 | === modified file 'src/main/java/com/akiban/sql/optimizer/rule/OperatorAssembler.java' |
406 | --- src/main/java/com/akiban/sql/optimizer/rule/OperatorAssembler.java 2013-07-10 18:42:14 +0000 |
407 | +++ src/main/java/com/akiban/sql/optimizer/rule/OperatorAssembler.java 2013-07-13 21:10:31 +0000 |
408 | @@ -1182,7 +1182,8 @@ |
409 | stream.operator = API.indexScan_Default(indexRowType, |
410 | assembleSpatialIndexKeyRange(indexScan, null), |
411 | API.ordering(), // TODO: what ordering? |
412 | - selector); |
413 | + selector, |
414 | + rulesContext.getPipelineConfiguration().getIndexScanLookaheadQuantum()); |
415 | indexRowType = indexRowType.physicalRowType(); |
416 | stream.rowType = indexRowType; |
417 | } |
418 | @@ -1190,7 +1191,8 @@ |
419 | stream.operator = API.indexScan_Default(indexRowType, |
420 | assembleIndexKeyRange(indexScan, null), |
421 | assembleIndexOrdering(indexScan, indexRowType), |
422 | - selector); |
423 | + selector, |
424 | + rulesContext.getPipelineConfiguration().getIndexScanLookaheadQuantum()); |
425 | stream.rowType = indexRowType; |
426 | } |
427 | else { |
428 | @@ -1221,7 +1223,8 @@ |
429 | Operator scan = API.indexScan_Default(indexRowType, |
430 | assembleIndexKeyRange(indexScan, null, rangeSegment), |
431 | assembleIndexOrdering(indexScan, indexRowType), |
432 | - selector); |
433 | + selector, |
434 | + rulesContext.getPipelineConfiguration().getIndexScanLookaheadQuantum()); |
435 | if (stream.operator == null) { |
436 | stream.operator = scan; |
437 | stream.rowType = indexRowType; |
438 | @@ -1529,6 +1532,7 @@ |
439 | stream.operator = API.map_NestedLoops(ostream.operator, |
440 | stream.operator, |
441 | currentBindingPosition(), |
442 | + rulesContext.getPipelineConfiguration().isMapEnabled(), |
443 | nestedBindingsDepth); |
444 | nestedBindingsDepth--; |
445 | popBoundRow(); |
446 | |
447 | === added file 'src/main/java/com/akiban/sql/optimizer/rule/PipelineConfiguration.java' |
448 | --- src/main/java/com/akiban/sql/optimizer/rule/PipelineConfiguration.java 1970-01-01 00:00:00 +0000 |
449 | +++ src/main/java/com/akiban/sql/optimizer/rule/PipelineConfiguration.java 2013-07-13 21:10:31 +0000 |
450 | @@ -0,0 +1,67 @@ |
451 | +/** |
452 | + * Copyright (C) 2009-2013 Akiban Technologies, Inc. |
453 | + * |
454 | + * This program is free software: you can redistribute it and/or modify |
455 | + * it under the terms of the GNU Affero General Public License as published by |
456 | + * the Free Software Foundation, either version 3 of the License, or |
457 | + * (at your option) any later version. |
458 | + * |
459 | + * This program is distributed in the hope that it will be useful, |
460 | + * but WITHOUT ANY WARRANTY; without even the implied warranty of |
461 | + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
462 | + * GNU Affero General Public License for more details. |
463 | + * |
464 | + * You should have received a copy of the GNU Affero General Public License |
465 | + * along with this program. If not, see <http://www.gnu.org/licenses/>. |
466 | + */ |
467 | + |
468 | +package com.akiban.sql.optimizer.rule; |
469 | + |
470 | +import java.util.Properties; |
471 | + |
472 | +public class PipelineConfiguration |
473 | +{ |
474 | + private boolean mapEnabled = false; |
475 | + private int indexScanLookaheadQuantum = 1; |
476 | + private int groupLookupLookaheadQuantum = 1; |
477 | + private boolean unionAllOpenBoth = false; |
478 | + |
479 | + public PipelineConfiguration() { |
480 | + } |
481 | + |
482 | + public PipelineConfiguration(Properties properties) { |
483 | + load(properties); |
484 | + } |
485 | + |
486 | + public boolean isMapEnabled() { |
487 | + return mapEnabled; |
488 | + } |
489 | + |
490 | + public int getIndexScanLookaheadQuantum() { |
491 | + return indexScanLookaheadQuantum; |
492 | + } |
493 | + |
494 | + public int getGroupLookupLookaheadQuantum() { |
495 | + return groupLookupLookaheadQuantum; |
496 | + } |
497 | + |
498 | + public boolean isUnionAllOpenBoth() { |
499 | + return unionAllOpenBoth; |
500 | + } |
501 | + |
502 | + public void load(Properties properties) { |
503 | + for (String prop : properties.stringPropertyNames()) { |
504 | + String val = properties.getProperty(prop); |
505 | + if ("map.enabled".equals(prop)) |
506 | + mapEnabled = Boolean.parseBoolean(val); |
507 | + else if ("indexScan.lookaheadQuantum".equals(prop)) |
508 | + indexScanLookaheadQuantum = Integer.parseInt(val); |
509 | + else if ("groupLookup.lookaheadQuantum".equals(prop)) |
510 | + groupLookupLookaheadQuantum = Integer.parseInt(val); |
511 | + else if ("unionAll.openBoth".equals(prop)) |
512 | + unionAllOpenBoth = Boolean.parseBoolean(val); |
513 | + else |
514 | + throw new IllegalArgumentException("Unknown property " + prop); |
515 | + } |
516 | + } |
517 | +} |
518 | |
519 | === modified file 'src/main/java/com/akiban/sql/optimizer/rule/SchemaRulesContext.java' |
520 | --- src/main/java/com/akiban/sql/optimizer/rule/SchemaRulesContext.java 2013-03-22 20:05:57 +0000 |
521 | +++ src/main/java/com/akiban/sql/optimizer/rule/SchemaRulesContext.java 2013-07-13 21:10:31 +0000 |
522 | @@ -36,6 +36,8 @@ |
523 | private FunctionsRegistry functionsRegistry; |
524 | private CostEstimator costEstimator; |
525 | private T3RegistryService t3Registry; |
526 | + private PipelineConfiguration pipelineConfiguration; |
527 | + |
528 | protected SchemaRulesContext() { |
529 | } |
530 | |
531 | @@ -55,12 +57,17 @@ |
532 | this.costEstimator = costEstimator; |
533 | } |
534 | |
535 | + protected void initPipelineConfiguration(PipelineConfiguration pipelineConfiguration) { |
536 | + this.pipelineConfiguration = pipelineConfiguration; |
537 | + } |
538 | + |
539 | @Override |
540 | protected void initDone() { |
541 | super.initDone(); |
542 | assert (schema != null) : "initSchema() not called"; |
543 | assert (functionsRegistry != null) : "initFunctionsRegistry() not called"; |
544 | assert (costEstimator != null) : "initCostEstimator() not called"; |
545 | + assert (pipelineConfiguration != null) : "initPipelineConfiguration() not called"; |
546 | } |
547 | |
548 | public Schema getSchema() { |
549 | @@ -89,4 +96,8 @@ |
550 | |
551 | public abstract String getDefaultSchemaName(); |
552 | |
553 | + public PipelineConfiguration getPipelineConfiguration() { |
554 | + return pipelineConfiguration; |
555 | + } |
556 | + |
557 | } |
558 | |
559 | === modified file 'src/main/java/com/akiban/sql/server/ServerOperatorCompiler.java' |
560 | --- src/main/java/com/akiban/sql/server/ServerOperatorCompiler.java 2013-06-11 16:30:17 +0000 |
561 | +++ src/main/java/com/akiban/sql/server/ServerOperatorCompiler.java 2013-07-13 21:10:31 +0000 |
562 | @@ -36,6 +36,7 @@ |
563 | initParser(server.getParser()); |
564 | initFunctionsRegistry(server.functionsRegistry()); |
565 | initCostEstimator(server.costEstimator(this, keyCreator), usePValues); |
566 | + initPipelineConfiguration(server.getPipelineConfiguration()); |
567 | if (usePValues) |
568 | initT3Registry(server.t3RegistryService()); |
569 | |
570 | |
571 | === modified file 'src/main/java/com/akiban/sql/server/ServerSession.java' |
572 | --- src/main/java/com/akiban/sql/server/ServerSession.java 2013-06-11 16:32:59 +0000 |
573 | +++ src/main/java/com/akiban/sql/server/ServerSession.java 2013-07-13 21:10:31 +0000 |
574 | @@ -23,6 +23,7 @@ |
575 | import com.akiban.sql.parser.SQLParser; |
576 | |
577 | import com.akiban.sql.optimizer.AISBinderContext; |
578 | +import com.akiban.sql.optimizer.rule.PipelineConfiguration; |
579 | import com.akiban.sql.optimizer.rule.cost.CostEstimator; |
580 | |
581 | import com.akiban.ais.model.AkibanInformationSchema; |
582 | @@ -165,4 +166,7 @@ |
583 | |
584 | /** Check access to given schema */ |
585 | public boolean isSchemaAccessible(String schemaName); |
586 | + |
587 | + /** Get the pipeline configuration. */ |
588 | + public PipelineConfiguration getPipelineConfiguration(); |
589 | } |
590 | |
591 | === modified file 'src/main/java/com/akiban/sql/server/ServerSessionBase.java' |
592 | --- src/main/java/com/akiban/sql/server/ServerSessionBase.java 2013-06-12 21:51:17 +0000 |
593 | +++ src/main/java/com/akiban/sql/server/ServerSessionBase.java 2013-07-13 21:10:31 +0000 |
594 | @@ -40,6 +40,7 @@ |
595 | import com.akiban.server.service.tree.KeyCreator; |
596 | import com.akiban.server.t3expressions.T3RegistryService; |
597 | import com.akiban.sql.optimizer.AISBinderContext; |
598 | +import com.akiban.sql.optimizer.rule.PipelineConfiguration; |
599 | import com.akiban.sql.optimizer.rule.cost.CostEstimator; |
600 | |
601 | import java.util.*; |
602 | @@ -47,10 +48,12 @@ |
603 | public abstract class ServerSessionBase extends AISBinderContext implements ServerSession |
604 | { |
605 | public static final String COMPILER_PROPERTIES_PREFIX = "optimizer."; |
606 | + public static final String PIPELINE_PROPERTIES_PREFIX = "akserver.pipeline."; |
607 | |
608 | protected final ServerServiceRequirements reqs; |
609 | protected Properties compilerProperties; |
610 | protected Map<String,Object> attributes = new HashMap<>(); |
611 | + protected PipelineConfiguration pipelineConfiguration; |
612 | |
613 | protected Session session; |
614 | protected Map<StoreAdapter.AdapterType, StoreAdapter> adapters = |
615 | @@ -401,4 +404,11 @@ |
616 | return reqs.securityService().isAccessible(session, schemaName); |
617 | } |
618 | |
619 | + @Override |
620 | + public PipelineConfiguration getPipelineConfiguration() { |
621 | + if (pipelineConfiguration == null) |
622 | + pipelineConfiguration = new PipelineConfiguration(reqs.config().deriveProperties(PIPELINE_PROPERTIES_PREFIX)); |
623 | + return pipelineConfiguration; |
624 | + } |
625 | + |
626 | } |
627 | |
628 | === modified file 'src/main/java/com/akiban/util/SparseArray.java' |
629 | --- src/main/java/com/akiban/util/SparseArray.java 2013-07-10 17:31:42 +0000 |
630 | +++ src/main/java/com/akiban/util/SparseArray.java 2013-07-13 21:10:31 +0000 |
631 | @@ -118,13 +118,14 @@ |
632 | |
633 | public StringBuilder describeElements(StringBuilder sb) { |
634 | sb.append('['); |
635 | + int ilen = sb.length(); |
636 | |
637 | int size = definedElements.size(); |
638 | for (int i = 0; i < size; ++i) { |
639 | if (isDefined(i)) |
640 | sb.append(internalGet(i)).append(", "); |
641 | } |
642 | - if (sb.length() > 1) // sb is not just the initial '[' |
643 | + if (sb.length() > ilen) // sb is not just the initial '[' |
644 | sb.setLength(sb.length() - 2); // snip off the trailing ", " |
645 | sb.append(']'); |
646 | |
647 | |
648 | === modified file 'src/main/resources/com/akiban/server/service/config/configuration-defaults.properties' |
649 | --- src/main/resources/com/akiban/server/service/config/configuration-defaults.properties 2013-06-11 21:35:24 +0000 |
650 | +++ src/main/resources/com/akiban/server/service/config/configuration-defaults.properties 2013-07-13 21:10:31 +0000 |
651 | @@ -43,6 +43,7 @@ |
652 | akserver.gc_monitor.interval=1000 |
653 | akserver.gc_monitor.log_threshold_ms=100 |
654 | akserver.write_lock_enabled=true |
655 | +akserver.lookaheadQuantum.indexScan=1 |
656 | |
657 | persistit.buffersize=16384 |
658 | |
659 | |
660 | === modified file 'src/test/java/com/akiban/server/test/ApiTestBase.java' |
661 | --- src/test/java/com/akiban/server/test/ApiTestBase.java 2013-07-03 15:32:40 +0000 |
662 | +++ src/test/java/com/akiban/server/test/ApiTestBase.java 2013-07-13 21:10:31 +0000 |
663 | @@ -1353,6 +1353,10 @@ |
664 | return true; |
665 | } |
666 | |
667 | + protected boolean pipelineMap() { |
668 | + return false; |
669 | + } |
670 | + |
671 | protected DDLFunctions ddlForAlter() { |
672 | return ddl(); |
673 | } |
674 | |
675 | === modified file 'src/test/java/com/akiban/server/test/costmodel/MapCT.java' |
676 | --- src/test/java/com/akiban/server/test/costmodel/MapCT.java 2013-07-10 18:42:14 +0000 |
677 | +++ src/test/java/com/akiban/server/test/costmodel/MapCT.java 2013-07-13 21:10:31 +0000 |
678 | @@ -83,7 +83,8 @@ |
679 | TimeOperator timeSetupOuter = new TimeOperator(setupOuter); |
680 | Operator setupInner = limit_Default(groupScan_Default(group), innerRows); |
681 | TimeOperator timeSetupInner = new TimeOperator(setupInner); |
682 | - Operator plan = map_NestedLoops(timeSetupOuter, timeSetupInner, 0, 1); |
683 | + Operator plan = map_NestedLoops(timeSetupOuter, timeSetupInner, |
684 | + 0, pipelineMap(), 1); |
685 | long start = System.nanoTime(); |
686 | for (int r = 0; r < runs; r++) { |
687 | Cursor cursor = cursor(plan, queryContext, queryBindings); |
688 | |
689 | === modified file 'src/test/java/com/akiban/server/test/it/qp/AncestorLookup_NestedIT.java' |
690 | --- src/test/java/com/akiban/server/test/it/qp/AncestorLookup_NestedIT.java 2013-07-10 21:30:34 +0000 |
691 | +++ src/test/java/com/akiban/server/test/it/qp/AncestorLookup_NestedIT.java 2013-07-13 21:10:31 +0000 |
692 | @@ -141,7 +141,7 @@ |
693 | map_NestedLoops( |
694 | indexScan_Default(aValueIndexRowType), |
695 | ancestorLookup_Nested(rabc, aValueIndexRowType, Collections.singleton(aRowType), 0), |
696 | - 0, 1); |
697 | + 0, pipelineMap(), 1); |
698 | Cursor cursor = cursor(plan, queryContext, queryBindings); |
699 | RowBase[] expected = new RowBase[]{ |
700 | row(aRowType, 13L, 1L, "a13"), |
701 | @@ -159,7 +159,7 @@ |
702 | map_NestedLoops( |
703 | indexScan_Default(aValueIndexRowType), |
704 | ancestorLookup_Nested(rabc, aValueIndexRowType, Arrays.asList(aRowType, rRowType), 0), |
705 | - 0, 1); |
706 | + 0, pipelineMap(), 1); |
707 | Cursor cursor = cursor(plan, queryContext, queryBindings); |
708 | RowBase[] expected = new RowBase[]{ |
709 | row(rRowType, 1L, "r1"), |
710 | @@ -181,7 +181,7 @@ |
711 | map_NestedLoops( |
712 | indexScan_Default(aValueIndexRowType), |
713 | ancestorLookup_Nested(rabc, aValueIndexRowType, Arrays.asList(rRowType, aRowType), 0), |
714 | - 0, 1); |
715 | + 0, pipelineMap(), 1); |
716 | Cursor cursor = cursor(plan, queryContext, queryBindings); |
717 | RowBase[] expected = new RowBase[]{ |
718 | row(rRowType, 1L, "r1"), |
719 | @@ -204,9 +204,9 @@ |
720 | map_NestedLoops( |
721 | indexScan_Default(aValueIndexRowType), |
722 | ancestorLookup_Nested(rabc, aValueIndexRowType, Arrays.asList(aRowType), 0), |
723 | - 0, 1), |
724 | + 0, pipelineMap(), 1), |
725 | ancestorLookup_Nested(rabc, aRowType, Arrays.asList(rRowType), 1), |
726 | - 1, 1); |
727 | + 1, pipelineMap(), 1); |
728 | Cursor cursor = cursor(plan, queryContext, queryBindings); |
729 | RowBase[] expected = new RowBase[]{ |
730 | row(rRowType, 1L, "r1"), |
731 | @@ -236,7 +236,7 @@ |
732 | map_NestedLoops( |
733 | abIndexScan, |
734 | ancestorLookup_Nested(rabc, abIndexScan.rowType(), Collections.singleton(rRowType), 0), |
735 | - 0, 1); |
736 | + 0, pipelineMap(), 1); |
737 | Cursor cursor = cursor(plan, queryContext, queryBindings); |
738 | RowBase[] expected = new RowBase[]{ |
739 | row(rRowType, 1L, "r1"), |
740 | @@ -272,7 +272,7 @@ |
741 | map_NestedLoops( |
742 | abcIndexScan, |
743 | ancestorLookup_Nested(rabc, abcIndexScan.rowType(), Collections.singleton(rRowType), 0), |
744 | - 0, 1); |
745 | + 0, pipelineMap(), 1); |
746 | Cursor cursor = cursor(plan, queryContext, queryBindings); |
747 | RowBase[] expected = new RowBase[]{ |
748 | row(rRowType, 1L, "r1"), |
749 | @@ -287,7 +287,7 @@ |
750 | map_NestedLoops( |
751 | indexScan_Default(aValueIndexRowType), |
752 | ancestorLookup_Nested(rabc, aValueIndexRowType, Collections.singleton(aRowType), 0), |
753 | - 0, 1); |
754 | + 0, pipelineMap(), 1); |
755 | CursorLifecycleTestCase testCase = new CursorLifecycleTestCase() |
756 | { |
757 | @Override |
758 | |
759 | === modified file 'src/test/java/com/akiban/server/test/it/qp/BranchLookup_NestedIT.java' |
760 | --- src/test/java/com/akiban/server/test/it/qp/BranchLookup_NestedIT.java 2013-07-10 21:30:34 +0000 |
761 | +++ src/test/java/com/akiban/server/test/it/qp/BranchLookup_NestedIT.java 2013-07-13 21:10:31 +0000 |
762 | @@ -154,7 +154,7 @@ |
763 | map_NestedLoops( |
764 | indexScan_Default(aValueIndexRowType), |
765 | branchLookup_Nested(rabc, aValueIndexRowType, rRowType, InputPreservationOption.DISCARD_INPUT, 0), |
766 | - 0, 1); |
767 | + 0, pipelineMap(), 1); |
768 | Cursor cursor = cursor(plan, queryContext, queryBindings); |
769 | RowBase[] expected = new RowBase[]{ |
770 | // Each r row, and everything below it, is duplicated, because the A index refers to each r value twice. |
771 | @@ -202,7 +202,7 @@ |
772 | Collections.singleton(aRowType), |
773 | InputPreservationOption.DISCARD_INPUT), |
774 | branchLookup_Nested(rabc, aRowType, rRowType, InputPreservationOption.DISCARD_INPUT, 0), |
775 | - 0, 1); |
776 | + 0, pipelineMap(), 1); |
777 | Cursor cursor = cursor(plan, queryContext, queryBindings); |
778 | RowBase[] expected = new RowBase[]{ |
779 | // Each r row, and everything below it, is duplicated, because the A index refers to each r value twice. |
780 | @@ -247,7 +247,7 @@ |
781 | groupScan_Default(rabc), |
782 | Collections.singleton(aRowType)), |
783 | branchLookup_Nested(rabc, aRowType, bRowType, InputPreservationOption.DISCARD_INPUT, 0), |
784 | - 0, 1); |
785 | + 0, pipelineMap(), 1); |
786 | Cursor cursor = cursor(plan, queryContext, queryBindings); |
787 | RowBase[] expected = new RowBase[]{ |
788 | row(bRowType, 15L, 1L, "b15"), |
789 | @@ -272,9 +272,9 @@ |
790 | groupScan_Default(rabc), |
791 | Collections.singleton(aRowType)), |
792 | branchLookup_Nested(rabc, aRowType, bRowType, InputPreservationOption.DISCARD_INPUT, 0), |
793 | - 0, 1), |
794 | + 0, pipelineMap(), 1), |
795 | branchLookup_Nested(rabc, bRowType, cRowType, InputPreservationOption.KEEP_INPUT, 1), |
796 | - 1, 1); |
797 | + 1, pipelineMap(), 1); |
798 | Cursor cursor = cursor(plan, queryContext, queryBindings); |
799 | RowBase[] expected = new RowBase[]{ |
800 | row(bRowType, 15L, 1L, "b15"), |
801 | @@ -324,7 +324,7 @@ |
802 | map_NestedLoops( |
803 | abIndexScan, |
804 | branchLookup_Nested(rabc, abIndexScan.rowType(), cRowType, InputPreservationOption.DISCARD_INPUT, 0), |
805 | - 0, 1); |
806 | + 0, pipelineMap(), 1); |
807 | Cursor cursor = cursor(plan, queryContext, queryBindings); |
808 | RowBase[] expected = new RowBase[]{ |
809 | row(cRowType, 17L, 1L, "c17"), |
810 | @@ -342,7 +342,7 @@ |
811 | groupScan_Default(rabc), |
812 | Collections.singleton(aRowType)), |
813 | branchLookup_Nested(rabc, aRowType, rRowType, aRowType, InputPreservationOption.DISCARD_INPUT, 0), |
814 | - 0, 1); |
815 | + 0, pipelineMap(), 1); |
816 | Cursor cursor = cursor(plan, queryContext, queryBindings); |
817 | RowBase[] expected = new RowBase[]{ |
818 | row(aRowType, 13L, 1L, "a13"), |
819 | @@ -366,7 +366,7 @@ |
820 | groupScan_Default(rabc), |
821 | Collections.singleton(aRowType)), |
822 | branchLookup_Nested(rabc, aRowType, rRowType, aRowType, InputPreservationOption.DISCARD_INPUT, 0), |
823 | - 0, 1); |
824 | + 0, pipelineMap(), 1); |
825 | CursorLifecycleTestCase testCase = new CursorLifecycleTestCase() |
826 | { |
827 | @Override |
828 | |
829 | === added file 'src/test/java/com/akiban/server/test/it/qp/IndexScanLookaheadIT.java' |
830 | --- src/test/java/com/akiban/server/test/it/qp/IndexScanLookaheadIT.java 1970-01-01 00:00:00 +0000 |
831 | +++ src/test/java/com/akiban/server/test/it/qp/IndexScanLookaheadIT.java 2013-07-13 21:10:31 +0000 |
832 | @@ -0,0 +1,227 @@ |
833 | +/** |
834 | + * Copyright (C) 2009-2013 Akiban Technologies, Inc. |
835 | + * |
836 | + * This program is free software: you can redistribute it and/or modify |
837 | + * it under the terms of the GNU Affero General Public License as published by |
838 | + * the Free Software Foundation, either version 3 of the License, or |
839 | + * (at your option) any later version. |
840 | + * |
841 | + * This program is distributed in the hope that it will be useful, |
842 | + * but WITHOUT ANY WARRANTY; without even the implied warranty of |
843 | + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
844 | + * GNU Affero General Public License for more details. |
845 | + * |
846 | + * You should have received a copy of the GNU Affero General Public License |
847 | + * along with this program. If not, see <http://www.gnu.org/licenses/>. |
848 | + */ |
849 | + |
850 | +package com.akiban.server.test.it.qp; |
851 | + |
852 | +import com.akiban.qp.expression.ExpressionRow; |
853 | +import com.akiban.qp.expression.IndexBound; |
854 | +import com.akiban.qp.expression.IndexKeyRange; |
855 | +import com.akiban.qp.expression.RowBasedUnboundExpressions; |
856 | +import com.akiban.qp.operator.Cursor; |
857 | +import com.akiban.qp.operator.ExpressionGenerator; |
858 | +import com.akiban.qp.operator.IndexScanSelector; |
859 | +import com.akiban.qp.operator.Operator; |
860 | +import com.akiban.qp.row.BindableRow; |
861 | +import com.akiban.qp.row.Row; |
862 | +import com.akiban.qp.rowtype.IndexRowType; |
863 | +import com.akiban.qp.rowtype.RowType; |
864 | +import com.akiban.server.api.dml.SetColumnSelector; |
865 | +import com.akiban.server.api.dml.scan.NewRow; |
866 | +import com.akiban.server.types.AkType; |
867 | +import com.akiban.server.types3.mcompat.mtypes.MNumeric; |
868 | +import com.akiban.server.types3.pvalue.PValue; |
869 | +import com.akiban.server.types3.texpressions.TPreparedExpression; |
870 | +import com.akiban.server.types3.texpressions.TPreparedLiteral; |
871 | + |
872 | +import static com.akiban.qp.operator.API.*; |
873 | +import static com.akiban.server.test.ExpressionGenerators.*; |
874 | + |
875 | +import org.junit.Test; |
876 | +import static junit.framework.Assert.assertEquals; |
877 | + |
878 | +import java.util.ArrayList; |
879 | +import java.util.Arrays; |
880 | +import java.util.Collection; |
881 | +import java.util.HashMap; |
882 | +import java.util.List; |
883 | +import java.util.Map; |
884 | + |
885 | +public class IndexScanLookaheadIT extends OperatorITBase |
886 | +{ |
887 | + @Override |
888 | + protected boolean pipelineMap() { |
889 | + return true; |
890 | + } |
891 | + |
892 | + protected int lookaheadQuantum() { |
893 | + return 4; |
894 | + } |
895 | + |
896 | + @Override |
897 | + protected void setupPostCreateSchema() { |
898 | + super.setupPostCreateSchema(); |
899 | + // Like super, but with a whole lot more cid=2 orders. |
900 | + db = new NewRow[]{createNewRow(customer, 1L, "xyz"), |
901 | + createNewRow(customer, 2L, "abc"), |
902 | + createNewRow(customer, 4L, "qqq"), |
903 | + createNewRow(order, 11L, 1L, "ori"), |
904 | + createNewRow(order, 12L, 1L, "david"), |
905 | + createNewRow(order, 21L, 2L, "tom"), |
906 | + createNewRow(order, 22L, 2L, "jack"), |
907 | + createNewRow(order, 23L, 2L, "dave"), |
908 | + createNewRow(order, 24L, 2L, "dave"), |
909 | + createNewRow(order, 25L, 2L, "dave"), |
910 | + createNewRow(order, 26L, 2L, "dave"), |
911 | + createNewRow(order, 27L, 2L, "dave"), |
912 | + createNewRow(order, 28L, 2L, "dave"), |
913 | + createNewRow(order, 29L, 2L, "dave"), |
914 | + createNewRow(order, 41L, 4L, "nathan"), |
915 | + createNewRow(item, 111L, 11L), |
916 | + createNewRow(item, 112L, 11L), |
917 | + createNewRow(item, 121L, 12L), |
918 | + createNewRow(item, 122L, 12L), |
919 | + createNewRow(item, 211L, 21L), |
920 | + createNewRow(item, 212L, 21L), |
921 | + createNewRow(item, 221L, 22L), |
922 | + createNewRow(item, 222L, 22L), |
923 | + createNewRow(item, 241L, 24L), |
924 | + createNewRow(item, 251L, 25L)}; |
925 | + use(db); |
926 | + } |
927 | + |
928 | + @Test |
929 | + public void testCursor() |
930 | + { |
931 | + Operator indexScan = indexScan_Default(itemIidIndexRowType, iidKeyRange(100, false, 125, false), ordering(itemIidIndexRowType), IndexScanSelector.leftJoinAfter(itemIidIndexRowType.index(), itemRowType.userTable()), lookaheadQuantum()); |
932 | + CursorLifecycleTestCase testCase = new CursorLifecycleTestCase() |
933 | + { |
934 | + @Override |
935 | + public boolean hKeyComparison() |
936 | + { |
937 | + return true; |
938 | + } |
939 | + |
940 | + @Override |
941 | + public String[] firstExpectedHKeys() |
942 | + { |
943 | + return new String[]{hkey(1, 11, 111), hkey(1, 11, 112), hkey(1, 12, 121), hkey(1, 12, 122)}; |
944 | + } |
945 | + }; |
946 | + testCursorLifecycle(indexScan, testCase); |
947 | + } |
948 | + |
949 | + @Test |
950 | + public void testSingle() |
951 | + { |
952 | + Operator indexScan = indexScan_Default(itemIidIndexRowType, iidKeyRange(212, true, 212, true), ordering(itemIidIndexRowType), IndexScanSelector.leftJoinAfter(itemIidIndexRowType.index(), itemRowType.userTable()), lookaheadQuantum()); |
953 | + Cursor cursor = cursor(indexScan, queryContext, queryBindings); |
954 | + String[] expected = new String[]{hkey(2, 21, 212)}; |
955 | + compareRenderedHKeys(expected, cursor); |
956 | + } |
957 | + |
958 | + @Test |
959 | + public void testMap() |
960 | + { |
961 | + RowType cidValueRowType = schema.newValuesType(AkType.INT); |
962 | + List<ExpressionGenerator> cidExprs = Arrays.asList(boundField(cidValueRowType, 1, 0)); |
963 | + IndexBound cidBound = |
964 | + new IndexBound( |
965 | + new RowBasedUnboundExpressions(orderCidIndexRowType, cidExprs), |
966 | + new SetColumnSelector(0)); |
967 | + IndexKeyRange cidRange = IndexKeyRange.bounded(orderCidIndexRowType, cidBound, true, cidBound, true); |
968 | + Operator plan = |
969 | + map_NestedLoops( |
970 | + valuesScan_Default( |
971 | + bindableExpressions(intRow(cidValueRowType, 2), |
972 | + intRow(cidValueRowType, 4), |
973 | + intRow(cidValueRowType, 6)), |
974 | + cidValueRowType), |
975 | + indexScan_Default(orderCidIndexRowType, cidRange, ordering(orderCidIndexRowType), IndexScanSelector.leftJoinAfter(orderCidIndexRowType.index(), orderRowType.userTable()), lookaheadQuantum()), |
976 | + 1, pipelineMap(), 1); |
977 | + Cursor cursor = cursor(plan, queryContext, queryBindings); |
978 | + String[] expected = new String[]{hkey(2, 21),hkey(2, 22),hkey(2, 23),hkey(2, 24),hkey(2, 25),hkey(2, 26),hkey(2, 27),hkey(2, 28),hkey(2, 29),hkey(4, 41)}; |
979 | + compareRenderedHKeys(expected, cursor); |
980 | + } |
981 | + |
982 | + @Test |
983 | + public void testNested() |
984 | + { |
985 | + RowType cidValueRowType = schema.newValuesType(AkType.INT); |
986 | + List<ExpressionGenerator> cidExprs = Arrays.asList(boundField(cidValueRowType, 1, 0)); |
987 | + IndexBound cidBound = |
988 | + new IndexBound( |
989 | + new RowBasedUnboundExpressions(orderCidIndexRowType, cidExprs), |
990 | + new SetColumnSelector(0)); |
991 | + IndexKeyRange cidRange = IndexKeyRange.bounded(orderCidIndexRowType, cidBound, true, cidBound, true); |
992 | + List<ExpressionGenerator> oidExprs = Arrays.asList(boundField(orderCidIndexRowType, 2, 1)); |
993 | + IndexBound oidBound = |
994 | + new IndexBound( |
995 | + new RowBasedUnboundExpressions(itemOidIndexRowType, oidExprs), |
996 | + new SetColumnSelector(0)); |
997 | + IndexKeyRange oidRange = IndexKeyRange.bounded(itemOidIndexRowType, oidBound, true, oidBound, true); |
998 | + Operator plan = |
999 | + map_NestedLoops( |
1000 | + valuesScan_Default( |
1001 | + bindableExpressions(intRow(cidValueRowType, 2), |
1002 | + intRow(cidValueRowType, 4), |
1003 | + intRow(cidValueRowType, 6)), |
1004 | + cidValueRowType), |
1005 | + map_NestedLoops( |
1006 | + indexScan_Default(orderCidIndexRowType, cidRange, ordering(orderCidIndexRowType), IndexScanSelector.leftJoinAfter(orderCidIndexRowType.index(), orderRowType.userTable()), lookaheadQuantum()), |
1007 | + indexScan_Default(itemOidIndexRowType, oidRange, ordering(itemOidIndexRowType), IndexScanSelector.leftJoinAfter(itemOidIndexRowType.index(), itemRowType.userTable()), lookaheadQuantum()), |
1008 | + 2, pipelineMap(), 2), |
1009 | + 1, pipelineMap(), 1); |
1010 | + Cursor cursor = cursor(plan, queryContext, queryBindings); |
1011 | + String[] expected = new String[]{hkey(2, 21, 211),hkey(2, 21, 212),hkey(2, 22, 221),hkey(2, 22, 222),hkey(2, 24, 241),hkey(2, 25, 251)}; |
1012 | + compareRenderedHKeys(expected, cursor); |
1013 | + } |
1014 | + |
1015 | + // For use by this class |
1016 | + |
1017 | + private IndexKeyRange iidKeyRange(int lo, boolean loInclusive, int hi, boolean hiInclusive) |
1018 | + { |
1019 | + return IndexKeyRange.bounded(itemIidIndexRowType, iidBound(lo), loInclusive, iidBound(hi), hiInclusive); |
1020 | + } |
1021 | + |
1022 | + private IndexBound iidBound(int iid) |
1023 | + { |
1024 | + return new IndexBound(row(itemIidIndexRowType, iid), new SetColumnSelector(0)); |
1025 | + } |
1026 | + |
1027 | + private Row intRow(RowType rowType, int x) |
1028 | + { |
1029 | + List<TPreparedExpression> pExpressions = Arrays.<TPreparedExpression>asList(new TPreparedLiteral(MNumeric.INT.instance(false), new PValue(MNumeric.INT.instance(false), x))); |
1030 | + return new ExpressionRow(rowType, queryContext, queryBindings, null, pExpressions); |
1031 | + } |
1032 | + |
1033 | + private Collection<? extends BindableRow> bindableExpressions(Row... rows) { |
1034 | + List<BindableRow> result = new ArrayList<>(); |
1035 | + for (Row row : rows) { |
1036 | + result.add(BindableRow.of(row, true)); |
1037 | + } |
1038 | + return result; |
1039 | + } |
1040 | + |
1041 | + private String hkey(int cid, int oid) |
1042 | + { |
1043 | + return String.format("{1,(long)%s,2,(long)%s}", cid, oid); |
1044 | + } |
1045 | + |
1046 | + private String hkey(int cid, int oid, int iid) |
1047 | + { |
1048 | + return String.format("{1,(long)%s,2,(long)%s,3,(long)%s}", cid, oid, iid); |
1049 | + } |
1050 | + |
1051 | + private Ordering ordering(IndexRowType indexRowType) { |
1052 | + Ordering ordering = new Ordering(); |
1053 | + for (int i = 0; i < indexRowType.nFields(); i++) { |
1054 | + ordering.append(field(indexRowType, i), true); |
1055 | + } |
1056 | + return ordering; |
1057 | + } |
1058 | + |
1059 | +} |
1060 | |
1061 | === modified file 'src/test/java/com/akiban/server/test/it/qp/Map_NestedLoopsIT.java' |
1062 | --- src/test/java/com/akiban/server/test/it/qp/Map_NestedLoopsIT.java 2013-07-10 21:30:34 +0000 |
1063 | +++ src/test/java/com/akiban/server/test/it/qp/Map_NestedLoopsIT.java 2013-07-13 21:10:31 +0000 |
1064 | @@ -102,25 +102,25 @@ |
1065 | @Test(expected = IllegalArgumentException.class) |
1066 | public void testLeftInputNull() |
1067 | { |
1068 | - map_NestedLoops(null, groupScan_Default(coi), 0, 1); |
1069 | + map_NestedLoops(null, groupScan_Default(coi), 0, pipelineMap(), 1); |
1070 | } |
1071 | |
1072 | @Test(expected = IllegalArgumentException.class) |
1073 | public void testRightInputNull() |
1074 | { |
1075 | - map_NestedLoops(groupScan_Default(coi), null, 0, 1); |
1076 | + map_NestedLoops(groupScan_Default(coi), null, 0, pipelineMap(), 1); |
1077 | } |
1078 | |
1079 | @Test(expected = IllegalArgumentException.class) |
1080 | public void testNegativeInputBindingPosition() |
1081 | { |
1082 | - map_NestedLoops(groupScan_Default(coi), groupScan_Default(coi), -1, 1); |
1083 | + map_NestedLoops(groupScan_Default(coi), groupScan_Default(coi), -1, pipelineMap(), 1); |
1084 | } |
1085 | |
1086 | @Test(expected = IllegalArgumentException.class) |
1087 | public void testNonPositiveDepth() |
1088 | { |
1089 | - map_NestedLoops(groupScan_Default(coi), groupScan_Default(coi), 0, 0); |
1090 | + map_NestedLoops(groupScan_Default(coi), groupScan_Default(coi), 0, pipelineMap(),0); |
1091 | } |
1092 | |
1093 | // Test operator execution |
1094 | @@ -132,7 +132,7 @@ |
1095 | map_NestedLoops( |
1096 | indexScan_Default(itemOidIndexRowType, false), |
1097 | ancestorLookup_Nested(coi, itemOidIndexRowType, Collections.singleton(itemRowType), 0), |
1098 | - 0, 1); |
1099 | + 0, pipelineMap(), 1); |
1100 | RowBase[] expected = new RowBase[]{ |
1101 | row(itemRowType, 1000L, 100L), |
1102 | row(itemRowType, 1001L, 100L), |
1103 | @@ -175,7 +175,7 @@ |
1104 | groupScan_Default(coi), |
1105 | Collections.singleton(customerRowType)), |
1106 | project, |
1107 | - 0, 1); |
1108 | + 0, pipelineMap(), 1); |
1109 | RowType projectRowType = project.rowType(); |
1110 | RowBase[] expected = new RowBase[]{ |
1111 | row(projectRowType, 1L, 100L), |
1112 | @@ -212,7 +212,7 @@ |
1113 | groupScan_Default(coi), |
1114 | Collections.singleton(customerRowType)), |
1115 | ifEmpty_Default(project, projectRowType, Arrays.asList(boundField(customerRowType, 0, 0), literal(null)), InputPreservationOption.KEEP_INPUT), |
1116 | - 0, 1); |
1117 | + 0, pipelineMap(), 1); |
1118 | RowBase[] expected = new RowBase[]{ |
1119 | row(projectRowType, 1L, 100L), |
1120 | row(projectRowType, 1L, 101L), |
1121 | @@ -234,7 +234,7 @@ |
1122 | map_NestedLoops( |
1123 | indexScan_Default(itemOidIndexRowType, false), |
1124 | ancestorLookup_Nested(coi, itemOidIndexRowType, Collections.singleton(itemRowType), 0), |
1125 | - 0, 1); |
1126 | + 0, pipelineMap(), 1); |
1127 | CursorLifecycleTestCase testCase = new CursorLifecycleTestCase() |
1128 | { |
1129 | @Override |
1130 | @@ -291,8 +291,8 @@ |
1131 | map_NestedLoops( |
1132 | indexScan_Default(customerCidIndexRowType, false, cidRange), |
1133 | ancestorLookup_Nested(coi, customerCidIndexRowType, Collections.singleton(customerRowType), 0), |
1134 | - 0, 2), |
1135 | - 1, 1); |
1136 | + 0, pipelineMap(), 2), |
1137 | + 1, pipelineMap(), 1); |
1138 | RowBase[] expected = new RowBase[]{ |
1139 | row(customerRowType, 1L, "northbridge"), |
1140 | row(customerRowType, 2L, "foundation"), |
1141 | |
1142 | === added file 'src/test/java/com/akiban/server/test/it/qp/Map_NestedLoopsPipelineIT.java' |
1143 | --- src/test/java/com/akiban/server/test/it/qp/Map_NestedLoopsPipelineIT.java 1970-01-01 00:00:00 +0000 |
1144 | +++ src/test/java/com/akiban/server/test/it/qp/Map_NestedLoopsPipelineIT.java 2013-07-13 21:10:31 +0000 |
1145 | @@ -0,0 +1,26 @@ |
1146 | +/** |
1147 | + * Copyright (C) 2009-2013 Akiban Technologies, Inc. |
1148 | + * |
1149 | + * This program is free software: you can redistribute it and/or modify |
1150 | + * it under the terms of the GNU Affero General Public License as published by |
1151 | + * the Free Software Foundation, either version 3 of the License, or |
1152 | + * (at your option) any later version. |
1153 | + * |
1154 | + * This program is distributed in the hope that it will be useful, |
1155 | + * but WITHOUT ANY WARRANTY; without even the implied warranty of |
1156 | + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
1157 | + * GNU Affero General Public License for more details. |
1158 | + * |
1159 | + * You should have received a copy of the GNU Affero General Public License |
1160 | + * along with this program. If not, see <http://www.gnu.org/licenses/>. |
1161 | + */ |
1162 | + |
1163 | +package com.akiban.server.test.it.qp; |
1164 | + |
1165 | +public class Map_NestedLoopsPipelineIT extends Map_NestedLoopsIT |
1166 | +{ |
1167 | + @Override |
1168 | + protected boolean pipelineMap() { |
1169 | + return true; |
1170 | + } |
1171 | +} |
1172 | |
1173 | === modified file 'src/test/java/com/akiban/server/test/it/qp/Sort_InsertionLimitedIT.java' |
1174 | --- src/test/java/com/akiban/server/test/it/qp/Sort_InsertionLimitedIT.java 2013-07-10 18:42:14 +0000 |
1175 | +++ src/test/java/com/akiban/server/test/it/qp/Sort_InsertionLimitedIT.java 2013-07-13 21:10:31 +0000 |
1176 | @@ -540,7 +540,7 @@ |
1177 | map_NestedLoops( |
1178 | filter_Default(groupScan_Default(coi), |
1179 | Collections.singleton(customerRowType)), |
1180 | - project, 0, 1), |
1181 | + project, 0, pipelineMap(), 1), |
1182 | projectType, |
1183 | ordering(field(projectType, 0), true), |
1184 | SortOption.PRESERVE_DUPLICATES, |
1185 | |
1186 | === modified file 'src/test/java/com/akiban/server/test/it/qp/UnionAll_DefaultIT.java' |
1187 | --- src/test/java/com/akiban/server/test/it/qp/UnionAll_DefaultIT.java 2013-07-10 18:42:14 +0000 |
1188 | +++ src/test/java/com/akiban/server/test/it/qp/UnionAll_DefaultIT.java 2013-07-13 21:10:31 +0000 |
1189 | @@ -270,7 +270,7 @@ |
1190 | xEQ9Range, |
1191 | ordering), |
1192 | txIndexRowType), |
1193 | - 0, 1); |
1194 | + 0, pipelineMap(), 1); |
1195 | String[] expected = new String[]{ |
1196 | hKey(1000), |
1197 | hKey(1002), |
1198 | |
1199 | === modified file 'src/test/java/com/akiban/sql/optimizer/OperatorCompilerTest.java' |
1200 | --- src/test/java/com/akiban/sql/optimizer/OperatorCompilerTest.java 2013-03-22 20:05:57 +0000 |
1201 | +++ src/test/java/com/akiban/sql/optimizer/OperatorCompilerTest.java 2013-07-13 21:10:31 +0000 |
1202 | @@ -33,6 +33,7 @@ |
1203 | import com.akiban.sql.optimizer.plan.ResultSet.ResultField; |
1204 | import com.akiban.sql.optimizer.rule.ExplainPlanContext; |
1205 | import com.akiban.sql.optimizer.rule.RulesTestHelper; |
1206 | +import com.akiban.sql.optimizer.rule.PipelineConfiguration; |
1207 | import com.akiban.sql.optimizer.rule.cost.TestCostEstimator; |
1208 | |
1209 | import com.akiban.junit.NamedParameterizedRunner; |
1210 | @@ -122,6 +123,7 @@ |
1211 | compiler.initT3Registry(t3Registry); |
1212 | } |
1213 | compiler.initCostEstimator(new TestCostEstimator(ais, compiler.getSchema(), statsFile, false, properties), usePValues); |
1214 | + compiler.initPipelineConfiguration(new PipelineConfiguration()); |
1215 | compiler.initDone(); |
1216 | return compiler; |
1217 | } |
1218 | |
1219 | === modified file 'src/test/java/com/akiban/sql/optimizer/rule/RulesTestContext.java' |
1220 | --- src/test/java/com/akiban/sql/optimizer/rule/RulesTestContext.java 2013-03-22 20:05:57 +0000 |
1221 | +++ src/test/java/com/akiban/sql/optimizer/rule/RulesTestContext.java 2013-07-13 21:10:31 +0000 |
1222 | @@ -50,6 +50,7 @@ |
1223 | context.initCostEstimator(new TestCostEstimator(ais, context.getSchema(), |
1224 | statsFile, statsIgnoreMissingIndexes, |
1225 | properties), false); |
1226 | + context.initPipelineConfiguration(new PipelineConfiguration()); |
1227 | context.initDone(); |
1228 | return context; |
1229 | } |
The static Integer and config lookup in the cursor() call is a little unfortunate. What about moving them to named methods on the QueryContext, which could then be initialized more explicitly?
Otherwise looks pretty good.