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

wip

上级 3b15a771
...@@ -68,6 +68,11 @@ public abstract class Command implements CommandInterface { ...@@ -68,6 +68,11 @@ public abstract class Command implements CommandInterface {
@Override @Override
public abstract boolean isQuery(); public abstract boolean isQuery();
/**
* Prepare join batching.
*/
public abstract void prepareJoinBatch();
/** /**
* Get the list of parameters. * Get the list of parameters.
* *
......
...@@ -7,6 +7,7 @@ package org.h2.command; ...@@ -7,6 +7,7 @@ package org.h2.command;
import java.util.ArrayList; import java.util.ArrayList;
import org.h2.api.DatabaseEventListener; import org.h2.api.DatabaseEventListener;
import org.h2.command.dml.Query;
import org.h2.expression.Parameter; import org.h2.expression.Parameter;
import org.h2.expression.ParameterInterface; import org.h2.expression.ParameterInterface;
import org.h2.result.ResultInterface; import org.h2.result.ResultInterface;
...@@ -44,6 +45,13 @@ public class CommandContainer extends Command { ...@@ -44,6 +45,13 @@ public class CommandContainer extends Command {
return prepared.isQuery(); return prepared.isQuery();
} }
@Override
public void prepareJoinBatch() {
if (session.isJoinBatchEnabled() && prepared.isQuery()) {
((Query) prepared).prepareJoinBatch();
}
}
private void recompileIfRequired() { private void recompileIfRequired() {
if (prepared.needRecompile()) { if (prepared.needRecompile()) {
// TODO test with 'always recompile' // TODO test with 'always recompile'
...@@ -65,6 +73,7 @@ public class CommandContainer extends Command { ...@@ -65,6 +73,7 @@ public class CommandContainer extends Command {
} }
prepared.prepare(); prepared.prepare();
prepared.setModificationMetaId(mod); prepared.setModificationMetaId(mod);
prepareJoinBatch();
} }
} }
......
...@@ -44,6 +44,11 @@ class CommandList extends Command { ...@@ -44,6 +44,11 @@ class CommandList extends Command {
return updateCount; return updateCount;
} }
@Override
public void prepareJoinBatch() {
command.prepareJoinBatch();
}
@Override @Override
public ResultInterface query(int maxrows) { public ResultInterface query(int maxrows) {
ResultInterface result = command.query(maxrows); ResultInterface result = command.query(maxrows);
......
...@@ -71,8 +71,18 @@ public abstract class Query extends Prepared { ...@@ -71,8 +71,18 @@ public abstract class Query extends Prepared {
super(session); super(session);
} }
/**
* Check if this is a UNION query.
*
* @return {@code true} if this is a UNION query
*/
public abstract boolean isUnion(); public abstract boolean isUnion();
/**
* Prepare join batching.
*/
public abstract void prepareJoinBatch();
/** /**
* Execute the query without checking the cache. If a target is specified, * Execute the query without checking the cache. If a target is specified,
* the results are written to it, and the method returns null. If no target * the results are written to it, and the method returns null. If no target
......
...@@ -947,12 +947,29 @@ public class Select extends Query { ...@@ -947,12 +947,29 @@ public class Select extends Query {
} }
expressionArray = new Expression[expressions.size()]; expressionArray = new Expression[expressions.size()];
expressions.toArray(expressionArray); expressions.toArray(expressionArray);
if (!session.isParsingView()) {
topTableFilter.prepareJoinBatch(0);
}
isPrepared = true; isPrepared = true;
} }
@Override
public void prepareJoinBatch() {
ArrayList<TableFilter> list = New.arrayList();
TableFilter f = getTopTableFilter();
do {
if (f.getNestedJoin() != null) {
// we do not support batching with nested joins
return;
}
list.add(f);
f = f.getJoin();
} while (f != null);
TableFilter[] fs = list.toArray(new TableFilter[list.size()]);
// prepare join batch
JoinBatch jb = null;
for (int i = fs.length - 1; i >= 0; i--) {
jb = fs[i].prepareJoinBatch(jb, fs, i);
}
}
public JoinBatch getJoinBatch() { public JoinBatch getJoinBatch() {
return getTopTableFilter().getJoinBatch(); return getTopTableFilter().getJoinBatch();
} }
......
...@@ -76,6 +76,12 @@ public class SelectUnion extends Query { ...@@ -76,6 +76,12 @@ public class SelectUnion extends Query {
return true; return true;
} }
@Override
public void prepareJoinBatch() {
left.prepareJoinBatch();
right.prepareJoinBatch();
}
public void setUnionType(int type) { public void setUnionType(int type) {
this.unionType = type; this.unionType = type;
} }
......
...@@ -497,6 +497,15 @@ public class Set extends Prepared { ...@@ -497,6 +497,15 @@ public class Set extends Prepared {
database.setRowFactory(rowFactory); database.setRowFactory(rowFactory);
break; break;
} }
case SetTypes.BATCH_JOINS: {
int value = getIntValue();
if (value != 0 && value != 1) {
throw DbException.getInvalidValueException("BATCH_JOINS",
getIntValue());
}
session.setJoinBatchEnabled(value == 1);
break;
}
default: default:
DbException.throwInternalError("type="+type); DbException.throwInternalError("type="+type);
} }
......
...@@ -228,6 +228,11 @@ public class SetTypes { ...@@ -228,6 +228,11 @@ public class SetTypes {
*/ */
public static final int ROW_FACTORY = 43; public static final int ROW_FACTORY = 43;
/**
* The type of SET BATCH_JOINS statement.
*/
public static final int BATCH_JOINS = 44;
private static final ArrayList<String> TYPES = New.arrayList(); private static final ArrayList<String> TYPES = New.arrayList();
private SetTypes() { private SetTypes() {
...@@ -280,6 +285,7 @@ public class SetTypes { ...@@ -280,6 +285,7 @@ public class SetTypes {
list.add(QUERY_STATISTICS, "QUERY_STATISTICS"); list.add(QUERY_STATISTICS, "QUERY_STATISTICS");
list.add(QUERY_STATISTICS_MAX_ENTRIES, "QUERY_STATISTICS_MAX_ENTRIES"); list.add(QUERY_STATISTICS_MAX_ENTRIES, "QUERY_STATISTICS_MAX_ENTRIES");
list.add(ROW_FACTORY, "ROW_FACTORY"); list.add(ROW_FACTORY, "ROW_FACTORY");
list.add(BATCH_JOINS, "BATCH_JOINS");
} }
/** /**
......
...@@ -30,12 +30,14 @@ import org.h2.mvstore.db.TransactionStore.Change; ...@@ -30,12 +30,14 @@ import org.h2.mvstore.db.TransactionStore.Change;
import org.h2.mvstore.db.TransactionStore.Transaction; import org.h2.mvstore.db.TransactionStore.Transaction;
import org.h2.result.LocalResult; import org.h2.result.LocalResult;
import org.h2.result.Row; import org.h2.result.Row;
import org.h2.result.SortOrder;
import org.h2.schema.Schema; import org.h2.schema.Schema;
import org.h2.store.DataHandler; import org.h2.store.DataHandler;
import org.h2.store.InDoubtTransaction; import org.h2.store.InDoubtTransaction;
import org.h2.store.LobStorageFrontend; import org.h2.store.LobStorageFrontend;
import org.h2.table.SubQueryInfo; import org.h2.table.SubQueryInfo;
import org.h2.table.Table; import org.h2.table.Table;
import org.h2.table.TableFilter;
import org.h2.util.New; import org.h2.util.New;
import org.h2.util.SmallLRUCache; import org.h2.util.SmallLRUCache;
import org.h2.value.Value; import org.h2.value.Value;
...@@ -115,6 +117,7 @@ public class Session extends SessionWithState { ...@@ -115,6 +117,7 @@ public class Session extends SessionWithState {
private int parsingView; private int parsingView;
private volatile SmallLRUCache<Object, ViewIndex> viewIndexCache; private volatile SmallLRUCache<Object, ViewIndex> viewIndexCache;
private HashMap<Object, ViewIndex> subQueryIndexCache; private HashMap<Object, ViewIndex> subQueryIndexCache;
private boolean joinBatchEnabled;
/** /**
* Temporary LOBs from result sets. Those are kept for some time. The * Temporary LOBs from result sets. Those are kept for some time. The
...@@ -148,12 +151,25 @@ public class Session extends SessionWithState { ...@@ -148,12 +151,25 @@ public class Session extends SessionWithState {
this.currentSchemaName = Constants.SCHEMA_MAIN; this.currentSchemaName = Constants.SCHEMA_MAIN;
} }
public void setJoinBatchEnabled(boolean joinBatchEnabled) {
this.joinBatchEnabled = joinBatchEnabled;
}
public boolean isJoinBatchEnabled() {
return joinBatchEnabled;
}
public Row createRow(Value[] data, int memory) { public Row createRow(Value[] data, int memory) {
return database.createRow(data, memory); return database.createRow(data, memory);
} }
public void setSubQueryInfo(SubQueryInfo subQueryInfo) { public void pushSubQueryInfo(int[] masks, TableFilter[] filters, int filter,
this.subQueryInfo = subQueryInfo; SortOrder sortOrder) {
subQueryInfo = new SubQueryInfo(subQueryInfo, masks, filters, filter, sortOrder);
}
public void popSubQueryInfo() {
subQueryInfo = subQueryInfo.getUpper();
} }
public SubQueryInfo getSubQueryInfo() { public SubQueryInfo getSubQueryInfo() {
...@@ -492,6 +508,7 @@ public class Session extends SessionWithState { ...@@ -492,6 +508,7 @@ public class Session extends SessionWithState {
// we can't reuse sub-query indexes, so just drop the whole cache // we can't reuse sub-query indexes, so just drop the whole cache
subQueryIndexCache = null; subQueryIndexCache = null;
} }
command.prepareJoinBatch();
if (queryCache != null) { if (queryCache != null) {
if (command.isCacheable()) { if (command.isCacheable()) {
queryCache.put(sql, command); queryCache.put(sql, command);
......
...@@ -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.engine.Database; import org.h2.engine.Database;
import org.h2.engine.DbObject; import org.h2.engine.DbObject;
......
...@@ -22,7 +22,6 @@ import org.h2.result.SortOrder; ...@@ -22,7 +22,6 @@ import org.h2.result.SortOrder;
import org.h2.table.Column; import org.h2.table.Column;
import org.h2.table.IndexColumn; import org.h2.table.IndexColumn;
import org.h2.table.JoinBatch; import org.h2.table.JoinBatch;
import org.h2.table.SubQueryInfo;
import org.h2.table.TableFilter; import org.h2.table.TableFilter;
import org.h2.table.TableView; import org.h2.table.TableView;
import org.h2.util.IntArray; import org.h2.util.IntArray;
...@@ -209,14 +208,11 @@ public class ViewIndex extends BaseIndex implements SpatialIndex { ...@@ -209,14 +208,11 @@ public class ViewIndex extends BaseIndex implements SpatialIndex {
TableFilter[] filters, int filter, SortOrder sortOrder, boolean preliminary) { TableFilter[] filters, int filter, SortOrder sortOrder, boolean preliminary) {
assert filters != null; assert filters != null;
Prepared p; Prepared p;
SubQueryInfo upper = session.getSubQueryInfo(); session.pushSubQueryInfo(masks, filters, filter, sortOrder);
SubQueryInfo info = new SubQueryInfo(upper,
masks, filters, filter, sortOrder, preliminary);
session.setSubQueryInfo(info);
try { try {
p = session.prepare(sql, true); p = session.prepare(sql, true);
} finally { } finally {
session.setSubQueryInfo(upper); session.popSubQueryInfo();
} }
return (Query) p; return (Query) p;
} }
......
...@@ -163,16 +163,16 @@ public final class JoinBatch { ...@@ -163,16 +163,16 @@ public final class JoinBatch {
current = new JoinRow(new Object[filters.length]); current = new JoinRow(new Object[filters.length]);
// initialize top cursor // initialize top cursor
Cursor cursor; Cursor cursor;
if (!batchedSubQuery) { if (batchedSubQuery) {
// it is a top level batched query // we are at the batched sub-query
assert viewTopFutureCursor != null;
cursor = get(viewTopFutureCursor);
} else {
// setup usual index cursor
TableFilter f = top.filter; TableFilter f = top.filter;
IndexCursor indexCursor = f.getIndexCursor(); IndexCursor indexCursor = f.getIndexCursor();
indexCursor.find(f.getSession(), f.getIndexConditions()); indexCursor.find(f.getSession(), f.getIndexConditions());
cursor = indexCursor; cursor = indexCursor;
} else {
// we are at the batched sub-query
assert viewTopFutureCursor != null;
cursor = get(viewTopFutureCursor);
} }
current.updateRow(top.id, cursor, JoinRow.S_NULL, JoinRow.S_CURSOR); current.updateRow(top.id, cursor, JoinRow.S_NULL, JoinRow.S_CURSOR);
// 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
...@@ -372,8 +372,6 @@ public final class JoinBatch { ...@@ -372,8 +372,6 @@ public final class JoinBatch {
* @return Adapter to allow joining to this batch in sub-queries and views. * @return Adapter to allow joining to this batch in sub-queries and views.
*/ */
private IndexLookupBatch viewIndexLookupBatch(ViewIndex viewIndex) { private IndexLookupBatch viewIndexLookupBatch(ViewIndex viewIndex) {
assert !batchedSubQuery;
batchedSubQuery = true;
return new ViewIndexLookupBatch(viewIndex); return new ViewIndexLookupBatch(viewIndex);
} }
...@@ -385,20 +383,18 @@ public final class JoinBatch { ...@@ -385,20 +383,18 @@ public final class JoinBatch {
*/ */
public static IndexLookupBatch createViewIndexLookupBatch(ViewIndex viewIndex) { public static IndexLookupBatch createViewIndexLookupBatch(ViewIndex viewIndex) {
Query query = viewIndex.getQuery(); Query query = viewIndex.getQuery();
query.prepareJoinBatch();
if (query.isUnion()) { if (query.isUnion()) {
ViewIndexLookupBatchUnion unionBatch = new ViewIndexLookupBatchUnion(viewIndex); return new ViewIndexLookupBatchUnion(viewIndex);
return unionBatch.initialize() ? unionBatch : null;
} else {
Select select = (Select) query;
// here we have already prepared plan for our sub-query,
// thus select already contains join batch for it (if it has to)
JoinBatch joinBatch = select.getJoinBatch();
if (joinBatch == null) {
// our sub-query itself is not batched, will run usual way
return null;
} }
return joinBatch.viewIndexLookupBatch(viewIndex); JoinBatch jb = ((Select) query).getJoinBatch();
if (jb == null) {
// our sub-query is not batched, will run usual way
return null;
} }
assert !jb.batchedSubQuery;
jb.batchedSubQuery = true;
return jb.viewIndexLookupBatch(viewIndex);
} }
@Override @Override
...@@ -850,12 +846,6 @@ public final class JoinBatch { ...@@ -850,12 +846,6 @@ public final class JoinBatch {
return top.isBatchFull(); return top.isBatchFull();
} }
@Override
public void reset() {
super.reset();
JoinBatch.this.reset();
}
@Override @Override
protected void startQueryRunners() { protected void startQueryRunners() {
// we do batched find only for top table filter and then lazily run the ViewIndex query // we do batched find only for top table filter and then lazily run the ViewIndex query
...@@ -913,29 +903,24 @@ public final class JoinBatch { ...@@ -913,29 +903,24 @@ public final class JoinBatch {
protected ViewIndexLookupBatchUnion(ViewIndex viewIndex) { protected ViewIndexLookupBatchUnion(ViewIndex viewIndex) {
super(viewIndex); super(viewIndex);
collectTopTableFilters(viewIndex.getQuery());
} }
/** private void collectTopTableFilters(Query query) {
* @return {@code true} if initialized successfully
*/
private boolean initialize() {
return collectTopTableFilters(viewIndex.getQuery());
}
private boolean collectTopTableFilters(Query query) {
if (query.isUnion()) { if (query.isUnion()) {
SelectUnion union = (SelectUnion) query; SelectUnion union = (SelectUnion) query;
return collectTopTableFilters(union.getLeft()) && collectTopTableFilters(union.getLeft());
collectTopTableFilters(union.getRight()); collectTopTableFilters(union.getRight());
} } else {
Select select = (Select) query; Select select = (Select) query;
JoinBatch jb = select.getJoinBatch(); JoinBatch jb = select.getJoinBatch();
if (jb == null) { if (jb == null) {
// if we've found non-batched select then we can't do batching at all here // TODO need some wrapper for a non-batched query
return false;
} }
assert !jb.batchedSubQuery;
jb.batchedSubQuery = true;
tops.add(jb.filters[0]); tops.add(jb.filters[0]);
return true; }
} }
@Override @Override
......
...@@ -19,7 +19,6 @@ public class SubQueryInfo { ...@@ -19,7 +19,6 @@ public class SubQueryInfo {
private TableFilter[] filters; private TableFilter[] filters;
private int filter; private int filter;
private SortOrder sortOrder; private SortOrder sortOrder;
private boolean preliminary;
private SubQueryInfo upper; private SubQueryInfo upper;
/** /**
...@@ -28,17 +27,14 @@ public class SubQueryInfo { ...@@ -28,17 +27,14 @@ public class SubQueryInfo {
* @param filters table filters * @param filters table filters
* @param filter current filter * @param filter current filter
* @param sortOrder sort order * @param sortOrder sort order
* @param preliminary if this is a preliminary query optimization
* without global conditions
*/ */
public SubQueryInfo(SubQueryInfo upper, int[] masks, TableFilter[] filters, int filter, public SubQueryInfo(SubQueryInfo upper, int[] masks, TableFilter[] filters, int filter,
SortOrder sortOrder, boolean preliminary) { SortOrder sortOrder) {
this.upper = upper; this.upper = upper;
this.masks = masks; this.masks = masks;
this.filters = filters; this.filters = filters;
this.filter = filter; this.filter = filter;
this.sortOrder = sortOrder; this.sortOrder = sortOrder;
this.preliminary = preliminary;
} }
public SubQueryInfo getUpper() { public SubQueryInfo getUpper() {
...@@ -60,8 +56,4 @@ public class SubQueryInfo { ...@@ -60,8 +56,4 @@ public class SubQueryInfo {
public SortOrder getSortOrder() { public SortOrder getSortOrder() {
return sortOrder; return sortOrder;
} }
public boolean isPreliminary() {
return preliminary;
}
} }
...@@ -127,6 +127,10 @@ public abstract class Table extends SchemaObjectBase { ...@@ -127,6 +127,10 @@ public abstract class Table extends SchemaObjectBase {
} }
} }
public boolean isView() {
return false;
}
/** /**
* Lock the table for the given session. * Lock the table for the given session.
* This method waits until the lock is granted. * This method waits until the lock is granted.
......
...@@ -340,8 +340,8 @@ public class TableFilter implements ColumnResolver { ...@@ -340,8 +340,8 @@ public class TableFilter implements ColumnResolver {
foundOne = false; foundOne = false;
} }
private boolean isAlwaysTopTableFilter(int id) { private boolean isAlwaysTopTableFilter(int filter) {
if (id != 0) { if (filter != 0) {
return false; return false;
} }
// check if we are at the top table filters all the way up // check if we are at the top table filters all the way up
...@@ -361,15 +361,12 @@ public class TableFilter implements ColumnResolver { ...@@ -361,15 +361,12 @@ public class TableFilter implements ColumnResolver {
* Attempt to initialize batched join. * Attempt to initialize batched join.
* *
* @param id join filter id (index of this table filter in join list) * @param id join filter id (index of this table filter in join list)
* @param jb join batch if it is already created
* @return join batch if query runs over index which supports batched lookups, {@code null} otherwise * @return join batch if query runs over index which supports batched lookups, {@code null} otherwise
*/ */
public JoinBatch prepareJoinBatch(int id) { public JoinBatch prepareJoinBatch(JoinBatch jb, TableFilter[] filters, int filter) {
joinBatch = null; joinBatch = null;
joinFilterId = -1; joinFilterId = -1;
JoinBatch jb = null;
if (join != null) {
jb = join.prepareJoinBatch(id + 1);
}
IndexLookupBatch lookupBatch = null; IndexLookupBatch lookupBatch = null;
// For globally top table filter we don't need to create lookup batch, because // For globally top table filter we don't need to create lookup batch, because
// currently it will not be used (this will be shown in ViewIndex.getPlanSQL()). Probably // currently it will not be used (this will be shown in ViewIndex.getPlanSQL()). Probably
...@@ -378,28 +375,40 @@ public class TableFilter implements ColumnResolver { ...@@ -378,28 +375,40 @@ public class TableFilter implements ColumnResolver {
// then we either not a top table filter or top table filter in a sub-query, which // then we either not a top table filter or top table filter in a sub-query, which
// in turn is not top in outer query, thus we need to enable batching here to allow // in turn is not top in outer query, thus we need to enable batching here to allow
// outer query run batched join against this sub-query. // outer query run batched join against this sub-query.
if (jb == null && select != null && !isAlwaysTopTableFilter(id)) { if (jb == null && select != null && !isAlwaysTopTableFilter(filter)) {
lookupBatch = index.createLookupBatch(this); lookupBatch = createLookupBatch(filters, filter);
if (lookupBatch != null) { if (lookupBatch != null) {
jb = new JoinBatch(id + 1, join); jb = new JoinBatch(filter + 1, join);
} }
} }
if (jb != null) { if (jb != null) {
if (nestedJoin != null) { if (nestedJoin != null) {
throw DbException.getUnsupportedException("nested join with batched index"); throw DbException.throwInternalError();
} }
joinBatch = jb; joinBatch = jb;
joinFilterId = id; joinFilterId = filter;
if (lookupBatch == null && !isAlwaysTopTableFilter(id)) { if (lookupBatch == null && !isAlwaysTopTableFilter(filter)) {
// index.createLookupBatch will be called at most once because jb can be // createLookupBatch will be called at most once because jb can be
// created only if lookupBatch is already not null from the call above. // created only if lookupBatch is already not null from the call above.
lookupBatch = index.createLookupBatch(this); lookupBatch = createLookupBatch(filters, filter);
} }
jb.register(this, lookupBatch); jb.register(this, lookupBatch);
} }
return jb; return jb;
} }
private IndexLookupBatch createLookupBatch(TableFilter[] filters, int filter) {
if (getTable().isView()) {
session.pushSubQueryInfo(masks, filters, filter, select.getSortOrder());
try {
return index.createLookupBatch(this);
} finally {
session.popSubQueryInfo();
}
}
return index.createLookupBatch(this);
}
public int getJoinFilterId() { public int getJoinFilterId() {
return joinFilterId; return joinFilterId;
} }
......
...@@ -218,6 +218,11 @@ public class TableView extends Table { ...@@ -218,6 +218,11 @@ public class TableView extends Table {
} }
} }
@Override
public boolean isView() {
return true;
}
/** /**
* Check if this view is currently invalid. * Check if this view is currently invalid.
* *
......
Markdown 格式
0%
您添加了 0 到此讨论。请谨慎行事。
请先完成此评论的编辑!
注册 或者 后发表评论