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

Experimental feature to support nested joins.

上级 36560928
...@@ -124,6 +124,7 @@ import org.h2.table.RangeTable; ...@@ -124,6 +124,7 @@ import org.h2.table.RangeTable;
import org.h2.table.Table; import org.h2.table.Table;
import org.h2.table.TableFilter; import org.h2.table.TableFilter;
import org.h2.table.TableView; import org.h2.table.TableView;
import org.h2.table.TableFilter.TableFilterVisitor;
import org.h2.util.Utils; import org.h2.util.Utils;
import org.h2.util.MathUtils; import org.h2.util.MathUtils;
import org.h2.util.New; import org.h2.util.New;
...@@ -969,8 +970,17 @@ public class Parser { ...@@ -969,8 +970,17 @@ public class Parser {
alias = session.getNextSystemIdentifier(sqlCommand); alias = session.getNextSystemIdentifier(sqlCommand);
table = TableView.createTempView(s, session.getUser(), alias, query, currentSelect); table = TableView.createTempView(s, session.getUser(), alias, query, currentSelect);
} else { } else {
TableFilter top = readTableFilter(fromOuter); TableFilter top;
if (SysProperties.NESTED_JOINS) {
String joinTable = Constants.PREFIX_JOIN + parseIndex;
top = new TableFilter(session, getDualTable(true), joinTable, rightsChecked, currentSelect);
TableFilter n = readTableFilter(false);
n = readJoin(n, currentSelect, false);
top.addJoin(n, false, true, null);
} else {
top = readTableFilter(fromOuter);
top = readJoin(top, currentSelect, fromOuter); top = readJoin(top, currentSelect, fromOuter);
}
read(")"); read(")");
alias = readFromAlias(null); alias = readFromAlias(null);
if (alias != null) { if (alias != null) {
...@@ -988,7 +998,7 @@ public class Parser { ...@@ -988,7 +998,7 @@ public class Parser {
read(","); read(",");
Expression max = readExpression(); Expression max = readExpression();
read(")"); read(")");
table = new RangeTable(mainSchema, min, max); table = new RangeTable(mainSchema, min, max, false);
} else { } else {
Expression func = readFunction(schema, tableName); Expression func = readFunction(schema, tableName);
if (!(func instanceof FunctionCall)) { if (!(func instanceof FunctionCall)) {
...@@ -997,7 +1007,7 @@ public class Parser { ...@@ -997,7 +1007,7 @@ public class Parser {
table = new FunctionTable(mainSchema, session, func, (FunctionCall) func); table = new FunctionTable(mainSchema, session, func, (FunctionCall) func);
} }
} else if (equalsToken("DUAL", tableName)) { } else if (equalsToken("DUAL", tableName)) {
table = getDualTable(); table = getDualTable(false);
} else { } else {
table = readTableOrView(tableName); table = readTableOrView(tableName);
} }
...@@ -1233,7 +1243,14 @@ public class Parser { ...@@ -1233,7 +1243,14 @@ public class Parser {
if (readIf("ON")) { if (readIf("ON")) {
on = readExpression(); on = readExpression();
} }
newTop.addJoin(top, true, fromOuter, on); if (SysProperties.NESTED_JOINS) {
String joinTable = Constants.PREFIX_JOIN + parseIndex;
TableFilter nt = new TableFilter(session, getDualTable(true), joinTable, rightsChecked, currentSelect);
nt.addJoin(top, false, true, null);
newTop.addJoin(nt, true, false, on);
} else {
newTop.addJoin(top, true, false, on);
}
top = newTop; top = newTop;
last = newTop; last = newTop;
} else if (readIf("LEFT")) { } else if (readIf("LEFT")) {
...@@ -1245,7 +1262,7 @@ public class Parser { ...@@ -1245,7 +1262,7 @@ public class Parser {
if (readIf("ON")) { if (readIf("ON")) {
on = readExpression(); on = readExpression();
} }
top.addJoin(join, true, fromOuter, on); top.addJoin(join, true, false, on);
last = join; last = join;
} else if (readIf("FULL")) { } else if (readIf("FULL")) {
throw getSyntaxError(); throw getSyntaxError();
...@@ -1258,9 +1275,9 @@ public class Parser { ...@@ -1258,9 +1275,9 @@ public class Parser {
on = readExpression(); on = readExpression();
} }
if (SysProperties.NESTED_JOINS) { if (SysProperties.NESTED_JOINS) {
top.addJoin(join, false, fromOuter, on); top.addJoin(join, false, false, on);
} else { } else {
top.addJoin(join, fromOuter, fromOuter, on); top.addJoin(join, fromOuter, false, on);
} }
last = join; last = join;
} else if (readIf("JOIN")) { } else if (readIf("JOIN")) {
...@@ -1271,18 +1288,18 @@ public class Parser { ...@@ -1271,18 +1288,18 @@ public class Parser {
on = readExpression(); on = readExpression();
} }
if (SysProperties.NESTED_JOINS) { if (SysProperties.NESTED_JOINS) {
top.addJoin(join, false, fromOuter, on); top.addJoin(join, false, false, on);
} else { } else {
top.addJoin(join, fromOuter, fromOuter, on); top.addJoin(join, fromOuter, false, 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);
if (SysProperties.NESTED_JOINS) { if (SysProperties.NESTED_JOINS) {
top.addJoin(join, false, fromOuter, null); top.addJoin(join, false, false, null);
} else { } else {
top.addJoin(join, fromOuter, fromOuter, null); top.addJoin(join, fromOuter, false, null);
} }
last = join; last = join;
} else if (readIf("NATURAL")) { } else if (readIf("NATURAL")) {
...@@ -1313,9 +1330,9 @@ public class Parser { ...@@ -1313,9 +1330,9 @@ public class Parser {
} }
} }
if (SysProperties.NESTED_JOINS) { if (SysProperties.NESTED_JOINS) {
top.addJoin(join, false, fromOuter, on); top.addJoin(join, false, false, on);
} else { } else {
top.addJoin(join, fromOuter, fromOuter, on); top.addJoin(join, fromOuter, false, on);
} }
last = join; last = join;
} else { } else {
...@@ -1558,16 +1575,18 @@ public class Parser { ...@@ -1558,16 +1575,18 @@ public class Parser {
} while (readIf(",")); } while (readIf(","));
} }
private void parseJoinTableFilter(TableFilter top, Select command) { private void parseJoinTableFilter(TableFilter top, final Select command) {
top = readJoin(top, command, top.isJoinOuter()); top = readJoin(top, command, top.isJoinOuter());
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()) { TableFilter n = top.getNestedJoin();
command.addTableFilter(n, false); if (n != null) {
for (TableFilter j = n.getJoin(); j != null; j = j.getJoin()) { n.visit(new TableFilterVisitor() {
command.addTableFilter(j, false); public void accept(TableFilter f) {
command.addTableFilter(f, false);
} }
});
} }
TableFilter join = top.getJoin(); TableFilter join = top.getJoin();
if (join == null) { if (join == null) {
...@@ -1652,7 +1671,7 @@ public class Parser { ...@@ -1652,7 +1671,7 @@ public class Parser {
if (!readIf("FROM")) { if (!readIf("FROM")) {
// select without FROM: convert to SELECT ... FROM // select without FROM: convert to SELECT ... FROM
// SYSTEM_RANGE(1,1) // SYSTEM_RANGE(1,1)
Table dual = getDualTable(); Table dual = getDualTable(false);
TableFilter filter = new TableFilter(session, dual, null, rightsChecked, currentSelect); TableFilter filter = new TableFilter(session, dual, null, rightsChecked, currentSelect);
command.addTableFilter(filter, true); command.addTableFilter(filter, true);
} else { } else {
...@@ -1688,10 +1707,10 @@ public class Parser { ...@@ -1688,10 +1707,10 @@ public class Parser {
return command; return command;
} }
private Table getDualTable() { private Table getDualTable(boolean noColumns) {
Schema main = database.findSchema(Constants.SCHEMA_MAIN); Schema main = database.findSchema(Constants.SCHEMA_MAIN);
Expression one = ValueExpression.get(ValueLong.get(1)); Expression one = ValueExpression.get(ValueLong.get(1));
return new RangeTable(main, one, one); return new RangeTable(main, one, one, noColumns);
} }
private void setSQL(Prepared command, String start, int startIndex) { private void setSQL(Prepared command, String start, int startIndex) {
......
...@@ -858,15 +858,23 @@ public class Select extends Query { ...@@ -858,15 +858,23 @@ public class Select extends Query {
if (condition != null) { if (condition != null) {
condition.setEvaluatable(f, true); condition.setEvaluatable(f, true);
} }
TableFilter n = f.getNestedJoin();
if (n != null) {
setEvaluatableRecursive(n);
}
Expression on = f.getJoinCondition(); Expression on = f.getJoinCondition();
if (on != null) { if (on != null) {
if (!on.isEverything(ExpressionVisitor.EVALUATABLE)) { if (!on.isEverything(ExpressionVisitor.EVALUATABLE)) {
if (SysProperties.NESTED_JOINS) {
int testCanSupport;
} 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
on = on.optimize(session); on = on.optimize(session);
// 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(); f.removeJoinCondition();
// need to check that all added are bound to a table // need to check that all added are bound to a table
on = on.optimize(session); on = on.optimize(session);
...@@ -885,10 +893,6 @@ public class Select extends Query { ...@@ -885,10 +893,6 @@ public class Select extends Query {
for (Expression e : expressions) { for (Expression e : expressions) {
e.setEvaluatable(f, true); e.setEvaluatable(f, true);
} }
TableFilter n = f.getNestedJoin();
if (n != null) {
setEvaluatableRecursive(n);
}
} }
} }
......
...@@ -222,6 +222,11 @@ public class Constants { ...@@ -222,6 +222,11 @@ public class Constants {
*/ */
public static final String PREFIX_INDEX = "INDEX_"; public static final String PREFIX_INDEX = "INDEX_";
/**
* The name prefix used for synthetic nested join tables.
*/
public static final String PREFIX_JOIN = "SYSTEM_JOIN_";
/** /**
* The name prefix used for primary key constraints that are not explicitly * The name prefix used for primary key constraints that are not explicitly
* named. * named.
......
...@@ -11,6 +11,7 @@ import org.h2.command.Parser; ...@@ -11,6 +11,7 @@ import org.h2.command.Parser;
import org.h2.command.dml.Select; import org.h2.command.dml.Select;
import org.h2.command.dml.SelectListColumnResolver; import org.h2.command.dml.SelectListColumnResolver;
import org.h2.constant.ErrorCode; import org.h2.constant.ErrorCode;
import org.h2.constant.SysProperties;
import org.h2.engine.Database; import org.h2.engine.Database;
import org.h2.engine.Session; import org.h2.engine.Session;
import org.h2.index.IndexCondition; import org.h2.index.IndexCondition;
...@@ -244,6 +245,12 @@ public class ExpressionColumn extends Expression { ...@@ -244,6 +245,12 @@ public class ExpressionColumn extends Expression {
// if the current value is known (evaluatable set) // if the current value is known (evaluatable set)
// 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 (getTableFilter() == null) {
return false;
}
return getTableFilter().isEvaluatable();
}
return evaluatable || visitor.getQueryLevel() < this.queryLevel; return evaluatable || visitor.getQueryLevel() < this.queryLevel;
case ExpressionVisitor.SET_MAX_DATA_MODIFICATION_ID: case ExpressionVisitor.SET_MAX_DATA_MODIFICATION_ID:
visitor.addDataModificationId(column.getTable().getMaxDataModificationId()); visitor.addDataModificationId(column.getTable().getMaxDataModificationId());
......
...@@ -11,6 +11,7 @@ import java.util.HashMap; ...@@ -11,6 +11,7 @@ import java.util.HashMap;
import org.h2.engine.Session; import org.h2.engine.Session;
import org.h2.expression.Expression; import org.h2.expression.Expression;
import org.h2.expression.ExpressionVisitor; import org.h2.expression.ExpressionVisitor;
import org.h2.table.TableFilter.TableFilterVisitor;
import org.h2.util.New; import org.h2.util.New;
/** /**
...@@ -33,20 +34,21 @@ public class Plan { ...@@ -33,20 +34,21 @@ public class Plan {
public Plan(TableFilter[] filters, int count, Expression condition) { public Plan(TableFilter[] filters, int count, Expression condition) {
this.filters = new TableFilter[count]; this.filters = new TableFilter[count];
System.arraycopy(filters, 0, this.filters, 0, count); System.arraycopy(filters, 0, this.filters, 0, count);
ArrayList<Expression> allCond = New.arrayList(); final ArrayList<Expression> allCond = New.arrayList();
ArrayList<TableFilter> all = New.arrayList(); final ArrayList<TableFilter> all = New.arrayList();
if (condition != null) { if (condition != null) {
allCond.add(condition); allCond.add(condition);
} }
for (int i = 0; i < count; i++) { for (int i = 0; i < count; i++) {
TableFilter f = filters[i]; TableFilter f = filters[i];
do { f.visit(new TableFilterVisitor() {
public void accept(TableFilter f) {
all.add(f); all.add(f);
if (f.getJoinCondition() != null) { if (f.getJoinCondition() != null) {
allCond.add(f.getJoinCondition()); allCond.add(f.getJoinCondition());
} }
f = f.getJoin(); }
} while(f != null); });
} }
allConditions = new Expression[allCond.size()]; allConditions = new Expression[allCond.size()];
allCond.toArray(allConditions); allCond.toArray(allConditions);
...@@ -126,6 +128,7 @@ public class Plan { ...@@ -126,6 +128,7 @@ public class Plan {
} }
private void setEvaluatable(TableFilter filter, boolean b) { private void setEvaluatable(TableFilter filter, boolean b) {
filter.setEvaluatable(filter, b);
for (Expression e : allConditions) { for (Expression e : allConditions) {
e.setEvaluatable(filter, b); e.setEvaluatable(filter, b);
} }
......
...@@ -38,9 +38,9 @@ public class RangeTable extends Table { ...@@ -38,9 +38,9 @@ public class RangeTable extends Table {
* @param min the start expression * @param min the start expression
* @param max the end expression * @param max the end expression
*/ */
public RangeTable(Schema schema, Expression min, Expression max) { public RangeTable(Schema schema, Expression min, Expression max, boolean noColumns) {
super(schema, 0, NAME, true, true); super(schema, 0, NAME, true, true);
Column[] cols = { new Column("X", Value.LONG) }; Column[] cols = noColumns ? new Column[0] : new Column[] { new Column("X", Value.LONG) };
this.min = min; this.min = min;
this.max = max; this.max = max;
setColumns(cols); setColumns(cols);
......
...@@ -40,6 +40,7 @@ public class TableFilter implements ColumnResolver { ...@@ -40,6 +40,7 @@ public class TableFilter implements ColumnResolver {
private Session session; private Session session;
private Index index; private Index index;
private int scanCount; private int scanCount;
private boolean evaluatable;
/** /**
* Indicates that this filter is used in the plan. * Indicates that this filter is used in the plan.
...@@ -186,6 +187,10 @@ public class TableFilter implements ColumnResolver { ...@@ -186,6 +187,10 @@ public class TableFilter implements ColumnResolver {
} }
private void setEvaluatable(TableFilter join) { private void setEvaluatable(TableFilter join) {
if (SysProperties.NESTED_JOINS) {
setEvaluatable(true);
return;
}
// this table filter is now evaluatable - in all sub-joins // this table filter is now evaluatable - in all sub-joins
do { do {
Expression e = join.getJoinCondition(); Expression e = join.getJoinCondition();
...@@ -306,27 +311,10 @@ public class TableFilter implements ColumnResolver { ...@@ -306,27 +311,10 @@ public class TableFilter implements ColumnResolver {
} else { } else {
// state == FOUND || NULL_ROW // state == FOUND || NULL_ROW
// the last row was ok - try next row of the join // the last row was ok - try next row of the join
if (nestedJoin != null) {
if (join == null) {
if (nestedJoin.next()) {
return true;
}
} else {
while (true) {
if (nestedJoin.next()) {
if (join.next()) {
return true;
}
join.reset();
}
}
}
} else {
if (join != null && join.next()) { if (join != null && join.next()) {
return true; return true;
} }
} }
}
while (true) { while (true) {
// go to the next row // go to the next row
if (state == NULL_ROW) { if (state == NULL_ROW) {
...@@ -334,6 +322,10 @@ public class TableFilter implements ColumnResolver { ...@@ -334,6 +322,10 @@ public class TableFilter implements ColumnResolver {
} }
if (cursor.isAlwaysFalse()) { if (cursor.isAlwaysFalse()) {
state = AFTER_LAST; state = AFTER_LAST;
} else if (nestedJoin != null) {
if (state == BEFORE_FIRST) {
state = FOUND;
}
} else { } else {
if ((++scanCount & 4095) == 0) { if ((++scanCount & 4095) == 0) {
checkTimeout(); checkTimeout();
...@@ -346,6 +338,16 @@ public class TableFilter implements ColumnResolver { ...@@ -346,6 +338,16 @@ public class TableFilter implements ColumnResolver {
state = AFTER_LAST; state = AFTER_LAST;
} }
} }
if (nestedJoin != null && state == FOUND) {
if (!nestedJoin.next()) {
state = AFTER_LAST;
if (joinOuter && !foundOne) {
// possibly null row
} else {
continue;
}
}
}
// if no more rows found, try the null row (for outer joins only) // if no more rows found, try the null row (for outer joins only)
if (state == AFTER_LAST) { if (state == AFTER_LAST) {
if (joinOuter && !foundOne) { if (joinOuter && !foundOne) {
...@@ -354,12 +356,6 @@ public class TableFilter implements ColumnResolver { ...@@ -354,12 +356,6 @@ public class TableFilter implements ColumnResolver {
break; break;
} }
} }
if (state == FOUND && nestedJoin != null) {
nestedJoin.reset();
if (!nestedJoin.next()) {
continue;
}
}
if (!isOk(filterCondition)) { if (!isOk(filterCondition)) {
continue; continue;
} }
...@@ -386,13 +382,16 @@ public class TableFilter implements ColumnResolver { ...@@ -386,13 +382,16 @@ public class TableFilter implements ColumnResolver {
return false; return false;
} }
private void setNullRow() { protected void setNullRow() {
state = NULL_ROW; state = NULL_ROW;
current = table.getNullRow(); current = table.getNullRow();
currentSearchRow = current; currentSearchRow = current;
if (nestedJoin != null) { if (nestedJoin != null) {
int todoRecurse; nestedJoin.visit(new TableFilterVisitor() {
nestedJoin.setNullRow(); public void accept(TableFilter f) {
f.setNullRow();
}
});
} }
} }
...@@ -484,9 +483,21 @@ public class TableFilter implements ColumnResolver { ...@@ -484,9 +483,21 @@ public class TableFilter implements ColumnResolver {
* @param outer if this is an outer join * @param outer if this is an outer join
* @param on the join condition * @param on the join condition
*/ */
public void addJoin(TableFilter filter, boolean outer, boolean nested, Expression on) { public void addJoin(TableFilter filter, boolean outer, boolean nested, final Expression on) {
if (on != null) { if (on != null) {
on.mapColumns(this, 0); on.mapColumns(this, 0);
if (SysProperties.NESTED_JOINS) {
visit(new TableFilterVisitor() {
public void accept(TableFilter f) {
on.mapColumns(f, 0);
}
});
filter.visit(new TableFilterVisitor() {
public void accept(TableFilter f) {
on.mapColumns(f, 0);
}
});
}
} }
if (nested && SysProperties.NESTED_JOINS) { if (nested && SysProperties.NESTED_JOINS) {
if (nestedJoin != null) { if (nestedJoin != null) {
...@@ -529,9 +540,6 @@ public class TableFilter implements ColumnResolver { ...@@ -529,9 +540,6 @@ 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) {
nestedJoin.mapAndAddFilter(on);
}
if (join != null) { if (join != null) {
join.mapAndAddFilter(on); join.mapAndAddFilter(on);
} }
...@@ -566,7 +574,22 @@ public class TableFilter implements ColumnResolver { ...@@ -566,7 +574,22 @@ public class TableFilter implements ColumnResolver {
} }
} }
if (nestedJoin != null) { if (nestedJoin != null) {
buff.append("("); buff.append("(\n");
TableFilter n = nestedJoin;
do {
buff.append(n.getPlanSQL(n != nestedJoin));
buff.append("\n");
n = (TableFilter) n.getJoin();
} while (n != null);
buff.append(") ON ");
if (joinCondition == null) {
// need to have a ON expression,
// otherwise the nesting is unclear
buff.append("1=1");
} else {
buff.append(StringUtils.unEnclose(joinCondition.getSQL()));
}
return buff.toString();
} }
buff.append(table.getSQL()); buff.append(table.getSQL());
if (alias != null) { if (alias != null) {
...@@ -586,11 +609,6 @@ public class TableFilter implements ColumnResolver { ...@@ -586,11 +609,6 @@ public class TableFilter implements ColumnResolver {
String plan = StringUtils.quoteRemarkSQL(planBuff.toString()); String plan = StringUtils.quoteRemarkSQL(planBuff.toString());
buff.append(plan).append(" */"); buff.append(plan).append(" */");
} }
if (nestedJoin != null) {
buff.append('\n');
buff.append(nestedJoin.getPlanSQL(true));
buff.append(")");
}
if (isJoin) { if (isJoin) {
buff.append(" ON "); buff.append(" ON ");
if (joinCondition == null) { if (joinCondition == null) {
...@@ -713,6 +731,7 @@ public class TableFilter implements ColumnResolver { ...@@ -713,6 +731,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);
if (filterCondition != null) { if (filterCondition != null) {
filterCondition.setEvaluatable(filter, b); filterCondition.setEvaluatable(filter, b);
} }
...@@ -727,6 +746,10 @@ public class TableFilter implements ColumnResolver { ...@@ -727,6 +746,10 @@ public class TableFilter implements ColumnResolver {
} }
} }
public void setEvaluatable(boolean evaluatable) {
this.evaluatable = evaluatable;
}
public String getSchemaName() { public String getSchemaName() {
return table.getSchema().getName(); return table.getSchema().getName();
} }
...@@ -855,4 +878,24 @@ public class TableFilter implements ColumnResolver { ...@@ -855,4 +878,24 @@ public class TableFilter implements ColumnResolver {
return nestedJoin; return nestedJoin;
} }
public void visit(TableFilterVisitor visitor) {
TableFilter f = this;
do {
visitor.accept(f);
TableFilter n = f.nestedJoin;
if (n != null) {
n.visit(visitor);
}
f = (TableFilter) f.join;
} while (f != null);
}
public static interface TableFilterVisitor {
public void accept(TableFilter f);
}
public boolean isEvaluatable() {
return evaluatable;
}
} }
Markdown 格式
0%
您添加了 0 到此讨论。请谨慎行事。
请先完成此评论的编辑!
注册 或者 后发表评论