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

Experimental feature to support nested joins.

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