提交 f48b3451 authored 作者: Thomas Mueller's avatar Thomas Mueller

--no commit message

--no commit message
上级 be8f8bc7
......@@ -280,6 +280,9 @@ It looks like the development of this database has stopped. The last release was
</tr><tr>
<td><a href="http://www.jenkov.com/mrpersister/introduction.tmpl">Mr. Persister</a></td>
<td>Simple, small and fast object relational mapping.</td>
</tr><tr>
<td><a href="http://docs.codewave.de/mytunesrss3/">MyTunesRss</a></td>
<td>MyTunesRSS lets you listen to your music whereever you are.</td>
</tr><tr>
<td><a href="http://www.polepos.org">PolePosition</a></td>
<td>Open source database benchmark.</td>
......
......@@ -40,8 +40,15 @@ Hypersonic SQL or HSQLDB. H2 is built from scratch.
<h3>Version 1.0 (Current)</h3>
<h3>Version 1.0 / TODO (Build xx)</h3><ul>
<li>Check and foreign key constraints now checks if the existing data is consistent (this can be disabled by appending NOCHECK).
It is also possible to check existing data when re-enabling referential integrity for a table.
<li>New experimental feature MVCC (multi version concurrency control).
Can be set as a option when opening the database (jdbc:h2:test;MVCC=TRUE)
or as a system property (-Dh2.mvcc=true).
</li><li>When used as as Servlet, the H2 Console did not work with SSL (using Tomcat). Fixed.
</li><li>When altering a table with foreign key constraint, if there was no manual index created
for the referenced columns, the automatically created index was dropped while still being used.
Fixed.
</li><li>Check and foreign key constraints now checks if the existing data is consistent (this can be disabled by appending NOCHECK).
It is also possible to check existing data when re-enabling referential integrity for a table.
</li><li>Some unit tests failed on Linux because the file system works differently. The unit tests are fixed and should work now.
</li><li>Can now incrementally translate the documentation. See also FAQ.
</li><li>Improved error messages: some tools can't show the root cause of an exception.
......@@ -1127,6 +1134,8 @@ Hypersonic SQL or HSQLDB. H2 is built from scratch.
</li><li>Read-only sessions (Connection.setReadOnly)
</li><li>Support compatibility for jdbc:hsqldb:res:
</li><li>In the MySQL and PostgreSQL, use lower case identifiers by default (DatabaseMetaData.storesLowerCaseIdentifiers = true)
</li><li>Provide a simple, lightweight O/R mapping tool
</li><li>Provide an Java SQL builder with standard and H2 syntax
</li><li>Data compression for in-memory database
</li></ul>
......
......@@ -3636,6 +3636,10 @@ public class Parser {
readIfEqualOrTo();
read();
return new NoOperation(session);
} else if(readIf("MVCC")) {
readIfEqualOrTo();
read();
return new NoOperation(session);
} else if(readIf("ACCESS_MODE_LOG")) {
readIfEqualOrTo();
read();
......
......@@ -70,7 +70,7 @@ public class AlterTableAddConstraint extends SchemaCommand {
}
Constraint constraint;
session.getUser().checkRight(table, Right.ALL);
table.lock(session, true);
table.lock(session, true, true);
switch(type) {
case CHECK: {
int id = getObjectId(true, true);
......@@ -215,6 +215,17 @@ public class AlterTableAddConstraint extends SchemaCommand {
return null;
}
private Index getIndex(Table t, Column[] cols) {
ObjectArray list = t.getIndexes();
for(int i=0; i<list.size(); i++) {
Index index = (Index) list.get(i);
if(canUseIndex(index, t, cols)) {
return index;
}
}
return null;
}
private boolean canUseUniqueIndex(Index index, Table table, Column[] cols) {
if(index.getTable() != table || !index.getIndexType().isUnique()) {
return false;
......@@ -238,22 +249,15 @@ public class AlterTableAddConstraint extends SchemaCommand {
return true;
}
private Index getIndex(Table t, Column[] cols) {
ObjectArray list = t.getIndexes();
for(int i=0; i<list.size(); i++) {
Index index = (Index) list.get(i);
if(canUseIndex(index, t, cols)) {
return index;
}
}
return null;
}
private boolean canUseIndex(Index index, Table table, Column[] cols) {
if(index.getTable() != table || index.getCreateSQL() == null) {
// can't use the scan index or index of another table
return false;
}
if(index.getIndexType().belongsToConstraint()) {
// the constraint might be dropped (also in an alter table statement)
return false;
}
Column[] indexCols = index.getColumns();
if(indexCols.length < cols.length) {
return false;
......
......@@ -8,7 +8,6 @@ import java.sql.SQLException;
import org.h2.command.Parser;
import org.h2.command.Prepared;
import org.h2.constant.ErrorCode;
import org.h2.constant.SysProperties;
import org.h2.constraint.ConstraintReferential;
import org.h2.engine.Database;
import org.h2.engine.DbObject;
......@@ -61,7 +60,7 @@ public class AlterTableAlterColumn extends SchemaCommand {
Database db = session.getDatabase();
session.getUser().checkRight(table, Right.ALL);
table.checkSupportAlter();
table.lock(session, true);
table.lock(session, true, true);
Sequence sequence = oldColumn == null ? null : oldColumn.getSequence();
switch(type) {
case NOT_NULL: {
......@@ -327,7 +326,7 @@ public class AlterTableAlterColumn extends SchemaCommand {
private void execute(String sql, boolean ddl) throws SQLException {
Prepared command = session.prepare(sql);
command.update();
if(ddl && SysProperties.MVCC) {
if(ddl && session.getDatabase().isMultiVersion()) {
// TODO this should work without MVCC, but avoid risks at the moment
session.commit(true);
}
......
......@@ -63,7 +63,7 @@ public class CreateIndex extends SchemaCommand {
boolean persistent = db.isPersistent();
Table table = getSchema().getTableOrView(session, tableName);
session.getUser().checkRight(table, Right.ALL);
table.lock(session, true);
table.lock(session, true, true);
if(!table.isPersistent()) {
persistent = false;
}
......
......@@ -59,7 +59,7 @@ public class DropTable extends SchemaCommand {
if(!table.canDrop()) {
throw Message.getSQLException(ErrorCode.CANNOT_DROP_TABLE_1, tableName);
}
table.lock(session, true);
table.lock(session, true, true);
}
if(next != null) {
next.prepareDrop();
......
......@@ -43,7 +43,7 @@ public class DropView extends SchemaCommand {
throw Message.getSQLException(ErrorCode.VIEW_NOT_FOUND_1, viewName);
}
session.getUser().checkRight(view, Right.ALL);
view.lock(session, true);
view.lock(session, true, true);
session.getDatabase().removeSchemaObject(session, view);
}
return 0;
......
......@@ -30,7 +30,7 @@ public class TruncateTable extends DefineCommand {
throw Message.getSQLException(ErrorCode.CANNOT_TRUNCATE_1, table.getSQL());
} else {
session.getUser().checkRight(table, Right.DELETE);
table.lock(session, true);
table.lock(session, true, true);
table.truncate(session);
}
return 0;
......
......@@ -40,12 +40,12 @@ public class Delete extends Prepared {
}
public int update() throws SQLException {
tableFilter.startQuery();
tableFilter.startQuery(session);
tableFilter.reset();
Table table = tableFilter.getTable();
session.getUser().checkRight(table, Right.DELETE);
table.fireBefore(session);
table.lock(session, true);
table.lock(session, true, false);
ObjectArray rows = new ObjectArray();
setCurrentRowNumber(0);
while (tableFilter.next()) {
......@@ -94,7 +94,7 @@ public class Delete extends Prepared {
if (condition != null) {
condition.mapColumns(tableFilter, 0);
condition = condition.optimize(session);
condition.createIndexConditions(tableFilter);
condition.createIndexConditions(session, tableFilter);
}
PlanItem item = tableFilter.getBestPlanItem(session);
tableFilter.setPlanItem(item);
......
......@@ -84,7 +84,7 @@ public class Insert extends Prepared {
table.fireBefore(session);
table.validateConvertUpdateSequence(session, newRow);
table.fireBeforeRow(session, null, newRow);
table.lock(session, true);
table.lock(session, true, false);
table.addRow(session, newRow);
session.log(table, UndoLogRecord.INSERT, newRow);
table.fireAfter(session);
......@@ -95,7 +95,7 @@ public class Insert extends Prepared {
LocalResult rows = query.query(0);
count = 0;
table.fireBefore(session);
table.lock(session, true);
table.lock(session, true, false);
while(rows.next()) {
checkCancelled();
count++;
......
......@@ -121,7 +121,7 @@ public class Merge extends Prepared {
LocalResult rows = query.query(0);
count = 0;
table.fireBefore(session);
table.lock(session, true);
table.lock(session, true, false);
while(rows.next()) {
checkCancelled();
count++;
......@@ -164,7 +164,7 @@ public class Merge extends Prepared {
table.fireBefore(session);
table.validateConvertUpdateSequence(session, row);
table.fireBeforeRow(session, null, row);
table.lock(session, true);
table.lock(session, true, false);
table.addRow(session, row);
session.log(table, UndoLogRecord.INSERT, row);
table.fireAfter(session);
......
......@@ -175,7 +175,7 @@ public class ScriptCommand extends ScriptBase {
});
for(int i=0; i<tables.size(); i++) {
Table table = (Table) tables.get(i);
table.lock(session, false);
table.lock(session, false, false);
String sql = table.getCreateSQL();
if(sql == null) {
// null for metadata tables
......@@ -195,7 +195,7 @@ public class ScriptCommand extends ScriptBase {
}
for(int i=0; i<tables.size(); i++) {
Table table = (Table) tables.get(i);
table.lock(session, false);
table.lock(session, false, false);
String sql = table.getCreateSQL();
if(sql == null) {
// null for metadata tables
......
......@@ -320,10 +320,10 @@ public class Select extends Query {
if(distinct) {
result.setDistinct();
}
topTableFilter.startQuery();
topTableFilter.startQuery(session);
topTableFilter.reset();
// TODO lock tables of sub queries
topTableFilter.lock(session, isForUpdate);
topTableFilter.lock(session, isForUpdate, isForUpdate);
if(isQuickQuery) {
queryQuick(columnCount, result);
} else if(isGroupQuery) {
......@@ -477,7 +477,7 @@ public class Select extends Query {
condition = condition.optimize(session);
for (int j = 0; j < filters.size(); j++) {
TableFilter f = (TableFilter) filters.get(j);
condition.createIndexConditions(f);
condition.createIndexConditions(session, f);
}
}
if(condition == null && isGroupQuery && groupIndex == null && havingIndex<0 && filters.size()==1) {
......
......@@ -56,7 +56,7 @@ public class Update extends Prepared {
}
public int update() throws SQLException {
tableFilter.startQuery();
tableFilter.startQuery(session);
tableFilter.reset();
// TODO optimization: update old / new list: maybe use a linked list (to avoid array allocation)
ObjectArray oldRows = new ObjectArray();
......@@ -64,7 +64,7 @@ public class Update extends Prepared {
Table table = tableFilter.getTable();
session.getUser().checkRight(table, Right.UPDATE);
table.fireBefore(session);
table.lock(session, true);
table.lock(session, true, false);
int columnCount = table.getColumns().length;
// get the old rows, compute the new rows
setCurrentRowNumber(0);
......@@ -145,7 +145,7 @@ public class Update extends Prepared {
if (condition != null) {
condition.mapColumns(tableFilter, 0);
condition = condition.optimize(session);
condition.createIndexConditions(tableFilter);
condition.createIndexConditions(session, tableFilter);
}
for (int i = 0; i < expressions.length; i++) {
Expression expr = expressions[i];
......
......@@ -311,6 +311,7 @@ public class ErrorCode {
public static final int RESULT_SET_NOT_UPDATABLE = 90127;
public static final int RESULT_SET_NOT_SCROLLABLE = 90128;
public static final int TRANSACTION_NOT_FOUND_1 = 90129;
public static final int METHOD_NOT_ALLOWED_FOR_PREPARED_STATEMENT = 90130;
/**
* INTERNAL
......
......@@ -46,7 +46,7 @@ public class ConnectionInfo {
"ACCESS_MODE_LOG", "ACCESS_MODE_DATA", "AUTOCOMMIT",
"CIPHER", "CREATE", "CACHE_TYPE",
"DB_CLOSE_ON_EXIT", "FILE_LOCK", "IGNORE_UNKNOWN_SETTINGS", "IFEXISTS",
"PASSWORD", "RECOVER", "STORAGE", "USER"
"PASSWORD", "RECOVER", "STORAGE", "USER", "MVCC"
};
for(int i=0; i<connectionTime.length; i++) {
String key = connectionTime[i];
......
......@@ -140,6 +140,7 @@ public class Database implements DataHandler {
private boolean indexSummaryValid = true;
private String accessModeLog, accessModeData;
private boolean referentialIntegrity = true;
private boolean multiVersion;
public Database(String name, ConnectionInfo ci, String cipher) throws SQLException {
this.compareMode = new CompareMode(null, null);
......@@ -172,6 +173,7 @@ public class Database implements DataHandler {
if(ignoreSummary != null) {
this.recovery = true;
}
this.multiVersion = ci.removeProperty("MVCC", false) || SysProperties.MVCC;
boolean closeAtVmShutdown = ci.removeProperty("DB_CLOSE_ON_EXIT", true);
int traceLevelFile = ci.getIntProperty(SetTypes.TRACE_LEVEL_FILE, TraceSystem.DEFAULT_TRACE_LEVEL_FILE);
int traceLevelSystemOut = ci.getIntProperty(SetTypes.TRACE_LEVEL_SYSTEM_OUT, TraceSystem.DEFAULT_TRACE_LEVEL_SYSTEM_OUT);
......@@ -464,7 +466,7 @@ public class Database implements DataHandler {
publicRole = new Role(this, 0, Constants.PUBLIC_ROLE_NAME, true);
roles.put(Constants.PUBLIC_ROLE_NAME, publicRole);
systemUser.setAdmin(true);
systemSession = new Session(this, systemUser, 0);
systemSession = new Session(this, systemUser, ++nextSessionId);
// TODO storage: antivir scans .script files, maybe other scanners scan .db files?
ObjectArray cols = new ObjectArray();
Column columnId = new Column("ID", Value.INT, 0, 0);
......@@ -631,9 +633,9 @@ public class Database implements DataHandler {
MetaRecord rec = new MetaRecord(obj);
rec.setRecord(r);
objectIds.set(obj.getId());
meta.lock(session, true);
meta.lock(session, true, true);
meta.addRow(session, r);
if(SysProperties.MVCC) {
if(isMultiVersion()) {
// TODO this should work without MVCC, but avoid risks at the moment
session.log(meta, UndoLogRecord.INSERT, r);
}
......@@ -645,9 +647,9 @@ public class Database implements DataHandler {
Cursor cursor = metaIdIndex.find(session, r, r);
if(cursor.next()) {
Row found = cursor.get();
meta.lock(session, true);
meta.lock(session, true, true);
meta.removeRow(session, found);
if(SysProperties.MVCC) {
if(isMultiVersion()) {
// TODO this should work without MVCC, but avoid risks at the moment
session.log(meta, UndoLogRecord.DELETE, found);
}
......@@ -1523,5 +1525,9 @@ public class Database implements DataHandler {
public boolean isStarting() {
return starting;
}
public boolean isMultiVersion() {
return multiVersion;
}
}
......@@ -5,6 +5,7 @@
package org.h2.engine;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
......@@ -191,10 +192,16 @@ public class Session implements SessionInterface {
logSystem.commit(this);
}
if(undoLog.size() > 0) {
if(SysProperties.MVCC) {
if(database.isMultiVersion()) {
ArrayList rows = new ArrayList();
while (undoLog.size() > 0) {
UndoLogRecord entry = undoLog.getAndRemoveLast();
entry.commit();
rows.add(entry.getRow());
}
for(int i=0; i<rows.size(); i++) {
Row r = (Row) rows.get(i);
r.commit();
}
}
undoLog.clear();
......@@ -293,7 +300,7 @@ public class Session implements SessionInterface {
// otherwise rollback will try to rollback a not-inserted row
if(SysProperties.CHECK) {
int lockMode = database.getLockMode();
if(lockMode != Constants.LOCK_MODE_OFF && !SysProperties.MVCC) {
if(lockMode != Constants.LOCK_MODE_OFF && !database.isMultiVersion()) {
if(locks.indexOf(log.getTable())<0 && log.getTable().getTableType() != Table.TABLE_LINK) {
throw Message.getInternalError();
}
......
......@@ -113,11 +113,10 @@ public class CompareLike extends Condition {
return esc;
}
public void createIndexConditions(TableFilter filter) throws SQLException {
public void createIndexConditions(Session session, TableFilter filter) throws SQLException {
if(regexp) {
return;
}
Session session = filter.getSession();
if(!(left instanceof ExpressionColumn)) {
return;
}
......
......@@ -228,7 +228,7 @@ public class Comparison extends Condition {
return new Comparison(session, type, left, right);
}
public void createIndexConditions(TableFilter filter) {
public void createIndexConditions(Session session, TableFilter filter) {
if(right==null) {
// TODO index usage: IS [NOT] NULL index usage is possible
return;
......
......@@ -51,10 +51,10 @@ public class ConditionAndOr extends Condition {
return "("+sql+")";
}
public void createIndexConditions(TableFilter filter) throws SQLException {
public void createIndexConditions(Session session, TableFilter filter) throws SQLException {
if (andOrType == AND) {
left.createIndexConditions(filter);
right.createIndexConditions(filter);
left.createIndexConditions(session, filter);
right.createIndexConditions(session, filter);
}
}
......
......@@ -120,7 +120,7 @@ public class ConditionIn extends Condition {
return this;
}
public void createIndexConditions(TableFilter filter) {
public void createIndexConditions(Session session, TableFilter filter) {
if(!SysProperties.OPTIMIZE_IN) {
return;
}
......
......@@ -56,7 +56,7 @@ public abstract class Expression {
return getValue(session).getBoolean();
}
public void createIndexConditions(TableFilter filter) throws SQLException {
public void createIndexConditions(Session session, TableFilter filter) throws SQLException {
// default is do nothing
}
......
......@@ -248,7 +248,7 @@ public class ExpressionColumn extends Expression {
return 2;
}
public void createIndexConditions(TableFilter filter) {
public void createIndexConditions(Session session, TableFilter filter) {
TableFilter tf = getTableFilter();
if(filter == tf && column.getType() == Value.BOOLEAN) {
IndexCondition cond = new IndexCondition(Comparison.EQUAL, this, ValueExpression.get(ValueBoolean.get(true)));
......
......@@ -39,7 +39,7 @@ public class ValueExpression extends Expression {
return value.getType();
}
public void createIndexConditions(TableFilter filter) {
public void createIndexConditions(Session session, TableFilter filter) {
if(value.getType() == Value.BOOLEAN) {
boolean v = ((ValueBoolean)value).getBoolean().booleanValue();
if(!v) {
......
......@@ -246,7 +246,7 @@ public abstract class BaseIndex extends SchemaObjectBase implements Index {
return table;
}
public void commit(Row row) throws SQLException {
public void commit(int operation, Row row) throws SQLException {
}
}
......@@ -258,7 +258,7 @@ public class BtreeLeaf extends BtreePage {
return size;
}
SearchRow getLast(Session sessioni) throws SQLException {
SearchRow getLast(Session session) throws SQLException {
if(pageData.size()==0) {
if(!Constants.ALLOW_EMPTY_BTREE_PAGES && !root) {
throw Message.getInternalError("Empty btree page");
......
......@@ -67,6 +67,6 @@ public interface Index extends SchemaObject {
public abstract Table getTable();
public abstract void commit(Row row) throws SQLException;
public abstract void commit(int operation, Row row) throws SQLException;
}
\ No newline at end of file
/*
* Copyright 2004-2007 H2 Group. Licensed under the H2 License, Version 1.0 (http://h2database.com/html/license.html).
* Initial Developer: H2 Group
*/
package org.h2.index;
import java.sql.SQLException;
......
/*
* Copyright 2004-2007 H2 Group. Licensed under the H2 License, Version 1.0 (http://h2database.com/html/license.html).
* Initial Developer: H2 Group
*/
package org.h2.index;
import java.sql.SQLException;
import org.h2.constant.SysProperties;
import org.h2.engine.Database;
import org.h2.engine.Session;
import org.h2.message.Message;
......@@ -30,7 +33,10 @@ public class MultiVersionIndex implements Index {
base.add(session, row);
// for example rolling back an delete operation
removeIfExists(session, row);
delta.add(session, row);
if(row.getSessionId() != 0) {
// don't insert rows that are added when creating an index
delta.add(session, row);
}
}
public void close(Session session) throws SQLException {
......@@ -44,12 +50,10 @@ public class MultiVersionIndex implements Index {
}
public boolean canGetFirstOrLast(boolean first) {
int todoMVCC_Min_Max_Optimization;
return false;
}
public SearchRow findFirstOrLast(Session session, boolean first) throws SQLException {
int todoMVCC_Min_Max_Optimization;
throw Message.getUnsupportedException();
}
......@@ -88,11 +92,11 @@ public class MultiVersionIndex implements Index {
}
public void truncate(Session session) throws SQLException {
int todoLockingRequired;
delta.truncate(session);
base.truncate(session);
}
public void commit(Row row) throws SQLException {
public void commit(int operation, Row row) throws SQLException {
removeIfExists(null, row);
}
......
......@@ -5,8 +5,6 @@
package org.h2.index;
import java.sql.SQLException;
import org.h2.constant.SysProperties;
import org.h2.engine.Session;
import org.h2.result.Row;
import org.h2.result.SearchRow;
......@@ -19,10 +17,12 @@ public class ScanCursor implements Cursor {
private ScanIndex scan;
private Row row;
private final Session session;
private final boolean multiVersion;
ScanCursor(Session session, ScanIndex scan) {
ScanCursor(Session session, ScanIndex scan, boolean multiVersion) {
this.session = session;
this.scan = scan;
this.multiVersion = multiVersion;
row = null;
}
......@@ -43,7 +43,7 @@ public class ScanCursor implements Cursor {
}
public boolean next() throws SQLException {
if(SysProperties.MVCC) {
if(multiVersion) {
while(true) {
row = scan.getNextRow(session, row);
if(row == null) {
......
......@@ -5,19 +5,18 @@
package org.h2.index;
import java.sql.SQLException;
import org.h2.constant.SysProperties;
import java.util.HashMap;
import org.h2.engine.Constants;
import org.h2.engine.Database;
import org.h2.engine.Session;
import org.h2.message.Message;
import org.h2.result.Row;
import org.h2.result.SearchRow;
import org.h2.store.Storage;
import org.h2.store.UndoLogRecord;
import org.h2.table.Column;
import org.h2.table.TableData;
import org.h2.util.IntIntHashMap;
import org.h2.util.ObjectArray;
import org.h2.util.ObjectUtils;
import org.h2.value.DataType;
import org.h2.value.Value;
import org.h2.value.ValueLob;
......@@ -32,20 +31,19 @@ public class ScanIndex extends BaseIndex {
private TableData tableData;
private boolean containsLargeObject;
private int rowCountDiff;
private IntIntHashMap sessionRowCount;
private HashMap sessionRowCount;
public ScanIndex(TableData table, int id, Column[] columns, IndexType indexType)
throws SQLException {
super(table, id, table.getName() + "_TABLE_SCAN", columns, indexType);
if(SysProperties.MVCC) {
sessionRowCount = new IntIntHashMap();
if(database.isMultiVersion()) {
sessionRowCount = new HashMap();
}
tableData = table;
Database db = table.getDatabase();
if(!db.isPersistent() || id < 0) {
if(!database.isPersistent() || id < 0) {
return;
}
this.storage = db.getStorage(table, id, true);
this.storage = database.getStorage(table, id, true);
int count = storage.getRecordCount();
rowCount = count;
table.setRowCount(count);
......@@ -76,6 +74,9 @@ public class ScanIndex extends BaseIndex {
}
tableData.setRowCount(0);
rowCount = 0;
if(database.isMultiVersion()) {
sessionRowCount.clear();
}
}
public String getCreateSQL() {
......@@ -121,18 +122,22 @@ public class ScanIndex extends BaseIndex {
rows.set(key, row);
}
}
incrementRowCount(session, 1);
incrementRowCount(session.getId(), 1);
rowCount++;
}
private void incrementRowCount(Session session, int count) {
if(SysProperties.MVCC) {
int id = session.getId();
int current = sessionRowCount.get(id);
if(current == -1) {
current = 0;
}
sessionRowCount.put(id, current + count);
public void commit(int operation, Row row) throws SQLException {
if(database.isMultiVersion()) {
incrementRowCount(row.getSessionId(), operation == UndoLogRecord.DELETE ? 1 : -1);
}
}
private void incrementRowCount(int sessionId, int count) {
if(database.isMultiVersion()) {
Integer id = ObjectUtils.getInteger(sessionId);
Integer c = (Integer) sessionRowCount.get(id);
int current = c == null ? 0 : c.intValue();
sessionRowCount.put(id, ObjectUtils.getInteger(current + count));
rowCountDiff += count;
}
}
......@@ -155,12 +160,12 @@ public class ScanIndex extends BaseIndex {
rows.set(key, free);
firstFree = key;
}
incrementRowCount(session, -1);
incrementRowCount(session.getId(), -1);
rowCount--;
}
public Cursor find(Session session, SearchRow first, SearchRow last) throws SQLException {
return new ScanCursor(session, this);
return new ScanCursor(session, this, database.isMultiVersion());
}
public double getCost(Session session, int[] masks) throws SQLException {
......@@ -172,11 +177,9 @@ public class ScanIndex extends BaseIndex {
}
public long getRowCount(Session session) {
if(SysProperties.MVCC) {
long count = sessionRowCount.get(session.getId());
if(count == -1) {
count = 0;
}
if(database.isMultiVersion()) {
Integer i = (Integer) sessionRowCount.get(ObjectUtils.getInteger(session.getId()));
long count = i == null ? 0 : i.intValue();
count += super.getRowCount(session);
count -= rowCountDiff;
return count;
......
......@@ -23,6 +23,7 @@ import java.sql.SQLException;
import java.util.Calendar;
import org.h2.command.CommandInterface;
import org.h2.constant.ErrorCode;
import org.h2.engine.SessionInterface;
import org.h2.expression.ParameterInterface;
import org.h2.message.Message;
......@@ -197,7 +198,7 @@ public class JdbcPreparedStatement extends JdbcStatement implements PreparedStat
public ResultSet executeQuery(String sql) throws SQLException {
try {
debugCodeCall("executeQuery", sql);
throw Message.getUnsupportedException();
throw Message.getSQLException(ErrorCode.METHOD_NOT_ALLOWED_FOR_PREPARED_STATEMENT);
} catch(Throwable e) {
throw logAndConvert(e);
}
......@@ -211,7 +212,7 @@ public class JdbcPreparedStatement extends JdbcStatement implements PreparedStat
public void addBatch(String sql) throws SQLException {
try {
debugCodeCall("addBatch", sql);
throw Message.getUnsupportedException();
throw Message.getSQLException(ErrorCode.METHOD_NOT_ALLOWED_FOR_PREPARED_STATEMENT);
} catch(Throwable e) {
throw logAndConvert(e);
}
......@@ -225,7 +226,7 @@ public class JdbcPreparedStatement extends JdbcStatement implements PreparedStat
public int executeUpdate(String sql) throws SQLException {
try {
debugCodeCall("executeUpdate", sql);
throw Message.getUnsupportedException();
throw Message.getSQLException(ErrorCode.METHOD_NOT_ALLOWED_FOR_PREPARED_STATEMENT);
} catch(Throwable e) {
throw logAndConvert(e);
}
......@@ -239,7 +240,7 @@ public class JdbcPreparedStatement extends JdbcStatement implements PreparedStat
public boolean execute(String sql) throws SQLException {
try {
debugCodeCall("execute", sql);
throw Message.getUnsupportedException();
throw Message.getSQLException(ErrorCode.METHOD_NOT_ALLOWED_FOR_PREPARED_STATEMENT);
} catch(Throwable e) {
throw logAndConvert(e);
}
......@@ -1092,7 +1093,7 @@ public class JdbcPreparedStatement extends JdbcStatement implements PreparedStat
if(debug()) {
debugCode("executeUpdate("+quote(sql)+", "+autoGeneratedKeys+");");
}
throw Message.getUnsupportedException();
throw Message.getSQLException(ErrorCode.METHOD_NOT_ALLOWED_FOR_PREPARED_STATEMENT);
} catch(Throwable e) {
throw logAndConvert(e);
}
......@@ -1109,7 +1110,7 @@ public class JdbcPreparedStatement extends JdbcStatement implements PreparedStat
if (debug()) {
debugCode("executeUpdate(" + quote(sql) + ", " + quoteIntArray(columnIndexes) + ");");
}
throw Message.getUnsupportedException();
throw Message.getSQLException(ErrorCode.METHOD_NOT_ALLOWED_FOR_PREPARED_STATEMENT);
} catch (Exception e) {
throw logAndConvert(e);
}
......@@ -1125,7 +1126,7 @@ public class JdbcPreparedStatement extends JdbcStatement implements PreparedStat
if (debug()) {
debugCode("executeUpdate(" + quote(sql) + ", " + quoteArray(columnNames) + ");");
}
throw Message.getUnsupportedException();
throw Message.getSQLException(ErrorCode.METHOD_NOT_ALLOWED_FOR_PREPARED_STATEMENT);
} catch (Exception e) {
throw logAndConvert(e);
}
......@@ -1141,7 +1142,7 @@ public class JdbcPreparedStatement extends JdbcStatement implements PreparedStat
if (debug()) {
debugCode("execute(" + quote(sql) + ", " + autoGeneratedKeys + ");");
}
throw Message.getUnsupportedException();
throw Message.getSQLException(ErrorCode.METHOD_NOT_ALLOWED_FOR_PREPARED_STATEMENT);
} catch (Exception e) {
throw logAndConvert(e);
}
......@@ -1157,7 +1158,7 @@ public class JdbcPreparedStatement extends JdbcStatement implements PreparedStat
if (debug()) {
debugCode("execute(" + quote(sql) + ", " + quoteIntArray(columnIndexes) + ");");
}
throw Message.getUnsupportedException();
throw Message.getSQLException(ErrorCode.METHOD_NOT_ALLOWED_FOR_PREPARED_STATEMENT);
} catch (Exception e) {
throw logAndConvert(e);
}
......@@ -1173,7 +1174,7 @@ public class JdbcPreparedStatement extends JdbcStatement implements PreparedStat
if (debug()) {
debugCode("execute(" + quote(sql) + ", " + quoteArray(columnNames) + ");");
}
throw Message.getUnsupportedException();
throw Message.getSQLException(ErrorCode.METHOD_NOT_ALLOWED_FOR_PREPARED_STATEMENT);
} catch (Exception e) {
throw logAndConvert(e);
}
......
......@@ -150,6 +150,7 @@
90127=Die Resultat-Zeilen k\u00F6nnen nicht ver\u00E4ndert werden. Die Abfrage muss alle Felder eines eindeutigen Schl\u00FCssels enthalten, und nur eine Tabelle enthalten.
90128=Kann nicht an den Anfang der Resultat-Zeilen springen. M\u00F6gliche L\u00F6sung\: conn.createStatement(ResultSet.TYPE_SCROLL_INSENSITIVE, ResultSet.CONCUR_READ_ONLY).
90129=Transaktion {0} nicht gefunden
90130=\#This method is not allowed for a prepared statement; use a regular statement instead.
HY000=Allgemeiner Fehler\: {0}
HY004=Unbekannter Datentyp\: {0}
HYC00=Dieses Feature wird unterst\u00FCtzt
......
......@@ -150,6 +150,7 @@
90127=The result set is not updatable. The query must select all columns from a unique key. Only one table may be selected.
90128=The result set is not scrollable and can not be reset. You may need to use conn.createStatement(ResultSet.TYPE_SCROLL_INSENSITIVE, ResultSet.CONCUR_READ_ONLY).
90129=Transaction {0} not found
90130=This method is not allowed for a prepared statement; use a regular statement instead.
HY000=General error\: {0}
HY004=Unknown data type\: {0}
HYC00=Feature not supported
......
......@@ -150,6 +150,7 @@
90127=\#The result set is not updatable. The query must select all columns from a unique key. Only one table may be selected.
90128=\#The result set is not scrollable and can not be reset. You may need to use conn.createStatement(ResultSet.TYPE_SCROLL_INSENSITIVE, ResultSet.CONCUR_READ_ONLY).
90129=\#Transaction {0} not found
90130=\#This method is not allowed for a prepared statement; use a regular statement instead.
HY000=Blad ogolny\: {0}
HY004=Nieznany typ danyche\: {0}
HYC00=Cecha nie jest wspierana
......
......@@ -82,11 +82,9 @@ public abstract class Record extends CacheObject {
if((isChanged() && !isLogWritten()) || isPinned()) {
return false;
}
if(SysProperties.MVCC) {
// TODO not required if we write the log only when committed
if(sessionId != 0) {
return false;
}
// TODO not required if we write the log only when committed
if(sessionId != 0) {
return false;
}
return true;
}
......
......@@ -139,7 +139,7 @@ public class Storage {
}
record.setDeleted(session, true);
int blockCount = record.getBlockCount();
if(SysProperties.MVCC) {
if(database.isMultiVersion()) {
int todoMustFreeSpaceOnCommit;
} else {
free(pos, blockCount);
......
......@@ -146,8 +146,11 @@ public class UndoLogRecord {
ObjectArray list = table.getIndexes();
for(int i=0; i<list.size(); i++) {
Index index = (Index) list.get(i);
index.commit(row);
index.commit(operation, row);
}
row.commit();
}
public Row getRow() {
return row;
}
}
......@@ -91,9 +91,11 @@ public class Column {
}
public Value computeValue(Session session, Row row) throws SQLException {
computeTableFilter.setSession(session);
computeTableFilter.set(row);
return defaultExpression.getValue(session);
synchronized(this) {
computeTableFilter.setSession(session);
computeTableFilter.set(row);
return defaultExpression.getValue(session);
}
}
public void setComputed(boolean computed, Expression expression) {
......@@ -154,7 +156,9 @@ public class Column {
if(defaultExpression == null) {
value = ValueNull.INSTANCE;
} else {
value = defaultExpression.getValue(session).convertTo(type);
synchronized(this) {
value = defaultExpression.getValue(session).convertTo(type);
}
if(primaryKey) {
session.setLastIdentity(value);
}
......@@ -162,7 +166,9 @@ public class Column {
}
if (value == ValueNull.INSTANCE) {
if(convertNullToDefault) {
value = defaultExpression.getValue(session).convertTo(type);
synchronized(this) {
value = defaultExpression.getValue(session).convertTo(type);
}
}
if (value == ValueNull.INSTANCE && !nullable) {
if(Mode.getCurrentMode().convertInsertNullToZero) {
......@@ -186,7 +192,10 @@ public class Column {
}
if(checkConstraint != null) {
resolver.setValue(value);
Value v = checkConstraint.getValue(session);
Value v;
synchronized(this) {
v = checkConstraint.getValue(session);
}
// Both TRUE and NULL are ok
if(Boolean.FALSE.equals(v.getBoolean())) {
throw Message.getSQLException(ErrorCode.CHECK_CONSTRAINT_VIOLATED_1, checkConstraint.getSQL());
......@@ -393,7 +402,9 @@ public class Column {
expr = expr.optimize(session);
resolver.setValue(ValueNull.INSTANCE);
// check if the column is mapped
expr.getValue(session);
synchronized(this) {
expr.getValue(session);
}
if(checkConstraint == null) {
checkConstraint = expr;
} else {
......
......@@ -59,7 +59,7 @@ public class FunctionTable extends Table {
setColumns(cols);
}
public void lock(Session session, boolean exclusive) throws SQLException {
public void lock(Session session, boolean exclusive, boolean force) throws SQLException {
}
public void close(Session session) throws SQLException {
......
......@@ -470,7 +470,7 @@ public class MetaTable extends Table {
throw Message.getUnsupportedException();
}
public void lock(Session session, boolean exclusive) throws SQLException {
public void lock(Session session, boolean exclusive, boolean force) throws SQLException {
// nothing to do
}
......
......@@ -42,7 +42,7 @@ public class RangeTable extends Table {
return NAME + "(" + min + ", " + max + ")";
}
public void lock(Session session, boolean exclusive) throws SQLException {
public void lock(Session session, boolean exclusive, boolean force) throws SQLException {
}
public void close(Session session) throws SQLException {
......
......@@ -118,7 +118,7 @@ public abstract class Table extends SchemaObjectBase {
columnMap.put(newName, column);
}
public abstract void lock(Session session, boolean exclusive) throws SQLException;
public abstract void lock(Session session, boolean exclusive, boolean force) throws SQLException;
public abstract void close(Session session) throws SQLException;
public abstract void unlock(Session s);
public abstract Index addIndex(Session session, String indexName, int indexId, Column[] cols, IndexType indexType, int headPos, String comment) throws SQLException;
......
......@@ -83,7 +83,7 @@ public class TableData extends Table implements RecordReader {
Index index = (Index) indexes.get(i);
index.add(session, row);
if(SysProperties.CHECK) {
if(!SysProperties.MVCC) {
if(!database.isMultiVersion()) {
long rc = index.getRowCount(session);
if(rc != rowCount+1) {
throw Message.getInternalError("rowCount expected "+(rowCount+1)+" got "+rc);
......@@ -98,7 +98,7 @@ public class TableData extends Table implements RecordReader {
Index index = (Index) indexes.get(i);
index.remove(session, row);
if(SysProperties.CHECK) {
if(!SysProperties.MVCC) {
if(!database.isMultiVersion()) {
long rc = index.getRowCount(session);
if(rc != rowCount) {
throw Message.getInternalError("rowCount expected "+(rowCount)+" got "+rc);
......@@ -160,7 +160,7 @@ public class TableData extends Table implements RecordReader {
index = new TreeIndex(this, indexId, indexName, cols, indexType);
}
}
if(SysProperties.MVCC) {
if(database.isMultiVersion()) {
index = new MultiVersionIndex(index, this);
}
if(index.needRebuild() && rowCount > 0) {
......@@ -241,11 +241,6 @@ public class TableData extends Table implements RecordReader {
}
for(int i=0; i<list.size(); i++) {
Row r = (Row) list.get(i);
if(SysProperties.MVCC) {
// when adding referential integrity to a table, the index is created first, and the rows are inserted to this index
// if the session is not set, it would look like an insert from another session
r.setDeleted(session, false);
}
index.add(session, r);
}
list.clear();
......@@ -256,7 +251,7 @@ public class TableData extends Table implements RecordReader {
}
public long getRowCount(Session session) {
if(SysProperties.MVCC) {
if(database.isMultiVersion()) {
return getScanIndex(session).getRowCount(session);
}
return rowCount;
......@@ -268,7 +263,7 @@ public class TableData extends Table implements RecordReader {
Index index = (Index) indexes.get(i);
index.remove(session, row);
if(SysProperties.CHECK) {
if(!SysProperties.MVCC) {
if(!database.isMultiVersion()) {
long rc = index.getRowCount(session);
if(rc != rowCount-1) {
throw Message.getInternalError("rowCount expected "+(rowCount-1)+" got "+rc);
......@@ -294,14 +289,11 @@ public class TableData extends Table implements RecordReader {
rowCount = 0;
}
public void lock(Session session, boolean exclusive) throws SQLException {
public void lock(Session session, boolean exclusive, boolean force) throws SQLException {
int lockMode = database.getLockMode();
if(lockMode == Constants.LOCK_MODE_OFF) {
return;
}
if(SysProperties.MVCC) {
return;
}
long max = System.currentTimeMillis() + session.getLockTimeout();
synchronized(database) {
while (true) {
......@@ -310,6 +302,9 @@ public class TableData extends Table implements RecordReader {
}
if (exclusive) {
if (lockExclusive == null) {
if(!force && database.isMultiVersion()) {
return;
}
if (lockShared.isEmpty()) {
traceLock(session, exclusive, "ok");
session.addLock(this);
......@@ -322,6 +317,9 @@ public class TableData extends Table implements RecordReader {
}
}
} else {
if(!force && database.isMultiVersion()) {
return;
}
if (lockExclusive == null) {
if(lockMode == Constants.LOCK_MODE_READ_COMMITTED && !SysProperties.multiThreadedKernel) {
// READ_COMMITTED read locks are acquired but they are released immediately
......
......@@ -69,21 +69,17 @@ public class TableFilter implements ColumnResolver {
return select;
}
public Session getSession() {
return session;
}
public Table getTable() {
return table;
}
public void lock(Session session, boolean exclusive) throws SQLException {
public void lock(Session session, boolean exclusive, boolean force) throws SQLException {
if(!rightsChecked) {
session.getUser().checkRight(table, Right.SELECT);
}
table.lock(session, exclusive);
table.lock(session, exclusive, force);
for(int i=0; joins != null && i<joins.size(); i++) {
getTableFilter(i).lock(session, exclusive);
getTableFilter(i).lock(session, exclusive, force);
}
}
......@@ -166,15 +162,16 @@ public class TableFilter implements ColumnResolver {
}
}
public void startQuery() {
public void startQuery(Session session) throws SQLException {
this.session = session;
scanCount = 0;
for(int i=0; joins != null && i<joins.size(); i++) {
TableFilter join = getTableFilter(i);
join.startQuery();
join.startQuery(session);
}
}
public void reset() throws SQLException {
public void reset() {
for(int i=0; joins != null && i<joins.size(); i++) {
TableFilter join = getTableFilter(i);
join.reset();
......@@ -372,7 +369,7 @@ public class TableFilter implements ColumnResolver {
private void mapAndAddFilter(Expression on) throws SQLException {
on.mapColumns(this, 0);
addFilterCondition(on, true);
on.createIndexConditions(this);
on.createIndexConditions(session, this);
for(int i=0; joins != null && i<joins.size(); i++) {
TableFilter join = getTableFilter(i);
join.mapAndAddFilter(on);
......
......@@ -212,7 +212,7 @@ public class TableLink extends Table {
throw Message.getUnsupportedException();
}
public void lock(Session session, boolean exclusive) throws SQLException {
public void lock(Session session, boolean exclusive, boolean force) throws SQLException {
// nothing to do
}
......
......@@ -136,7 +136,7 @@ public class TableView extends Table {
public void checkRename() throws SQLException {
}
public void lock(Session session, boolean exclusive) throws SQLException {
public void lock(Session session, boolean exclusive, boolean force) throws SQLException {
// exclusive lock means: the view will be dropped
}
......
/*
* Copyright 2004-2007 H2 Group. Licensed under the H2 License, Version 1.0 (http://h2database.com/html/license.html).
* Initial Developer: H2 Group
*/
package org.h2.samples;
import java.io.*;
......
......@@ -7,6 +7,7 @@ package org.h2.test;
import java.sql.SQLException;
import java.util.Properties;
import org.h2.constant.SysProperties;
import org.h2.server.TcpServer;
import org.h2.test.jdbc.*;
import org.h2.test.jdbc.xa.TestXA;
......@@ -42,6 +43,7 @@ import org.h2.test.unit.TestTools;
import org.h2.test.unit.TestValueHashMap;
import org.h2.tools.DeleteDbFiles;
import org.h2.tools.Server;
import org.h2.util.FileUtils;
import org.h2.util.StringUtils;
/**
......@@ -92,25 +94,24 @@ java -Xmx512m -Xrunhprof:cpu=samples,depth=8 org.h2.tools.RunScript -url jdbc:h2
TestAll test = new TestAll();
test.printSystem();
//int testMVCC;
// System.setProperty("h2.mvcc", "true");
int testMVCC;
System.setProperty("h2.mvcc", "true");
/*
CREATE TABLE Parent(ID INT PRIMARY KEY, Name VARCHAR);
CREATE TABLE Child(ID INT, P INT, PRIMARY KEY(ID, P));
ALTER TABLE Child ADD FOREIGN KEY(ID) REFERENCES Parent(ID);
ALTER TABLE Child ADD FOREIGN KEY(P) REFERENCES Parent(ID);
INSERT INTO Parent VALUES(1, '0'), (2, '0'), (3, '0');
INSERT INTO Child VALUES(2, 1);
ALTER TABLE Parent ALTER COLUMN Name BOOLEAN NULL;
DELETE FROM Parent WHERE ID=3;
DROP TABLE Parent, Child;
add link to:
http://zvikico.typepad.com/problog/2007/08/h2-database-eng.html
m2-repo
m.i.a.
documented?:
problem: new table created with constraints (so pointing to the same index), then constraint deleted: index is deleted (even though it is used).
Another problem: two constraints using the same index, one constraint is deleted.
Possible solution: never delete indexes? What about unique indexes? Create another index if one exists but it belongs to a constraint?
only admins can use nested tables:
CREATE USER TEST PASSWORD 'TEST';
SELECT * FROM (SELECT * FROM DUAL);
......@@ -572,6 +573,10 @@ SELECT COUNT(*) AS A FROM TEST GROUP BY ID HAVING A>0;
testAll();
}
public boolean isMVCC() {
return SysProperties.MVCC;
}
void testAll() throws Exception {
DeleteDbFiles.execute(TestBase.BASE_DIR, null, true);
......@@ -621,14 +626,18 @@ SELECT COUNT(*) AS A FROM TEST GROUP BY ID HAVING A>0;
new TestCsv().runTest(this);
new TestFunctions().runTest(this);
new TestIndex().runTest(this);
new TestLinkedTable().runTest(this);
if(!SysProperties.MVCC) {
new TestLinkedTable().runTest(this);
}
new TestListener().runTest(this);
new TestLob().runTest(this);
new TestLogFile().runTest(this);
new TestMemoryUsage().runTest(this);
new TestMultiConn().runTest(this);
new TestMultiDimension().runTest(this);
new TestMultiThread().runTest(this);
if(!SysProperties.MVCC) {
new TestMultiThread().runTest(this);
}
new TestOpenClose().runTest(this);
new TestOptimizations().runTest(this);
new TestPowerOff().runTest(this);
......@@ -665,6 +674,7 @@ SELECT COUNT(*) AS A FROM TEST GROUP BY ID HAVING A>0;
}
public void beforeTest() throws SQLException {
FileUtils.deleteRecursive("trace.db");
if(networked) {
TcpServer.logInternalErrors = true;
String[] args = ssl ? new String[]{"-tcpSSL", "true"} : new String[0];
......@@ -678,7 +688,8 @@ SELECT COUNT(*) AS A FROM TEST GROUP BY ID HAVING A>0;
}
}
public void afterTest() {
public void afterTest() throws SQLException {
FileUtils.deleteRecursive("trace.db");
if(networked && server != null) {
server.stop();
}
......
......@@ -39,7 +39,7 @@ public abstract class TestBase {
config.beforeTest();
}
protected void stopServerIfRequired() {
protected void stopServerIfRequired() throws SQLException {
config.afterTest();
}
......@@ -569,8 +569,10 @@ public abstract class TestBase {
ArrayList list2 = new ArrayList();
while(rs1.next()) {
check(rs2.next());
list1.add(rs1.getString(1));
list2.add(rs2.getString(1));
String s1 = rs1.getString(1);
list1.add(s1);
String s2 = rs2.getString(1);
list2.add(s2);
}
for(int i=0; i<list1.size(); i++) {
String s = (String)list1.get(i);
......
db1 = H2, org.h2.Driver, jdbc:h2:data/test;LOCK_TIMEOUT=10000;LOCK_MODE=3, sa, sa
db1 = H2 (MVCC), org.h2.Driver, jdbc:h2:data/test_mvcc;MVCC=TRUE, sa, sa
db2 = H2, org.h2.Driver, jdbc:h2:data/test;LOCK_TIMEOUT=10000;LOCK_MODE=3, sa, sa
#xdb2 = H2 (XTEA), org.h2.Driver, jdbc:h2:data/test_xtea;LOCK_TIMEOUT=10000;LOCK_MODE=3;CIPHER=XTEA, sa, sa 123
#xdb3 = H2 (AES), org.h2.Driver, jdbc:h2:data/test_aes;LOCK_TIMEOUT=10000;LOCK_MODE=3;CIPHER=AES, sa, sa 123
#xdb4 = H2, org.h2.Driver, jdbc:h2:data/test;LOCK_TIMEOUT=10000;LOCK_MODE=3;write_mode_log=rws;write_delay=0, sa, sa
#xdb5 = H2_PG, org.postgresql.Driver, jdbc:postgresql://localhost:5435/h2test, sa, sa
db2 = HSQLDB, org.hsqldb.jdbcDriver, jdbc:hsqldb:data/test;hsqldb.default_table_type=cached;sql.enforce_size=true, sa
#db2 = HSQLDB, org.hsqldb.jdbcDriver, jdbc:hsqldb:data/test;hsqldb.default_table_type=cached;sql.enforce_size=true, sa
db3 = Derby, org.apache.derby.jdbc.EmbeddedDriver, jdbc:derby:data/test;create=true, sa, sa
db4 = H2, org.h2.Driver, jdbc:h2:tcp://localhost/data/testServer;LOCK_TIMEOUT=10000;LOCK_MODE=3, sa, sa
......
......@@ -258,9 +258,14 @@ public class TestIndex extends TestBase {
ResultSet rs = stat.executeQuery(sql);
int cols = rs.getMetaData().getColumnCount();
while(rs.next()) {
StringBuffer buff = new StringBuffer();
for(int i=0; i<cols; i++) {
trace("["+i+"]="+rs.getString(i+1));
if(i>0) {
buff.append(", ");
}
buff.append("["+i+"]="+rs.getString(i+1));
}
trace(buff.toString());
}
trace("---done---");
}
......
......@@ -32,7 +32,6 @@ public class TestScriptSimple extends TestBase {
break;
}
sql = sql.trim();
// System.out.println(sql);
if("@reconnect".equals(sql.toLowerCase())) {
reconnect();
} else if(sql.length() == 0) {
......
......@@ -13,6 +13,14 @@ public class TestTransactionIsolation extends TestBase {
Connection conn1, conn2;
public void test() throws Exception {
if(config.isMVCC()) {
// no tests yet
} else {
testTableLevelLocking();
}
}
void testTableLevelLocking() throws Exception {
deleteDb("transactionIsolation");
conn1 = getConnection("transactionIsolation");
check(conn1.getTransactionIsolation(), Connection.TRANSACTION_READ_COMMITTED);
......
/*
* Copyright 2004-2007 H2 Group. Licensed under the H2 License, Version 1.0 (http://h2database.com/html/license.html).
* Initial Developer: H2 Group
*/
package org.h2.test.mvcc;
import java.sql.*;
......@@ -15,26 +19,63 @@ public class TestMVCC {
}
void test() throws Exception {
// TODO Prio 1: document: exclusive table lock still used when altering tables, adding indexes, select ... for update; table level locks are checked
// TODO Prio 1: make unit test work
// TODO Prio 1: free up disk space (for deleted rows and old versions of updated rows) on commit
// TODO Prio 1: ScanIndex: never remove uncommitted data from cache (lost sessionId)
// TODO Prio 1: getRowCount: different row count for different sessions: TableData (remove field?)
// TODO Prio 1: getRowCount: different row count for different sessions: MultiVersionIndex (hash set for deltas?)
// TODO Prio 1: Test with Hibernate
// TODO Prio 2: if MVCC is used, rows of transactions need to fit in memory
// TODO Prio 2: write the log only when committed; remove restriction at Record.canRemove
// TODO Prio 2: getRowCount: different row count for different indexes (MultiVersionIndex)
// TODO Prio 2: getRowCount: different row count for different sessions: TableLink (use different connections?)
// TODO Prio 2: getFirst / getLast in MultiVersionIndex
// TODO Prio 2: Support snapshot isolation (currently read-committed, not repeatable read)
// TODO Prio 2: snapshot isolation (currently read-committed, not repeatable read)
// TODO test: one thread appends, the other selects new data (select * from test where id > ?) and deletes
System.setProperty("h2.mvcc", "true");
// System.setProperty("h2.mvcc", "true");
DeleteDbFiles.execute(null, "test", true);
Class.forName("org.h2.Driver");
c1 = DriverManager.getConnection("jdbc:h2:test");
c1 = DriverManager.getConnection("jdbc:h2:test;MVCC=TRUE", "sa", "sa");
s1 = c1.createStatement();
c2 = DriverManager.getConnection("jdbc:h2:test");
c2 = DriverManager.getConnection("jdbc:h2:test;MVCC=TRUE", "sa", "sa");
s2 = c2.createStatement();
c1.setAutoCommit(false);
c2.setAutoCommit(false);
s1.execute("CREATE TABLE TEST(ID INT)");
s1.execute("INSERT INTO TEST VALUES(1)");
c1.commit();
s1.execute("DELETE FROM TEST");
test(s1, "SELECT COUNT(*) FROM TEST", "0");
c1.commit();
test(s1, "SELECT COUNT(*) FROM TEST", "0");
s1.execute("INSERT INTO TEST VALUES(1)");
s1.execute("DELETE FROM TEST");
c1.commit();
test(s1, "SELECT COUNT(*) FROM TEST", "0");
s1.execute("DROP TABLE TEST");
s1.execute("CREATE TABLE TEST(ID INT PRIMARY KEY, NAME VARCHAR)");
s1.execute("INSERT INTO TEST VALUES(1, 'Hello'), (2, 'World')");
test(s2, "SELECT COUNT(*) FROM TEST", "0");
c1.commit();
test(s2, "SELECT COUNT(*) FROM TEST", "2");
s1.execute("INSERT INTO TEST VALUES(3, '!')");
c1.rollback();
test(s2, "SELECT COUNT(*) FROM TEST", "2");
s1.execute("DROP TABLE TEST");
c1.commit();
s1.execute("CREATE TABLE TEST(ID INT PRIMARY KEY, NAME VARCHAR)");
s1.execute("INSERT INTO TEST VALUES(1, 'Hello')");
s1.execute("DELETE FROM TEST");
test(s2, "SELECT COUNT(*) FROM TEST", "0");
c1.commit();
test(s2, "SELECT COUNT(*) FROM TEST", "0");
s1.execute("DROP TABLE TEST");
c1.commit();
s1.execute("CREATE TABLE TEST(ID INT IDENTITY, NAME VARCHAR)");
s1.execute("INSERT INTO TEST(NAME) VALUES('Ruebezahl')");
test(s2, "SELECT COUNT(*) FROM TEST", "0");
......
--- special grammar and test cases ---------------------------------------------------------------------------------------------
CREATE TABLE Parent(ID INT PRIMARY KEY, Name VARCHAR);
> ok
CREATE TABLE Child(ID INT);
> ok
ALTER TABLE Child ADD FOREIGN KEY(ID) REFERENCES Parent(ID);
> ok
INSERT INTO Parent VALUES(1, '0'), (2, '0'), (3, '0');
> update count: 3
INSERT INTO Child VALUES(1);
> update count: 1
ALTER TABLE Parent ALTER COLUMN Name BOOLEAN NULL;
> ok
DELETE FROM Parent WHERE ID=3;
> update count: 1
DROP TABLE Parent, Child;
> ok
set autocommit false;
> ok
......
......@@ -500,4 +500,6 @@ prorettype pronamespace groname inlining nopmd openfire joda fastutil ibatis ign
irstv trac iict geosysin fukushima yusuke msi odbcad recent viewed calculation installs embedding relation
resizing translator liqui prepends liquibase typo restarting refactorings manage review
mathematicians instantiation homepage supporter grained tilde subscribe baseline wrapped bundle finer relying dangerous
finalizer textbase newsfeeds quicksort
\ No newline at end of file
finalizer textbase newsfeeds quicksort
prio zvikico incrementally nocheck differently eng admins problog nio though typepad channels rolling
lightweight builder
\ No newline at end of file
Markdown 格式
0%
您添加了 0 到此讨论。请谨慎行事。
请先完成此评论的编辑!
注册 或者 后发表评论