提交 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
<h1>Change Log</h1>
<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.
</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
......
......@@ -103,6 +103,13 @@ public class DbSettings extends SettingsBase {
*/
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:
* 1000).<br />
......
......@@ -20,6 +20,7 @@ import org.h2.schema.SchemaObjectBase;
import org.h2.table.Column;
import org.h2.table.IndexColumn;
import org.h2.table.Table;
import org.h2.table.TableFilter;
import org.h2.util.MathUtils;
import org.h2.util.StatementBuilder;
import org.h2.util.StringUtils;
......@@ -93,6 +94,11 @@ public abstract class BaseIndex extends SchemaObjectBase implements Index {
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
* iterate over the result. The base implementation doesn't support this feature.
......
......@@ -13,6 +13,7 @@ import org.h2.schema.SchemaObject;
import org.h2.table.Column;
import org.h2.table.IndexColumn;
import org.h2.table.Table;
import org.h2.table.TableFilter;
/**
* An index. Indexes are used to speed up searching data.
......@@ -59,6 +60,16 @@ public interface Index extends SchemaObject {
*/
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.
* There is one element per column in the search mask.
......
......@@ -18,6 +18,7 @@ import org.h2.result.SortOrder;
import org.h2.table.Column;
import org.h2.table.IndexColumn;
import org.h2.table.Table;
import org.h2.table.TableFilter;
import org.h2.value.Value;
import org.h2.value.ValueNull;
......@@ -26,7 +27,9 @@ import org.h2.value.ValueNull;
* and IN(SELECT ...) optimizations.
*/
public class IndexCursor implements Cursor {
private Session session;
private final TableFilter tableFilter;
private Index index;
private Table table;
private IndexColumn[] indexColumns;
......@@ -40,6 +43,10 @@ public class IndexCursor implements Cursor {
private ResultInterface inResult;
private HashSet<Value> inResultTested;
public IndexCursor(TableFilter filter) {
this.tableFilter = filter;
}
public void setIndex(Index index) {
this.index = index;
this.table = index.getTable();
......@@ -135,7 +142,7 @@ public class IndexCursor implements Cursor {
return;
}
if (!alwaysFalse) {
cursor = index.find(s, start, end);
cursor = index.find(tableFilter, start, end);
}
}
......@@ -265,7 +272,7 @@ public class IndexCursor implements Cursor {
start = table.getTemplateRow();
}
start.setValue(id, v);
cursor = index.find(session, start, start);
cursor = index.find(tableFilter, start, start);
}
public boolean previous() {
......
......@@ -18,6 +18,7 @@ import org.h2.table.Column;
import org.h2.table.IndexColumn;
import org.h2.table.RegularTable;
import org.h2.table.Table;
import org.h2.table.TableFilter;
import org.h2.value.Value;
import org.h2.value.ValueNull;
......@@ -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) {
synchronized (sync) {
Cursor baseCursor = base.find(session, first, last);
......
......@@ -13,6 +13,7 @@ import org.h2.result.Row;
import org.h2.result.SearchRow;
import org.h2.table.IndexColumn;
import org.h2.table.RegularTable;
import org.h2.table.TableFilter;
import org.h2.value.Value;
import org.h2.value.ValueNull;
......@@ -275,7 +276,15 @@ public class TreeIndex extends BaseIndex {
return result;
}
public Cursor find(TableFilter filter, SearchRow first, SearchRow last) {
return find(first, last);
}
public Cursor find(Session session, SearchRow first, SearchRow last) {
return find(first, last);
}
public Cursor find(SearchRow first, SearchRow last) {
if (first == null) {
TreeNode x = root, n;
while (x != null) {
......
......@@ -19,6 +19,7 @@ import org.h2.util.New;
* on the order the tables are accessed.
*/
public class Plan {
private final TableFilter[] filters;
private final HashMap<TableFilter, PlanItem> planItems = New.hashMap();
private final Expression[] allConditions;
......@@ -82,7 +83,7 @@ public class Plan {
for (int i = 0; i < allFilters.length; i++) {
TableFilter f = allFilters[i];
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,
// otherwise the expression is calculated twice unnecessarily
// (not that bad but not optimal)
......
......@@ -116,7 +116,7 @@ public class TableFilter implements ColumnResolver {
this.table = table;
this.alias = alias;
this.select = select;
this.cursor = new IndexCursor();
this.cursor = new IndexCursor(this);
if (!rightsChecked) {
session.getUser().checkRight(table, Right.SELECT);
}
......@@ -967,6 +967,10 @@ public class TableFilter implements ColumnResolver {
return evaluatable;
}
public Session getSession() {
return session;
}
/**
* A visitor for table filters.
*/
......
......@@ -14,6 +14,7 @@ import java.util.ArrayList;
import org.h2.api.TableEngine;
import org.h2.command.ddl.CreateTableData;
import org.h2.engine.Session;
import org.h2.expression.Expression;
import org.h2.index.BaseIndex;
import org.h2.index.Cursor;
import org.h2.index.SingleRowCursor;
......@@ -24,7 +25,12 @@ import org.h2.result.SearchRow;
import org.h2.table.IndexColumn;
import org.h2.table.TableBase;
import org.h2.table.Table;
import org.h2.table.TableFilter;
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.
......@@ -46,6 +52,25 @@ public class TestTableEngines extends TestBase {
if (config.mvcc) {
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");
Connection conn = getConnection("tableEngine");
......@@ -112,7 +137,7 @@ public class TestTableEngines extends TestBase {
/**
* A scan index for one row.
*/
private class Scan extends BaseIndex {
public class Scan extends BaseIndex {
Scan(Table table) {
initBaseIndex(table, table.getId(), table.getName() + "_SCAN",
......@@ -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) {
super(data);
......@@ -293,6 +318,72 @@ public class TestTableEngines extends TestBase {
public OneRowTable createTable(CreateTableData 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 到此讨论。请谨慎行事。
请先完成此评论的编辑!
注册 或者 后发表评论