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

Each table now has a pseudo-column "_ROWID_" to get the unique row id (only…

Each table now has a pseudo-column "_ROWID_" to get the unique row id (only enabled for version 1.3.x).
上级 ae220fd9
......@@ -445,6 +445,13 @@ public class Select extends Query {
}
}
}
if (sortCols.length == 1 && sortCols[0].getColumnId() == -1) {
// special case: order by _ROWID_
Index index = topTableFilter.getTable().getScanIndex(session);
if (index.isRowIdIndex()) {
return index;
}
}
return null;
}
......@@ -825,7 +832,7 @@ public class Select extends Query {
if (current.getIndexType().isScan() || current == index) {
topTableFilter.setIndex(index);
if (!topTableFilter.hasInComparisons()) {
// in(select ...) and in(1,2,3) my return the key is another order
// in(select ...) and in(1,2,3) my return the key in another order
sortUsingIndex = true;
}
} else if (index.getIndexColumns().length >= current.getIndexColumns().length) {
......
......@@ -61,6 +61,10 @@ public class SelectListColumnResolver implements ColumnResolver {
return null;
}
public Column getRowIdColumn() {
return null;
}
public String getTableAlias() {
return null;
}
......
......@@ -272,6 +272,13 @@ public class DbSettings extends SettingsBase {
*/
public final int reconnectCheckDelay = get("RECONNECT_CHECK_DELAY", 200);
/**
* Database setting <code>ROWID</code>
* (default: false for version 1.2, true for version 1.3).<br />
* If set, each table has a pseudo-column _ROWID_.
*/
public final boolean rowId = get("ROWID", Constants.VERSION_MINOR >= 3);
/**
* Database setting <code>SELECT_FOR_UPDATE_MVCC</code>
* (default: false).<br />
......
......@@ -28,6 +28,7 @@ import org.h2.value.ValueBoolean;
* A expression that represents a column of a table or view.
*/
public class ExpressionColumn extends Expression {
private Database database;
private String schemaName;
private String tableAlias;
......@@ -82,6 +83,13 @@ public class ExpressionColumn extends Expression {
return;
}
}
if (Column.ROWID.equals(columnName)) {
Column col = resolver.getRowIdColumn();
if (col != null) {
mapColumn(resolver, col, level);
return;
}
}
Column[] columns = resolver.getSystemColumns();
for (int i = 0; columns != null && i < columns.length; i++) {
Column col = columns[i];
......
......@@ -379,4 +379,8 @@ public abstract class BaseIndex extends SchemaObjectBase implements Index {
return table.isHidden();
}
public boolean isRowIdIndex() {
return false;
}
}
......@@ -267,4 +267,11 @@ public interface Index extends SchemaObject {
*/
Row getRow(Session session, long key);
/**
* Does this index support lookup by row id?
*
* @return true if it does
*/
boolean isRowIdIndex();
}
......@@ -98,13 +98,15 @@ public class IndexCursor implements Cursor {
boolean isStart = condition.isStart();
boolean isEnd = condition.isEnd();
int id = column.getColumnId();
IndexColumn idxCol = indexColumns[id];
if (idxCol != null && (idxCol.sortType & SortOrder.DESCENDING) != 0) {
// if the index column is sorted the other way, we swap end and start
// NULLS_FIRST / NULLS_LAST is not a problem, as nulls never match anyway
boolean temp = isStart;
isStart = isEnd;
isEnd = temp;
if (id >= 0) {
IndexColumn idxCol = indexColumns[id];
if (idxCol != null && (idxCol.sortType & SortOrder.DESCENDING) != 0) {
// if the index column is sorted the other way, we swap end and start
// NULLS_FIRST / NULLS_LAST is not a problem, as nulls never match anyway
boolean temp = isStart;
isStart = isEnd;
isEnd = temp;
}
}
if (isStart) {
start = getSearchRow(start, id, v, true);
......@@ -160,7 +162,11 @@ public class IndexCursor implements Cursor {
} else {
v = getMax(row.getValue(id), v, max);
}
row.setValue(id, v);
if (id < 0) {
row.setKey(v.getLong());
} else {
row.setValue(id, v);
}
return row;
}
......
......@@ -341,4 +341,8 @@ public class MultiVersionIndex implements Index {
return base.isHidden();
}
public boolean isRowIdIndex() {
return base.isRowIdIndex() && delta.isRowIdIndex();
}
}
......@@ -40,6 +40,11 @@ import org.h2.value.ValueUuid;
*/
public class Column {
/**
* The name of the rowid pseudo column.
*/
public static final String ROWID = "_ROWID_";
/**
* This column is not nullable.
*/
......
......@@ -34,10 +34,17 @@ public interface ColumnResolver {
/**
* Get the list of system columns, if any.
*
* @return the system columns
* @return the system columns or null
*/
Column[] getSystemColumns();
/**
* Get the row id pseudo column, if there is one.
*
* @return the row id column or null
*/
Column getRowIdColumn();
/**
* Get the schema name.
*
......
......@@ -225,4 +225,8 @@ public class FunctionTable extends Table {
return false;
}
public boolean hasRowIdColumn() {
return false;
}
}
......@@ -1791,4 +1791,8 @@ public class MetaTable extends Table {
return false;
}
public boolean hasRowIdColumn() {
return false;
}
}
......@@ -173,4 +173,8 @@ public class RangeTable extends Table {
return false;
}
public boolean hasRowIdColumn() {
return false;
}
}
......@@ -50,6 +50,7 @@ import org.h2.value.Value;
* indexes. There is at least one index, the scan index.
*/
public class RegularTable extends TableBase {
private Index scanIndex;
private long rowCount;
private volatile Session lockExclusive;
......@@ -727,4 +728,8 @@ public class RegularTable extends TableBase {
return true;
}
public boolean hasRowIdColumn() {
return true;
}
}
......@@ -56,6 +56,10 @@ public class SingleColumnResolver implements ColumnResolver {
return null;
}
public Column getRowIdColumn() {
return null;
}
public Expression optimize(ExpressionColumn expressionColumn, Column col) {
return expressionColumn;
}
......
......@@ -277,6 +277,13 @@ public abstract class Table extends SchemaObjectBase {
*/
public abstract boolean canDrop();
/**
* Check if this table has a row id column.
*
* @return true if it has
*/
public abstract boolean hasRowIdColumn();
/**
* Get the row count for this table.
*
......
......@@ -27,6 +27,7 @@ import org.h2.util.New;
import org.h2.util.StatementBuilder;
import org.h2.util.StringUtils;
import org.h2.value.Value;
import org.h2.value.ValueLong;
/**
* A table filter represents a table that is used in a query. There is one such
......@@ -34,6 +35,7 @@ import org.h2.value.Value;
* following query has 2 table filters: SELECT * FROM TEST T1, TEST T2.
*/
public class TableFilter implements ColumnResolver {
private static final int BEFORE_FIRST = 0, FOUND = 1, AFTER_LAST = 2, NULL_ROW = 3;
protected Session session;
......@@ -167,7 +169,9 @@ public class TableFilter implements ColumnResolver {
break;
}
int id = condition.getColumn().getColumnId();
masks[id] |= condition.getMask(indexConditions);
if (id >= 0) {
masks[id] |= condition.getMask(indexConditions);
}
}
}
item = table.getBestPlanItem(s, masks);
......@@ -243,9 +247,11 @@ public class TableFilter implements ColumnResolver {
IndexCondition condition = indexConditions.get(i);
if (!condition.isAlwaysFalse()) {
Column col = condition.getColumn();
if (index.getColumnIndex(col) < 0) {
indexConditions.remove(i);
i--;
if (col.getColumnId() >= 0) {
if (index.getColumnIndex(col) < 0) {
indexConditions.remove(i);
i--;
}
}
}
}
......@@ -828,11 +834,23 @@ public class TableFilter implements ColumnResolver {
return sys;
}
public Column getRowIdColumn() {
if (table.hasRowIdColumn() && session.getDatabase().getSettings().rowId) {
Column col = new Column(Column.ROWID, Value.LONG);
col.setTable(table, -1);
return col;
}
return null;
}
public Value getValue(Column column) {
if (currentSearchRow == null) {
return null;
}
int columnId = column.getColumnId();
if (columnId == -1) {
return ValueLong.get(currentSearchRow.getKey());
}
if (current == null) {
Value v = currentSearchRow.getValue(columnId);
if (v != null) {
......
......@@ -610,4 +610,8 @@ public class TableLink extends Table {
return null;
}
public boolean hasRowIdColumn() {
return false;
}
}
......@@ -408,4 +408,8 @@ public class TableView extends Table {
return tableExpression;
}
public boolean hasRowIdColumn() {
return false;
}
}
......@@ -36,6 +36,8 @@ public class TestOptimizations extends TestBase {
}
public void test() throws Exception {
deleteDb("optimizations");
testRowId();
testSortIndex();
testAutoAnalyze();
testInAndBetween();
......@@ -61,6 +63,45 @@ public class TestOptimizations extends TestBase {
deleteDb("optimizations");
}
private void testRowId() throws SQLException {
if (config.memory) {
return;
}
Connection conn = getConnection("optimizations");
Statement stat = conn.createStatement();
stat.execute("create table test(id int primary key, name varchar)");
stat.execute("insert into test values(0, 'Hello')");
stat.execute("insert into test values(3, 'Hello')");
stat.execute("insert into test values(2, 'Hello')");
ResultSet rs;
rs = stat.executeQuery("explain select * from test where _rowid_ = 2");
rs.next();
assertContains(rs.getString(1), ".tableScan: _ROWID_ =");
rs = stat.executeQuery("explain select * from test where _rowid_ > 2");
rs.next();
assertContains(rs.getString(1), ".tableScan: _ROWID_ >");
rs = stat.executeQuery("explain select * from test order by _rowid_");
rs.next();
assertContains(rs.getString(1), "/* index sorted */");
rs = stat.executeQuery("select _rowid_, * from test order by _rowid_");
rs.next();
assertEquals(0, rs.getInt(1));
assertEquals(0, rs.getInt(2));
rs.next();
assertEquals(2, rs.getInt(1));
assertEquals(2, rs.getInt(2));
rs.next();
assertEquals(3, rs.getInt(1));
assertEquals(3, rs.getInt(2));
stat.execute("drop table test");
conn.close();
}
private void testSortIndex() throws SQLException {
Connection conn = getConnection("optimizations");
Statement stat = conn.createStatement();
......
......@@ -293,6 +293,11 @@ public class TestTableEngines extends TestBase {
// do nothing
}
@Override
public boolean hasRowIdColumn() {
return false;
}
}
/**
......
......@@ -281,7 +281,7 @@ public class TestNestedJoins extends TestBase {
rs = stat.executeQuery("explain select * from test a inner join test b on a.id=b.id left outer join o on o.id=a.id where b.x=1");
assertTrue(rs.next());
sql = rs.getString(1);
int todo;
// TODO Support optimizing queries with both inner and outer joins
// assertTrue("using table scan", sql.indexOf("tableScan") < 0);
stat.execute("drop table test");
stat.execute("drop table o");
......
......@@ -53,7 +53,21 @@ public class TestSampleApps extends TestBase {
org.h2.samples.CsvSample.class);
testApp("",
org.h2.samples.CachedPreparedStatements.class);
testApp("2 is prime\n3 is prime\n5 is prime\n7 is prime\n11 is prime\n13 is prime\n17 is prime\n19 is prime\n30\n20\n0/0\n0/1\n1/0\n1/1",
testApp("2 is prime\n" +
"3 is prime\n" +
"5 is prime\n" +
"7 is prime\n" +
"11 is prime\n" +
"13 is prime\n" +
"17 is prime\n" +
"19 is prime\n" +
"30\n" +
"20\n" +
"0/0\n" +
"0/1\n" +
"1/0\n" +
"1/1\n" +
"10",
org.h2.samples.Function.class);
// Not compatible with PostgreSQL JDBC driver (throws a NullPointerException)
//testApp(org.h2.samples.SecurePassword.class, null, "Joe");
......
Markdown 格式
0%
您添加了 0 到此讨论。请谨慎行事。
请先完成此评论的编辑!
注册 或者 后发表评论