提交 1e25c068 authored 作者: Thomas Mueller's avatar Thomas Mueller

Experimental feature to support nested joins.

上级 81de9e71
...@@ -1229,7 +1229,7 @@ public class Parser { ...@@ -1229,7 +1229,7 @@ public class Parser {
if (readIf("ON")) { if (readIf("ON")) {
on = readExpression(); on = readExpression();
} }
newTop.addJoin(top, true, on); newTop.addJoin(top, true, fromOuter, on);
top = newTop; top = newTop;
last = newTop; last = newTop;
} else if (readIf("LEFT")) { } else if (readIf("LEFT")) {
...@@ -1241,10 +1241,10 @@ public class Parser { ...@@ -1241,10 +1241,10 @@ public class Parser {
if (readIf("ON")) { if (readIf("ON")) {
on = readExpression(); on = readExpression();
} }
top.addJoin(join, true, on); top.addJoin(join, true, fromOuter, on);
last = join; last = join;
} else if (readIf("FULL")) { } else if (readIf("FULL")) {
throw this.getSyntaxError(); throw getSyntaxError();
} else if (readIf("INNER")) { } else if (readIf("INNER")) {
read("JOIN"); read("JOIN");
TableFilter join = readTableFilter(fromOuter); TableFilter join = readTableFilter(fromOuter);
...@@ -1253,7 +1253,11 @@ public class Parser { ...@@ -1253,7 +1253,11 @@ public class Parser {
if (readIf("ON")) { if (readIf("ON")) {
on = readExpression(); on = readExpression();
} }
top.addJoin(join, fromOuter, on); if (SysProperties.NESTED_JOINS) {
top.addJoin(join, false, fromOuter, on);
} else {
top.addJoin(join, fromOuter, fromOuter, on);
}
last = join; last = join;
} else if (readIf("JOIN")) { } else if (readIf("JOIN")) {
TableFilter join = readTableFilter(fromOuter); TableFilter join = readTableFilter(fromOuter);
...@@ -1262,12 +1266,20 @@ public class Parser { ...@@ -1262,12 +1266,20 @@ public class Parser {
if (readIf("ON")) { if (readIf("ON")) {
on = readExpression(); on = readExpression();
} }
top.addJoin(join, fromOuter, on); if (SysProperties.NESTED_JOINS) {
top.addJoin(join, false, fromOuter, on);
} else {
top.addJoin(join, fromOuter, fromOuter, on);
}
last = join; last = join;
} else if (readIf("CROSS")) { } else if (readIf("CROSS")) {
read("JOIN"); read("JOIN");
TableFilter join = readTableFilter(fromOuter); TableFilter join = readTableFilter(fromOuter);
top.addJoin(join, fromOuter, null); if (SysProperties.NESTED_JOINS) {
top.addJoin(join, false, fromOuter, null);
} else {
top.addJoin(join, fromOuter, fromOuter, null);
}
last = join; last = join;
} else if (readIf("NATURAL")) { } else if (readIf("NATURAL")) {
read("JOIN"); read("JOIN");
...@@ -1296,7 +1308,11 @@ public class Parser { ...@@ -1296,7 +1308,11 @@ public class Parser {
} }
} }
} }
top.addJoin(join, fromOuter, on); if (SysProperties.NESTED_JOINS) {
top.addJoin(join, false, fromOuter, on);
} else {
top.addJoin(join, fromOuter, fromOuter, on);
}
last = join; last = join;
} else { } else {
break; break;
...@@ -1543,6 +1559,12 @@ public class Parser { ...@@ -1543,6 +1559,12 @@ public class Parser {
command.addTableFilter(top, true); command.addTableFilter(top, true);
boolean isOuter = false; boolean isOuter = false;
while (true) { while (true) {
for (TableFilter n = top.getNestedJoin(); n != null; n = n.getNestedJoin()) {
command.addTableFilter(n, false);
for (TableFilter j = n.getJoin(); j != null; j = j.getJoin()) {
command.addTableFilter(j, false);
}
}
TableFilter join = top.getJoin(); TableFilter join = top.getJoin();
if (join == null) { if (join == null) {
break; break;
...@@ -1810,7 +1832,7 @@ public class Parser { ...@@ -1810,7 +1832,7 @@ public class Parser {
int idx = filters.indexOf(rightFilter); int idx = filters.indexOf(rightFilter);
if (idx >= 0) { if (idx >= 0) {
filters.remove(idx); filters.remove(idx);
leftFilter.addJoin(rightFilter, true, r); leftFilter.addJoin(rightFilter, true, false, r);
} else { } else {
rightFilter.mapAndAddFilter(r); rightFilter.mapAndAddFilter(r);
} }
......
...@@ -232,7 +232,7 @@ public class Optimizer { ...@@ -232,7 +232,7 @@ public class Optimizer {
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, null); f2[i].addJoin(f2[i + 1], false, false, null);
} }
for (TableFilter f : f2) { for (TableFilter f : f2) {
PlanItem item = bestPlan.getItem(f); PlanItem item = bestPlan.getItem(f);
......
...@@ -845,9 +845,15 @@ public class Select extends Query { ...@@ -845,9 +845,15 @@ public class Select extends Query {
optimizer.optimize(); optimizer.optimize();
topTableFilter = optimizer.getTopFilter(); topTableFilter = optimizer.getTopFilter();
double planCost = optimizer.getCost(); double planCost = optimizer.getCost();
setEvaluatableRecursive(topTableFilter);
TableFilter f = topTableFilter; topTableFilter.prepare();
while (f != null) { return planCost;
}
private void setEvaluatableRecursive(TableFilter f) {
for (; f != null; f = f.getJoin()) {
f.setEvaluatable(f, true); f.setEvaluatable(f, true);
if (condition != null) { if (condition != null) {
condition.setEvaluatable(f, true); condition.setEvaluatable(f, true);
...@@ -879,10 +885,11 @@ public class Select extends Query { ...@@ -879,10 +885,11 @@ public class Select extends Query {
for (Expression e : expressions) { for (Expression e : expressions) {
e.setEvaluatable(f, true); e.setEvaluatable(f, true);
} }
f = f.getJoin(); TableFilter n = f.getNestedJoin();
if (n != null) {
setEvaluatableRecursive(n);
}
} }
topTableFilter.prepare();
return planCost;
} }
public String getPlanSQL() { public String getPlanSQL() {
......
...@@ -385,6 +385,12 @@ public class SysProperties { ...@@ -385,6 +385,12 @@ public class SysProperties {
* The minimum write delay that causes commits to be delayed. * The minimum write delay that causes commits to be delayed.
*/ */
public static final int MIN_WRITE_DELAY = getIntSetting("h2.minWriteDelay", 5); public static final int MIN_WRITE_DELAY = getIntSetting("h2.minWriteDelay", 5);
/**
* System property <code>h2.nestedJoins</code> (default: false).<br />
* Whether nested joins should be supported.
*/
public static final boolean NESTED_JOINS = getBooleanSetting("h2.nestedJoins", false);
/** /**
* System property <code>h2.nioLoadMapped</code> (default: false).<br /> * System property <code>h2.nioLoadMapped</code> (default: false).<br />
......
...@@ -21,6 +21,7 @@ public class PlanItem { ...@@ -21,6 +21,7 @@ public class PlanItem {
private Index index; private Index index;
private PlanItem joinPlan; private PlanItem joinPlan;
private PlanItem nestedJoinPlan;
void setIndex(Index index) { void setIndex(Index index) {
this.index = index; this.index = index;
...@@ -33,9 +34,17 @@ public class PlanItem { ...@@ -33,9 +34,17 @@ public class PlanItem {
PlanItem getJoinPlan() { PlanItem getJoinPlan() {
return joinPlan; return joinPlan;
} }
PlanItem getNestedJoinPlan() {
return nestedJoinPlan;
}
void setJoinPlan(PlanItem joinPlan) { void setJoinPlan(PlanItem joinPlan) {
this.joinPlan = joinPlan; this.joinPlan = joinPlan;
} }
void setNestedJoinPlan(PlanItem nestedJoinPlan) {
this.nestedJoinPlan = nestedJoinPlan;
}
} }
...@@ -38,6 +38,7 @@ import org.h2.test.db.TestMultiConn; ...@@ -38,6 +38,7 @@ import org.h2.test.db.TestMultiConn;
import org.h2.test.db.TestMultiDimension; import org.h2.test.db.TestMultiDimension;
import org.h2.test.db.TestMultiThread; import org.h2.test.db.TestMultiThread;
import org.h2.test.db.TestMultiThreadedKernel; import org.h2.test.db.TestMultiThreadedKernel;
import org.h2.test.db.TestNestedJoins;
import org.h2.test.db.TestOpenClose; import org.h2.test.db.TestOpenClose;
import org.h2.test.db.TestOptimizations; import org.h2.test.db.TestOptimizations;
import org.h2.test.db.TestOutOfMemory; import org.h2.test.db.TestOutOfMemory;
...@@ -302,6 +303,7 @@ java org.h2.test.TestAll timer ...@@ -302,6 +303,7 @@ java org.h2.test.TestAll timer
// System.setProperty("h2.largeTransactions", "true"); // System.setProperty("h2.largeTransactions", "true");
// System.setProperty("h2.lobInDatabase", "true"); // System.setProperty("h2.lobInDatabase", "true");
// System.setProperty("h2.analyzeAuto", "100"); // System.setProperty("h2.analyzeAuto", "100");
// System.setProperty("h2.nestedJoins", "true");
int speedup; int speedup;
// System.setProperty("h2.syncMethod", ""); // System.setProperty("h2.syncMethod", "");
...@@ -516,6 +518,7 @@ kill -9 `jps -l | grep "org.h2.test." | cut -d " " -f 1` ...@@ -516,6 +518,7 @@ kill -9 `jps -l | grep "org.h2.test." | cut -d " " -f 1`
new TestMultiDimension().runTest(this); new TestMultiDimension().runTest(this);
new TestMultiThread().runTest(this); new TestMultiThread().runTest(this);
new TestMultiThreadedKernel().runTest(this); new TestMultiThreadedKernel().runTest(this);
new TestNestedJoins().runTest(this);
new TestOpenClose().runTest(this); new TestOpenClose().runTest(this);
new TestOptimizations().runTest(this); new TestOptimizations().runTest(this);
new TestOutOfMemory().runTest(this); new TestOutOfMemory().runTest(this);
......
/*
* Copyright 2004-2010 H2 Group. Multiple-Licensed under the H2 License,
* Version 1.0, and under the Eclipse Public License, Version 1.0
* (http://h2database.com/html/license.html).
* Initial Developer: H2 Group
*/
package org.h2.test.db;
import java.io.StringReader;
import java.sql.Connection;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import org.h2.constant.SysProperties;
import org.h2.test.TestBase;
import org.h2.util.ScriptReader;
/**
* Tests nested joins and right outer joins.
*/
public class TestNestedJoins extends TestBase {
/**
* Run just this test.
*
* @param a ignored
*/
public static void main(String... a) throws Exception {
System.setProperty("h2.nestedJoins", "true");
TestBase.createCaller().init().test();
}
public void test() throws Exception {
if (!SysProperties.NESTED_JOINS) {
return;
}
deleteDb("nestedJoins");
Connection conn = getConnection("nestedJoins");
Statement stat = conn.createStatement();
ResultSet rs;
String sql;
// not yet supported:
/*
stat.execute("drop table a, b, c");
stat.execute("create table a(x int)");
stat.execute("create table b(x int)");
stat.execute("create table c(x int, y int)");
stat.execute("insert into a values(1), (2)");
stat.execute("insert into b values(3)");
stat.execute("insert into c values(1, 3)");
stat.execute("insert into c values(4, 5)");
rs = stat.executeQuery("explain select * from a left outer join (b left outer join c on b.x = c.y) on a.x = c.x");
assertTrue(rs.next());
sql = cleanRemarks(conn, rs.getString(1));
assertEquals("", sql);
rs = stat.executeQuery("select * from a left outer join (b left outer join c on b.x = c.y) on a.x = c.x");
// expected result: 1 3 1 3; 2 null null null
assertTrue(rs.next());
assertEquals("1", rs.getString(1));
assertEquals("3", rs.getString(2));
assertEquals("1", rs.getString(3));
assertEquals("3", rs.getString(4));
assertTrue(rs.next());
assertEquals("2", rs.getString(1));
assertEquals(null, rs.getString(2));
assertEquals(null, rs.getString(3));
assertEquals(null, rs.getString(4));
assertFalse(rs.next());
*/
stat.execute("create table a(x int primary key)");
stat.execute("insert into a values(0), (1)");
stat.execute("create table b(x int primary key)");
stat.execute("insert into b values(0)");
stat.execute("create table c(x int primary key)");
rs = stat.executeQuery("select a.*, b.*, c.* from a left outer join (b inner join c on b.x = c.x) on a.x = b.x");
// expected result: 0, null, null; 1, null, null
assertTrue(rs.next());
assertEquals("0", rs.getString(1));
assertEquals(null, rs.getString(2));
assertEquals(null, rs.getString(3));
assertTrue(rs.next());
assertEquals("1", rs.getString(1));
assertEquals(null, rs.getString(2));
assertEquals(null, rs.getString(3));
assertFalse(rs.next());
rs = stat.executeQuery("select * from a left outer join b on a.x = b.x inner join c on b.x = c.x");
// expected result: -
assertFalse(rs.next());
rs = stat.executeQuery("select * from a left outer join b on a.x = b.x left outer join c on b.x = c.x");
// expected result: 0 0 null; 1 null null
assertTrue(rs.next());
assertEquals("0", rs.getString(1));
assertEquals("0", rs.getString(2));
assertEquals(null, rs.getString(3));
assertTrue(rs.next());
assertEquals("1", rs.getString(1));
assertEquals(null, rs.getString(2));
assertEquals(null, rs.getString(3));
assertFalse(rs.next());
rs = stat.executeQuery("select * from a left outer join (b inner join c on b.x = c.x) on a.x = b.x");
// expected result: 0 null null; 1 null null
assertTrue(rs.next());
assertEquals("0", rs.getString(1));
assertEquals(null, rs.getString(2));
assertEquals(null, rs.getString(3));
assertTrue(rs.next());
assertEquals("1", rs.getString(1));
assertEquals(null, rs.getString(2));
assertEquals(null, rs.getString(3));
assertFalse(rs.next());
rs = stat.executeQuery("explain select * from a left outer join (b inner join c on c.x = 1) on a.x = b.x");
assertTrue(rs.next());
sql = cleanRemarks(conn, rs.getString(1));
assertEquals("SELECT A.X, B.X, C.X FROM PUBLIC.A LEFT OUTER JOIN (PUBLIC.B INNER JOIN PUBLIC.C ON (C.X = 1) AND (A.X = B.X)) ON A.X = B.X", sql);
stat.execute("create table test(id int primary key)");
stat.execute("insert into test values(0), (1), (2)");
rs = stat.executeQuery("select * from test a left outer join (test b inner join test c on b.id = c.id - 2) on a.id = b.id + 1");
// drop table test;
// create table test(id int primary key);
// insert into test values(0), (1), (2);
// select * from test a left outer join (test b inner join test c on b.id = c.id - 2) on a.id = b.id + 1;
// expected result: 0 null null; 1 0 2; 2 null null
assertTrue(rs.next());
assertEquals("0", rs.getString(1));
assertEquals(null, rs.getString(2));
assertEquals(null, rs.getString(3));
assertTrue(rs.next());
assertEquals("1", rs.getString(1));
assertEquals("0", rs.getString(2));
assertEquals("2", rs.getString(3));
assertTrue(rs.next());
assertEquals("2", rs.getString(1));
assertEquals(null, rs.getString(2));
assertEquals(null, rs.getString(3));
assertFalse(rs.next());
// while (rs.next()) {
// for (int i = 0; i < rs.getMetaData().getColumnCount(); i++) {
// System.out.print(rs.getString(i + 1) + " ");
// }
// System.out.println();
// }
conn.close();
deleteDb("nestedJoins");
}
private String cleanRemarks(Connection conn, String sql) throws SQLException {
ScriptReader r = new ScriptReader(new StringReader(sql));
r.setSkipRemarks(true);
sql = r.readStatement();
sql = sql.replaceAll("\\n", " ");
// sql = sql.replaceAll("\\/\\*.*\\*\\/", "");
while (sql.indexOf(" ") >= 0) {
sql = sql.replaceAll(" ", " ");
}
return sql;
}
}
...@@ -2,23 +2,6 @@ Nested Outer Joins ...@@ -2,23 +2,6 @@ Nested Outer Joins
----------------- -----------------
Example: Example:
drop table a, b, c;
create table a(x int primary key);
insert into a values(0), (1);
create table b(x int primary key);
insert into b values(0);
create table c(x int primary key);
select * from a left outer join b on a.x = b.x inner join c on b.x = c.x;
select * from a left outer join b on a.x = b.x left outer join c on b.x = c.x;
select * from a left outer join (b inner join c on b.x = c.x) on a.x = b.x;
select a.x, b_x, c_x from a left outer join (select b.x b_x, c.x c_x from b inner join c on b.x = c.x) on a.x = b_x;
drop table test;
create table test(id int primary key);
insert into test values(0), (1), (2);
select * from test a left outer join (test b inner join test c on b.id = c.id - 2) on a.id = b.id + 1;
select a.id, b_id, c_id from test a left outer join (select b.id b_id, c.id c_id from test b inner join test c on b.id = c.id - 2) on a.id = b_id + 1;
create table a(x int); create table a(x int);
create table b(x int); create table b(x int);
create table c(x int, y int); create table c(x int, y int);
......
Markdown 格式
0%
您添加了 0 到此讨论。请谨慎行事。
请先完成此评论的编辑!
注册 或者 后发表评论