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

Sub-query info added for cost calculation.

上级 84fd6dae
......@@ -84,19 +84,26 @@ class Optimizer {
}
private void calculateBestPlan() {
start = System.currentTimeMillis();
cost = -1;
if (filters.length == 1 || !isJoinReorderingEnabled()) {
testPlan(filters);
} else if (filters.length <= MAX_BRUTE_FORCE_FILTERS) {
calculateBruteForceAll();
} else {
calculateBruteForceSome();
random = new Random(0);
calculateGenetic();
start = System.currentTimeMillis();
if (filters.length <= MAX_BRUTE_FORCE_FILTERS) {
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) {
if ((x & 127) == 0) {
long t = System.currentTimeMillis() - start;
......@@ -234,15 +241,24 @@ class Optimizer {
/**
* 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() {
calculateBestPlan();
bestPlan.removeUnusableIndexConditions();
void optimize(boolean parse) {
if (parse) {
calculateFakePlan();
} else {
calculateBestPlan();
bestPlan.removeUnusableIndexConditions();
}
TableFilter[] f2 = bestPlan.getFilters();
topFilter = f2[0];
for (int i = 0; i < f2.length - 1; i++) {
f2[i].addJoin(f2[i + 1], false, false, null);
}
if (parse) {
return;
}
for (TableFilter f : f2) {
PlanItem item = bestPlan.getItem(f);
f.setPlanItem(item);
......
......@@ -863,7 +863,7 @@ public class Select extends Query {
isQuickAggregateQuery = isEverything(optimizable);
}
}
cost = preparePlan();
cost = preparePlan(session.isParsingView());
if (distinct && session.getDatabase().getSettings().optimizeDistinct &&
!isGroupQuery && filters.size() == 1 &&
expressions.size() == 1 && condition == null) {
......@@ -967,7 +967,7 @@ public class Select extends Query {
}
}
private double preparePlan() {
private double preparePlan(boolean parse) {
TableFilter[] topArray = topFilters.toArray(
new TableFilter[topFilters.size()]);
for (TableFilter t : topArray) {
......@@ -975,13 +975,15 @@ public class Select extends Query {
}
Optimizer optimizer = new Optimizer(topArray, condition, session);
optimizer.optimize();
optimizer.optimize(parse);
topTableFilter = optimizer.getTopFilter();
double planCost = optimizer.getCost();
setEvaluatableRecursive(topTableFilter);
topTableFilter.prepare();
if (!parse) {
topTableFilter.prepare();
}
return planCost;
}
......
......@@ -5,13 +5,13 @@
*/
package org.h2.engine;
import java.util.ArrayDeque;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.Random;
import org.h2.api.ErrorCode;
import org.h2.command.Command;
import org.h2.command.CommandInterface;
......@@ -33,6 +33,7 @@ import org.h2.schema.Schema;
import org.h2.store.DataHandler;
import org.h2.store.InDoubtTransaction;
import org.h2.store.LobStorageFrontend;
import org.h2.table.SubQueryInfo;
import org.h2.table.Table;
import org.h2.util.New;
import org.h2.util.SmallLRUCache;
......@@ -109,6 +110,8 @@ public class Session extends SessionWithState {
private final int queryCacheSize;
private SmallLRUCache<String, Command> queryCache;
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
......@@ -142,6 +145,31 @@ public class Session extends SessionWithState {
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
public ArrayList<String> getClusterServers() {
return new ArrayList<String>();
......
......@@ -5,8 +5,6 @@
*/
package org.h2.index;
import java.util.List;
import java.util.concurrent.Future;
import org.h2.engine.Session;
import org.h2.result.Row;
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
* sequential batched lookups.
*
* @see Index#createLookupBatch(TableFilter)
* @see Index#createLookupBatch(org.h2.table.TableFilter)
* @author Sergi Vladykin
*/
public interface IndexLookupBatch {
......
......@@ -6,8 +6,8 @@
package org.h2.index;
import java.util.ArrayList;
import org.h2.api.ErrorCode;
import org.h2.command.Prepared;
import org.h2.command.dml.Query;
import org.h2.command.dml.SelectUnion;
import org.h2.engine.Constants;
......@@ -21,6 +21,7 @@ import org.h2.result.SearchRow;
import org.h2.result.SortOrder;
import org.h2.table.Column;
import org.h2.table.IndexColumn;
import org.h2.table.SubQueryInfo;
import org.h2.table.TableFilter;
import org.h2.table.TableView;
import org.h2.util.IntArray;
......@@ -46,6 +47,14 @@ public class ViewIndex extends BaseIndex implements SpatialIndex {
private Query query;
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,
ArrayList<Parameter> originalParameters, boolean recursive) {
initBaseIndex(view, 0, null, null, IndexType.createNonUnique(false));
......@@ -58,8 +67,19 @@ public class ViewIndex extends BaseIndex implements SpatialIndex {
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,
int[] masks) {
int[] masks, TableFilter[] filters, int filter, SortOrder sortOrder) {
initBaseIndex(view, 0, null, null, IndexType.createNonUnique(false));
this.view = view;
this.querySQL = index.querySQL;
......@@ -69,7 +89,7 @@ public class ViewIndex extends BaseIndex implements SpatialIndex {
this.createSession = session;
columns = new Column[0];
if (!recursive) {
query = getQuery(session, masks);
query = getQuery(session, masks, filters, filter, sortOrder);
}
}
......@@ -129,20 +149,13 @@ public class ViewIndex extends BaseIndex implements SpatialIndex {
return cachedCost.cost;
}
}
Query q = (Query) session.prepare(querySQL, true);
Query q = prepareSubQuery(querySQL, session, masks, filters, filter, sortOrder, true);
if (masks != null) {
IntArray paramIndex = new IntArray();
for (int i = 0; i < masks.length; i++) {
int mask = masks[i];
for (int idx = 0; idx < masks.length; idx++) {
int mask = masks[idx];
if (mask == 0) {
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();
if ((mask & IndexCondition.EQUALITY) != 0) {
Parameter param = new Parameter(nextParamIndex);
......@@ -162,7 +175,7 @@ public class ViewIndex extends BaseIndex implements SpatialIndex {
}
}
String sql = q.getPlanSQL();
q = (Query) session.prepare(sql, true);
q = prepareSubQuery(sql, session, masks, filters, filter, sortOrder, false);
}
double cost = q.getCost();
cachedCost = new CostElement();
......@@ -182,6 +195,19 @@ public class ViewIndex extends BaseIndex implements SpatialIndex {
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,
SearchRow intersection) {
if (recursive) {
......@@ -284,8 +310,9 @@ public class ViewIndex extends BaseIndex implements SpatialIndex {
param.setValue(v);
}
private Query getQuery(Session session, int[] masks) {
Query q = (Query) session.prepare(querySQL, true);
private Query getQuery(Session session, int[] masks,
TableFilter[] filters, int filter, SortOrder sortOrder) {
Query q = prepareSubQuery(querySQL, session, masks, filters, filter, sortOrder, true);
if (masks == null) {
return q;
}
......@@ -368,7 +395,7 @@ public class ViewIndex extends BaseIndex implements SpatialIndex {
}
String sql = q.getPlanSQL();
q = (Query) session.prepare(sql, true);
q = prepareSubQuery(sql, session, masks, filters, filter, sortOrder, false);
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 {
}
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)) {
throw DbException.getSyntaxError(sql, 0);
}
......@@ -241,7 +247,7 @@ public class TableView extends Table {
// 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
// a FunctionTable.
ViewIndex i2 = new ViewIndex(this, index, session, masks);
ViewIndex i2 = new ViewIndex(this, index, session, masks, filters, filter, sortOrder);
synchronized (this) {
// have to check again in case another session has beat us to it
ViewIndex i3 = indexCache.get(cacheKey);
......
......@@ -70,6 +70,7 @@ public class TestTableEngines extends TestBase {
@Override
public void test() throws Exception {
testSubQueryInfo();
testEarlyFilter();
testEngineParams();
testSimpleQuery();
......@@ -342,7 +343,21 @@ public class TestTableEngines extends TestBase {
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 {
deleteDb("tableEngine");
Connection conn = getConnection("tableEngine;OPTIMIZE_REUSE_RESULTS=0");
......@@ -1207,6 +1222,11 @@ public class TestTableEngines extends TestBase {
@Override
public double getCost(Session session, int[] masks,
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);
}
......
Markdown 格式
0%
您添加了 0 到此讨论。请谨慎行事。
请先完成此评论的编辑!
注册 或者 后发表评论