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

Experimental feature to support nested joins.

上级 81de9e71
......@@ -1229,7 +1229,7 @@ public class Parser {
if (readIf("ON")) {
on = readExpression();
}
newTop.addJoin(top, true, on);
newTop.addJoin(top, true, fromOuter, on);
top = newTop;
last = newTop;
} else if (readIf("LEFT")) {
......@@ -1241,10 +1241,10 @@ public class Parser {
if (readIf("ON")) {
on = readExpression();
}
top.addJoin(join, true, on);
top.addJoin(join, true, fromOuter, on);
last = join;
} else if (readIf("FULL")) {
throw this.getSyntaxError();
throw getSyntaxError();
} else if (readIf("INNER")) {
read("JOIN");
TableFilter join = readTableFilter(fromOuter);
......@@ -1253,7 +1253,11 @@ public class Parser {
if (readIf("ON")) {
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;
} else if (readIf("JOIN")) {
TableFilter join = readTableFilter(fromOuter);
......@@ -1262,12 +1266,20 @@ public class Parser {
if (readIf("ON")) {
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;
} else if (readIf("CROSS")) {
read("JOIN");
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;
} else if (readIf("NATURAL")) {
read("JOIN");
......@@ -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;
} else {
break;
......@@ -1543,6 +1559,12 @@ public class Parser {
command.addTableFilter(top, true);
boolean isOuter = false;
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();
if (join == null) {
break;
......@@ -1810,7 +1832,7 @@ public class Parser {
int idx = filters.indexOf(rightFilter);
if (idx >= 0) {
filters.remove(idx);
leftFilter.addJoin(rightFilter, true, r);
leftFilter.addJoin(rightFilter, true, false, r);
} else {
rightFilter.mapAndAddFilter(r);
}
......
......@@ -232,7 +232,7 @@ public class Optimizer {
TableFilter[] f2 = bestPlan.getFilters();
topFilter = f2[0];
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) {
PlanItem item = bestPlan.getItem(f);
......
......@@ -846,8 +846,14 @@ public class Select extends Query {
topTableFilter = optimizer.getTopFilter();
double planCost = optimizer.getCost();
TableFilter f = topTableFilter;
while (f != null) {
setEvaluatableRecursive(topTableFilter);
topTableFilter.prepare();
return planCost;
}
private void setEvaluatableRecursive(TableFilter f) {
for (; f != null; f = f.getJoin()) {
f.setEvaluatable(f, true);
if (condition != null) {
condition.setEvaluatable(f, true);
......@@ -879,10 +885,11 @@ public class Select extends Query {
for (Expression e : expressions) {
e.setEvaluatable(f, true);
}
f = f.getJoin();
TableFilter n = f.getNestedJoin();
if (n != null) {
setEvaluatableRecursive(n);
}
}
topTableFilter.prepare();
return planCost;
}
public String getPlanSQL() {
......
......@@ -386,6 +386,12 @@ public class SysProperties {
*/
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 />
* If the mapped buffer should be loaded when the file is opened.
......
......@@ -21,6 +21,7 @@ public class PlanItem {
private Index index;
private PlanItem joinPlan;
private PlanItem nestedJoinPlan;
void setIndex(Index index) {
this.index = index;
......@@ -34,8 +35,16 @@ public class PlanItem {
return joinPlan;
}
PlanItem getNestedJoinPlan() {
return nestedJoinPlan;
}
void setJoinPlan(PlanItem joinPlan) {
this.joinPlan = joinPlan;
}
void setNestedJoinPlan(PlanItem nestedJoinPlan) {
this.nestedJoinPlan = nestedJoinPlan;
}
}
......@@ -38,6 +38,7 @@ import org.h2.test.db.TestMultiConn;
import org.h2.test.db.TestMultiDimension;
import org.h2.test.db.TestMultiThread;
import org.h2.test.db.TestMultiThreadedKernel;
import org.h2.test.db.TestNestedJoins;
import org.h2.test.db.TestOpenClose;
import org.h2.test.db.TestOptimizations;
import org.h2.test.db.TestOutOfMemory;
......@@ -302,6 +303,7 @@ java org.h2.test.TestAll timer
// System.setProperty("h2.largeTransactions", "true");
// System.setProperty("h2.lobInDatabase", "true");
// System.setProperty("h2.analyzeAuto", "100");
// System.setProperty("h2.nestedJoins", "true");
int speedup;
// System.setProperty("h2.syncMethod", "");
......@@ -516,6 +518,7 @@ kill -9 `jps -l | grep "org.h2.test." | cut -d " " -f 1`
new TestMultiDimension().runTest(this);
new TestMultiThread().runTest(this);
new TestMultiThreadedKernel().runTest(this);
new TestNestedJoins().runTest(this);
new TestOpenClose().runTest(this);
new TestOptimizations().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
-----------------
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 b(x int);
create table c(x int, y int);
......
Markdown 格式
0%
您添加了 0 到此讨论。请谨慎行事。
请先完成此评论的编辑!
注册 或者 后发表评论