提交 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 { ...@@ -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; return null;
} }
...@@ -825,7 +832,7 @@ public class Select extends Query { ...@@ -825,7 +832,7 @@ public class Select extends Query {
if (current.getIndexType().isScan() || current == index) { if (current.getIndexType().isScan() || current == index) {
topTableFilter.setIndex(index); topTableFilter.setIndex(index);
if (!topTableFilter.hasInComparisons()) { 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; sortUsingIndex = true;
} }
} else if (index.getIndexColumns().length >= current.getIndexColumns().length) { } else if (index.getIndexColumns().length >= current.getIndexColumns().length) {
......
...@@ -61,6 +61,10 @@ public class SelectListColumnResolver implements ColumnResolver { ...@@ -61,6 +61,10 @@ public class SelectListColumnResolver implements ColumnResolver {
return null; return null;
} }
public Column getRowIdColumn() {
return null;
}
public String getTableAlias() { public String getTableAlias() {
return null; return null;
} }
......
...@@ -272,6 +272,13 @@ public class DbSettings extends SettingsBase { ...@@ -272,6 +272,13 @@ public class DbSettings extends SettingsBase {
*/ */
public final int reconnectCheckDelay = get("RECONNECT_CHECK_DELAY", 200); 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> * Database setting <code>SELECT_FOR_UPDATE_MVCC</code>
* (default: false).<br /> * (default: false).<br />
......
...@@ -28,6 +28,7 @@ import org.h2.value.ValueBoolean; ...@@ -28,6 +28,7 @@ import org.h2.value.ValueBoolean;
* A expression that represents a column of a table or view. * A expression that represents a column of a table or view.
*/ */
public class ExpressionColumn extends Expression { public class ExpressionColumn extends Expression {
private Database database; private Database database;
private String schemaName; private String schemaName;
private String tableAlias; private String tableAlias;
...@@ -82,6 +83,13 @@ public class ExpressionColumn extends Expression { ...@@ -82,6 +83,13 @@ public class ExpressionColumn extends Expression {
return; return;
} }
} }
if (Column.ROWID.equals(columnName)) {
Column col = resolver.getRowIdColumn();
if (col != null) {
mapColumn(resolver, col, level);
return;
}
}
Column[] columns = resolver.getSystemColumns(); Column[] columns = resolver.getSystemColumns();
for (int i = 0; columns != null && i < columns.length; i++) { for (int i = 0; columns != null && i < columns.length; i++) {
Column col = columns[i]; Column col = columns[i];
......
...@@ -379,4 +379,8 @@ public abstract class BaseIndex extends SchemaObjectBase implements Index { ...@@ -379,4 +379,8 @@ public abstract class BaseIndex extends SchemaObjectBase implements Index {
return table.isHidden(); return table.isHidden();
} }
public boolean isRowIdIndex() {
return false;
}
} }
...@@ -267,4 +267,11 @@ public interface Index extends SchemaObject { ...@@ -267,4 +267,11 @@ public interface Index extends SchemaObject {
*/ */
Row getRow(Session session, long key); 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 { ...@@ -98,13 +98,15 @@ public class IndexCursor implements Cursor {
boolean isStart = condition.isStart(); boolean isStart = condition.isStart();
boolean isEnd = condition.isEnd(); boolean isEnd = condition.isEnd();
int id = column.getColumnId(); int id = column.getColumnId();
IndexColumn idxCol = indexColumns[id]; if (id >= 0) {
if (idxCol != null && (idxCol.sortType & SortOrder.DESCENDING) != 0) { IndexColumn idxCol = indexColumns[id];
// if the index column is sorted the other way, we swap end and start if (idxCol != null && (idxCol.sortType & SortOrder.DESCENDING) != 0) {
// NULLS_FIRST / NULLS_LAST is not a problem, as nulls never match anyway // if the index column is sorted the other way, we swap end and start
boolean temp = isStart; // NULLS_FIRST / NULLS_LAST is not a problem, as nulls never match anyway
isStart = isEnd; boolean temp = isStart;
isEnd = temp; isStart = isEnd;
isEnd = temp;
}
} }
if (isStart) { if (isStart) {
start = getSearchRow(start, id, v, true); start = getSearchRow(start, id, v, true);
...@@ -160,7 +162,11 @@ public class IndexCursor implements Cursor { ...@@ -160,7 +162,11 @@ public class IndexCursor implements Cursor {
} else { } else {
v = getMax(row.getValue(id), v, max); 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; return row;
} }
......
...@@ -341,4 +341,8 @@ public class MultiVersionIndex implements Index { ...@@ -341,4 +341,8 @@ public class MultiVersionIndex implements Index {
return base.isHidden(); return base.isHidden();
} }
public boolean isRowIdIndex() {
return base.isRowIdIndex() && delta.isRowIdIndex();
}
} }
...@@ -40,6 +40,11 @@ import org.h2.value.ValueUuid; ...@@ -40,6 +40,11 @@ import org.h2.value.ValueUuid;
*/ */
public class Column { public class Column {
/**
* The name of the rowid pseudo column.
*/
public static final String ROWID = "_ROWID_";
/** /**
* This column is not nullable. * This column is not nullable.
*/ */
......
...@@ -34,10 +34,17 @@ public interface ColumnResolver { ...@@ -34,10 +34,17 @@ public interface ColumnResolver {
/** /**
* Get the list of system columns, if any. * Get the list of system columns, if any.
* *
* @return the system columns * @return the system columns or null
*/ */
Column[] getSystemColumns(); 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. * Get the schema name.
* *
......
...@@ -225,4 +225,8 @@ public class FunctionTable extends Table { ...@@ -225,4 +225,8 @@ public class FunctionTable extends Table {
return false; return false;
} }
public boolean hasRowIdColumn() {
return false;
}
} }
...@@ -1791,4 +1791,8 @@ public class MetaTable extends Table { ...@@ -1791,4 +1791,8 @@ public class MetaTable extends Table {
return false; return false;
} }
public boolean hasRowIdColumn() {
return false;
}
} }
...@@ -173,4 +173,8 @@ public class RangeTable extends Table { ...@@ -173,4 +173,8 @@ public class RangeTable extends Table {
return false; return false;
} }
public boolean hasRowIdColumn() {
return false;
}
} }
...@@ -50,6 +50,7 @@ import org.h2.value.Value; ...@@ -50,6 +50,7 @@ import org.h2.value.Value;
* indexes. There is at least one index, the scan index. * indexes. There is at least one index, the scan index.
*/ */
public class RegularTable extends TableBase { public class RegularTable extends TableBase {
private Index scanIndex; private Index scanIndex;
private long rowCount; private long rowCount;
private volatile Session lockExclusive; private volatile Session lockExclusive;
...@@ -727,4 +728,8 @@ public class RegularTable extends TableBase { ...@@ -727,4 +728,8 @@ public class RegularTable extends TableBase {
return true; return true;
} }
public boolean hasRowIdColumn() {
return true;
}
} }
...@@ -56,6 +56,10 @@ public class SingleColumnResolver implements ColumnResolver { ...@@ -56,6 +56,10 @@ public class SingleColumnResolver implements ColumnResolver {
return null; return null;
} }
public Column getRowIdColumn() {
return null;
}
public Expression optimize(ExpressionColumn expressionColumn, Column col) { public Expression optimize(ExpressionColumn expressionColumn, Column col) {
return expressionColumn; return expressionColumn;
} }
......
...@@ -277,6 +277,13 @@ public abstract class Table extends SchemaObjectBase { ...@@ -277,6 +277,13 @@ public abstract class Table extends SchemaObjectBase {
*/ */
public abstract boolean canDrop(); 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. * Get the row count for this table.
* *
......
...@@ -27,6 +27,7 @@ import org.h2.util.New; ...@@ -27,6 +27,7 @@ import org.h2.util.New;
import org.h2.util.StatementBuilder; import org.h2.util.StatementBuilder;
import org.h2.util.StringUtils; import org.h2.util.StringUtils;
import org.h2.value.Value; 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 * 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; ...@@ -34,6 +35,7 @@ import org.h2.value.Value;
* following query has 2 table filters: SELECT * FROM TEST T1, TEST T2. * following query has 2 table filters: SELECT * FROM TEST T1, TEST T2.
*/ */
public class TableFilter implements ColumnResolver { public class TableFilter implements ColumnResolver {
private static final int BEFORE_FIRST = 0, FOUND = 1, AFTER_LAST = 2, NULL_ROW = 3; private static final int BEFORE_FIRST = 0, FOUND = 1, AFTER_LAST = 2, NULL_ROW = 3;
protected Session session; protected Session session;
...@@ -167,7 +169,9 @@ public class TableFilter implements ColumnResolver { ...@@ -167,7 +169,9 @@ public class TableFilter implements ColumnResolver {
break; break;
} }
int id = condition.getColumn().getColumnId(); int id = condition.getColumn().getColumnId();
masks[id] |= condition.getMask(indexConditions); if (id >= 0) {
masks[id] |= condition.getMask(indexConditions);
}
} }
} }
item = table.getBestPlanItem(s, masks); item = table.getBestPlanItem(s, masks);
...@@ -243,9 +247,11 @@ public class TableFilter implements ColumnResolver { ...@@ -243,9 +247,11 @@ public class TableFilter implements ColumnResolver {
IndexCondition condition = indexConditions.get(i); IndexCondition condition = indexConditions.get(i);
if (!condition.isAlwaysFalse()) { if (!condition.isAlwaysFalse()) {
Column col = condition.getColumn(); Column col = condition.getColumn();
if (index.getColumnIndex(col) < 0) { if (col.getColumnId() >= 0) {
indexConditions.remove(i); if (index.getColumnIndex(col) < 0) {
i--; indexConditions.remove(i);
i--;
}
} }
} }
} }
...@@ -828,11 +834,23 @@ public class TableFilter implements ColumnResolver { ...@@ -828,11 +834,23 @@ public class TableFilter implements ColumnResolver {
return sys; 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) { public Value getValue(Column column) {
if (currentSearchRow == null) { if (currentSearchRow == null) {
return null; return null;
} }
int columnId = column.getColumnId(); int columnId = column.getColumnId();
if (columnId == -1) {
return ValueLong.get(currentSearchRow.getKey());
}
if (current == null) { if (current == null) {
Value v = currentSearchRow.getValue(columnId); Value v = currentSearchRow.getValue(columnId);
if (v != null) { if (v != null) {
......
...@@ -610,4 +610,8 @@ public class TableLink extends Table { ...@@ -610,4 +610,8 @@ public class TableLink extends Table {
return null; return null;
} }
public boolean hasRowIdColumn() {
return false;
}
} }
...@@ -408,4 +408,8 @@ public class TableView extends Table { ...@@ -408,4 +408,8 @@ public class TableView extends Table {
return tableExpression; return tableExpression;
} }
public boolean hasRowIdColumn() {
return false;
}
} }
...@@ -36,6 +36,8 @@ public class TestOptimizations extends TestBase { ...@@ -36,6 +36,8 @@ public class TestOptimizations extends TestBase {
} }
public void test() throws Exception { public void test() throws Exception {
deleteDb("optimizations");
testRowId();
testSortIndex(); testSortIndex();
testAutoAnalyze(); testAutoAnalyze();
testInAndBetween(); testInAndBetween();
...@@ -61,6 +63,45 @@ public class TestOptimizations extends TestBase { ...@@ -61,6 +63,45 @@ public class TestOptimizations extends TestBase {
deleteDb("optimizations"); 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 { private void testSortIndex() throws SQLException {
Connection conn = getConnection("optimizations"); Connection conn = getConnection("optimizations");
Statement stat = conn.createStatement(); Statement stat = conn.createStatement();
......
...@@ -293,6 +293,11 @@ public class TestTableEngines extends TestBase { ...@@ -293,6 +293,11 @@ public class TestTableEngines extends TestBase {
// do nothing // do nothing
} }
@Override
public boolean hasRowIdColumn() {
return false;
}
} }
/** /**
......
...@@ -281,7 +281,7 @@ public class TestNestedJoins extends TestBase { ...@@ -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"); 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()); assertTrue(rs.next());
sql = rs.getString(1); sql = rs.getString(1);
int todo; // TODO Support optimizing queries with both inner and outer joins
// assertTrue("using table scan", sql.indexOf("tableScan") < 0); // assertTrue("using table scan", sql.indexOf("tableScan") < 0);
stat.execute("drop table test"); stat.execute("drop table test");
stat.execute("drop table o"); stat.execute("drop table o");
......
...@@ -53,7 +53,21 @@ public class TestSampleApps extends TestBase { ...@@ -53,7 +53,21 @@ public class TestSampleApps extends TestBase {
org.h2.samples.CsvSample.class); org.h2.samples.CsvSample.class);
testApp("", testApp("",
org.h2.samples.CachedPreparedStatements.class); 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); org.h2.samples.Function.class);
// Not compatible with PostgreSQL JDBC driver (throws a NullPointerException) // Not compatible with PostgreSQL JDBC driver (throws a NullPointerException)
//testApp(org.h2.samples.SecurePassword.class, null, "Joe"); //testApp(org.h2.samples.SecurePassword.class, null, "Joe");
......
Markdown 格式
0%
您添加了 0 到此讨论。请谨慎行事。
请先完成此评论的编辑!
注册 或者 后发表评论