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

Sub-query info added for cost calculation.

上级 84fd6dae
...@@ -84,19 +84,26 @@ class Optimizer { ...@@ -84,19 +84,26 @@ class Optimizer {
} }
private void calculateBestPlan() { private void calculateBestPlan() {
start = System.currentTimeMillis();
cost = -1; cost = -1;
if (filters.length == 1 || !isJoinReorderingEnabled()) { if (filters.length == 1 || !isJoinReorderingEnabled()) {
testPlan(filters); testPlan(filters);
} else if (filters.length <= MAX_BRUTE_FORCE_FILTERS) {
calculateBruteForceAll();
} else { } else {
calculateBruteForceSome(); start = System.currentTimeMillis();
random = new Random(0); if (filters.length <= MAX_BRUTE_FORCE_FILTERS) {
calculateGenetic(); calculateBruteForceAll();
} else {
calculateBruteForceSome();
random = new Random(0);
calculateGenetic();
}
} }
} }
private void calculateFakePlan() {
cost = -1;
bestPlan = new Plan(filters, filters.length, condition);
}
private boolean canStop(int x) { private boolean canStop(int x) {
if ((x & 127) == 0) { if ((x & 127) == 0) {
long t = System.currentTimeMillis() - start; long t = System.currentTimeMillis() - start;
...@@ -234,15 +241,24 @@ class Optimizer { ...@@ -234,15 +241,24 @@ class Optimizer {
/** /**
* Calculate the best query plan to use. * Calculate the best query plan to use.
*
* @param parse If we do not need to really get the best plan because it is view a parsing stage.
*/ */
void optimize() { void optimize(boolean parse) {
calculateBestPlan(); if (parse) {
bestPlan.removeUnusableIndexConditions(); calculateFakePlan();
} else {
calculateBestPlan();
bestPlan.removeUnusableIndexConditions();
}
TableFilter[] f2 = bestPlan.getFilters(); TableFilter[] f2 = bestPlan.getFilters();
topFilter = f2[0]; topFilter = f2[0];
for (int i = 0; i < f2.length - 1; i++) { for (int i = 0; i < f2.length - 1; i++) {
f2[i].addJoin(f2[i + 1], false, false, null); f2[i].addJoin(f2[i + 1], false, false, null);
} }
if (parse) {
return;
}
for (TableFilter f : f2) { for (TableFilter f : f2) {
PlanItem item = bestPlan.getItem(f); PlanItem item = bestPlan.getItem(f);
f.setPlanItem(item); f.setPlanItem(item);
......
...@@ -863,7 +863,7 @@ public class Select extends Query { ...@@ -863,7 +863,7 @@ public class Select extends Query {
isQuickAggregateQuery = isEverything(optimizable); isQuickAggregateQuery = isEverything(optimizable);
} }
} }
cost = preparePlan(); cost = preparePlan(session.isParsingView());
if (distinct && session.getDatabase().getSettings().optimizeDistinct && if (distinct && session.getDatabase().getSettings().optimizeDistinct &&
!isGroupQuery && filters.size() == 1 && !isGroupQuery && filters.size() == 1 &&
expressions.size() == 1 && condition == null) { expressions.size() == 1 && condition == null) {
...@@ -967,7 +967,7 @@ public class Select extends Query { ...@@ -967,7 +967,7 @@ public class Select extends Query {
} }
} }
private double preparePlan() { private double preparePlan(boolean parse) {
TableFilter[] topArray = topFilters.toArray( TableFilter[] topArray = topFilters.toArray(
new TableFilter[topFilters.size()]); new TableFilter[topFilters.size()]);
for (TableFilter t : topArray) { for (TableFilter t : topArray) {
...@@ -975,13 +975,15 @@ public class Select extends Query { ...@@ -975,13 +975,15 @@ public class Select extends Query {
} }
Optimizer optimizer = new Optimizer(topArray, condition, session); Optimizer optimizer = new Optimizer(topArray, condition, session);
optimizer.optimize(); optimizer.optimize(parse);
topTableFilter = optimizer.getTopFilter(); topTableFilter = optimizer.getTopFilter();
double planCost = optimizer.getCost(); double planCost = optimizer.getCost();
setEvaluatableRecursive(topTableFilter); setEvaluatableRecursive(topTableFilter);
topTableFilter.prepare(); if (!parse) {
topTableFilter.prepare();
}
return planCost; return planCost;
} }
......
...@@ -5,13 +5,13 @@ ...@@ -5,13 +5,13 @@
*/ */
package org.h2.engine; package org.h2.engine;
import java.util.ArrayDeque;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.HashMap; import java.util.HashMap;
import java.util.HashSet; import java.util.HashSet;
import java.util.Iterator; import java.util.Iterator;
import java.util.LinkedList; import java.util.LinkedList;
import java.util.Random; import java.util.Random;
import org.h2.api.ErrorCode; import org.h2.api.ErrorCode;
import org.h2.command.Command; import org.h2.command.Command;
import org.h2.command.CommandInterface; import org.h2.command.CommandInterface;
...@@ -33,6 +33,7 @@ import org.h2.schema.Schema; ...@@ -33,6 +33,7 @@ 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.Table; import org.h2.table.Table;
import org.h2.util.New; import org.h2.util.New;
import org.h2.util.SmallLRUCache; import org.h2.util.SmallLRUCache;
...@@ -109,6 +110,8 @@ public class Session extends SessionWithState { ...@@ -109,6 +110,8 @@ public class Session extends SessionWithState {
private final int queryCacheSize; private final int queryCacheSize;
private SmallLRUCache<String, Command> queryCache; private SmallLRUCache<String, Command> queryCache;
private long modificationMetaID = -1; private long modificationMetaID = -1;
private ArrayDeque<SubQueryInfo> subQueryInfoStack = new ArrayDeque<SubQueryInfo>();
private int parsingView;
/** /**
* Temporary LOBs from result sets. Those are kept for some time. The * Temporary LOBs from result sets. Those are kept for some time. The
...@@ -142,6 +145,31 @@ public class Session extends SessionWithState { ...@@ -142,6 +145,31 @@ public class Session extends SessionWithState {
this.currentSchemaName = Constants.SCHEMA_MAIN; this.currentSchemaName = Constants.SCHEMA_MAIN;
} }
public void pushSubQueryInfo(SubQueryInfo subQueryInfo) {
assert subQueryInfo != null;
subQueryInfoStack.addLast(subQueryInfo);
}
public void popSubQueryInfo(SubQueryInfo subQueryInfo) {
SubQueryInfo popped = subQueryInfoStack.pollLast();
assert popped == subQueryInfo;
}
public SubQueryInfo getSubQueryInfo() {
return subQueryInfoStack.peekLast();
}
public void setParsingView(boolean parsingView) {
// It can be recursive, thus implemented as counter.
this.parsingView += parsingView ? 1 : -1;
assert this.parsingView >= 0;
}
public boolean isParsingView() {
assert parsingView >= 0;
return parsingView != 0;
}
@Override @Override
public ArrayList<String> getClusterServers() { public ArrayList<String> getClusterServers() {
return new ArrayList<String>(); return new ArrayList<String>();
......
...@@ -5,8 +5,6 @@ ...@@ -5,8 +5,6 @@
*/ */
package org.h2.index; package org.h2.index;
import java.util.List;
import java.util.concurrent.Future;
import org.h2.engine.Session; import org.h2.engine.Session;
import org.h2.result.Row; import org.h2.result.Row;
import org.h2.result.SearchRow; import org.h2.result.SearchRow;
......
...@@ -17,7 +17,7 @@ import org.h2.result.SearchRow; ...@@ -17,7 +17,7 @@ import org.h2.result.SearchRow;
* Note that a single instance of {@link IndexLookupBatch} can be reused for multiple * Note that a single instance of {@link IndexLookupBatch} can be reused for multiple
* sequential batched lookups. * sequential batched lookups.
* *
* @see Index#createLookupBatch(TableFilter) * @see Index#createLookupBatch(org.h2.table.TableFilter)
* @author Sergi Vladykin * @author Sergi Vladykin
*/ */
public interface IndexLookupBatch { public interface IndexLookupBatch {
......
...@@ -6,8 +6,8 @@ ...@@ -6,8 +6,8 @@
package org.h2.index; package org.h2.index;
import java.util.ArrayList; import java.util.ArrayList;
import org.h2.api.ErrorCode; import org.h2.api.ErrorCode;
import org.h2.command.Prepared;
import org.h2.command.dml.Query; import org.h2.command.dml.Query;
import org.h2.command.dml.SelectUnion; import org.h2.command.dml.SelectUnion;
import org.h2.engine.Constants; import org.h2.engine.Constants;
...@@ -21,6 +21,7 @@ import org.h2.result.SearchRow; ...@@ -21,6 +21,7 @@ import org.h2.result.SearchRow;
import org.h2.result.SortOrder; 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.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;
...@@ -46,6 +47,14 @@ public class ViewIndex extends BaseIndex implements SpatialIndex { ...@@ -46,6 +47,14 @@ public class ViewIndex extends BaseIndex implements SpatialIndex {
private Query query; private Query query;
private final Session createSession; private final Session createSession;
/**
* Constructor for the original index in {@link TableView}.
*
* @param view the table view
* @param querySQL the query SQL
* @param originalParameters the original parameters
* @param recursive if the view is recursive
*/
public ViewIndex(TableView view, String querySQL, public ViewIndex(TableView view, String querySQL,
ArrayList<Parameter> originalParameters, boolean recursive) { ArrayList<Parameter> originalParameters, boolean recursive) {
initBaseIndex(view, 0, null, null, IndexType.createNonUnique(false)); initBaseIndex(view, 0, null, null, IndexType.createNonUnique(false));
...@@ -58,8 +67,19 @@ public class ViewIndex extends BaseIndex implements SpatialIndex { ...@@ -58,8 +67,19 @@ public class ViewIndex extends BaseIndex implements SpatialIndex {
this.indexMasks = null; this.indexMasks = null;
} }
/**
* Constructor for plan item generation. Over this index the query will be executed.
*
* @param view the table view
* @param index the view index
* @param session the session
* @param masks the masks
* @param filters table filters
* @param filter current filter
* @param sortOrder sort order
*/
public ViewIndex(TableView view, ViewIndex index, Session session, public ViewIndex(TableView view, ViewIndex index, Session session,
int[] masks) { int[] masks, TableFilter[] filters, int filter, SortOrder sortOrder) {
initBaseIndex(view, 0, null, null, IndexType.createNonUnique(false)); initBaseIndex(view, 0, null, null, IndexType.createNonUnique(false));
this.view = view; this.view = view;
this.querySQL = index.querySQL; this.querySQL = index.querySQL;
...@@ -69,7 +89,7 @@ public class ViewIndex extends BaseIndex implements SpatialIndex { ...@@ -69,7 +89,7 @@ public class ViewIndex extends BaseIndex implements SpatialIndex {
this.createSession = session; this.createSession = session;
columns = new Column[0]; columns = new Column[0];
if (!recursive) { if (!recursive) {
query = getQuery(session, masks); query = getQuery(session, masks, filters, filter, sortOrder);
} }
} }
...@@ -129,20 +149,13 @@ public class ViewIndex extends BaseIndex implements SpatialIndex { ...@@ -129,20 +149,13 @@ public class ViewIndex extends BaseIndex implements SpatialIndex {
return cachedCost.cost; return cachedCost.cost;
} }
} }
Query q = (Query) session.prepare(querySQL, true); Query q = prepareSubQuery(querySQL, session, masks, filters, filter, sortOrder, true);
if (masks != null) { if (masks != null) {
IntArray paramIndex = new IntArray(); for (int idx = 0; idx < masks.length; idx++) {
for (int i = 0; i < masks.length; i++) { int mask = masks[idx];
int mask = masks[i];
if (mask == 0) { if (mask == 0) {
continue; continue;
} }
paramIndex.add(i);
}
int len = paramIndex.size();
for (int i = 0; i < len; i++) {
int idx = paramIndex.get(i);
int mask = masks[idx];
int nextParamIndex = q.getParameters().size() + view.getParameterOffset(); int nextParamIndex = q.getParameters().size() + view.getParameterOffset();
if ((mask & IndexCondition.EQUALITY) != 0) { if ((mask & IndexCondition.EQUALITY) != 0) {
Parameter param = new Parameter(nextParamIndex); Parameter param = new Parameter(nextParamIndex);
...@@ -162,7 +175,7 @@ public class ViewIndex extends BaseIndex implements SpatialIndex { ...@@ -162,7 +175,7 @@ public class ViewIndex extends BaseIndex implements SpatialIndex {
} }
} }
String sql = q.getPlanSQL(); String sql = q.getPlanSQL();
q = (Query) session.prepare(sql, true); q = prepareSubQuery(sql, session, masks, filters, filter, sortOrder, false);
} }
double cost = q.getCost(); double cost = q.getCost();
cachedCost = new CostElement(); cachedCost = new CostElement();
...@@ -182,6 +195,19 @@ public class ViewIndex extends BaseIndex implements SpatialIndex { ...@@ -182,6 +195,19 @@ public class ViewIndex extends BaseIndex implements SpatialIndex {
return find(filter.getSession(), null, null, intersection); return find(filter.getSession(), null, null, intersection);
} }
private static Query prepareSubQuery(String sql, Session session, int[] masks,
TableFilter[] filters, int filter, SortOrder sortOrder, boolean preliminary) {
Prepared p;
SubQueryInfo info = new SubQueryInfo(masks, filters, filter, sortOrder, preliminary);
session.pushSubQueryInfo(info);
try {
p = session.prepare(sql, true);
} finally {
session.popSubQueryInfo(info);
}
return (Query) p;
}
private Cursor find(Session session, SearchRow first, SearchRow last, private Cursor find(Session session, SearchRow first, SearchRow last,
SearchRow intersection) { SearchRow intersection) {
if (recursive) { if (recursive) {
...@@ -284,8 +310,9 @@ public class ViewIndex extends BaseIndex implements SpatialIndex { ...@@ -284,8 +310,9 @@ public class ViewIndex extends BaseIndex implements SpatialIndex {
param.setValue(v); param.setValue(v);
} }
private Query getQuery(Session session, int[] masks) { private Query getQuery(Session session, int[] masks,
Query q = (Query) session.prepare(querySQL, true); TableFilter[] filters, int filter, SortOrder sortOrder) {
Query q = prepareSubQuery(querySQL, session, masks, filters, filter, sortOrder, true);
if (masks == null) { if (masks == null) {
return q; return q;
} }
...@@ -368,7 +395,7 @@ public class ViewIndex extends BaseIndex implements SpatialIndex { ...@@ -368,7 +395,7 @@ public class ViewIndex extends BaseIndex implements SpatialIndex {
} }
String sql = q.getPlanSQL(); String sql = q.getPlanSQL();
q = (Query) session.prepare(sql, true); q = prepareSubQuery(sql, session, masks, filters, filter, sortOrder, false);
return q; return q;
} }
......
/*
* Copyright 2004-2014 H2 Group. Multiple-Licensed under the MPL 2.0,
* and the EPL 1.0 (http://h2database.com/html/license.html).
* Initial Developer: H2 Group
*/
package org.h2.table;
import org.h2.result.SortOrder;
/**
* Information about current sub-query being prepared.
*
* @author Sergi Vladykin
*/
public class SubQueryInfo {
private int[] masks;
private TableFilter[] filters;
private int filter;
private SortOrder sortOrder;
private boolean preliminary;
/**
* @param masks index conditions masks
* @param filters table filters
* @param filter current filter
* @param sortOrder sort order
* @param preliminary if this is a preliminary query optimization
* without global conditions
*/
public SubQueryInfo(int[] masks, TableFilter[] filters, int filter,
SortOrder sortOrder, boolean preliminary) {
this.masks = masks;
this.filters = filters;
this.filter = filter;
this.sortOrder = sortOrder;
this.preliminary = preliminary;
}
public int[] getMasks() {
return masks;
}
public TableFilter[] getFilters() {
return filters;
}
public int getFilter() {
return filter;
}
public SortOrder getSortOrder() {
return sortOrder;
}
public boolean isPreliminary() {
return preliminary;
}
}
...@@ -103,7 +103,13 @@ public class TableView extends Table { ...@@ -103,7 +103,13 @@ public class TableView extends Table {
} }
private static Query compileViewQuery(Session session, String sql) { private static Query compileViewQuery(Session session, String sql) {
Prepared p = session.prepare(sql); Prepared p;
session.setParsingView(true);
try {
p = session.prepare(sql);
} finally {
session.setParsingView(false);
}
if (!(p instanceof Query)) { if (!(p instanceof Query)) {
throw DbException.getSyntaxError(sql, 0); throw DbException.getSyntaxError(sql, 0);
} }
...@@ -241,7 +247,7 @@ public class TableView extends Table { ...@@ -241,7 +247,7 @@ public class TableView extends Table {
// We cannot hold the lock during the ViewIndex creation or we risk ABBA // We cannot hold the lock during the ViewIndex creation or we risk ABBA
// deadlocks if the view creation calls back into H2 via something like // deadlocks if the view creation calls back into H2 via something like
// a FunctionTable. // a FunctionTable.
ViewIndex i2 = new ViewIndex(this, index, session, masks); ViewIndex i2 = new ViewIndex(this, index, session, masks, filters, filter, sortOrder);
synchronized (this) { synchronized (this) {
// have to check again in case another session has beat us to it // have to check again in case another session has beat us to it
ViewIndex i3 = indexCache.get(cacheKey); ViewIndex i3 = indexCache.get(cacheKey);
......
...@@ -70,6 +70,7 @@ public class TestTableEngines extends TestBase { ...@@ -70,6 +70,7 @@ public class TestTableEngines extends TestBase {
@Override @Override
public void test() throws Exception { public void test() throws Exception {
testSubQueryInfo();
testEarlyFilter(); testEarlyFilter();
testEngineParams(); testEngineParams();
testSimpleQuery(); testSimpleQuery();
...@@ -342,7 +343,21 @@ public class TestTableEngines extends TestBase { ...@@ -342,7 +343,21 @@ public class TestTableEngines extends TestBase {
deleteDb("tableEngine"); deleteDb("tableEngine");
} }
private void testSubQueryInfo() throws SQLException {
deleteDb("testSubQueryInfo");
Connection conn = getConnection("testSubQueryInfo");
Statement stat = conn.createStatement();
stat.execute("create table SUB_QUERY_TEST(id int primary key, name varchar) ENGINE \"" +
TreeSetIndexTableEngine.class.getName() + "\"");
stat.execute("create table t1(id int, birthday date)");
stat.executeQuery("select t1.birthday from t1, "
+ "(select t2.id from sub_query_test t2, "
+ "(select t3.id from sub_query_test t3 where t3.name = '') t4 "
+ "where t2.id = t4.id) t5 where t1.id = t5.id");
deleteDb("testSubQueryInfo");
}
private void testBatchedJoin() throws SQLException { private void testBatchedJoin() throws SQLException {
deleteDb("tableEngine"); deleteDb("tableEngine");
Connection conn = getConnection("tableEngine;OPTIMIZE_REUSE_RESULTS=0"); Connection conn = getConnection("tableEngine;OPTIMIZE_REUSE_RESULTS=0");
...@@ -1207,6 +1222,11 @@ public class TestTableEngines extends TestBase { ...@@ -1207,6 +1222,11 @@ public class TestTableEngines extends TestBase {
@Override @Override
public double getCost(Session session, int[] masks, public double getCost(Session session, int[] masks,
TableFilter[] filters, int filter, SortOrder sortOrder) { TableFilter[] filters, int filter, SortOrder sortOrder) {
if (getTable().getName().equals("SUB_QUERY_TEST")) {
if (session.getSubQueryInfo() == null) {
throw new IllegalStateException("No qubquery info found.");
}
}
return getCostRangeIndex(masks, set.size(), filters, filter, sortOrder); return getCostRangeIndex(masks, set.size(), filters, filter, sortOrder);
} }
......
Markdown 格式
0%
您添加了 0 到此讨论。请谨慎行事。
请先完成此评论的编辑!
注册 或者 后发表评论