提交 0dd766c9 authored 作者: S.Vladykin's avatar S.Vladykin

wip

上级 1e9b6d64
...@@ -6,8 +6,6 @@ ...@@ -6,8 +6,6 @@
package org.h2.index; package org.h2.index;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.Future;
import org.h2.api.ErrorCode; import org.h2.api.ErrorCode;
import org.h2.command.Prepared; import org.h2.command.Prepared;
import org.h2.command.dml.Query; import org.h2.command.dml.Query;
...@@ -103,12 +101,8 @@ public class ViewIndex extends BaseIndex implements SpatialIndex { ...@@ -103,12 +101,8 @@ public class ViewIndex extends BaseIndex implements SpatialIndex {
// we do not support batching for recursive queries // we do not support batching for recursive queries
return null; return null;
} }
IndexLookupBatch lookupBatch = query.isUnion() ? // currently do not support unions
createLookupBatchUnion((SelectUnion) query) : return query.isUnion() ? null : createLookupBatchSimple((Select) query);
createLookupBatchSimple((Select) query);
// TODO not initialize index cursor on the top table filter but work as usual batching
// TODO return wrapper which goes through all the joins an collects all the rows
return null;
} }
private IndexLookupBatch createLookupBatchSimple(Select select) { private IndexLookupBatch createLookupBatchSimple(Select select) {
...@@ -119,30 +113,7 @@ public class ViewIndex extends BaseIndex implements SpatialIndex { ...@@ -119,30 +113,7 @@ public class ViewIndex extends BaseIndex implements SpatialIndex {
// our sub-query itself is not batched, will run usual way // our sub-query itself is not batched, will run usual way
return null; return null;
} }
// TODO wrap the join batch into lookup batch return joinBatch.asViewIndexLookupBatch(this);
return null;
}
private IndexLookupBatch createLookupBatchUnion(SelectUnion union) {
Query left = union.getLeft();
IndexLookupBatch leftLookupBatch = left.isUnion() ?
createLookupBatchUnion((SelectUnion) left):
createLookupBatchSimple((Select) left);
Query right = union.getRight();
IndexLookupBatch rightLookupBatch = right.isUnion() ?
createLookupBatchUnion((SelectUnion) right) :
createLookupBatchSimple((Select) right);
if (leftLookupBatch == null) {
if (rightLookupBatch == null) {
return null;
}
leftLookupBatch = null; // TODO
} else if (rightLookupBatch == null) {
rightLookupBatch = null; // TODO
}
return new UnionLookupBatch(leftLookupBatch, rightLookupBatch);
} }
public Session getSession() { public Session getSession() {
...@@ -263,57 +234,60 @@ public class ViewIndex extends BaseIndex implements SpatialIndex { ...@@ -263,57 +234,60 @@ public class ViewIndex extends BaseIndex implements SpatialIndex {
return (Query) p; return (Query) p;
} }
private Cursor find(Session session, SearchRow first, SearchRow last, private Cursor findRecursive(Session session, SearchRow first, SearchRow last,
SearchRow intersection) { SearchRow intersection) {
if (recursive) { assert recursive;
LocalResult recResult = view.getRecursiveResult(); LocalResult recResult = view.getRecursiveResult();
if (recResult != null) { if (recResult != null) {
recResult.reset(); recResult.reset();
return new ViewCursor(this, recResult, first, last); return new ViewCursor(this, recResult, first, last);
} }
if (query == null) { if (query == null) {
query = (Query) createSession.prepare(querySQL, true); query = (Query) createSession.prepare(querySQL, true);
} }
if (!query.isUnion()) { if (!query.isUnion()) {
throw DbException.get(ErrorCode.SYNTAX_ERROR_2, throw DbException.get(ErrorCode.SYNTAX_ERROR_2,
"recursive queries without UNION ALL"); "recursive queries without UNION ALL");
} }
SelectUnion union = (SelectUnion) query; SelectUnion union = (SelectUnion) query;
if (union.getUnionType() != SelectUnion.UNION_ALL) { if (union.getUnionType() != SelectUnion.UNION_ALL) {
throw DbException.get(ErrorCode.SYNTAX_ERROR_2, throw DbException.get(ErrorCode.SYNTAX_ERROR_2,
"recursive queries without UNION ALL"); "recursive queries without UNION ALL");
}
Query left = union.getLeft();
// to ensure the last result is not closed
left.disableCache();
LocalResult r = left.query(0);
LocalResult result = union.getEmptyResult();
// ensure it is not written to disk,
// because it is not closed normally
result.setMaxMemoryRows(Integer.MAX_VALUE);
while (r.next()) {
result.addRow(r.currentRow());
}
Query right = union.getRight();
r.reset();
view.setRecursiveResult(r);
// to ensure the last result is not closed
right.disableCache();
while (true) {
r = right.query(0);
if (r.getRowCount() == 0) {
break;
} }
Query left = union.getLeft();
// to ensure the last result is not closed
left.disableCache();
LocalResult r = left.query(0);
LocalResult result = union.getEmptyResult();
// ensure it is not written to disk,
// because it is not closed normally
result.setMaxMemoryRows(Integer.MAX_VALUE);
while (r.next()) { while (r.next()) {
result.addRow(r.currentRow()); result.addRow(r.currentRow());
} }
Query right = union.getRight();
r.reset(); r.reset();
view.setRecursiveResult(r); view.setRecursiveResult(r);
// to ensure the last result is not closed
right.disableCache();
while (true) {
r = right.query(0);
if (r.getRowCount() == 0) {
break;
}
while (r.next()) {
result.addRow(r.currentRow());
}
r.reset();
view.setRecursiveResult(r);
}
view.setRecursiveResult(null);
result.done();
return new ViewCursor(this, result, first, last);
} }
view.setRecursiveResult(null);
result.done();
return new ViewCursor(this, result, first, last);
}
public void setupQueryParameters(Session session, SearchRow first, SearchRow last,
SearchRow intersection) {
ArrayList<Parameter> paramList = query.getParameters(); ArrayList<Parameter> paramList = query.getParameters();
if (originalParameters != null) { if (originalParameters != null) {
for (int i = 0, size = originalParameters.size(); i < size; i++) { for (int i = 0, size = originalParameters.size(); i < size; i++) {
...@@ -350,6 +324,14 @@ public class ViewIndex extends BaseIndex implements SpatialIndex { ...@@ -350,6 +324,14 @@ public class ViewIndex extends BaseIndex implements SpatialIndex {
setParameter(paramList, idx++, intersection.getValue(i)); setParameter(paramList, idx++, intersection.getValue(i));
} }
} }
}
private Cursor find(Session session, SearchRow first, SearchRow last,
SearchRow intersection) {
if (recursive) {
return findRecursive(session, first, last, intersection);
}
setupQueryParameters(session, first, last, intersection);
LocalResult result = query.query(0); LocalResult result = query.query(0);
return new ViewCursor(this, result, first, last); return new ViewCursor(this, result, first, last);
} }
...@@ -506,42 +488,4 @@ public class ViewIndex extends BaseIndex implements SpatialIndex { ...@@ -506,42 +488,4 @@ public class ViewIndex extends BaseIndex implements SpatialIndex {
public boolean isRecursive() { public boolean isRecursive() {
return recursive; return recursive;
} }
/**
* Lookup batch for unions.
*/
private static final class UnionLookupBatch implements IndexLookupBatch {
private final IndexLookupBatch left;
private final IndexLookupBatch right;
private UnionLookupBatch(IndexLookupBatch left, IndexLookupBatch right) {
this.left = left;
this.right = right;
}
@Override
public void addSearchRows(SearchRow first, SearchRow last) {
assert !left.isBatchFull();
assert !right.isBatchFull();
left.addSearchRows(first, last);
right.addSearchRows(first, last);
}
@Override
public boolean isBatchFull() {
return left.isBatchFull() || right.isBatchFull();
}
@Override
public List<Future<Cursor>> find() {
// TODO Auto-generated method stub
return null;
}
@Override
public void reset() {
left.reset();
right.reset();
}
}
} }
...@@ -6,6 +6,7 @@ ...@@ -6,6 +6,7 @@
package org.h2.table; package org.h2.table;
import java.util.AbstractList; import java.util.AbstractList;
import java.util.ArrayList;
import java.util.Arrays; import java.util.Arrays;
import java.util.Collections; import java.util.Collections;
import java.util.List; import java.util.List;
...@@ -13,10 +14,12 @@ import java.util.concurrent.Future; ...@@ -13,10 +14,12 @@ import java.util.concurrent.Future;
import org.h2.index.Cursor; import org.h2.index.Cursor;
import org.h2.index.IndexCursor; import org.h2.index.IndexCursor;
import org.h2.index.IndexLookupBatch; import org.h2.index.IndexLookupBatch;
import org.h2.index.ViewIndex;
import org.h2.message.DbException; import org.h2.message.DbException;
import org.h2.result.Row; import org.h2.result.Row;
import org.h2.result.SearchRow; import org.h2.result.SearchRow;
import org.h2.util.DoneFuture; import org.h2.util.DoneFuture;
import org.h2.util.New;
import org.h2.value.Value; import org.h2.value.Value;
import org.h2.value.ValueLong; import org.h2.value.ValueLong;
...@@ -28,7 +31,6 @@ import org.h2.value.ValueLong; ...@@ -28,7 +31,6 @@ import org.h2.value.ValueLong;
* @author Sergi Vladykin * @author Sergi Vladykin
*/ */
public final class JoinBatch { public final class JoinBatch {
private static final Cursor EMPTY_CURSOR = new Cursor() { private static final Cursor EMPTY_CURSOR = new Cursor() {
@Override @Override
public boolean previous() { public boolean previous() {
...@@ -54,7 +56,11 @@ public final class JoinBatch { ...@@ -54,7 +56,11 @@ public final class JoinBatch {
public String toString() { public String toString() {
return "EMPTY_CURSOR"; return "EMPTY_CURSOR";
} }
}; };
private static final Future<Cursor> PLACEHOLDER = new DoneFuture<Cursor>(null);
private ViewIndexLookupBatch lookupBatchWrapper;
private JoinFilter[] filters; private JoinFilter[] filters;
private JoinFilter top; private JoinFilter top;
...@@ -148,14 +154,16 @@ public final class JoinBatch { ...@@ -148,14 +154,16 @@ public final class JoinBatch {
} }
private void start() { private void start() {
// TODO if filters[0].isBatched() then use batching instead of top.filter.getIndexCursor()
// initialize current row // initialize current row
current = new JoinRow(new Object[filters.length]); current = new JoinRow(new Object[filters.length]);
current.updateRow(top.id, top.filter.getIndexCursor(), JoinRow.S_NULL, JoinRow.S_CURSOR); if (lookupBatchWrapper == null) {
current.updateRow(top.id, top.filter.getIndexCursor(), JoinRow.S_NULL, JoinRow.S_CURSOR);
// initialize top cursor // initialize top cursor
top.filter.getIndexCursor().find(top.filter.getSession(), top.filter.getIndexConditions()); top.filter.getIndexCursor().find(top.filter.getSession(), top.filter.getIndexConditions());
} else {
// we are sub-query and joined to upper level query
// TODO setup
}
// we need fake first row because batchedNext always will move to the next row // we need fake first row because batchedNext always will move to the next row
JoinRow fake = new JoinRow(null); JoinRow fake = new JoinRow(null);
fake.next = current; fake.next = current;
...@@ -239,7 +247,7 @@ public final class JoinBatch { ...@@ -239,7 +247,7 @@ public final class JoinBatch {
current = join.find(current); current = join.find(current);
} }
if (current.row(join.id) != null) { if (current.row(join.id) != null) {
// either find called or outer join with null row // either find called or outer join with null-row
jfId = join.id; jfId = join.id;
continue; continue;
} }
...@@ -340,13 +348,23 @@ public final class JoinBatch { ...@@ -340,13 +348,23 @@ public final class JoinBatch {
} }
} }
/**
* @return Adapter to allow joining to this batch in sub-queries.
*/
public IndexLookupBatch asViewIndexLookupBatch(ViewIndex viewIndex) {
if (lookupBatchWrapper == null) {
lookupBatchWrapper = new ViewIndexLookupBatch(viewIndex);
}
return lookupBatchWrapper;
}
@Override @Override
public String toString() { public String toString() {
return "JoinBatch->\nprev->" + (current == null ? null : current.prev) + return "JoinBatch->\nprev->" + (current == null ? null : current.prev) +
"\ncurr->" + current + "\ncurr->" + current +
"\nnext->" + (current == null ? null : current.next); "\nnext->" + (current == null ? null : current.next);
} }
/** /**
* Table filter participating in batched join. * Table filter participating in batched join.
*/ */
...@@ -404,6 +422,10 @@ public final class JoinBatch { ...@@ -404,6 +422,10 @@ public final class JoinBatch {
return true; return true;
} }
private List<Future<Cursor>> find() {
return lookupBatch.find();
}
private JoinRow find(JoinRow current) { private JoinRow find(JoinRow current) {
assert current != null; assert current != null;
...@@ -655,5 +677,70 @@ public final class JoinBatch { ...@@ -655,5 +677,70 @@ public final class JoinBatch {
return 1; return 1;
} }
} }
/**
* Lookup batch over this join batch for a sub-query or view.
*/
private final class ViewIndexLookupBatch implements IndexLookupBatch {
private final ViewIndex viewIndex;
private final ArrayList<Future<Cursor>> result = New.arrayList();
private boolean findCalled;
private ViewIndexLookupBatch(ViewIndex viewIndex) {
this.viewIndex = viewIndex;
}
@Override
public void addSearchRows(SearchRow first, SearchRow last) {
if (findCalled) {
result.clear();
findCalled = false;
}
viewIndex.setupQueryParameters(viewIndex.getSession(), first, last, null);
if (top.collectSearchRows()) {
result.add(PLACEHOLDER);
if (top.isBatchFull()) {
// TODO
}
} else {
result.add(null);
}
}
@Override
public boolean isBatchFull() {
assert !findCalled;
return top.isBatchFull();
}
@Override
public List<Future<Cursor>> find() {
JoinBatch jb = JoinBatch.this;
List<Future<Cursor>> topCursors = top.find();
for (int i = 0; i < topCursors.size(); i++) {
// jb.setCursor
while (jb.next()) {
// collect result
}
}
findCalled = true;
return result;
}
@Override
public void reset() {
findCalled = false;
result.clear();
JoinBatch.this.reset();
}
}
/**
* State of the
*/
enum State {
}
} }
...@@ -371,7 +371,10 @@ public class TableFilter implements ColumnResolver { ...@@ -371,7 +371,10 @@ public class TableFilter implements ColumnResolver {
jb = join.prepareBatch(id + 1); jb = join.prepareBatch(id + 1);
} }
IndexLookupBatch lookupBatch = null; IndexLookupBatch lookupBatch = null;
// TODO review the !isAlwaysTopTableFilter condition // the globally top table filter does not need batching, if isAlwaysTopTableFilter is false
// then we either not a top table filter or top table filter in a sub-query which is not
// top in outer query, thus we need to enable batching here to allow outer query run batched
// join against this sub-query
if (jb == null && select != null && !isAlwaysTopTableFilter(id)) { if (jb == null && select != null && !isAlwaysTopTableFilter(id)) {
lookupBatch = index.createLookupBatch(this); lookupBatch = index.createLookupBatch(this);
if (lookupBatch != null) { if (lookupBatch != null) {
...@@ -384,8 +387,13 @@ public class TableFilter implements ColumnResolver { ...@@ -384,8 +387,13 @@ public class TableFilter implements ColumnResolver {
} }
joinBatch = jb; joinBatch = jb;
joinFilterId = id; joinFilterId = id;
// TODO review the !isAlwaysTopTableFilter condition // for globally top table filter we don't need to create lookup batch
// currently it will not be used, probably later on it will make sense
// to create it to better support X IN (...) conditions, but this needs
// to be implemented separately
if (lookupBatch == null && !isAlwaysTopTableFilter(id)) { if (lookupBatch == null && !isAlwaysTopTableFilter(id)) {
// index.createLookupBatch will be called only once because jb can be created only if
// lookupBatch is not null from the first call above
lookupBatch = index.createLookupBatch(this); lookupBatch = index.createLookupBatch(this);
} }
jb.register(this, lookupBatch); jb.register(this, lookupBatch);
......
Markdown 格式
0%
您添加了 0 到此讨论。请谨慎行事。
请先完成此评论的编辑!
注册 或者 后发表评论