提交 87791526 authored 作者: Thomas Mueller's avatar Thomas Mueller

Issue 290: Conditions using subqueries were sometimes evaluated before much…

Issue 290: Conditions using subqueries were sometimes evaluated before much simpler conditions, which was very bad for performance. This includes subqueries returning a value, EXISTS subqueries, and IN(SELECT ..) subqueries.
上级 39db262a
...@@ -18,7 +18,10 @@ Change Log ...@@ -18,7 +18,10 @@ Change Log
<h1>Change Log</h1> <h1>Change Log</h1>
<h2>Next Version (unreleased)</h2> <h2>Next Version (unreleased)</h2>
<ul><li>Issue 288: Some queries with right outer joins or nested joins could throw a NullPointerException. <ul><li>Issue 290: Conditions using subqueries were sometimes evaluated before much simpler conditions,
which was very bad for performance. This includes subqueries returning a value, EXISTS subqueries,
and IN(SELECT ..) subqueries.
</li><li>Issue 288: Some queries with right outer joins or nested joins could throw a NullPointerException.
</li><li>The database can now be compiled with Java 7. </li><li>The database can now be compiled with Java 7.
Stub methods for the new JDBC 4.1 methods have been implemented, Stub methods for the new JDBC 4.1 methods have been implemented,
but the new features can't be used yet. but the new features can't be used yet.
......
...@@ -92,6 +92,19 @@ public abstract class Query extends Prepared { ...@@ -92,6 +92,19 @@ public abstract class Query extends Prepared {
*/ */
public abstract double getCost(); public abstract double getCost();
/**
* Calculate the cost when used as a subquery.
* This method returns a value between 10 and 1000000,
* to ensure adding other values can't result in an integer overflow.
*
* @return the estimated cost as an integer
*/
public int getCostAsExpression() {
// ensure the cost is not larger than 1 million,
// so that adding other values can't overflow
return (int) Math.min(1000000.0, 10.0 + 10.0 * getCost());
}
/** /**
* Get all tables that are involved in this query. * Get all tables that are involved in this query.
* *
......
...@@ -1053,6 +1053,7 @@ public class Select extends Query { ...@@ -1053,6 +1053,7 @@ public class Select extends Query {
buff.append("\n/* group sorted */"); buff.append("\n/* group sorted */");
} }
} }
// buff.append("\n/* cost: " + cost + " */");
return buff.toString(); return buff.toString();
} }
......
...@@ -11,6 +11,7 @@ import org.h2.engine.Session; ...@@ -11,6 +11,7 @@ import org.h2.engine.Session;
import org.h2.result.ResultInterface; import org.h2.result.ResultInterface;
import org.h2.table.ColumnResolver; import org.h2.table.ColumnResolver;
import org.h2.table.TableFilter; import org.h2.table.TableFilter;
import org.h2.util.StringUtils;
import org.h2.value.Value; import org.h2.value.Value;
import org.h2.value.ValueBoolean; import org.h2.value.ValueBoolean;
...@@ -39,7 +40,7 @@ public class ConditionExists extends Condition { ...@@ -39,7 +40,7 @@ public class ConditionExists extends Condition {
} }
public String getSQL() { public String getSQL() {
return "EXISTS(" + query.getPlanSQL() + ")"; return "EXISTS(\n" + StringUtils.indent(query.getPlanSQL(), 4, false) + ")";
} }
public void updateAggregate(Session session) { public void updateAggregate(Session session) {
...@@ -62,7 +63,7 @@ public class ConditionExists extends Condition { ...@@ -62,7 +63,7 @@ public class ConditionExists extends Condition {
} }
public int getCost() { public int getCost() {
return 10 + (int) (10 * query.getCost()); return query.getCostAsExpression();
} }
} }
...@@ -146,7 +146,7 @@ public class ConditionInSelect extends Condition { ...@@ -146,7 +146,7 @@ public class ConditionInSelect extends Condition {
} }
public int getCost() { public int getCost() {
return left.getCost() + 10 + (int) (10 * query.getCost()); return left.getCost() + query.getCostAsExpression();
} }
public void createIndexConditions(Session session, TableFilter filter) { public void createIndexConditions(Session session, TableFilter filter) {
......
...@@ -120,7 +120,7 @@ public class Subquery extends Expression { ...@@ -120,7 +120,7 @@ public class Subquery extends Expression {
} }
public int getCost() { public int getCost() {
return 10 + (int) (10 * query.getCost()); return query.getCostAsExpression();
} }
public Expression[] getExpressionColumns(Session session) { public Expression[] getExpressionColumns(Session session) {
......
...@@ -38,6 +38,7 @@ public class TestOptimizations extends TestBase { ...@@ -38,6 +38,7 @@ public class TestOptimizations extends TestBase {
public void test() throws Exception { public void test() throws Exception {
deleteDb("optimizations"); deleteDb("optimizations");
testExistsSubquery();
testQueryCacheConcurrentUse(); testQueryCacheConcurrentUse();
testQueryCacheResetParams(); testQueryCacheResetParams();
testRowId(); testRowId();
...@@ -66,6 +67,18 @@ public class TestOptimizations extends TestBase { ...@@ -66,6 +67,18 @@ public class TestOptimizations extends TestBase {
deleteDb("optimizations"); deleteDb("optimizations");
} }
private void testExistsSubquery() throws Exception {
Connection conn = getConnection("optimizations");
Statement stat = conn.createStatement();
stat.execute("create table test(id int) as select x from system_range(1, 10)");
ResultSet rs = stat.executeQuery("explain select * from test where exists(select 1 from test, test, test) and id = 10");
rs.next();
// ensure the ID = 10 part is evaluated first
assertContains(rs.getString(1), "WHERE (ID = 10)");
stat.execute("drop table test");
conn.close();
}
private void testQueryCacheConcurrentUse() throws Exception { private void testQueryCacheConcurrentUse() throws Exception {
final Connection conn = getConnection("optimizations"); final Connection conn = getConnection("optimizations");
Statement stat = conn.createStatement(); Statement stat = conn.createStatement();
......
Markdown 格式
0%
您添加了 0 到此讨论。请谨慎行事。
请先完成此评论的编辑!
注册 或者 后发表评论