提交 543cf97b authored 作者: Thomas Mueller's avatar Thomas Mueller

New database setting EARLY_FILTER to allow table implementations to apply filter…

New database setting EARLY_FILTER to allow table implementations to apply filter conditions early on.
上级 2900c117
...@@ -18,7 +18,9 @@ Change Log ...@@ -18,7 +18,9 @@ Change Log
<h1>Change Log</h1> <h1>Change Log</h1>
<h2>Next Version (unreleased)</h2> <h2>Next Version (unreleased)</h2>
<ul><li>Cluster: in a two node cluster, if cluster node stopped, and autocommit was enabled, <ul><li>New database setting EARLY_FILTER to allow table implementations
to apply filter conditions early on.
</li><li>Cluster: in a two node cluster, if cluster node stopped, and autocommit was enabled,
then changes on the remaining cluster node didn't get committed automatically. then changes on the remaining cluster node didn't get committed automatically.
</li><li>Issue 291: Allow org.h2.value.Value as FUNCTION ALIAS params and return value. </li><li>Issue 291: Allow org.h2.value.Value as FUNCTION ALIAS params and return value.
</li><li>Issue 265: Linked tables: auto-reconnect if the backside connection is lost </li><li>Issue 265: Linked tables: auto-reconnect if the backside connection is lost
......
...@@ -103,6 +103,13 @@ public class DbSettings extends SettingsBase { ...@@ -103,6 +103,13 @@ public class DbSettings extends SettingsBase {
*/ */
public final boolean dropRestrict = get("DROP_RESTRICT", Constants.VERSION_MINOR >= 3); public final boolean dropRestrict = get("DROP_RESTRICT", Constants.VERSION_MINOR >= 3);
/**
* Database setting <code>EARLY_FILTER</code> (default: false).<br />
* This setting allows table implementations to apply filter conditions
* early on.
*/
public final boolean earlyFilter = get("EARLY_FILTER", false);
/** /**
* Database setting <code>ESTIMATED_FUNCTION_TABLE_ROWS</code> (default: * Database setting <code>ESTIMATED_FUNCTION_TABLE_ROWS</code> (default:
* 1000).<br /> * 1000).<br />
......
...@@ -20,6 +20,7 @@ import org.h2.schema.SchemaObjectBase; ...@@ -20,6 +20,7 @@ import org.h2.schema.SchemaObjectBase;
import org.h2.table.Column; import org.h2.table.Column;
import org.h2.table.IndexColumn; import org.h2.table.IndexColumn;
import org.h2.table.Table; import org.h2.table.Table;
import org.h2.table.TableFilter;
import org.h2.util.MathUtils; import org.h2.util.MathUtils;
import org.h2.util.StatementBuilder; import org.h2.util.StatementBuilder;
import org.h2.util.StringUtils; import org.h2.util.StringUtils;
...@@ -93,6 +94,11 @@ public abstract class BaseIndex extends SchemaObjectBase implements Index { ...@@ -93,6 +94,11 @@ public abstract class BaseIndex extends SchemaObjectBase implements Index {
return false; return false;
} }
public Cursor find(TableFilter filter, SearchRow first, SearchRow last) {
return find(filter.getSession(), first, last);
}
/** /**
* Find a row or a list of rows that is larger and create a cursor to * Find a row or a list of rows that is larger and create a cursor to
* iterate over the result. The base implementation doesn't support this feature. * iterate over the result. The base implementation doesn't support this feature.
......
...@@ -13,6 +13,7 @@ import org.h2.schema.SchemaObject; ...@@ -13,6 +13,7 @@ import org.h2.schema.SchemaObject;
import org.h2.table.Column; import org.h2.table.Column;
import org.h2.table.IndexColumn; import org.h2.table.IndexColumn;
import org.h2.table.Table; import org.h2.table.Table;
import org.h2.table.TableFilter;
/** /**
* An index. Indexes are used to speed up searching data. * An index. Indexes are used to speed up searching data.
...@@ -59,6 +60,16 @@ public interface Index extends SchemaObject { ...@@ -59,6 +60,16 @@ public interface Index extends SchemaObject {
*/ */
Cursor find(Session session, SearchRow first, SearchRow last); Cursor find(Session session, SearchRow first, SearchRow last);
/**
* Find a row or a list of rows and create a cursor to iterate over the result.
*
* @param filter the table filter (which possibly knows about additional conditions)
* @param first the first row, or null for no limit
* @param last the last row, or null for no limit
* @return the cursor to iterate over the results
*/
Cursor find(TableFilter filter, SearchRow first, SearchRow last);
/** /**
* Estimate the cost to search for rows given the search mask. * Estimate the cost to search for rows given the search mask.
* There is one element per column in the search mask. * There is one element per column in the search mask.
......
...@@ -18,6 +18,7 @@ import org.h2.result.SortOrder; ...@@ -18,6 +18,7 @@ import org.h2.result.SortOrder;
import org.h2.table.Column; import org.h2.table.Column;
import org.h2.table.IndexColumn; import org.h2.table.IndexColumn;
import org.h2.table.Table; import org.h2.table.Table;
import org.h2.table.TableFilter;
import org.h2.value.Value; import org.h2.value.Value;
import org.h2.value.ValueNull; import org.h2.value.ValueNull;
...@@ -26,7 +27,9 @@ import org.h2.value.ValueNull; ...@@ -26,7 +27,9 @@ import org.h2.value.ValueNull;
* and IN(SELECT ...) optimizations. * and IN(SELECT ...) optimizations.
*/ */
public class IndexCursor implements Cursor { public class IndexCursor implements Cursor {
private Session session; private Session session;
private final TableFilter tableFilter;
private Index index; private Index index;
private Table table; private Table table;
private IndexColumn[] indexColumns; private IndexColumn[] indexColumns;
...@@ -40,6 +43,10 @@ public class IndexCursor implements Cursor { ...@@ -40,6 +43,10 @@ public class IndexCursor implements Cursor {
private ResultInterface inResult; private ResultInterface inResult;
private HashSet<Value> inResultTested; private HashSet<Value> inResultTested;
public IndexCursor(TableFilter filter) {
this.tableFilter = filter;
}
public void setIndex(Index index) { public void setIndex(Index index) {
this.index = index; this.index = index;
this.table = index.getTable(); this.table = index.getTable();
...@@ -135,7 +142,7 @@ public class IndexCursor implements Cursor { ...@@ -135,7 +142,7 @@ public class IndexCursor implements Cursor {
return; return;
} }
if (!alwaysFalse) { if (!alwaysFalse) {
cursor = index.find(s, start, end); cursor = index.find(tableFilter, start, end);
} }
} }
...@@ -265,7 +272,7 @@ public class IndexCursor implements Cursor { ...@@ -265,7 +272,7 @@ public class IndexCursor implements Cursor {
start = table.getTemplateRow(); start = table.getTemplateRow();
} }
start.setValue(id, v); start.setValue(id, v);
cursor = index.find(session, start, start); cursor = index.find(tableFilter, start, start);
} }
public boolean previous() { public boolean previous() {
......
...@@ -18,6 +18,7 @@ import org.h2.table.Column; ...@@ -18,6 +18,7 @@ import org.h2.table.Column;
import org.h2.table.IndexColumn; import org.h2.table.IndexColumn;
import org.h2.table.RegularTable; import org.h2.table.RegularTable;
import org.h2.table.Table; import org.h2.table.Table;
import org.h2.table.TableFilter;
import org.h2.value.Value; import org.h2.value.Value;
import org.h2.value.ValueNull; import org.h2.value.ValueNull;
...@@ -62,6 +63,14 @@ public class MultiVersionIndex implements Index { ...@@ -62,6 +63,14 @@ public class MultiVersionIndex implements Index {
} }
} }
public Cursor find(TableFilter filter, SearchRow first, SearchRow last) {
synchronized (sync) {
Cursor baseCursor = base.find(filter, first, last);
Cursor deltaCursor = delta.find(filter, first, last);
return new MultiVersionCursor(filter.getSession(), this, baseCursor, deltaCursor, sync);
}
}
public Cursor find(Session session, SearchRow first, SearchRow last) { public Cursor find(Session session, SearchRow first, SearchRow last) {
synchronized (sync) { synchronized (sync) {
Cursor baseCursor = base.find(session, first, last); Cursor baseCursor = base.find(session, first, last);
......
...@@ -13,6 +13,7 @@ import org.h2.result.Row; ...@@ -13,6 +13,7 @@ import org.h2.result.Row;
import org.h2.result.SearchRow; import org.h2.result.SearchRow;
import org.h2.table.IndexColumn; import org.h2.table.IndexColumn;
import org.h2.table.RegularTable; import org.h2.table.RegularTable;
import org.h2.table.TableFilter;
import org.h2.value.Value; import org.h2.value.Value;
import org.h2.value.ValueNull; import org.h2.value.ValueNull;
...@@ -275,7 +276,15 @@ public class TreeIndex extends BaseIndex { ...@@ -275,7 +276,15 @@ public class TreeIndex extends BaseIndex {
return result; return result;
} }
public Cursor find(TableFilter filter, SearchRow first, SearchRow last) {
return find(first, last);
}
public Cursor find(Session session, SearchRow first, SearchRow last) { public Cursor find(Session session, SearchRow first, SearchRow last) {
return find(first, last);
}
public Cursor find(SearchRow first, SearchRow last) {
if (first == null) { if (first == null) {
TreeNode x = root, n; TreeNode x = root, n;
while (x != null) { while (x != null) {
......
...@@ -19,6 +19,7 @@ import org.h2.util.New; ...@@ -19,6 +19,7 @@ import org.h2.util.New;
* on the order the tables are accessed. * on the order the tables are accessed.
*/ */
public class Plan { public class Plan {
private final TableFilter[] filters; private final TableFilter[] filters;
private final HashMap<TableFilter, PlanItem> planItems = New.hashMap(); private final HashMap<TableFilter, PlanItem> planItems = New.hashMap();
private final Expression[] allConditions; private final Expression[] allConditions;
...@@ -82,7 +83,7 @@ public class Plan { ...@@ -82,7 +83,7 @@ public class Plan {
for (int i = 0; i < allFilters.length; i++) { for (int i = 0; i < allFilters.length; i++) {
TableFilter f = allFilters[i]; TableFilter f = allFilters[i];
setEvaluatable(f, true); setEvaluatable(f, true);
if (i < allFilters.length - 1) { if (i < allFilters.length - 1 || f.getSession().getDatabase().getSettings().earlyFilter) {
// the last table doesn't need the optimization, // the last table doesn't need the optimization,
// otherwise the expression is calculated twice unnecessarily // otherwise the expression is calculated twice unnecessarily
// (not that bad but not optimal) // (not that bad but not optimal)
......
...@@ -116,7 +116,7 @@ public class TableFilter implements ColumnResolver { ...@@ -116,7 +116,7 @@ public class TableFilter implements ColumnResolver {
this.table = table; this.table = table;
this.alias = alias; this.alias = alias;
this.select = select; this.select = select;
this.cursor = new IndexCursor(); this.cursor = new IndexCursor(this);
if (!rightsChecked) { if (!rightsChecked) {
session.getUser().checkRight(table, Right.SELECT); session.getUser().checkRight(table, Right.SELECT);
} }
...@@ -967,6 +967,10 @@ public class TableFilter implements ColumnResolver { ...@@ -967,6 +967,10 @@ public class TableFilter implements ColumnResolver {
return evaluatable; return evaluatable;
} }
public Session getSession() {
return session;
}
/** /**
* A visitor for table filters. * A visitor for table filters.
*/ */
......
...@@ -14,6 +14,7 @@ import java.util.ArrayList; ...@@ -14,6 +14,7 @@ import java.util.ArrayList;
import org.h2.api.TableEngine; import org.h2.api.TableEngine;
import org.h2.command.ddl.CreateTableData; import org.h2.command.ddl.CreateTableData;
import org.h2.engine.Session; import org.h2.engine.Session;
import org.h2.expression.Expression;
import org.h2.index.BaseIndex; import org.h2.index.BaseIndex;
import org.h2.index.Cursor; import org.h2.index.Cursor;
import org.h2.index.SingleRowCursor; import org.h2.index.SingleRowCursor;
...@@ -24,7 +25,12 @@ import org.h2.result.SearchRow; ...@@ -24,7 +25,12 @@ import org.h2.result.SearchRow;
import org.h2.table.IndexColumn; import org.h2.table.IndexColumn;
import org.h2.table.TableBase; import org.h2.table.TableBase;
import org.h2.table.Table; import org.h2.table.Table;
import org.h2.table.TableFilter;
import org.h2.test.TestBase; import org.h2.test.TestBase;
import org.h2.value.Value;
import org.h2.value.ValueInt;
import org.h2.value.ValueNull;
import org.h2.value.ValueString;
/** /**
* The class for external table engines mechanism testing. * The class for external table engines mechanism testing.
...@@ -46,6 +52,25 @@ public class TestTableEngines extends TestBase { ...@@ -46,6 +52,25 @@ public class TestTableEngines extends TestBase {
if (config.mvcc) { if (config.mvcc) {
return; return;
} }
testEarlyFilter();
testSimpleQuery();
}
private void testEarlyFilter() throws SQLException {
deleteDb("tableEngine");
Connection conn = getConnection("tableEngine;EARLY_FILTER=TRUE");
Statement stat = conn.createStatement();
stat.execute("CREATE TABLE t1(id int, name varchar) ENGINE \"" + EndlessTableEngine.class.getName() + "\"");
ResultSet rs = stat.executeQuery("SELECT name FROM t1 where id=1 and name is not null");
assertTrue(rs.next());
assertEquals("((ID = 1)\n AND (NAME IS NOT NULL))", rs.getString(1));
rs.close();
conn.close();
deleteDb("tableEngine");
}
private void testSimpleQuery() throws SQLException {
deleteDb("tableEngine"); deleteDb("tableEngine");
Connection conn = getConnection("tableEngine"); Connection conn = getConnection("tableEngine");
...@@ -112,7 +137,7 @@ public class TestTableEngines extends TestBase { ...@@ -112,7 +137,7 @@ public class TestTableEngines extends TestBase {
/** /**
* A scan index for one row. * A scan index for one row.
*/ */
private class Scan extends BaseIndex { public class Scan extends BaseIndex {
Scan(Table table) { Scan(Table table) {
initBaseIndex(table, table.getId(), table.getName() + "_SCAN", initBaseIndex(table, table.getId(), table.getName() + "_SCAN",
...@@ -172,9 +197,9 @@ public class TestTableEngines extends TestBase { ...@@ -172,9 +197,9 @@ public class TestTableEngines extends TestBase {
} }
} }
volatile Row row; protected Index scanIndex;
private final Index scanIndex; volatile Row row;
OneRowTable(CreateTableData data) { OneRowTable(CreateTableData data) {
super(data); super(data);
...@@ -293,6 +318,72 @@ public class TestTableEngines extends TestBase { ...@@ -293,6 +318,72 @@ public class TestTableEngines extends TestBase {
public OneRowTable createTable(CreateTableData data) { public OneRowTable createTable(CreateTableData data) {
return new OneRowTable(data); return new OneRowTable(data);
} }
}
/**
* A test table factory.
*/
public static class EndlessTableEngine implements TableEngine {
/**
* A table implementation with one row.
*/
private static class EndlessTable extends OneRowTableEngine.OneRowTable {
EndlessTable(CreateTableData data) {
super(data);
row = new Row(new Value[] { ValueInt.get(1), ValueNull.INSTANCE }, 0);
scanIndex = new Auto(this);
}
/**
* A scan index for one row.
*/
public class Auto extends OneRowTableEngine.OneRowTable.Scan {
Auto(Table table) {
super(table);
}
public Cursor find(TableFilter filter, SearchRow first, SearchRow last) {
return find(filter.getSession(), filter.getFilterCondition(), first, last);
}
public Cursor find(Session session, SearchRow first, SearchRow last) {
return find(session, null, first, last);
}
/**
* Search within the table.
*
* @param session the session
* @param filter the table filter (optional)
* @param first the first row (optional)
* @param last the last row (optional)
* @return the cursor
*/
private Cursor find(Session session, Expression filter, SearchRow first, SearchRow last) {
if (filter != null) {
row.setValue(1, ValueString.get(filter.getSQL()));
}
return new SingleRowCursor(row);
}
}
}
/**
* Create a new table.
*
* @param data the meta data of the table to create
* @return the new table
*/
public EndlessTable createTable(CreateTableData data) {
return new EndlessTable(data);
}
} }
} }
Markdown 格式
0%
您添加了 0 到此讨论。请谨慎行事。
请先完成此评论的编辑!
注册 或者 后发表评论