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 {
return false;
}
@Override
public boolean isFindUsingFullTableScan() {
return false;
}
@Override
public Cursor find(TableFilter filter, SearchRow first, SearchRow last) {
......
......@@ -6,45 +6,23 @@
package org.h2.index;
import org.h2.engine.Session;
import org.h2.message.DbException;
import org.h2.result.ResultInterface;
import org.h2.result.Row;
import org.h2.result.SearchRow;
import org.h2.value.Value;
/**
* 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 Value[] values;
private Row row;
FunctionCursor(Session session, ResultInterface result) {
this.session = session;
FunctionCursor(FunctionIndex index, SearchRow first, SearchRow last, Session session, ResultInterface result) {
super(index, first, last, session);
this.result = result;
}
@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() {
boolean nextImpl() {
row = null;
if (result != null && result.next()) {
values = result.currentRow();
......@@ -54,9 +32,4 @@ public class FunctionCursor implements Cursor {
return values != null;
}
@Override
public boolean previous() {
throw DbException.throwInternalError(toString());
}
}
......@@ -10,7 +10,6 @@ import java.sql.ResultSetMetaData;
import java.sql.SQLException;
import org.h2.engine.Session;
import org.h2.message.DbException;
import org.h2.result.Row;
import org.h2.result.SearchRow;
import org.h2.value.DataType;
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.
*/
public class FunctionCursorResultSet implements Cursor {
public class FunctionCursorResultSet extends AbstractFunctionCursor {
private final Session session;
private final ResultSet result;
private final ResultSetMetaData meta;
private Value[] values;
private Row row;
FunctionCursorResultSet(Session session, ResultSet result) {
this.session = session;
FunctionCursorResultSet(FunctionIndex index, SearchRow first, SearchRow last, Session session, ResultSet result) {
super(index, first, last, session);
this.result = result;
try {
this.meta = result.getMetaData();
......@@ -37,23 +33,7 @@ public class FunctionCursorResultSet implements Cursor {
}
@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() {
boolean nextImpl() {
row = null;
try {
if (result != null && result.next()) {
......@@ -72,9 +52,4 @@ public class FunctionCursorResultSet implements Cursor {
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;
import org.h2.table.TableFilter;
/**
* An index for a function that returns a result set. This index can only scan
* through all rows, search is not supported.
* An index for a function that returns a result set. Search in this index
* performs scan over all rows and should be avoided.
*/
public class FunctionIndex extends BaseIndex {
......@@ -44,12 +44,17 @@ public class FunctionIndex extends BaseIndex {
throw DbException.getUnsupportedException("ALIAS");
}
@Override
public boolean isFindUsingFullTableScan() {
return true;
}
@Override
public Cursor find(Session session, SearchRow first, SearchRow last) {
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));
}
......
......@@ -51,6 +51,15 @@ public interface Index extends SchemaObject {
*/
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
* result.
......
......@@ -89,6 +89,11 @@ public class IndexCursor implements Cursor {
alwaysFalse = true;
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();
if (condition.getCompareType() == Comparison.IN_LIST) {
if (start == null && end == null) {
......
......@@ -72,6 +72,11 @@ public class MultiVersionIndex implements Index {
}
}
@Override
public boolean isFindUsingFullTableScan() {
return base.isFindUsingFullTableScan();
}
@Override
public Cursor find(TableFilter filter, SearchRow first, SearchRow last) {
synchronized (sync) {
......
......@@ -10,19 +10,25 @@ import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import java.sql.Types;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Random;
import java.util.concurrent.atomic.AtomicInteger;
import org.h2.api.ErrorCode;
import org.h2.command.dml.Select;
import org.h2.result.SortOrder;
import org.h2.test.TestBase;
import org.h2.tools.SimpleResultSet;
import org.h2.value.ValueInt;
/**
* Index tests.
*/
public class TestIndex extends TestBase {
private static int testFunctionIndexCounter;
private Connection conn;
private Statement stat;
private final Random random = new Random();
......@@ -89,6 +95,8 @@ public class TestIndex extends TestBase {
testMultiColumnHashIndex();
testFunctionIndex();
conn.close();
deleteDb("index");
}
......@@ -702,4 +710,39 @@ public class TestIndex extends TestBase {
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 到此讨论。请谨慎行事。
请先完成此评论的编辑!
注册 或者 后发表评论