提交 8e2e57e5 authored 作者: Thomas Mueller's avatar Thomas Mueller

deadlock detection

上级 14dee4d0
......@@ -17,10 +17,13 @@ Change Log
<h2>Next Version (unreleased)</h2>
<ul>
<li>The Lucene fulltext index was always re-created when opening a database with fulltext index enabled.
</li><li>Support for overloaded Java methods. A user defined function can now be bound to
multiple Java methods, if the Java methods have the same name but a different number
of parameters. Thanks to Gary Tong for providing a patch!
<li>Deadlocks are now detected. One transaction is rolled back automatically.
</li><li>The Lucene fulltext index was always re-created when opening a
database with fulltext index enabled.
</li><li>Support for overloaded Java methods. A user defined function can
now be bound to multiple Java methods, if the Java methods have the same
name but a different number of parameters. Thanks to Gary Tong for
providing a patch!
</li></ul>
<h2>Version 1.0.73 (2008-05-31)</h2>
......
......@@ -102,13 +102,13 @@ via PayPal:
</li><li>Pete Haidinyak, USA
</li><li>Jun Iyama, Japan
</li><li>Antonio Casqueiro, Portugal
</li><li>lumber-mill.co.jp, Japan
</li><li>Oliver Computing LLC, USA
</li><li>Harpal Grover Consulting Inc., USA
</li><li>Elisabetta Berlini, Italy
</li><li>William Gilbert, USA
</li><li>Antonio Dieguez, Chile
</li><li><a href="http://ontologyworks.com/">Ontology Works, USA</a>
</li><li>lumber-mill.co.jp, Japan
</li></ul>
</div></td></tr></table><!-- analytics --></body></html>
This source diff could not be displayed because it is too large. You can view the blob instead.
......@@ -11,6 +11,7 @@
23001=Unique index or primary key violation\: {0}
23002=Referential integrity constraint violation\: {0}
23003=Referential integrity constraint violation\: {0}
40001=Deadlock detected. The current transaction was rolled back. Details\: {0}
42000=Syntax error in SQL statement {0}
42001=Syntax error in SQL statement {0}; expected {1}
42S01=Table {0} already exists
......@@ -128,7 +129,6 @@
90105=Exception calling user-defined function
90106=Cannot truncate {0}
90107=Cannot drop {0} because {1} depends on it
90108=Stack overflow (recursive query or function?)
90109=View {0} is invalid\: {1}
90110={0} out of range
90111=Error accessing linked table with SQL statement {0}, cause\: {1}
......
......@@ -199,7 +199,11 @@ public abstract class Command implements CommandInterface {
} catch (SQLException e) {
database.exceptionThrown(e, sql);
database.checkPowerOff();
session.rollbackTo(rollback);
if (e.getErrorCode() == ErrorCode.DEADLOCK_1) {
session.rollback();
} else {
session.rollbackTo(rollback);
}
throw e;
} finally {
stop();
......
......@@ -129,7 +129,7 @@ public class AlterTableAddConstraint extends SchemaCommand {
}
}
if (index == null) {
IndexType indexType = IndexType.createPrimaryKey(table.isPersistent(), primaryKeyHash);
IndexType indexType = IndexType.createPrimaryKey(table.getPersistent(), primaryKeyHash);
String indexName = table.getSchema().getUniqueIndexName(table, Constants.PREFIX_PRIMARY_KEY);
int id = getObjectId(true, false);
try {
......@@ -254,11 +254,11 @@ public class AlterTableAddConstraint extends SchemaCommand {
if (unique) {
// TODO default index (hash or not; memory or not or same as table)
// for unique constraints
indexType = IndexType.createUnique(t.isPersistent(), false);
indexType = IndexType.createUnique(t.getPersistent(), false);
} else {
// TODO default index (memory or not or same as table) for unique
// constraints
indexType = IndexType.createNonUnique(t.isPersistent());
indexType = IndexType.createNonUnique(t.getPersistent());
}
indexType.setBelongsToConstraint(true);
String prefix = constraintName == null ? "CONSTRAINT" : constraintName;
......@@ -301,7 +301,7 @@ public class AlterTableAddConstraint extends SchemaCommand {
}
private boolean canUseUniqueIndex(Index idx, Table table, IndexColumn[] cols) {
if (idx.getTable() != table || !idx.getIndexType().isUnique()) {
if (idx.getTable() != table || !idx.getIndexType().getUnique()) {
return false;
}
Column[] indexCols = idx.getColumns();
......
......@@ -234,7 +234,7 @@ public class AlterTableAlterColumn extends SchemaCommand {
newColumns.remove(position);
newColumns.add(position, newColumn);
}
boolean persistent = table.isPersistent();
boolean persistent = table.getPersistent();
// create a table object in order to get the SQL statement
// can't just use this table, because most column objects are 'shared'
// with the old table
......@@ -253,7 +253,7 @@ public class AlterTableAlterColumn extends SchemaCommand {
continue;
} else if (child instanceof Index) {
Index idx = (Index) child;
if (idx.getIndexType().belongsToConstraint()) {
if (idx.getIndexType().getBelongsToConstraint()) {
continue;
}
}
......@@ -419,7 +419,7 @@ public class AlterTableAlterColumn extends SchemaCommand {
continue;
}
IndexType indexType = index.getIndexType();
if (indexType.isPrimaryKey() || indexType.isHash()) {
if (indexType.getPrimaryKey() || indexType.getHash()) {
throw Message.getSQLException(ErrorCode.COLUMN_IS_PART_OF_INDEX_1, index.getSQL());
}
}
......
......@@ -60,7 +60,7 @@ public class CreateIndex extends SchemaCommand {
Table table = getSchema().getTableOrView(session, tableName);
session.getUser().checkRight(table, Right.ALL);
table.lock(session, true, true);
if (!table.isPersistent()) {
if (!table.getPersistent()) {
persistent = false;
}
int id = getObjectId(true, false);
......
......@@ -299,7 +299,7 @@ public class ScriptCommand extends ScriptBase {
ObjectArray indexes = table.getIndexes();
for (int j = 0; indexes != null && j < indexes.size(); j++) {
Index index = (Index) indexes.get(j);
if (!index.getIndexType().belongsToConstraint()) {
if (!index.getIndexType().getBelongsToConstraint()) {
add(index.getCreateSQL(), false);
}
}
......
......@@ -218,7 +218,7 @@ public class Select extends Query {
ObjectArray indexes = topTableFilter.getTable().getIndexes();
for (int i = 0; indexes != null && i < indexes.size(); i++) {
Index index = (Index) indexes.get(i);
if (index.getIndexType().isScan()) {
if (index.getIndexType().getScan()) {
continue;
}
if (isGroupSortedIndex(index)) {
......@@ -391,7 +391,7 @@ public class Select extends Query {
// can't use the scan index
continue;
}
if (index.getIndexType().isHash()) {
if (index.getIndexType().getHash()) {
continue;
}
IndexColumn[] indexCols = index.getIndexColumns();
......@@ -713,10 +713,10 @@ public class Select extends Query {
boolean ascending = columnIndex.getIndexColumns()[0].sortType == SortOrder.ASCENDING;
Index current = topTableFilter.getIndex();
// if another index is faster
if (columnIndex.canFindNext() && ascending && (current == null || current.getIndexType().isScan() || columnIndex == current)) {
if (columnIndex.canFindNext() && ascending && (current == null || current.getIndexType().getScan() || columnIndex == current)) {
IndexType type = columnIndex.getIndexType();
// hash indexes don't work, and unique single column indexes don't work
if (!type.isHash() && (!type.isUnique() || columnIndex.getColumns().length > 1)) {
if (!type.getHash() && (!type.getUnique() || columnIndex.getColumns().length > 1)) {
topTableFilter.setIndex(columnIndex);
isDistinctQuery = true;
}
......@@ -727,7 +727,7 @@ public class Select extends Query {
if (sort != null && !isQuickAggregateQuery && !isGroupQuery) {
Index index = getSortIndex();
Index current = topTableFilter.getIndex();
if (index != null && (current.getIndexType().isScan() || current == index)) {
if (index != null && (current.getIndexType().getScan() || current == index)) {
topTableFilter.setIndex(index);
if (!distinct || isDistinctQuery) {
// sort using index would not work correctly for distinct result sets
......@@ -739,7 +739,7 @@ public class Select extends Query {
if (SysProperties.OPTIMIZE_GROUP_SORTED && !isQuickAggregateQuery && isGroupQuery && getGroupByExpressionCount() > 0) {
Index index = getGroupSortedIndex();
Index current = topTableFilter.getIndex();
if (index != null && (current.getIndexType().isScan() || current == index)) {
if (index != null && (current.getIndexType().getScan() || current == index)) {
topTableFilter.setIndex(index);
isGroupSortedQuery = true;
}
......
......@@ -161,6 +161,22 @@ public class ErrorCode {
public static final int REFERENTIAL_INTEGRITY_VIOLATED_CHILD_EXISTS_1 = 23003;
// 3B: savepoint exception
/**
* The error with code <code>40001</code> is thrown when the database
* engine has detected a deadlock. The transaction of this session has been
* rolled back to solve the problem. A deadlock occurs when a session tries
* to lock a table another session has locked, while the other session wants
* to lock a table the first session has locked. As an example, session 1
* has locked table A, while session 2 has locked table B. If session 1 now
* tries to lock table B and session 2 tries to lock table A, a deadlock has
* occured. Deadlocks that involve more than two sessions are also possible.
* To solve deadlock problems, an application should lock tables always in
* the same order, such as always lock table A before locking table B. For
* details, see <a href="http://en.wikipedia.org/wiki/Deadlock">Wikipedia
* Deadlock</a>.
*/
public static final int DEADLOCK_1 = 40001;
// 42: syntax error or access rule violation
/**
......@@ -1790,7 +1806,7 @@ public class ErrorCode {
*/
public static final int CAN_ONLY_ASSIGN_TO_VARIABLE_1 = 90137;
// next is 90073, 90108
// next is 90108, 90138
private ErrorCode() {
// utility class
......
......@@ -190,6 +190,12 @@ public class Constants {
* there is no administrator user registered.
*/
public static final String DBA_NAME = "DBA";
/**
* The number of milliseconds after which to check for a deadlock if locking
* is not successful.
*/
public static final int DEADLOCK_CHECK = 500;
/**
* The default value of the ALLOW_LITERALS setting
......@@ -305,7 +311,7 @@ public class Constants {
* For testing, the lock timeout is smaller than for interactive use cases.
* This value could be increased to about 5 or 10 seconds.
*/
public static final int INITIAL_LOCK_TIMEOUT = 1000;
public static final int INITIAL_LOCK_TIMEOUT = 2000;
/**
* The block size for I/O operations.
......
......@@ -1377,7 +1377,7 @@ public class Database implements DataHandler {
boolean inTempDir = readOnly;
String name = databaseName;
if (!persistent) {
name = FileSystem.MEMORY_PREFIX + name;
name = FileSystem.PREFIX_MEMORY + name;
}
return FileUtils.createTempFile(name, Constants.SUFFIX_TEMP_FILE, true, inTempDir);
} catch (IOException e) {
......
......@@ -87,6 +87,7 @@ public class Session implements SessionInterface {
private int queryTimeout = SysProperties.getMaxQueryTimeout();
private int lastUncommittedDelete;
private boolean commitOrRollbackDisabled;
private Table waitForLock;
public Session() {
// nothing to do
......@@ -518,11 +519,11 @@ public class Session implements SessionInterface {
ObjectArray list = new ObjectArray(localTempTables.values());
for (int i = 0; i < list.size(); i++) {
Table table = (Table) list.get(i);
if (closeSession || table.isOnCommitDrop()) {
if (closeSession || table.getOnCommitDrop()) {
table.setModified();
localTempTables.remove(table.getName());
table.removeChildrenAndResources(this);
} else if (table.isOnCommitTruncate()) {
} else if (table.getOnCommitTruncate()) {
table.truncate(this);
}
}
......@@ -847,6 +848,10 @@ public class Session implements SessionInterface {
public int hashCode() {
return serialId;
}
public String toString() {
return "#" + serialId + " (user: " + user.getName() + ")";
}
public void setUndoLogEnabled(boolean b) {
this.undoLogEnabled = b;
......@@ -944,4 +949,12 @@ public class Session implements SessionInterface {
return queryTimeout;
}
public void setWaitForLock(Table table) {
this.waitForLock = table;
}
public Table getWaitForLock() {
return waitForLock;
}
}
......@@ -194,7 +194,7 @@ public abstract class BaseIndex extends SchemaObjectBase implements Index {
int index = column.getColumnId();
int mask = masks[index];
if ((mask & IndexCondition.EQUALITY) == IndexCondition.EQUALITY) {
if (i == columns.length - 1 && getIndexType().isUnique()) {
if (i == columns.length - 1 && getIndexType().getUnique()) {
cost = getLookupCost(rowCount) + 1;
break;
}
......@@ -301,7 +301,7 @@ public abstract class BaseIndex extends SchemaObjectBase implements Index {
StringBuffer buff = new StringBuffer();
buff.append("CREATE ");
buff.append(indexType.getSQL());
if (!indexType.isPrimaryKey()) {
if (!indexType.getPrimaryKey()) {
buff.append(' ');
buff.append(quotedName);
}
......
......@@ -62,7 +62,7 @@ public class BtreeLeaf extends BtreePage {
SearchRow row = (SearchRow) pageData.get(i);
int comp = index.compareRows(row, newRow);
if (comp == 0) {
if (index.indexType.isUnique()) {
if (index.indexType.getUnique()) {
if (!index.isNull(newRow)) {
throw index.getDuplicateKeyException();
}
......
......@@ -82,7 +82,7 @@ public class BtreeNode extends BtreePage {
SearchRow row = getData(i);
int comp = index.compareRows(row, newRow);
if (comp == 0) {
if (index.indexType.isUnique()) {
if (index.indexType.getUnique()) {
if (!index.isNull(newRow)) {
throw index.getDuplicateKeyException();
}
......
......@@ -10,7 +10,7 @@ package org.h2.index;
* Represents information about the properties of an index
*/
public class IndexType {
private boolean isPrimaryKey, isPersistent, isUnique, isHash, isScan;
private boolean primaryKey, persistent, unique, hash, scan;
private boolean belongsToConstraint;
/**
......@@ -22,10 +22,10 @@ public class IndexType {
*/
public static IndexType createPrimaryKey(boolean persistent, boolean hash) {
IndexType type = new IndexType();
type.isPrimaryKey = true;
type.isPersistent = persistent;
type.isHash = hash;
type.isUnique = true;
type.primaryKey = true;
type.persistent = persistent;
type.hash = hash;
type.unique = true;
return type;
}
......@@ -38,9 +38,9 @@ public class IndexType {
*/
public static IndexType createUnique(boolean persistent, boolean hash) {
IndexType type = new IndexType();
type.isUnique = true;
type.isPersistent = persistent;
type.isHash = hash;
type.unique = true;
type.persistent = persistent;
type.hash = hash;
return type;
}
......@@ -52,7 +52,7 @@ public class IndexType {
*/
public static IndexType createNonUnique(boolean persistent) {
IndexType type = new IndexType();
type.isPersistent = persistent;
type.persistent = persistent;
return type;
}
......@@ -64,8 +64,8 @@ public class IndexType {
*/
public static IndexType createScan(boolean persistent) {
IndexType type = new IndexType();
type.isPersistent = persistent;
type.isScan = true;
type.persistent = persistent;
type.scan = true;
return type;
}
......@@ -84,7 +84,7 @@ public class IndexType {
*
* @return if the index belongs to a constraint
*/
public boolean belongsToConstraint() {
public boolean getBelongsToConstraint() {
return belongsToConstraint;
}
......@@ -93,8 +93,8 @@ public class IndexType {
*
* @return true if it is a hash index
*/
public boolean isHash() {
return isHash;
public boolean getHash() {
return hash;
}
/**
......@@ -102,8 +102,8 @@ public class IndexType {
*
* @return true if it is persistent
*/
public boolean isPersistent() {
return isPersistent;
public boolean getPersistent() {
return persistent;
}
/**
......@@ -111,8 +111,8 @@ public class IndexType {
*
* @return true if it references a primary key constraint
*/
public boolean isPrimaryKey() {
return isPrimaryKey;
public boolean getPrimaryKey() {
return primaryKey;
}
/**
......@@ -120,8 +120,8 @@ public class IndexType {
*
* @return true if it is
*/
public boolean isUnique() {
return isUnique;
public boolean getUnique() {
return unique;
}
/**
......@@ -131,16 +131,16 @@ public class IndexType {
*/
public String getSQL() {
StringBuffer buff = new StringBuffer();
if (isPrimaryKey) {
if (primaryKey) {
buff.append("PRIMARY KEY");
if (isHash) {
if (hash) {
buff.append(" HASH");
}
} else {
if (isUnique) {
if (unique) {
buff.append("UNIQUE ");
}
if (isHash) {
if (hash) {
buff.append("HASH ");
}
buff.append("INDEX");
......@@ -153,8 +153,8 @@ public class IndexType {
*
* @return true if it is
*/
public boolean isScan() {
return isScan;
public boolean getScan() {
return scan;
}
}
......@@ -72,7 +72,7 @@ public class ScanIndex extends BaseIndex {
} else {
storage.truncate(session);
}
if (tableData.getContainsLargeObject() && tableData.isPersistent()) {
if (tableData.getContainsLargeObject() && tableData.getPersistent()) {
ValueLob.removeAllForTable(database, table.getId());
}
tableData.setRowCount(0);
......
......@@ -52,7 +52,7 @@ public class TreeIndex extends BaseIndex {
Row r = n.row;
int compare = compareRows(row, r);
if (compare == 0) {
if (indexType.isUnique()) {
if (indexType.getUnique()) {
if (!isNull(row)) {
throw getDuplicateKeyException();
}
......
......@@ -11,6 +11,7 @@
23001=Eindeutiger Index oder Primarschl\u00FCssel verletzt\: {0}
23002=Referentielle Integrit\u00E4t verletzt\: {0}
23003=Referentielle Integrit\u00E4t verletzt\: {0}
40001=Eine Verklemmung (Deadlock) ist aufgetreten. Die aktuelle Transaktion wurde rl\u00FCckg\u00E4ngig gemacht. Details\: {0}
42000=Syntax Fehler in SQL Befehl {0}
42001=Syntax Fehler in SQL Befehl {0}; erwartet {1}
42S01=Tabelle {0} besteht bereits
......@@ -128,7 +129,6 @@
90105=Fehler beim Aufruf eine benutzerdefinierten Funktion
90106=Kann {0} nicht zur\u00FCcksetzen per TRUNCATE
90107=Kann {0} nicht l\u00F6schen weil {1} davon abh\u00E4ngt
90108=Stack\u00FCberlauf (Rekursive Abfrage oder Funktion?)
90109=View {0} ist ung\u00FCltig\: {1}
90110={0} ausserhalb des Bereichts
90111=Fehler beim Zugriff auf eine verkn\u00FCpfte Tabelle mit SQL Befehl {0}, Grund\: {1}
......
......@@ -11,6 +11,7 @@
23001=Unique index or primary key violation\: {0}
23002=Referential integrity constraint violation\: {0}
23003=Referential integrity constraint violation\: {0}
40001=Deadlock detected. The current transaction was rolled back. Details\: {0}
42000=Syntax error in SQL statement {0}
42001=Syntax error in SQL statement {0}; expected {1}
42S01=Table {0} already exists
......@@ -128,7 +129,6 @@
90105=Exception calling user-defined function
90106=Cannot truncate {0}
90107=Cannot drop {0} because {1} depends on it
90108=Stack overflow (recursive query or function?)
90109=View {0} is invalid\: {1}
90110={0} out of range
90111=Error accessing linked table with SQL statement {0}, cause\: {1}
......
......@@ -11,6 +11,7 @@
23001=\u30E6\u30CB\u30FC\u30AF\u30A4\u30F3\u30C7\u30C3\u30AF\u30B9\u3001\u307E\u305F\u306F\u30D7\u30E9\u30A4\u30DE\u30EA\u30AD\u30FC\u9055\u53CD\: {0}
23002=\u53C2\u7167\u6574\u5408\u6027\u5236\u7D04\u9055\u53CD\: {0}
23003=\u53C2\u7167\u6574\u5408\u6027\u5236\u7D04\u9055\u53CD\: {0}
40001=\#Deadlock detected. The current transaction was rolled back. Details\: {0}
42000=SQL\u30B9\u30C6\u30FC\u30C8\u30E1\u30F3\u30C8\u306B\u6587\u6CD5\u30A8\u30E9\u30FC\u304C\u3042\u308A\u307E\u3059 {0}
42001=SQL\u30B9\u30C6\u30FC\u30C8\u30E1\u30F3\u30C8\u306B\u6587\u6CD5\u30A8\u30E9\u30FC\u304C\u3042\u308A\u307E\u3059 {0}; \u671F\u5F85\u3055\u308C\u308B\u30B9\u30C6\u30FC\u30C8\u30E1\u30F3\u30C8 {1}
42S01=\u30C6\u30FC\u30D6\u30EB {0} \u306F\u3059\u3067\u306B\u5B58\u5728\u3057\u307E\u3059
......@@ -128,7 +129,6 @@
90105=\u30E6\u30FC\u30B6\u5B9A\u7FA9\u95A2\u6570\u3092\u5B9F\u884C\u4E2D\u306B\u4F8B\u5916\u304C\u767A\u751F\u3057\u307E\u3057\u305F
90106={0} \u3092\u7A7A\u306B\u3067\u304D\u307E\u305B\u3093
90107={1} \u304C\u4F9D\u5B58\u3057\u3066\u3044\u308B\u305F\u3081\u3001{0} \u3092\u30C9\u30ED\u30C3\u30D7\u3059\u308B\u3053\u3068\u306F\u3067\u304D\u307E\u305B\u3093
90108=\u30B9\u30BF\u30C3\u30AF\u30AA\u30FC\u30D0\u30FC\u30D5\u30ED\u30FC (\u518D\u5E30\u7684\u306A\u30AF\u30A8\u30EA\u3001\u95A2\u6570\u306E\u53EF\u80FD\u6027)
90109=\u30D3\u30E5\u30FC {0} \u304C\u7121\u52B9\u3067\u3059\: {1}
90110={0} \u306F\u7BC4\u56F2\u5916\u3067\u3059
90111=SQL\u30B9\u30C6\u30FC\u30C8\u30E1\u30F3\u30C8 {0} \u306B\u3088\u308B\u7D50\u5408\u30C6\u30FC\u30D6\u30EB\u30A2\u30AF\u30BB\u30B9\u30A8\u30E9\u30FC
......
......@@ -11,6 +11,7 @@
23001=Naruszenie ograniczenia Klucza Glownego lub Indeksu Unikalnego\: {0}
23002=\#Referential integrity constraint violation\: {0}
23003=\#Referential integrity constraint violation\: {0}
40001=\#Deadlock detected. The current transaction was rolled back. Details\: {0}
42000=Blad skladniowy w wyrazeniu SQL {0}
42001=Blad skladniowy w wyrazeniu SQL {0}; oczekiwano {1}
42S01=Tablela {0} juz istnieje
......@@ -128,7 +129,6 @@
90105=Wyjatek wywoluje funkcje uzytkownika
90106=Nie mozna obciac {0}
90107=Nie mozna skasowac {0} poniewaz zalezy od {1}
90108=Przepelnienie stosu (rekursywna kwerenda lub funkcja?)
90109=Widok {0} jest nieprawidlowy
90110={0} poza zakresem
90111=\#Error accessing linked table with SQL statement {0}, cause\: {1}
......
......@@ -11,6 +11,7 @@
23001=Viola\u00E7\u00E3o de \u00EDndice \u00FAnico ou de chave prim\u00E1ria\: {0}
23002=Viola\u00E7\u00E3o da integridade de restri\u00E7\u00E3o\: {0}
23003=Viola\u00E7\u00E3o da integridade de restri\u00E7\u00E3o\: {0}
40001=\#Deadlock detected. The current transaction was rolled back. Details\: {0}
42000=Erro de sintax na declara\u00E7\u00E3o SQL {0}
42001=Erro de sintax na declara\u00E7\u00E3o SQL {0}; esperado {1}
42S01=Tabela {0} j\u00E1 existe
......@@ -128,7 +129,6 @@
90105=Exce\u00E7\u00E3o na chamada da fun\u00E7\u00E3o definida pelo usu\u00E1rio
90106=N\u00E3o pode fazer o truncate {0}
90107=N\u00E3o pode apagar {0} por que depende de {1}
90108=Stack overflow (consulta ou fun\u00E7\u00E3o recursiva?)
90109=Vista {0} \u00E9 inv\u00E1lida\: {1}
90110={0} out of range
90111=Erro ao acessar a tabela lincada com a instru\u00E7\u00E3o SQL {0}, causa\: {1}
......
......@@ -199,8 +199,6 @@ public class FtpServer implements Service {
allowTask = true;
}
}
fs = FileSystem.getInstance(root);
root = fs.normalize(root);
}
public String getURL() {
......@@ -208,6 +206,8 @@ public class FtpServer implements Service {
}
public void start() throws SQLException {
fs = FileSystem.getInstance(root);
root = fs.normalize(root);
fs.mkdirs(root);
serverSocket = NetUtils.createServerSocket(port, false);
}
......@@ -219,6 +219,7 @@ public class FtpServer implements Service {
traceError(e);
}
serverSocket = null;
fs.close();
}
public boolean isRunning(boolean traceError) {
......
......@@ -160,7 +160,7 @@ public abstract class DataPage {
public abstract void fill(int len);
/**
* Create a new data page for the given hander. The
* Create a new data page for the given handler. The
* handler will decide what type of buffer is created.
*
* @param handler the data handler
......@@ -175,7 +175,7 @@ public abstract class DataPage {
}
/**
* Create a new data page using the given data for the given hander. The
* Create a new data page using the given data for the given handler. The
* handler will decide what type of buffer is created.
*
* @param handler the data handler
......@@ -215,14 +215,28 @@ public abstract class DataPage {
return pos;
}
/**
* Get the byte array used for this page.
*
* @return the byte array
*/
public byte[] getBytes() {
return data;
}
/**
* Set the position to 0.
*/
public void reset() {
pos = 0;
}
/**
* Append the contents of the given data page to this page.
* The filler is not appended.
*
* @param page the page that will be appended
*/
public void writeDataPageNoSize(DataPage page) {
checkCapacity(page.pos);
// don't write filler
......@@ -231,6 +245,12 @@ public abstract class DataPage {
pos += len;
}
/**
* Read a data page from this page. The data from the current position to
* the end of the page is copied.
*
* @return the new page
*/
public DataPage readDataPageNoSize() {
int len = data.length - pos;
DataPage page = DataPage.create(handler, len);
......@@ -239,6 +259,13 @@ public abstract class DataPage {
return page;
}
/**
* Append a number of bytes to this data page.
*
* @param buff the data
* @param off the offset in the data
* @param len the length in bytes
*/
public void write(byte[] buff, int off, int len) {
checkCapacity(len);
System.arraycopy(buff, off, data, pos, len);
......@@ -258,23 +285,48 @@ public abstract class DataPage {
pos += len;
}
/**
* Append one single byte.
*
* @param x the value
*/
public void writeByte(byte x) {
data[pos++] = x;
}
/**
* Read one single byte.
*
* @return the value
*/
public int readByte() {
return data[pos++];
}
/**
* Read a long value. This method reads two int values and combines them.
*
* @return the long value
*/
public long readLong() {
return ((long) (readInt()) << 32) + (readInt() & 0xffffffffL);
}
/**
* Append a long value. This method writes two int values.
*
* @param x the value
*/
public void writeLong(long x) {
writeInt((int) (x >>> 32));
writeInt((int) x);
}
/**
* Append a value.
*
* @param v the value
*/
public void writeValue(Value v) throws SQLException {
if (SysProperties.CHECK) {
checkCapacity(8);
......@@ -454,6 +506,11 @@ public abstract class DataPage {
}
}
/**
* Read a value.
*
* @return the value
*/
public Value readValue() throws SQLException {
int dataType = data[pos++];
if (dataType == '-') {
......@@ -555,6 +612,11 @@ public abstract class DataPage {
fill(MathUtils.roundUp(pos + 2, Constants.FILE_BLOCK_SIZE));
}
/**
* Set the current read / write position.
*
* @param pos the new position
*/
public void setPos(int pos) {
this.pos = pos;
}
......
......@@ -701,7 +701,7 @@ public class DiskFile implements CacheWriter {
* Set the owner of a page.
*
* @param page the page id
* @param the storage id of this page
* @param storageId the storage id of this page
*/
public void setPageOwner(int page, int storageId) throws SQLException {
int old = pageOwners.get(page);
......
......@@ -16,10 +16,25 @@ import java.sql.SQLException;
*/
public abstract class FileSystem {
public static final String MEMORY_PREFIX = "memFS:";
public static final String MEMORY_PREFIX_LZF = "memLZF:";
public static final String DB_PREFIX = "jdbc:";
public static final String ZIP_PREFIX = "zip:";
/**
* The prefix used for an in-memory file system.
*/
public static final String PREFIX_MEMORY = "memFS:";
/**
* The prefix used for a compressed in-memory file system.
*/
public static final String PREFIX_MEMORY_LZF = "memLZF:";
/**
* The prefix used for a database based file system.
*/
public static final String PREFIX_DB = "jdbc:";
/**
* The prefix used for a read-only zip-file based file system.
*/
public static final String PREFIX_ZIP = "zip:";
/**
* Get the file system object.
......@@ -30,16 +45,16 @@ public abstract class FileSystem {
public static FileSystem getInstance(String fileName) {
if (isInMemory(fileName)) {
return FileSystemMemory.getInstance();
} else if (fileName.startsWith(DB_PREFIX)) {
} else if (fileName.startsWith(PREFIX_DB)) {
return FileSystemDatabase.getInstance(fileName);
} else if (fileName.startsWith(ZIP_PREFIX)) {
} else if (fileName.startsWith(PREFIX_ZIP)) {
return FileSystemZip.getInstance();
}
return FileSystemDisk.getInstance();
}
private static boolean isInMemory(String fileName) {
return fileName != null && (fileName.startsWith(MEMORY_PREFIX) || fileName.startsWith(MEMORY_PREFIX_LZF));
return fileName != null && (fileName.startsWith(PREFIX_MEMORY) || fileName.startsWith(PREFIX_MEMORY_LZF));
}
/**
......@@ -247,4 +262,14 @@ public abstract class FileSystem {
* @return the input stream
*/
public abstract InputStream openFileInputStream(String fileName) throws IOException;
/**
* Close the file system. This call normally does not have an effect, except
* if the file system is kept in a database, in which case the connection is
* closed.
*/
public void close() {
// do nothing
}
}
......@@ -95,6 +95,9 @@ public class FileSystemDatabase extends FileSystem {
}
}
/**
* Close the underlying database.
*/
public void close() {
JdbcUtils.closeSilently(conn);
}
......
......@@ -204,7 +204,7 @@ public class FileSystemMemory extends FileSystem {
synchronized (MEMORY_FILES) {
FileObjectMemory m = (FileObjectMemory) MEMORY_FILES.get(fileName);
if (m == null) {
boolean compress = fileName.startsWith(FileSystem.MEMORY_PREFIX_LZF);
boolean compress = fileName.startsWith(FileSystem.PREFIX_MEMORY_LZF);
m = new FileObjectMemory(fileName, compress);
MEMORY_FILES.put(fileName, m);
}
......
......@@ -221,8 +221,8 @@ public class FileSystemZip extends FileSystem {
}
private String translateFileName(String fileName) {
if (fileName.startsWith(FileSystem.ZIP_PREFIX)) {
fileName = fileName.substring(FileSystem.ZIP_PREFIX.length());
if (fileName.startsWith(FileSystem.PREFIX_ZIP)) {
fileName = fileName.substring(FileSystem.PREFIX_ZIP.length());
}
int idx = fileName.indexOf('!');
if (idx >= 0) {
......
......@@ -574,7 +574,7 @@ public class MetaTable extends Table {
storageType = "LOCAL TEMPORARY";
}
} else {
storageType = table.isPersistent() ? "CACHED" : "MEMORY";
storageType = table.getPersistent() ? "CACHED" : "MEMORY";
}
add(rows, new String[] {
// TABLE_CATALOG
......@@ -683,7 +683,7 @@ public class MetaTable extends Table {
// TABLE_NAME
tableName,
// NON_UNIQUE
index.getIndexType().isUnique() ? "FALSE" : "TRUE",
index.getIndexType().getUnique() ? "FALSE" : "TRUE",
// INDEX_NAME
identifier(index.getName()),
// ORDINAL_POSITION
......@@ -693,11 +693,11 @@ public class MetaTable extends Table {
// CARDINALITY
"0",
// PRIMARY_KEY
index.getIndexType().isPrimaryKey() ? "TRUE" : "FALSE",
index.getIndexType().getPrimaryKey() ? "TRUE" : "FALSE",
// INDEX_TYPE_NAME
index.getIndexType().getSQL(),
// IS_GENERATED
index.getIndexType().belongsToConstraint() ? "TRUE" : "FALSE",
index.getIndexType().getBelongsToConstraint() ? "TRUE" : "FALSE",
// INDEX_TYPE
"" + DatabaseMetaData.tableIndexOther,
// ASC_OR_DESC
......@@ -1486,7 +1486,7 @@ public class MetaTable extends Table {
// SESSION_ID
"" + s.getId(),
// LOCK_TYPE
table.isLockExclusive(s) ? "WRITE" : "READ",
table.isLockedExclusivelyBy(s) ? "WRITE" : "READ",
});
}
}
......
......@@ -66,6 +66,15 @@ public class TableFilter implements ColumnResolver {
private boolean foundOne;
private Expression fullCondition;
/**
* Create a new table filter object.
*
* @param session the session
* @param table the table from where to read data
* @param alias the alias name
* @param rightsChecked true if rights are already checked
* @param select the select statement
*/
public TableFilter(Session session, Table table, String alias, boolean rightsChecked, Select select)
throws SQLException {
this.session = session;
......@@ -85,6 +94,13 @@ public class TableFilter implements ColumnResolver {
return table;
}
/**
* Lock the table. This will also lock joined tables.
*
* @param session the session
* @param exclusive true if an exclusive lock is required
* @param force lock even in the MVCC mode
*/
public void lock(Session session, boolean exclusive, boolean force) throws SQLException {
table.lock(session, exclusive, force);
if (join != null) {
......@@ -92,6 +108,12 @@ public class TableFilter implements ColumnResolver {
}
}
/**
* Get the best plan item (index, cost) to use use for the current join order.
*
* @param session the session
* @return the best plan item
*/
public PlanItem getBestPlanItem(Session session) throws SQLException {
PlanItem item;
if (indexConditions.size() == 0) {
......@@ -135,6 +157,11 @@ public class TableFilter implements ColumnResolver {
} while (join != null);
}
/**
* Set what plan item (index, cost) to use use.
*
* @param item the plan item
*/
public void setPlanItem(PlanItem item) {
setIndex(item.getIndex());
if (join != null) {
......@@ -144,6 +171,10 @@ public class TableFilter implements ColumnResolver {
}
}
/**
* Prepare reading rows. This method will remove all index conditions that
* can not be used, and optimize the conditions.
*/
public void prepare() throws SQLException {
// forget all unused index conditions
for (int i = 0; i < indexConditions.size(); i++) {
......@@ -170,7 +201,12 @@ public class TableFilter implements ColumnResolver {
}
}
public void startQuery(Session session) throws SQLException {
/**
* Start the query. This will reset the scan counts.
*
* @param session the session
*/
public void startQuery(Session session) {
this.session = session;
scanCount = 0;
if (join != null) {
......@@ -178,6 +214,9 @@ public class TableFilter implements ColumnResolver {
}
}
/**
* Reset to the current position.
*/
public void reset() {
if (join != null) {
join.reset();
......@@ -186,6 +225,11 @@ public class TableFilter implements ColumnResolver {
foundOne = false;
}
/**
* Check if there are more rows to read.
*
* @return true if there are
*/
public boolean next() throws SQLException {
boolean alwaysFalse = false;
if (state == AFTER_LAST) {
......@@ -307,9 +351,14 @@ public class TableFilter implements ColumnResolver {
return Boolean.TRUE.equals(condition.getBooleanValue(session));
}
/**
* Get the current row.
*
* @return the current row, or null
*/
public Row get() throws SQLException {
if (current == null && currentSearchRow != null) {
if (table.isClustered()) {
if (table.getClustered()) {
current = table.getTemplateRow();
for (int i = 0; i < currentSearchRow.getColumnCount(); i++) {
current.setValue(i, currentSearchRow.getValue(i));
......@@ -321,6 +370,11 @@ public class TableFilter implements ColumnResolver {
return current;
}
/**
* Set the current row.
*
* @param current the current row
*/
public void set(Row current) {
// this is currently only used so that check constraints work - to set
// the current (new) row
......@@ -328,6 +382,12 @@ public class TableFilter implements ColumnResolver {
this.currentSearchRow = current;
}
/**
* Get the table alias name. If no alias is specified, the table name is
* returned.
*
* @return the alias name
*/
public String getTableAlias() {
if (alias != null) {
return alias;
......@@ -335,10 +395,21 @@ public class TableFilter implements ColumnResolver {
return table.getName();
}
/**
* Add an index condition.
*
* @param condition the index condition
*/
public void addIndexCondition(IndexCondition condition) {
indexConditions.add(condition);
}
/**
* Add a filter condition.
*
* @param condition the condition
* @param join if this is in fact a join condition
*/
public void addFilterCondition(Expression condition, boolean join) {
if (join) {
if (joinCondition == null) {
......@@ -355,6 +426,13 @@ public class TableFilter implements ColumnResolver {
}
}
/**
* Add a joined table.
*
* @param filter the joined table filter
* @param outer if this is an outer join
* @param on the join condition
*/
public void addJoin(TableFilter filter, boolean outer, Expression on) throws SQLException {
if (on != null) {
on.mapColumns(this, 0);
......@@ -383,10 +461,21 @@ public class TableFilter implements ColumnResolver {
return join;
}
/**
* Check if this is an outer joined table.
*
* @return true if it is
*/
public boolean isJoinOuter() {
return outerJoin;
}
/**
* Get the query execution plan text to use for this table filter.
*
* @param join if this is a joined table
* @return the SQL statement snippet
*/
public String getPlanSQL(boolean join) {
StringBuffer buff = new StringBuffer();
if (join) {
......@@ -472,6 +561,9 @@ public class TableFilter implements ColumnResolver {
this.session = session;
}
/**
* Remove the joined table
*/
public void removeJoin() {
this.join = null;
}
......@@ -480,6 +572,9 @@ public class TableFilter implements ColumnResolver {
return joinCondition;
}
/**
* Remove the join condition.
*/
public void removeJoinCondition() {
this.joinCondition = null;
}
......@@ -488,6 +583,9 @@ public class TableFilter implements ColumnResolver {
return filterCondition;
}
/**
* Remove the filter condition.
*/
public void removeFilterCondition() {
this.filterCondition = null;
}
......@@ -499,6 +597,12 @@ public class TableFilter implements ColumnResolver {
}
}
/**
* Optimize the full condition. This will add the full condition to the
* filter condition.
*
* @param fromOuterJoin if this method was called from an outer joined table
*/
void optimizeFullCondition(boolean fromOuterJoin) {
if (fullCondition != null) {
fullCondition.addFilterConditions(this, fromOuterJoin || outerJoin);
......@@ -508,6 +612,13 @@ public class TableFilter implements ColumnResolver {
}
}
/**
* Update the filter and join conditions of this and all joined tables with
* the information that the given table filter can now return rows or not.
*
* @param filter the table filter
* @param b the new flag
*/
public void setEvaluatable(TableFilter filter, boolean b) {
if (filterCondition != null) {
filterCondition.setEvaluatable(filter, b);
......@@ -528,6 +639,13 @@ public class TableFilter implements ColumnResolver {
return table.getColumns();
}
/**
* Get the system columns that this table understands. This is used for
* compatibility with other databases. The columns are only returned if the
* current mode supports system columns.
*
* @return the system columns
*/
public Column[] getSystemColumns() {
if (!session.getDatabase().getMode().systemColumns) {
return null;
......
......@@ -299,6 +299,13 @@ public class TableLink extends Table {
return qualifiedTableName;
}
/**
* Get a prepared statement object for the given statement. Prepared
* statements are kept in a hash map to avoid re-creating them.
*
* @param sql the SQL statement.
* @return the prepared statement
*/
public PreparedStatement getPreparedStatement(String sql) throws SQLException {
if (conn == null) {
throw connectException;
......@@ -362,7 +369,7 @@ public class TableLink extends Table {
public Index getUniqueIndex() {
for (int i = 0; i < indexes.size(); i++) {
Index idx = (Index) indexes.get(i);
if (idx.getIndexType().isUnique()) {
if (idx.getIndexType().getUnique()) {
return idx;
}
}
......
......@@ -116,6 +116,11 @@ public class TableView extends Table {
setColumns(cols);
}
/**
* Check if this view is currently invalid.
*
* @return true if it is
*/
public boolean getInvalid() {
return createException != null;
}
......@@ -252,6 +257,11 @@ public class TableView extends Table {
return null;
}
/**
* Re-compile the view query.
*
* @param session the session
*/
public void recompile(Session session) throws SQLException {
for (int i = 0; i < tables.size(); i++) {
Table t = (Table) tables.get(i);
......@@ -308,11 +318,19 @@ public class TableView extends Table {
return owner;
}
public static TableView createTempView(Session s, User owner, Query query) throws SQLException {
String tempViewName = s.getNextTempViewName();
Schema mainSchema = s.getDatabase().getSchema(Constants.SCHEMA_MAIN);
/**
* Create a temporary view out of the given query.
*
* @param session the session
* @param owner the owner of the query
* @param query the query
* @return the view table
*/
public static TableView createTempView(Session session, User owner, Query query) throws SQLException {
String tempViewName = session.getNextTempViewName();
Schema mainSchema = session.getDatabase().getSchema(Constants.SCHEMA_MAIN);
String querySQL = query.getPlanSQL();
TableView v = new TableView(mainSchema, 0, tempViewName, querySQL, query.getParameters(), null, s,
TableView v = new TableView(mainSchema, 0, tempViewName, querySQL, query.getParameters(), null, session,
false);
if (v.createException != null) {
throw v.createException;
......
......@@ -19,6 +19,7 @@ import org.h2.test.db.TestCheckpoint;
import org.h2.test.db.TestCluster;
import org.h2.test.db.TestCompatibility;
import org.h2.test.db.TestCsv;
import org.h2.test.db.TestDeadlock;
import org.h2.test.db.TestEncryptedDb;
import org.h2.test.db.TestExclusive;
import org.h2.test.db.TestFullText;
......@@ -168,29 +169,9 @@ java org.h2.test.TestAll timer
/*
CREATE ALIAS IF NOT EXISTS FTL_INIT FOR "org.h2.fulltext.FullTextLucene.init";
CALL FTL_INIT();
DROP TABLE IF EXISTS TEST;
CREATE TABLE TEST(ID INT PRIMARY KEY, NAME VARCHAR);
INSERT INTO TEST VALUES(1, 'Hello World');
CALL FTL_CREATE_INDEX('PUBLIC', 'TEST', NULL);
SELECT * FROM FTL_SEARCH('Hello', 0, 0);
SELECT * FROM FTL_SEARCH('Hallo', 0, 0);
INSERT INTO TEST VALUES(2, 'Hallo Welt');
SELECT * FROM FTL_SEARCH('Hello', 0, 0);
SELECT * FROM FTL_SEARCH('Hallo', 0, 0);
CALL FTL_REINDEX();
SELECT * FROM FTL_SEARCH('Hello', 0, 0);
SELECT * FROM FTL_SEARCH('Hallo', 0, 0);
INSERT INTO TEST VALUES(3, 'Hello World');
INSERT INTO TEST VALUES(4, 'Hello World');
INSERT INTO TEST VALUES(5, 'Hello World');
SELECT * FROM FTL_SEARCH('World', 0, 0);
SELECT * FROM FTL_SEARCH('World', 1, 0);
C:\download\Data Concurrency and Consistency.pdf
Console says English but is German
detect deadlock alarm
detect deadlock: alarm
not tested:
PreparedProcedure PREPARE <name>(column,...) AS ...
......@@ -472,6 +453,7 @@ Roadmap:
new TestCluster().runTest(this);
new TestCompatibility().runTest(this);
new TestCsv().runTest(this);
new TestDeadlock().runTest(this);
new TestEncryptedDb().runTest(this);
new TestExclusive().runTest(this);
new TestFullText().runTest(this);
......
/*
* Copyright 2004-2008 H2 Group. Multiple-Licensed under the H2 License,
* Version 1.0, and under the Eclipse Public License, Version 1.0
* (http://h2database.com/html/license.html).
* Initial Developer: H2 Group
*/
package org.h2.test.db;
import java.sql.Connection;
import java.sql.SQLException;
import org.h2.constant.ErrorCode;
import org.h2.test.TestBase;
/**
* Test the deadlock detection mechanism.
*/
public class TestDeadlock extends TestBase {
Connection c1, c2, c3;
volatile SQLException lastException;
public void test() throws Exception {
deleteDb("deadlock");
testDiningPhilosophers();
testLockUpgrade();
testThreePhilosophers();
testNoDeadlock();
}
void init() throws Exception {
c1 = getConnection("deadlock");
c2 = getConnection("deadlock");
c3 = getConnection("deadlock");
c1.createStatement().execute("SET LOCK_TIMEOUT 1000000");
c2.createStatement().execute("SET LOCK_TIMEOUT 1000000");
c3.createStatement().execute("SET LOCK_TIMEOUT 1000000");
c1.setAutoCommit(false);
c2.setAutoCommit(false);
c3.setAutoCommit(false);
lastException = null;
}
void end() throws SQLException {
c1.close();
c2.close();
c3.close();
}
/**
* This class wraps exception handling to simplify creating small threads
* that execute a statement.
*/
abstract class DoIt extends Thread {
abstract void execute() throws SQLException;
public void run() {
try {
execute();
} catch (SQLException e) {
catchDeadlock(e);
}
}
}
void catchDeadlock(SQLException e) {
if (lastException != null) {
lastException.setNextException(e);
} else {
lastException = e;
}
}
void testNoDeadlock() throws Exception {
init();
c1.createStatement().execute("CREATE TABLE TEST_A(ID INT PRIMARY KEY)");
c1.createStatement().execute("CREATE TABLE TEST_B(ID INT PRIMARY KEY)");
c1.createStatement().execute("CREATE TABLE TEST_C(ID INT PRIMARY KEY)");
c1.commit();
c1.createStatement().execute("INSERT INTO TEST_A VALUES(1)");
c2.createStatement().execute("INSERT INTO TEST_B VALUES(1)");
c3.createStatement().execute("INSERT INTO TEST_C VALUES(1)");
DoIt t2 = new DoIt() {
public void execute() throws SQLException {
c1.createStatement().execute("DELETE FROM TEST_B");
c1.commit();
}
};
t2.start();
DoIt t3 = new DoIt() {
public void execute() throws SQLException {
c2.createStatement().execute("DELETE FROM TEST_C");
c2.commit();
}
};
t3.start();
Thread.sleep(500);
try {
c3.createStatement().execute("DELETE FROM TEST_C");
c3.commit();
} catch (SQLException e) {
catchDeadlock(e);
}
t2.join();
t3.join();
if (lastException != null) {
throw lastException;
}
c1.commit();
c2.commit();
c3.commit();
c1.createStatement().execute("DROP TABLE TEST_A, TEST_B, TEST_C");
end();
}
void testThreePhilosophers() throws Exception {
if (config.mvcc) {
return;
}
init();
c1.createStatement().execute("CREATE TABLE TEST_A(ID INT PRIMARY KEY)");
c1.createStatement().execute("CREATE TABLE TEST_B(ID INT PRIMARY KEY)");
c1.createStatement().execute("CREATE TABLE TEST_C(ID INT PRIMARY KEY)");
c1.commit();
c1.createStatement().execute("INSERT INTO TEST_A VALUES(1)");
c2.createStatement().execute("INSERT INTO TEST_B VALUES(1)");
c3.createStatement().execute("INSERT INTO TEST_C VALUES(1)");
DoIt t2 = new DoIt() {
public void execute() throws SQLException {
c1.createStatement().execute("DELETE FROM TEST_B");
c1.commit();
}
};
t2.start();
DoIt t3 = new DoIt() {
public void execute() throws SQLException {
c2.createStatement().execute("DELETE FROM TEST_C");
c2.commit();
}
};
t3.start();
try {
c3.createStatement().execute("DELETE FROM TEST_A");
c3.commit();
} catch (SQLException e) {
catchDeadlock(e);
}
t2.join();
t3.join();
checkDeadlock();
c1.commit();
c2.commit();
c3.commit();
c1.createStatement().execute("DROP TABLE TEST_A, TEST_B, TEST_C");
end();
}
void testLockUpgrade() throws Exception {
if (config.mvcc) {
return;
}
init();
c1.createStatement().execute("CREATE TABLE TEST(ID INT PRIMARY KEY)");
c1.createStatement().execute("INSERT INTO TEST VALUES(1)");
c1.commit();
c1.setTransactionIsolation(Connection.TRANSACTION_SERIALIZABLE);
c2.setTransactionIsolation(Connection.TRANSACTION_SERIALIZABLE);
c1.createStatement().executeQuery("SELECT * FROM TEST");
c2.createStatement().executeQuery("SELECT * FROM TEST");
Thread t1 = new DoIt() {
public void execute() throws SQLException {
c1.createStatement().execute("DELETE FROM TEST");
c1.commit();
}
};
t1.start();
try {
c2.createStatement().execute("DELETE FROM TEST");
c2.commit();
} catch (SQLException e) {
catchDeadlock(e);
}
t1.join();
checkDeadlock();
c1.commit();
c2.commit();
c1.createStatement().execute("DROP TABLE TEST");
end();
}
void testDiningPhilosophers() throws Exception {
if (config.mvcc) {
return;
}
init();
c1.createStatement().execute("CREATE TABLE T1(ID INT)");
c1.createStatement().execute("CREATE TABLE T2(ID INT)");
c1.createStatement().execute("INSERT INTO T1 VALUES(1)");
c2.createStatement().execute("INSERT INTO T2 VALUES(1)");
DoIt t1 = new DoIt() {
public void execute() throws SQLException {
c1.createStatement().execute("INSERT INTO T2 VALUES(2)");
c1.commit();
}
};
t1.start();
try {
c2.createStatement().execute("INSERT INTO T1 VALUES(2)");
} catch (SQLException e) {
catchDeadlock(e);
}
t1.join();
checkDeadlock();
c1.commit();
c2.commit();
c1.createStatement().execute("DROP TABLE T1, T2");
end();
}
private void checkDeadlock() throws Exception {
assertTrue(lastException != null);
assertKnownException(lastException);
assertEquals(ErrorCode.DEADLOCK_1, lastException.getErrorCode());
SQLException e2 = lastException.getNextException();
if (e2 != null) {
// we have two exception, but there may only be one
throw e2;
}
}
}
......@@ -81,7 +81,7 @@ public class TestRandomSQL extends TestBase {
private void deleteDb() throws SQLException {
String name = getDatabaseName();
if (name.startsWith(FileSystem.MEMORY_PREFIX)) {
if (name.startsWith(FileSystem.PREFIX_MEMORY)) {
DeleteDbFiles.execute("memFS:/", name, true);
} else {
DeleteDbFiles.execute(baseDir, name, true);
......
......@@ -30,10 +30,10 @@ public class TestFileSystem extends TestBase {
testDatabaseInMemFileSys();
testDatabaseInJar();
testFileSystem(baseDir + "/fs");
testFileSystem(FileSystem.MEMORY_PREFIX);
testFileSystem(FileSystem.PREFIX_MEMORY);
// testFileSystem("jdbc:h2:mem:fs;TRACE_LEVEL_FILE=3");
testFileSystem("jdbc:h2:mem:fs");
testFileSystem(FileSystem.MEMORY_PREFIX_LZF);
testFileSystem(FileSystem.PREFIX_MEMORY_LZF);
testUserHome();
}
......@@ -162,10 +162,10 @@ public class TestFileSystem extends TestBase {
assertTrue(fs.tryDelete(fsBase + "/test2"));
fs.delete(fsBase + "/test");
if (!fsBase.startsWith(FileSystem.MEMORY_PREFIX) && !fsBase.startsWith(FileSystem.MEMORY_PREFIX_LZF)) {
if (!fsBase.startsWith(FileSystem.PREFIX_MEMORY) && !fsBase.startsWith(FileSystem.PREFIX_MEMORY_LZF)) {
fs.createDirs(fsBase + "/testDir/test");
assertTrue(fs.isDirectory(fsBase + "/testDir"));
if (!fsBase.startsWith(FileSystem.DB_PREFIX)) {
if (!fsBase.startsWith(FileSystem.PREFIX_DB)) {
fs.deleteRecursive("/testDir");
assertTrue(!fs.exists("/testDir"));
}
......
......@@ -516,4 +516,5 @@ placing refer informational unlocks memo unlimited unmounted keeping hints
hides heterogeneous construction rutema prepending rowscn overrides jconsole
mbean explicit directs leaves printing holds covariant redirector piped alarm
indicate timezone unmounting beep ignoring gary tong extending respective
overloaded
\ No newline at end of file
overloaded decide clash involve verification dining recursively originating
philosophers
\ No newline at end of file
Markdown 格式
0%
您添加了 0 到此讨论。请谨慎行事。
请先完成此评论的编辑!
注册 或者 后发表评论