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

Experimental feature to support nested joins.

上级 1a47715d
...@@ -866,7 +866,12 @@ public class Select extends Query { ...@@ -866,7 +866,12 @@ public class Select extends Query {
if (on != null) { if (on != null) {
if (!on.isEverything(ExpressionVisitor.EVALUATABLE)) { if (!on.isEverything(ExpressionVisitor.EVALUATABLE)) {
if (SysProperties.NESTED_JOINS) { if (SysProperties.NESTED_JOINS) {
int testCanSupport; f.removeJoinCondition();
// need to check that all added are bound to a table
on = on.optimize(session);
if (!f.isJoinOuter() && !f.isJoinOuterIndirect()) {
addCondition(on);
}
} else { } else {
if (f.isJoinOuter()) { if (f.isJoinOuter()) {
// this will check if all columns exist - it may or may not throw an exception // this will check if all columns exist - it may or may not throw an exception
...@@ -874,11 +879,11 @@ public class Select extends Query { ...@@ -874,11 +879,11 @@ public class Select extends Query {
// it is not supported even if the columns exist // it is not supported even if the columns exist
throw DbException.get(ErrorCode.UNSUPPORTED_OUTER_JOIN_CONDITION_1, on.getSQL()); throw DbException.get(ErrorCode.UNSUPPORTED_OUTER_JOIN_CONDITION_1, on.getSQL());
} }
f.removeJoinCondition();
// need to check that all added are bound to a table
on = on.optimize(session);
addCondition(on);
} }
f.removeJoinCondition();
// need to check that all added are bound to a table
on = on.optimize(session);
addCondition(on);
} }
} }
on = f.getFilterCondition(); on = f.getFilterCondition();
......
...@@ -246,6 +246,9 @@ public class ExpressionColumn extends Expression { ...@@ -246,6 +246,9 @@ public class ExpressionColumn extends Expression {
// or if this columns belongs to a 'higher level' query and is // or if this columns belongs to a 'higher level' query and is
// therefore just a parameter // therefore just a parameter
if (SysProperties.NESTED_JOINS) { if (SysProperties.NESTED_JOINS) {
if (visitor.getQueryLevel() < this.queryLevel) {
return true;
}
if (getTableFilter() == null) { if (getTableFilter() == null) {
return false; return false;
} }
......
...@@ -34,10 +34,10 @@ import org.h2.value.Value; ...@@ -34,10 +34,10 @@ import org.h2.value.Value;
*/ */
public class TableFilter implements ColumnResolver { public class TableFilter implements ColumnResolver {
private static final int BEFORE_FIRST = 0, FOUND = 1, AFTER_LAST = 2, NULL_ROW = 3; private static final int BEFORE_FIRST = 0, FOUND = 1, AFTER_LAST = 2, NULL_ROW = 3;
protected Session session;
private final Table table; private final Table table;
private final Select select; private final Select select;
private String alias; private String alias;
private Session session;
private Index index; private Index index;
private int scanCount; private int scanCount;
private boolean evaluatable; private boolean evaluatable;
...@@ -78,10 +78,15 @@ public class TableFilter implements ColumnResolver { ...@@ -78,10 +78,15 @@ public class TableFilter implements ColumnResolver {
private TableFilter join; private TableFilter join;
/** /**
* Whether this table is an outer join. * Whether this is an outer join.
*/ */
private boolean joinOuter; private boolean joinOuter;
/**
* Whether this is a direct or indirect (nested) outer join
*/
protected boolean joinOuterIndirect;
/** /**
* The nested joined table (if there is one). * The nested joined table (if there is one).
*/ */
...@@ -505,6 +510,13 @@ public class TableFilter implements ColumnResolver { ...@@ -505,6 +510,13 @@ public class TableFilter implements ColumnResolver {
} }
nestedJoin = filter; nestedJoin = filter;
filter.joinOuter = outer; filter.joinOuter = outer;
if (outer) {
visit(new TableFilterVisitor() {
public void accept(TableFilter f) {
f.joinOuterIndirect = true;
}
});
}
if (on != null) { if (on != null) {
filter.mapAndAddFilter(on); filter.mapAndAddFilter(on);
} }
...@@ -540,6 +552,10 @@ public class TableFilter implements ColumnResolver { ...@@ -540,6 +552,10 @@ public class TableFilter implements ColumnResolver {
on.mapColumns(this, 0); on.mapColumns(this, 0);
addFilterCondition(on, true); addFilterCondition(on, true);
on.createIndexConditions(session, this); on.createIndexConditions(session, this);
if (nestedJoin != null) {
on.mapColumns(nestedJoin, 0);
on.createIndexConditions(session, nestedJoin);
}
if (join != null) { if (join != null) {
join.mapAndAddFilter(on); join.mapAndAddFilter(on);
} }
...@@ -550,7 +566,7 @@ public class TableFilter implements ColumnResolver { ...@@ -550,7 +566,7 @@ public class TableFilter implements ColumnResolver {
} }
/** /**
* Check if this is an outer joined table. * Whether this is an outer joined table.
* *
* @return true if it is * @return true if it is
*/ */
...@@ -558,6 +574,15 @@ public class TableFilter implements ColumnResolver { ...@@ -558,6 +574,15 @@ public class TableFilter implements ColumnResolver {
return joinOuter; return joinOuter;
} }
/**
* Whether this is indirectly an outer joined table (nested within an inner join).
*
* @return true if it is
*/
public boolean isJoinOuterIndirect() {
return joinOuterIndirect;
}
/** /**
* Get the query execution plan text to use for this table filter. * Get the query execution plan text to use for this table filter.
* *
...@@ -583,7 +608,7 @@ public class TableFilter implements ColumnResolver { ...@@ -583,7 +608,7 @@ public class TableFilter implements ColumnResolver {
} while (n != null); } while (n != null);
buff.append(") ON "); buff.append(") ON ");
if (joinCondition == null) { if (joinCondition == null) {
// need to have a ON expression, // need to have a ON expression,
// otherwise the nesting is unclear // otherwise the nesting is unclear
buff.append("1=1"); buff.append("1=1");
} else { } else {
...@@ -731,7 +756,7 @@ public class TableFilter implements ColumnResolver { ...@@ -731,7 +756,7 @@ public class TableFilter implements ColumnResolver {
* @param b the new flag * @param b the new flag
*/ */
public void setEvaluatable(TableFilter filter, boolean b) { public void setEvaluatable(TableFilter filter, boolean b) {
setEvaluatable(b); filter.setEvaluatable(b);
if (filterCondition != null) { if (filterCondition != null) {
filterCondition.setEvaluatable(filter, b); filterCondition.setEvaluatable(filter, b);
} }
......
...@@ -305,6 +305,8 @@ java org.h2.test.TestAll timer ...@@ -305,6 +305,8 @@ java org.h2.test.TestAll timer
// 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"); // System.setProperty("h2.nestedJoins", "true");
// System.setProperty("h2.optimizeOr", "true");
// System.setProperty("h2.dropRestrict", "true");
int speedup; int speedup;
// System.setProperty("h2.syncMethod", ""); // System.setProperty("h2.syncMethod", "");
......
...@@ -37,7 +37,7 @@ public class TestNestedJoins extends TestBase { ...@@ -37,7 +37,7 @@ public class TestNestedJoins extends TestBase {
public static void main(String... a) throws Exception { public static void main(String... a) throws Exception {
System.setProperty("h2.nestedJoins", "true"); System.setProperty("h2.nestedJoins", "true");
TestBase test = TestBase.createCaller().init(); TestBase test = TestBase.createCaller().init();
test.config.traceTest = true; // test.config.traceTest = true;
test.test(); test.test();
} }
...@@ -62,17 +62,15 @@ public class TestNestedJoins extends TestBase { ...@@ -62,17 +62,15 @@ public class TestNestedJoins extends TestBase {
} catch (Exception e) { } catch (Exception e) {
// database not installed - ok // database not installed - ok
} }
// Derby doesn't work currently // Derby doesn't work currently
deleteDerby(); // deleteDerby();
try { // try {
Class.forName("org.apache.derby.jdbc.EmbeddedDriver"); // Class.forName("org.apache.derby.jdbc.EmbeddedDriver");
Connection c2 = DriverManager.getConnection("jdbc:derby:" + getBaseDir() + "/derby/test;create=true", "sa", "sa"); // Connection c2 = DriverManager.getConnection("jdbc:derby:" + getBaseDir() + "/derby/test;create=true", "sa", "sa");
dbs.add(c2.createStatement()); // dbs.add(c2.createStatement());
} catch (Exception e) { // } catch (Exception e) {
// database not installed - ok // // database not installed - ok
}
// if (dbs.size() == 0) {
// return;
// } // }
String shortest = null; String shortest = null;
Throwable shortestEx = null; Throwable shortestEx = null;
...@@ -121,12 +119,11 @@ public class TestNestedJoins extends TestBase { ...@@ -121,12 +119,11 @@ public class TestNestedJoins extends TestBase {
execute(sql); execute(sql);
} catch (Throwable e) { } catch (Throwable e) {
if (e instanceof SQLException) { if (e instanceof SQLException) {
SQLException se = (SQLException) e;
trace(sql); trace(sql);
int todoFail; fail(sql);
int test; // SQLException se = (SQLException) e;
System.out.println(se); // System.out.println(se);
System.out.println(" " + sql); // System.out.println(" " + sql);
} }
if (e != null) { if (e != null) {
if (shortest == null || sql.length() < shortest.length()) { if (shortest == null || sql.length() < shortest.length()) {
...@@ -243,6 +240,18 @@ int test; ...@@ -243,6 +240,18 @@ int test;
ResultSet rs; ResultSet rs;
String sql; String sql;
/*
create table test(id int primary key);
explain select * from test a left outer join (test c) on a.id = c.id;
-- expected: uses the primary key index
*/
stat.execute("create table test(id int primary key)");
rs = stat.executeQuery("explain select * from test a left outer join (test c) on a.id = c.id");
assertTrue(rs.next());
sql = rs.getString(1);
assertTrue(sql.indexOf("PRIMARY_KEY") >= 0);
stat.execute("drop table test");
/* /*
create table t1(a int, b int); create table t1(a int, b int);
create table t2(a int, b int); create table t2(a int, b int);
...@@ -315,6 +324,18 @@ int test; ...@@ -315,6 +324,18 @@ int test;
assertFalse(rs.next()); assertFalse(rs.next());
stat.execute("drop table a, b, c"); stat.execute("drop table a, b, c");
/*
drop table a, b, c;
create table a(x int);
create table b(x int);
create table c(x int, y int);
insert into a values(1), (2);
insert into b values(3);
insert into c values(1, 3);
insert into c values(4, 5);
explain select * from a left outer join (b left outer join c on b.x = c.y) on a.x = c.x;
select * from a left outer join (b left outer join c on b.x = c.y) on a.x = c.x;
*/
stat.execute("create table a(x int)"); stat.execute("create table a(x int)");
stat.execute("create table b(x int)"); stat.execute("create table b(x int)");
stat.execute("create table c(x int, y int)"); stat.execute("create table c(x int, y int)");
...@@ -490,7 +511,6 @@ int test; ...@@ -490,7 +511,6 @@ int test;
r.setSkipRemarks(true); r.setSkipRemarks(true);
sql = r.readStatement(); sql = r.readStatement();
sql = sql.replaceAll("\\n", " "); sql = sql.replaceAll("\\n", " ");
// sql = sql.replaceAll("\\/\\*.*\\*\\/", "");
while (sql.indexOf(" ") >= 0) { while (sql.indexOf(" ") >= 0) {
sql = sql.replaceAll(" ", " "); sql = sql.replaceAll(" ", " ");
} }
......
...@@ -885,9 +885,6 @@ select * from ((test d1 inner join test d2 on d1.id = d2.id) inner join test d3 ...@@ -885,9 +885,6 @@ select * from ((test d1 inner join test d2 on d1.id = d2.id) inner join test d3
> -- -- -- -- > -- -- -- --
> rows: 0 > rows: 0
select * from dual a left join dual b on b.x=(select max(x) from dual);
> exception
drop table test; drop table test;
> ok > ok
......
Markdown 格式
0%
您添加了 0 到此讨论。请谨慎行事。
请先完成此评论的编辑!
注册 或者 后发表评论