Unverified 提交 801913db authored 作者: Noel Grandin's avatar Noel Grandin 提交者: GitHub

Merge pull request #903 from katzyn/function_cursor

Handle first and last arguments of FunctionIndex.find()
/*
* Copyright 2004-2018 H2 Group. Multiple-Licensed under the MPL 2.0,
* and the EPL 1.0 (http://h2database.com/html/license.html).
* Initial Developer: H2 Group
*/
package org.h2.index;
import org.h2.engine.Session;
import org.h2.message.DbException;
import org.h2.result.Row;
import org.h2.result.SearchRow;
import org.h2.value.Value;
/**
* Abstract function cursor.
*/
abstract class AbstractFunctionCursor implements Cursor {
private final FunctionIndex index;
private final SearchRow first;
private final SearchRow last;
final Session session;
Value[] values;
Row row;
/**
* @param index
* index
* @param first
* first row
* @param last
* last row
* @param session
* session
*/
AbstractFunctionCursor(FunctionIndex index, SearchRow first, SearchRow last, Session session) {
this.index = index;
this.first = first;
this.last = last;
this.session = session;
}
@Override
public Row get() {
if (values == null) {
return null;
}
if (row == null) {
row = session.createRow(values, 1);
}
return row;
}
@Override
public SearchRow getSearchRow() {
return get();
}
@Override
public boolean next() {
final SearchRow first = this.first, last = this.last;
if (first == null && last == null) {
return nextImpl();
}
while (nextImpl()) {
Row current = get();
if (first != null) {
int comp = index.compareRows(current, first);
if (comp < 0) {
continue;
}
}
if (last != null) {
int comp = index.compareRows(current, last);
if (comp > 0) {
continue;
}
}
return true;
}
return false;
}
abstract boolean nextImpl();
@Override
public boolean previous() {
throw DbException.throwInternalError(toString());
}
}
...@@ -120,6 +120,10 @@ public abstract class BaseIndex extends SchemaObjectBase implements Index { ...@@ -120,6 +120,10 @@ public abstract class BaseIndex extends SchemaObjectBase implements Index {
return false; return false;
} }
@Override
public boolean isFindUsingFullTableScan() {
return false;
}
@Override @Override
public Cursor find(TableFilter filter, SearchRow first, SearchRow last) { public Cursor find(TableFilter filter, SearchRow first, SearchRow last) {
......
...@@ -6,45 +6,23 @@ ...@@ -6,45 +6,23 @@
package org.h2.index; package org.h2.index;
import org.h2.engine.Session; import org.h2.engine.Session;
import org.h2.message.DbException;
import org.h2.result.ResultInterface; import org.h2.result.ResultInterface;
import org.h2.result.Row;
import org.h2.result.SearchRow; import org.h2.result.SearchRow;
import org.h2.value.Value;
/** /**
* A cursor for a function that returns a result. * A cursor for a function that returns a result.
*/ */
public class FunctionCursor implements Cursor { public class FunctionCursor extends AbstractFunctionCursor {
private final Session session;
private final ResultInterface result; private final ResultInterface result;
private Value[] values;
private Row row;
FunctionCursor(Session session, ResultInterface result) { FunctionCursor(FunctionIndex index, SearchRow first, SearchRow last, Session session, ResultInterface result) {
this.session = session; super(index, first, last, session);
this.result = result; this.result = result;
} }
@Override @Override
public Row get() { boolean nextImpl() {
if (values == null) {
return null;
}
if (row == null) {
row = session.createRow(values, 1);
}
return row;
}
@Override
public SearchRow getSearchRow() {
return get();
}
@Override
public boolean next() {
row = null; row = null;
if (result != null && result.next()) { if (result != null && result.next()) {
values = result.currentRow(); values = result.currentRow();
...@@ -54,9 +32,4 @@ public class FunctionCursor implements Cursor { ...@@ -54,9 +32,4 @@ public class FunctionCursor implements Cursor {
return values != null; return values != null;
} }
@Override
public boolean previous() {
throw DbException.throwInternalError(toString());
}
} }
...@@ -10,7 +10,6 @@ import java.sql.ResultSetMetaData; ...@@ -10,7 +10,6 @@ import java.sql.ResultSetMetaData;
import java.sql.SQLException; import java.sql.SQLException;
import org.h2.engine.Session; import org.h2.engine.Session;
import org.h2.message.DbException; import org.h2.message.DbException;
import org.h2.result.Row;
import org.h2.result.SearchRow; import org.h2.result.SearchRow;
import org.h2.value.DataType; import org.h2.value.DataType;
import org.h2.value.Value; import org.h2.value.Value;
...@@ -18,16 +17,13 @@ import org.h2.value.Value; ...@@ -18,16 +17,13 @@ import org.h2.value.Value;
/** /**
* A cursor for a function that returns a JDBC result set. * A cursor for a function that returns a JDBC result set.
*/ */
public class FunctionCursorResultSet implements Cursor { public class FunctionCursorResultSet extends AbstractFunctionCursor {
private final Session session;
private final ResultSet result; private final ResultSet result;
private final ResultSetMetaData meta; private final ResultSetMetaData meta;
private Value[] values;
private Row row;
FunctionCursorResultSet(Session session, ResultSet result) { FunctionCursorResultSet(FunctionIndex index, SearchRow first, SearchRow last, Session session, ResultSet result) {
this.session = session; super(index, first, last, session);
this.result = result; this.result = result;
try { try {
this.meta = result.getMetaData(); this.meta = result.getMetaData();
...@@ -37,23 +33,7 @@ public class FunctionCursorResultSet implements Cursor { ...@@ -37,23 +33,7 @@ public class FunctionCursorResultSet implements Cursor {
} }
@Override @Override
public Row get() { boolean nextImpl() {
if (values == null) {
return null;
}
if (row == null) {
row = session.createRow(values, 1);
}
return row;
}
@Override
public SearchRow getSearchRow() {
return get();
}
@Override
public boolean next() {
row = null; row = null;
try { try {
if (result != null && result.next()) { if (result != null && result.next()) {
...@@ -72,9 +52,4 @@ public class FunctionCursorResultSet implements Cursor { ...@@ -72,9 +52,4 @@ public class FunctionCursorResultSet implements Cursor {
return values != null; return values != null;
} }
@Override
public boolean previous() {
throw DbException.throwInternalError(toString());
}
} }
\ No newline at end of file
...@@ -17,8 +17,8 @@ import org.h2.table.IndexColumn; ...@@ -17,8 +17,8 @@ import org.h2.table.IndexColumn;
import org.h2.table.TableFilter; import org.h2.table.TableFilter;
/** /**
* An index for a function that returns a result set. This index can only scan * An index for a function that returns a result set. Search in this index
* through all rows, search is not supported. * performs scan over all rows and should be avoided.
*/ */
public class FunctionIndex extends BaseIndex { public class FunctionIndex extends BaseIndex {
...@@ -44,12 +44,17 @@ public class FunctionIndex extends BaseIndex { ...@@ -44,12 +44,17 @@ public class FunctionIndex extends BaseIndex {
throw DbException.getUnsupportedException("ALIAS"); throw DbException.getUnsupportedException("ALIAS");
} }
@Override
public boolean isFindUsingFullTableScan() {
return true;
}
@Override @Override
public Cursor find(Session session, SearchRow first, SearchRow last) { public Cursor find(Session session, SearchRow first, SearchRow last) {
if (functionTable.isBufferResultSetToLocalTemp()) { if (functionTable.isBufferResultSetToLocalTemp()) {
return new FunctionCursor(session, functionTable.getResult(session)); return new FunctionCursor(this, first, last, session, functionTable.getResult(session));
} }
return new FunctionCursorResultSet(session, return new FunctionCursorResultSet(this, first, last, session,
functionTable.getResultSet(session)); functionTable.getResultSet(session));
} }
......
...@@ -51,6 +51,15 @@ public interface Index extends SchemaObject { ...@@ -51,6 +51,15 @@ public interface Index extends SchemaObject {
*/ */
void remove(Session session, Row row); void remove(Session session, Row row);
/**
* Returns {@code true} if {@code find()} implementation performs scan over all
* index, {@code false} if {@code find()} performs the fast lookup.
*
* @return {@code true} if {@code find()} implementation performs scan over all
* index, {@code false} if {@code find()} performs the fast lookup
*/
boolean isFindUsingFullTableScan();
/** /**
* Find a row or a list of rows and create a cursor to iterate over the * Find a row or a list of rows and create a cursor to iterate over the
* result. * result.
......
...@@ -89,6 +89,11 @@ public class IndexCursor implements Cursor { ...@@ -89,6 +89,11 @@ public class IndexCursor implements Cursor {
alwaysFalse = true; alwaysFalse = true;
break; break;
} }
// If index can perform only full table scan do not try to use it for regular
// lookups, each such lookup will perform an own table scan.
if (index.isFindUsingFullTableScan()) {
continue;
}
Column column = condition.getColumn(); Column column = condition.getColumn();
if (condition.getCompareType() == Comparison.IN_LIST) { if (condition.getCompareType() == Comparison.IN_LIST) {
if (start == null && end == null) { if (start == null && end == null) {
......
...@@ -72,6 +72,11 @@ public class MultiVersionIndex implements Index { ...@@ -72,6 +72,11 @@ public class MultiVersionIndex implements Index {
} }
} }
@Override
public boolean isFindUsingFullTableScan() {
return base.isFindUsingFullTableScan();
}
@Override @Override
public Cursor find(TableFilter filter, SearchRow first, SearchRow last) { public Cursor find(TableFilter filter, SearchRow first, SearchRow last) {
synchronized (sync) { synchronized (sync) {
......
...@@ -10,19 +10,25 @@ import java.sql.PreparedStatement; ...@@ -10,19 +10,25 @@ import java.sql.PreparedStatement;
import java.sql.ResultSet; import java.sql.ResultSet;
import java.sql.SQLException; import java.sql.SQLException;
import java.sql.Statement; import java.sql.Statement;
import java.sql.Types;
import java.util.HashMap; import java.util.HashMap;
import java.util.HashSet; import java.util.HashSet;
import java.util.Random; import java.util.Random;
import java.util.concurrent.atomic.AtomicInteger; import java.util.concurrent.atomic.AtomicInteger;
import org.h2.api.ErrorCode; import org.h2.api.ErrorCode;
import org.h2.command.dml.Select;
import org.h2.result.SortOrder; import org.h2.result.SortOrder;
import org.h2.test.TestBase; import org.h2.test.TestBase;
import org.h2.tools.SimpleResultSet;
import org.h2.value.ValueInt;
/** /**
* Index tests. * Index tests.
*/ */
public class TestIndex extends TestBase { public class TestIndex extends TestBase {
private static int testFunctionIndexCounter;
private Connection conn; private Connection conn;
private Statement stat; private Statement stat;
private final Random random = new Random(); private final Random random = new Random();
...@@ -89,6 +95,8 @@ public class TestIndex extends TestBase { ...@@ -89,6 +95,8 @@ public class TestIndex extends TestBase {
testMultiColumnHashIndex(); testMultiColumnHashIndex();
testFunctionIndex();
conn.close(); conn.close();
deleteDb("index"); deleteDb("index");
} }
...@@ -702,4 +710,39 @@ public class TestIndex extends TestBase { ...@@ -702,4 +710,39 @@ public class TestIndex extends TestBase {
trace("---done---"); trace("---done---");
} }
public static ResultSet testFunctionIndexFunction() {
// There are additional callers like JdbcConnection.prepareCommand() and
// CommandContainer.recompileIfReqired()
for (StackTraceElement element : Thread.currentThread().getStackTrace()) {
if (element.getClassName().startsWith(Select.class.getName())) {
testFunctionIndexCounter++;
break;
}
}
SimpleResultSet rs = new SimpleResultSet();
rs.addColumn("ID", Types.INTEGER, ValueInt.PRECISION, 0);
rs.addColumn("VALUE", Types.INTEGER, ValueInt.PRECISION, 0);
rs.addRow(1, 10);
rs.addRow(2, 20);
rs.addRow(3, 30);
return rs;
}
private void testFunctionIndex() throws SQLException {
testFunctionIndexCounter = 0;
stat.execute("CREATE ALIAS TEST_INDEX FOR \"" + TestIndex.class.getName() + ".testFunctionIndexFunction\"");
try (ResultSet rs = stat.executeQuery("SELECT * FROM TEST_INDEX() WHERE ID = 1 OR ID = 3")) {
assertTrue(rs.next());
assertEquals(1, rs.getInt(1));
assertEquals(10, rs.getInt(2));
assertTrue(rs.next());
assertEquals(3, rs.getInt(1));
assertEquals(30, rs.getInt(2));
assertFalse(rs.next());
} finally {
stat.execute("DROP ALIAS TEST_INDEX");
}
assertEquals(1, testFunctionIndexCounter);
}
} }
Markdown 格式
0%
您添加了 0 到此讨论。请谨慎行事。
请先完成此评论的编辑!
注册 或者 后发表评论