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

deadlock detection

上级 14dee4d0
...@@ -17,10 +17,13 @@ Change Log ...@@ -17,10 +17,13 @@ Change Log
<h2>Next Version (unreleased)</h2> <h2>Next Version (unreleased)</h2>
<ul> <ul>
<li>The Lucene fulltext index was always re-created when opening a database with fulltext index enabled. <li>Deadlocks are now detected. One transaction is rolled back automatically.
</li><li>Support for overloaded Java methods. A user defined function can now be bound to </li><li>The Lucene fulltext index was always re-created when opening a
multiple Java methods, if the Java methods have the same name but a different number database with fulltext index enabled.
of parameters. Thanks to Gary Tong for providing a patch! </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> </li></ul>
<h2>Version 1.0.73 (2008-05-31)</h2> <h2>Version 1.0.73 (2008-05-31)</h2>
......
...@@ -102,13 +102,13 @@ via PayPal: ...@@ -102,13 +102,13 @@ via PayPal:
</li><li>Pete Haidinyak, USA </li><li>Pete Haidinyak, USA
</li><li>Jun Iyama, Japan </li><li>Jun Iyama, Japan
</li><li>Antonio Casqueiro, Portugal </li><li>Antonio Casqueiro, Portugal
</li><li>lumber-mill.co.jp, Japan
</li><li>Oliver Computing LLC, USA </li><li>Oliver Computing LLC, USA
</li><li>Harpal Grover Consulting Inc., USA </li><li>Harpal Grover Consulting Inc., USA
</li><li>Elisabetta Berlini, Italy </li><li>Elisabetta Berlini, Italy
</li><li>William Gilbert, USA </li><li>William Gilbert, USA
</li><li>Antonio Dieguez, Chile </li><li>Antonio Dieguez, Chile
</li><li><a href="http://ontologyworks.com/">Ontology Works, USA</a> </li><li><a href="http://ontologyworks.com/">Ontology Works, USA</a>
</li><li>lumber-mill.co.jp, Japan
</li></ul> </li></ul>
</div></td></tr></table><!-- analytics --></body></html> </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 @@ ...@@ -11,6 +11,7 @@
23001=Unique index or primary key violation\: {0} 23001=Unique index or primary key violation\: {0}
23002=Referential integrity constraint violation\: {0} 23002=Referential integrity constraint violation\: {0}
23003=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} 42000=Syntax error in SQL statement {0}
42001=Syntax error in SQL statement {0}; expected {1} 42001=Syntax error in SQL statement {0}; expected {1}
42S01=Table {0} already exists 42S01=Table {0} already exists
...@@ -128,7 +129,6 @@ ...@@ -128,7 +129,6 @@
90105=Exception calling user-defined function 90105=Exception calling user-defined function
90106=Cannot truncate {0} 90106=Cannot truncate {0}
90107=Cannot drop {0} because {1} depends on it 90107=Cannot drop {0} because {1} depends on it
90108=Stack overflow (recursive query or function?)
90109=View {0} is invalid\: {1} 90109=View {0} is invalid\: {1}
90110={0} out of range 90110={0} out of range
90111=Error accessing linked table with SQL statement {0}, cause\: {1} 90111=Error accessing linked table with SQL statement {0}, cause\: {1}
......
...@@ -199,7 +199,11 @@ public abstract class Command implements CommandInterface { ...@@ -199,7 +199,11 @@ public abstract class Command implements CommandInterface {
} catch (SQLException e) { } catch (SQLException e) {
database.exceptionThrown(e, sql); database.exceptionThrown(e, sql);
database.checkPowerOff(); database.checkPowerOff();
if (e.getErrorCode() == ErrorCode.DEADLOCK_1) {
session.rollback();
} else {
session.rollbackTo(rollback); session.rollbackTo(rollback);
}
throw e; throw e;
} finally { } finally {
stop(); stop();
......
...@@ -129,7 +129,7 @@ public class AlterTableAddConstraint extends SchemaCommand { ...@@ -129,7 +129,7 @@ public class AlterTableAddConstraint extends SchemaCommand {
} }
} }
if (index == null) { 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); String indexName = table.getSchema().getUniqueIndexName(table, Constants.PREFIX_PRIMARY_KEY);
int id = getObjectId(true, false); int id = getObjectId(true, false);
try { try {
...@@ -254,11 +254,11 @@ public class AlterTableAddConstraint extends SchemaCommand { ...@@ -254,11 +254,11 @@ public class AlterTableAddConstraint extends SchemaCommand {
if (unique) { if (unique) {
// TODO default index (hash or not; memory or not or same as table) // TODO default index (hash or not; memory or not or same as table)
// for unique constraints // for unique constraints
indexType = IndexType.createUnique(t.isPersistent(), false); indexType = IndexType.createUnique(t.getPersistent(), false);
} else { } else {
// TODO default index (memory or not or same as table) for unique // TODO default index (memory or not or same as table) for unique
// constraints // constraints
indexType = IndexType.createNonUnique(t.isPersistent()); indexType = IndexType.createNonUnique(t.getPersistent());
} }
indexType.setBelongsToConstraint(true); indexType.setBelongsToConstraint(true);
String prefix = constraintName == null ? "CONSTRAINT" : constraintName; String prefix = constraintName == null ? "CONSTRAINT" : constraintName;
...@@ -301,7 +301,7 @@ public class AlterTableAddConstraint extends SchemaCommand { ...@@ -301,7 +301,7 @@ public class AlterTableAddConstraint extends SchemaCommand {
} }
private boolean canUseUniqueIndex(Index idx, Table table, IndexColumn[] cols) { 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; return false;
} }
Column[] indexCols = idx.getColumns(); Column[] indexCols = idx.getColumns();
......
...@@ -234,7 +234,7 @@ public class AlterTableAlterColumn extends SchemaCommand { ...@@ -234,7 +234,7 @@ public class AlterTableAlterColumn extends SchemaCommand {
newColumns.remove(position); newColumns.remove(position);
newColumns.add(position, newColumn); newColumns.add(position, newColumn);
} }
boolean persistent = table.isPersistent(); boolean persistent = table.getPersistent();
// create a table object in order to get the SQL statement // create a table object in order to get the SQL statement
// can't just use this table, because most column objects are 'shared' // can't just use this table, because most column objects are 'shared'
// with the old table // with the old table
...@@ -253,7 +253,7 @@ public class AlterTableAlterColumn extends SchemaCommand { ...@@ -253,7 +253,7 @@ public class AlterTableAlterColumn extends SchemaCommand {
continue; continue;
} else if (child instanceof Index) { } else if (child instanceof Index) {
Index idx = (Index) child; Index idx = (Index) child;
if (idx.getIndexType().belongsToConstraint()) { if (idx.getIndexType().getBelongsToConstraint()) {
continue; continue;
} }
} }
...@@ -419,7 +419,7 @@ public class AlterTableAlterColumn extends SchemaCommand { ...@@ -419,7 +419,7 @@ public class AlterTableAlterColumn extends SchemaCommand {
continue; continue;
} }
IndexType indexType = index.getIndexType(); 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()); throw Message.getSQLException(ErrorCode.COLUMN_IS_PART_OF_INDEX_1, index.getSQL());
} }
} }
......
...@@ -60,7 +60,7 @@ public class CreateIndex extends SchemaCommand { ...@@ -60,7 +60,7 @@ public class CreateIndex extends SchemaCommand {
Table table = getSchema().getTableOrView(session, tableName); Table table = getSchema().getTableOrView(session, tableName);
session.getUser().checkRight(table, Right.ALL); session.getUser().checkRight(table, Right.ALL);
table.lock(session, true, true); table.lock(session, true, true);
if (!table.isPersistent()) { if (!table.getPersistent()) {
persistent = false; persistent = false;
} }
int id = getObjectId(true, false); int id = getObjectId(true, false);
......
...@@ -299,7 +299,7 @@ public class ScriptCommand extends ScriptBase { ...@@ -299,7 +299,7 @@ public class ScriptCommand extends ScriptBase {
ObjectArray indexes = table.getIndexes(); ObjectArray indexes = table.getIndexes();
for (int j = 0; indexes != null && j < indexes.size(); j++) { for (int j = 0; indexes != null && j < indexes.size(); j++) {
Index index = (Index) indexes.get(j); Index index = (Index) indexes.get(j);
if (!index.getIndexType().belongsToConstraint()) { if (!index.getIndexType().getBelongsToConstraint()) {
add(index.getCreateSQL(), false); add(index.getCreateSQL(), false);
} }
} }
......
...@@ -218,7 +218,7 @@ public class Select extends Query { ...@@ -218,7 +218,7 @@ public class Select extends Query {
ObjectArray indexes = topTableFilter.getTable().getIndexes(); ObjectArray indexes = topTableFilter.getTable().getIndexes();
for (int i = 0; indexes != null && i < indexes.size(); i++) { for (int i = 0; indexes != null && i < indexes.size(); i++) {
Index index = (Index) indexes.get(i); Index index = (Index) indexes.get(i);
if (index.getIndexType().isScan()) { if (index.getIndexType().getScan()) {
continue; continue;
} }
if (isGroupSortedIndex(index)) { if (isGroupSortedIndex(index)) {
...@@ -391,7 +391,7 @@ public class Select extends Query { ...@@ -391,7 +391,7 @@ public class Select extends Query {
// can't use the scan index // can't use the scan index
continue; continue;
} }
if (index.getIndexType().isHash()) { if (index.getIndexType().getHash()) {
continue; continue;
} }
IndexColumn[] indexCols = index.getIndexColumns(); IndexColumn[] indexCols = index.getIndexColumns();
...@@ -713,10 +713,10 @@ public class Select extends Query { ...@@ -713,10 +713,10 @@ public class Select extends Query {
boolean ascending = columnIndex.getIndexColumns()[0].sortType == SortOrder.ASCENDING; boolean ascending = columnIndex.getIndexColumns()[0].sortType == SortOrder.ASCENDING;
Index current = topTableFilter.getIndex(); Index current = topTableFilter.getIndex();
// if another index is faster // 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(); IndexType type = columnIndex.getIndexType();
// hash indexes don't work, and unique single column indexes don't work // 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); topTableFilter.setIndex(columnIndex);
isDistinctQuery = true; isDistinctQuery = true;
} }
...@@ -727,7 +727,7 @@ public class Select extends Query { ...@@ -727,7 +727,7 @@ public class Select extends Query {
if (sort != null && !isQuickAggregateQuery && !isGroupQuery) { if (sort != null && !isQuickAggregateQuery && !isGroupQuery) {
Index index = getSortIndex(); Index index = getSortIndex();
Index current = topTableFilter.getIndex(); Index current = topTableFilter.getIndex();
if (index != null && (current.getIndexType().isScan() || current == index)) { if (index != null && (current.getIndexType().getScan() || current == index)) {
topTableFilter.setIndex(index); topTableFilter.setIndex(index);
if (!distinct || isDistinctQuery) { if (!distinct || isDistinctQuery) {
// sort using index would not work correctly for distinct result sets // sort using index would not work correctly for distinct result sets
...@@ -739,7 +739,7 @@ public class Select extends Query { ...@@ -739,7 +739,7 @@ public class Select extends Query {
if (SysProperties.OPTIMIZE_GROUP_SORTED && !isQuickAggregateQuery && isGroupQuery && getGroupByExpressionCount() > 0) { if (SysProperties.OPTIMIZE_GROUP_SORTED && !isQuickAggregateQuery && isGroupQuery && getGroupByExpressionCount() > 0) {
Index index = getGroupSortedIndex(); Index index = getGroupSortedIndex();
Index current = topTableFilter.getIndex(); Index current = topTableFilter.getIndex();
if (index != null && (current.getIndexType().isScan() || current == index)) { if (index != null && (current.getIndexType().getScan() || current == index)) {
topTableFilter.setIndex(index); topTableFilter.setIndex(index);
isGroupSortedQuery = true; isGroupSortedQuery = true;
} }
......
...@@ -162,6 +162,22 @@ public class ErrorCode { ...@@ -162,6 +162,22 @@ public class ErrorCode {
// 3B: savepoint exception // 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 // 42: syntax error or access rule violation
/** /**
* The error with code <code>42000</code> is thrown when * The error with code <code>42000</code> is thrown when
...@@ -1790,7 +1806,7 @@ public class ErrorCode { ...@@ -1790,7 +1806,7 @@ public class ErrorCode {
*/ */
public static final int CAN_ONLY_ASSIGN_TO_VARIABLE_1 = 90137; public static final int CAN_ONLY_ASSIGN_TO_VARIABLE_1 = 90137;
// next is 90073, 90108 // next is 90108, 90138
private ErrorCode() { private ErrorCode() {
// utility class // utility class
......
...@@ -191,6 +191,12 @@ public class Constants { ...@@ -191,6 +191,12 @@ public class Constants {
*/ */
public static final String DBA_NAME = "DBA"; 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 * The default value of the ALLOW_LITERALS setting
*/ */
...@@ -305,7 +311,7 @@ public class Constants { ...@@ -305,7 +311,7 @@ public class Constants {
* For testing, the lock timeout is smaller than for interactive use cases. * For testing, the lock timeout is smaller than for interactive use cases.
* This value could be increased to about 5 or 10 seconds. * 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. * The block size for I/O operations.
......
...@@ -1377,7 +1377,7 @@ public class Database implements DataHandler { ...@@ -1377,7 +1377,7 @@ public class Database implements DataHandler {
boolean inTempDir = readOnly; boolean inTempDir = readOnly;
String name = databaseName; String name = databaseName;
if (!persistent) { if (!persistent) {
name = FileSystem.MEMORY_PREFIX + name; name = FileSystem.PREFIX_MEMORY + name;
} }
return FileUtils.createTempFile(name, Constants.SUFFIX_TEMP_FILE, true, inTempDir); return FileUtils.createTempFile(name, Constants.SUFFIX_TEMP_FILE, true, inTempDir);
} catch (IOException e) { } catch (IOException e) {
......
...@@ -87,6 +87,7 @@ public class Session implements SessionInterface { ...@@ -87,6 +87,7 @@ public class Session implements SessionInterface {
private int queryTimeout = SysProperties.getMaxQueryTimeout(); private int queryTimeout = SysProperties.getMaxQueryTimeout();
private int lastUncommittedDelete; private int lastUncommittedDelete;
private boolean commitOrRollbackDisabled; private boolean commitOrRollbackDisabled;
private Table waitForLock;
public Session() { public Session() {
// nothing to do // nothing to do
...@@ -518,11 +519,11 @@ public class Session implements SessionInterface { ...@@ -518,11 +519,11 @@ public class Session implements SessionInterface {
ObjectArray list = new ObjectArray(localTempTables.values()); ObjectArray list = new ObjectArray(localTempTables.values());
for (int i = 0; i < list.size(); i++) { for (int i = 0; i < list.size(); i++) {
Table table = (Table) list.get(i); Table table = (Table) list.get(i);
if (closeSession || table.isOnCommitDrop()) { if (closeSession || table.getOnCommitDrop()) {
table.setModified(); table.setModified();
localTempTables.remove(table.getName()); localTempTables.remove(table.getName());
table.removeChildrenAndResources(this); table.removeChildrenAndResources(this);
} else if (table.isOnCommitTruncate()) { } else if (table.getOnCommitTruncate()) {
table.truncate(this); table.truncate(this);
} }
} }
...@@ -848,6 +849,10 @@ public class Session implements SessionInterface { ...@@ -848,6 +849,10 @@ public class Session implements SessionInterface {
return serialId; return serialId;
} }
public String toString() {
return "#" + serialId + " (user: " + user.getName() + ")";
}
public void setUndoLogEnabled(boolean b) { public void setUndoLogEnabled(boolean b) {
this.undoLogEnabled = b; this.undoLogEnabled = b;
} }
...@@ -944,4 +949,12 @@ public class Session implements SessionInterface { ...@@ -944,4 +949,12 @@ public class Session implements SessionInterface {
return queryTimeout; 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 { ...@@ -194,7 +194,7 @@ public abstract class BaseIndex extends SchemaObjectBase implements Index {
int index = column.getColumnId(); int index = column.getColumnId();
int mask = masks[index]; int mask = masks[index];
if ((mask & IndexCondition.EQUALITY) == IndexCondition.EQUALITY) { if ((mask & IndexCondition.EQUALITY) == IndexCondition.EQUALITY) {
if (i == columns.length - 1 && getIndexType().isUnique()) { if (i == columns.length - 1 && getIndexType().getUnique()) {
cost = getLookupCost(rowCount) + 1; cost = getLookupCost(rowCount) + 1;
break; break;
} }
...@@ -301,7 +301,7 @@ public abstract class BaseIndex extends SchemaObjectBase implements Index { ...@@ -301,7 +301,7 @@ public abstract class BaseIndex extends SchemaObjectBase implements Index {
StringBuffer buff = new StringBuffer(); StringBuffer buff = new StringBuffer();
buff.append("CREATE "); buff.append("CREATE ");
buff.append(indexType.getSQL()); buff.append(indexType.getSQL());
if (!indexType.isPrimaryKey()) { if (!indexType.getPrimaryKey()) {
buff.append(' '); buff.append(' ');
buff.append(quotedName); buff.append(quotedName);
} }
......
...@@ -62,7 +62,7 @@ public class BtreeLeaf extends BtreePage { ...@@ -62,7 +62,7 @@ public class BtreeLeaf extends BtreePage {
SearchRow row = (SearchRow) pageData.get(i); SearchRow row = (SearchRow) pageData.get(i);
int comp = index.compareRows(row, newRow); int comp = index.compareRows(row, newRow);
if (comp == 0) { if (comp == 0) {
if (index.indexType.isUnique()) { if (index.indexType.getUnique()) {
if (!index.isNull(newRow)) { if (!index.isNull(newRow)) {
throw index.getDuplicateKeyException(); throw index.getDuplicateKeyException();
} }
......
...@@ -82,7 +82,7 @@ public class BtreeNode extends BtreePage { ...@@ -82,7 +82,7 @@ public class BtreeNode extends BtreePage {
SearchRow row = getData(i); SearchRow row = getData(i);
int comp = index.compareRows(row, newRow); int comp = index.compareRows(row, newRow);
if (comp == 0) { if (comp == 0) {
if (index.indexType.isUnique()) { if (index.indexType.getUnique()) {
if (!index.isNull(newRow)) { if (!index.isNull(newRow)) {
throw index.getDuplicateKeyException(); throw index.getDuplicateKeyException();
} }
......
...@@ -10,7 +10,7 @@ package org.h2.index; ...@@ -10,7 +10,7 @@ package org.h2.index;
* Represents information about the properties of an index * Represents information about the properties of an index
*/ */
public class IndexType { public class IndexType {
private boolean isPrimaryKey, isPersistent, isUnique, isHash, isScan; private boolean primaryKey, persistent, unique, hash, scan;
private boolean belongsToConstraint; private boolean belongsToConstraint;
/** /**
...@@ -22,10 +22,10 @@ public class IndexType { ...@@ -22,10 +22,10 @@ public class IndexType {
*/ */
public static IndexType createPrimaryKey(boolean persistent, boolean hash) { public static IndexType createPrimaryKey(boolean persistent, boolean hash) {
IndexType type = new IndexType(); IndexType type = new IndexType();
type.isPrimaryKey = true; type.primaryKey = true;
type.isPersistent = persistent; type.persistent = persistent;
type.isHash = hash; type.hash = hash;
type.isUnique = true; type.unique = true;
return type; return type;
} }
...@@ -38,9 +38,9 @@ public class IndexType { ...@@ -38,9 +38,9 @@ public class IndexType {
*/ */
public static IndexType createUnique(boolean persistent, boolean hash) { public static IndexType createUnique(boolean persistent, boolean hash) {
IndexType type = new IndexType(); IndexType type = new IndexType();
type.isUnique = true; type.unique = true;
type.isPersistent = persistent; type.persistent = persistent;
type.isHash = hash; type.hash = hash;
return type; return type;
} }
...@@ -52,7 +52,7 @@ public class IndexType { ...@@ -52,7 +52,7 @@ public class IndexType {
*/ */
public static IndexType createNonUnique(boolean persistent) { public static IndexType createNonUnique(boolean persistent) {
IndexType type = new IndexType(); IndexType type = new IndexType();
type.isPersistent = persistent; type.persistent = persistent;
return type; return type;
} }
...@@ -64,8 +64,8 @@ public class IndexType { ...@@ -64,8 +64,8 @@ public class IndexType {
*/ */
public static IndexType createScan(boolean persistent) { public static IndexType createScan(boolean persistent) {
IndexType type = new IndexType(); IndexType type = new IndexType();
type.isPersistent = persistent; type.persistent = persistent;
type.isScan = true; type.scan = true;
return type; return type;
} }
...@@ -84,7 +84,7 @@ public class IndexType { ...@@ -84,7 +84,7 @@ public class IndexType {
* *
* @return if the index belongs to a constraint * @return if the index belongs to a constraint
*/ */
public boolean belongsToConstraint() { public boolean getBelongsToConstraint() {
return belongsToConstraint; return belongsToConstraint;
} }
...@@ -93,8 +93,8 @@ public class IndexType { ...@@ -93,8 +93,8 @@ public class IndexType {
* *
* @return true if it is a hash index * @return true if it is a hash index
*/ */
public boolean isHash() { public boolean getHash() {
return isHash; return hash;
} }
/** /**
...@@ -102,8 +102,8 @@ public class IndexType { ...@@ -102,8 +102,8 @@ public class IndexType {
* *
* @return true if it is persistent * @return true if it is persistent
*/ */
public boolean isPersistent() { public boolean getPersistent() {
return isPersistent; return persistent;
} }
/** /**
...@@ -111,8 +111,8 @@ public class IndexType { ...@@ -111,8 +111,8 @@ public class IndexType {
* *
* @return true if it references a primary key constraint * @return true if it references a primary key constraint
*/ */
public boolean isPrimaryKey() { public boolean getPrimaryKey() {
return isPrimaryKey; return primaryKey;
} }
/** /**
...@@ -120,8 +120,8 @@ public class IndexType { ...@@ -120,8 +120,8 @@ public class IndexType {
* *
* @return true if it is * @return true if it is
*/ */
public boolean isUnique() { public boolean getUnique() {
return isUnique; return unique;
} }
/** /**
...@@ -131,16 +131,16 @@ public class IndexType { ...@@ -131,16 +131,16 @@ public class IndexType {
*/ */
public String getSQL() { public String getSQL() {
StringBuffer buff = new StringBuffer(); StringBuffer buff = new StringBuffer();
if (isPrimaryKey) { if (primaryKey) {
buff.append("PRIMARY KEY"); buff.append("PRIMARY KEY");
if (isHash) { if (hash) {
buff.append(" HASH"); buff.append(" HASH");
} }
} else { } else {
if (isUnique) { if (unique) {
buff.append("UNIQUE "); buff.append("UNIQUE ");
} }
if (isHash) { if (hash) {
buff.append("HASH "); buff.append("HASH ");
} }
buff.append("INDEX"); buff.append("INDEX");
...@@ -153,8 +153,8 @@ public class IndexType { ...@@ -153,8 +153,8 @@ public class IndexType {
* *
* @return true if it is * @return true if it is
*/ */
public boolean isScan() { public boolean getScan() {
return isScan; return scan;
} }
} }
...@@ -72,7 +72,7 @@ public class ScanIndex extends BaseIndex { ...@@ -72,7 +72,7 @@ public class ScanIndex extends BaseIndex {
} else { } else {
storage.truncate(session); storage.truncate(session);
} }
if (tableData.getContainsLargeObject() && tableData.isPersistent()) { if (tableData.getContainsLargeObject() && tableData.getPersistent()) {
ValueLob.removeAllForTable(database, table.getId()); ValueLob.removeAllForTable(database, table.getId());
} }
tableData.setRowCount(0); tableData.setRowCount(0);
......
...@@ -52,7 +52,7 @@ public class TreeIndex extends BaseIndex { ...@@ -52,7 +52,7 @@ public class TreeIndex extends BaseIndex {
Row r = n.row; Row r = n.row;
int compare = compareRows(row, r); int compare = compareRows(row, r);
if (compare == 0) { if (compare == 0) {
if (indexType.isUnique()) { if (indexType.getUnique()) {
if (!isNull(row)) { if (!isNull(row)) {
throw getDuplicateKeyException(); throw getDuplicateKeyException();
} }
......
...@@ -11,6 +11,7 @@ ...@@ -11,6 +11,7 @@
23001=Eindeutiger Index oder Primarschl\u00FCssel verletzt\: {0} 23001=Eindeutiger Index oder Primarschl\u00FCssel verletzt\: {0}
23002=Referentielle Integrit\u00E4t verletzt\: {0} 23002=Referentielle Integrit\u00E4t verletzt\: {0}
23003=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} 42000=Syntax Fehler in SQL Befehl {0}
42001=Syntax Fehler in SQL Befehl {0}; erwartet {1} 42001=Syntax Fehler in SQL Befehl {0}; erwartet {1}
42S01=Tabelle {0} besteht bereits 42S01=Tabelle {0} besteht bereits
...@@ -128,7 +129,6 @@ ...@@ -128,7 +129,6 @@
90105=Fehler beim Aufruf eine benutzerdefinierten Funktion 90105=Fehler beim Aufruf eine benutzerdefinierten Funktion
90106=Kann {0} nicht zur\u00FCcksetzen per TRUNCATE 90106=Kann {0} nicht zur\u00FCcksetzen per TRUNCATE
90107=Kann {0} nicht l\u00F6schen weil {1} davon abh\u00E4ngt 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} 90109=View {0} ist ung\u00FCltig\: {1}
90110={0} ausserhalb des Bereichts 90110={0} ausserhalb des Bereichts
90111=Fehler beim Zugriff auf eine verkn\u00FCpfte Tabelle mit SQL Befehl {0}, Grund\: {1} 90111=Fehler beim Zugriff auf eine verkn\u00FCpfte Tabelle mit SQL Befehl {0}, Grund\: {1}
......
...@@ -11,6 +11,7 @@ ...@@ -11,6 +11,7 @@
23001=Unique index or primary key violation\: {0} 23001=Unique index or primary key violation\: {0}
23002=Referential integrity constraint violation\: {0} 23002=Referential integrity constraint violation\: {0}
23003=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} 42000=Syntax error in SQL statement {0}
42001=Syntax error in SQL statement {0}; expected {1} 42001=Syntax error in SQL statement {0}; expected {1}
42S01=Table {0} already exists 42S01=Table {0} already exists
...@@ -128,7 +129,6 @@ ...@@ -128,7 +129,6 @@
90105=Exception calling user-defined function 90105=Exception calling user-defined function
90106=Cannot truncate {0} 90106=Cannot truncate {0}
90107=Cannot drop {0} because {1} depends on it 90107=Cannot drop {0} because {1} depends on it
90108=Stack overflow (recursive query or function?)
90109=View {0} is invalid\: {1} 90109=View {0} is invalid\: {1}
90110={0} out of range 90110={0} out of range
90111=Error accessing linked table with SQL statement {0}, cause\: {1} 90111=Error accessing linked table with SQL statement {0}, cause\: {1}
......
...@@ -11,6 +11,7 @@ ...@@ -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} 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} 23002=\u53C2\u7167\u6574\u5408\u6027\u5236\u7D04\u9055\u53CD\: {0}
23003=\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} 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} 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 42S01=\u30C6\u30FC\u30D6\u30EB {0} \u306F\u3059\u3067\u306B\u5B58\u5728\u3057\u307E\u3059
...@@ -128,7 +129,6 @@ ...@@ -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 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 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 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} 90109=\u30D3\u30E5\u30FC {0} \u304C\u7121\u52B9\u3067\u3059\: {1}
90110={0} \u306F\u7BC4\u56F2\u5916\u3067\u3059 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 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 @@ ...@@ -11,6 +11,7 @@
23001=Naruszenie ograniczenia Klucza Glownego lub Indeksu Unikalnego\: {0} 23001=Naruszenie ograniczenia Klucza Glownego lub Indeksu Unikalnego\: {0}
23002=\#Referential integrity constraint violation\: {0} 23002=\#Referential integrity constraint violation\: {0}
23003=\#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} 42000=Blad skladniowy w wyrazeniu SQL {0}
42001=Blad skladniowy w wyrazeniu SQL {0}; oczekiwano {1} 42001=Blad skladniowy w wyrazeniu SQL {0}; oczekiwano {1}
42S01=Tablela {0} juz istnieje 42S01=Tablela {0} juz istnieje
...@@ -128,7 +129,6 @@ ...@@ -128,7 +129,6 @@
90105=Wyjatek wywoluje funkcje uzytkownika 90105=Wyjatek wywoluje funkcje uzytkownika
90106=Nie mozna obciac {0} 90106=Nie mozna obciac {0}
90107=Nie mozna skasowac {0} poniewaz zalezy od {1} 90107=Nie mozna skasowac {0} poniewaz zalezy od {1}
90108=Przepelnienie stosu (rekursywna kwerenda lub funkcja?)
90109=Widok {0} jest nieprawidlowy 90109=Widok {0} jest nieprawidlowy
90110={0} poza zakresem 90110={0} poza zakresem
90111=\#Error accessing linked table with SQL statement {0}, cause\: {1} 90111=\#Error accessing linked table with SQL statement {0}, cause\: {1}
......
...@@ -11,6 +11,7 @@ ...@@ -11,6 +11,7 @@
23001=Viola\u00E7\u00E3o de \u00EDndice \u00FAnico ou de chave prim\u00E1ria\: {0} 23001=Viola\u00E7\u00E3o de \u00EDndice \u00FAnico ou de chave prim\u00E1ria\: {0}
23002=Viola\u00E7\u00E3o da integridade de restri\u00E7\u00E3o\: {0} 23002=Viola\u00E7\u00E3o da integridade de restri\u00E7\u00E3o\: {0}
23003=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} 42000=Erro de sintax na declara\u00E7\u00E3o SQL {0}
42001=Erro de sintax na declara\u00E7\u00E3o SQL {0}; esperado {1} 42001=Erro de sintax na declara\u00E7\u00E3o SQL {0}; esperado {1}
42S01=Tabela {0} j\u00E1 existe 42S01=Tabela {0} j\u00E1 existe
...@@ -128,7 +129,6 @@ ...@@ -128,7 +129,6 @@
90105=Exce\u00E7\u00E3o na chamada da fun\u00E7\u00E3o definida pelo usu\u00E1rio 90105=Exce\u00E7\u00E3o na chamada da fun\u00E7\u00E3o definida pelo usu\u00E1rio
90106=N\u00E3o pode fazer o truncate {0} 90106=N\u00E3o pode fazer o truncate {0}
90107=N\u00E3o pode apagar {0} por que depende de {1} 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} 90109=Vista {0} \u00E9 inv\u00E1lida\: {1}
90110={0} out of range 90110={0} out of range
90111=Erro ao acessar a tabela lincada com a instru\u00E7\u00E3o SQL {0}, causa\: {1} 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 { ...@@ -199,8 +199,6 @@ public class FtpServer implements Service {
allowTask = true; allowTask = true;
} }
} }
fs = FileSystem.getInstance(root);
root = fs.normalize(root);
} }
public String getURL() { public String getURL() {
...@@ -208,6 +206,8 @@ public class FtpServer implements Service { ...@@ -208,6 +206,8 @@ public class FtpServer implements Service {
} }
public void start() throws SQLException { public void start() throws SQLException {
fs = FileSystem.getInstance(root);
root = fs.normalize(root);
fs.mkdirs(root); fs.mkdirs(root);
serverSocket = NetUtils.createServerSocket(port, false); serverSocket = NetUtils.createServerSocket(port, false);
} }
...@@ -219,6 +219,7 @@ public class FtpServer implements Service { ...@@ -219,6 +219,7 @@ public class FtpServer implements Service {
traceError(e); traceError(e);
} }
serverSocket = null; serverSocket = null;
fs.close();
} }
public boolean isRunning(boolean traceError) { public boolean isRunning(boolean traceError) {
......
...@@ -160,7 +160,7 @@ public abstract class DataPage { ...@@ -160,7 +160,7 @@ public abstract class DataPage {
public abstract void fill(int len); 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. * handler will decide what type of buffer is created.
* *
* @param handler the data handler * @param handler the data handler
...@@ -175,7 +175,7 @@ public abstract class DataPage { ...@@ -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. * handler will decide what type of buffer is created.
* *
* @param handler the data handler * @param handler the data handler
...@@ -215,14 +215,28 @@ public abstract class DataPage { ...@@ -215,14 +215,28 @@ public abstract class DataPage {
return pos; return pos;
} }
/**
* Get the byte array used for this page.
*
* @return the byte array
*/
public byte[] getBytes() { public byte[] getBytes() {
return data; return data;
} }
/**
* Set the position to 0.
*/
public void reset() { public void reset() {
pos = 0; 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) { public void writeDataPageNoSize(DataPage page) {
checkCapacity(page.pos); checkCapacity(page.pos);
// don't write filler // don't write filler
...@@ -231,6 +245,12 @@ public abstract class DataPage { ...@@ -231,6 +245,12 @@ public abstract class DataPage {
pos += len; 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() { public DataPage readDataPageNoSize() {
int len = data.length - pos; int len = data.length - pos;
DataPage page = DataPage.create(handler, len); DataPage page = DataPage.create(handler, len);
...@@ -239,6 +259,13 @@ public abstract class DataPage { ...@@ -239,6 +259,13 @@ public abstract class DataPage {
return page; 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) { public void write(byte[] buff, int off, int len) {
checkCapacity(len); checkCapacity(len);
System.arraycopy(buff, off, data, pos, len); System.arraycopy(buff, off, data, pos, len);
...@@ -258,23 +285,48 @@ public abstract class DataPage { ...@@ -258,23 +285,48 @@ public abstract class DataPage {
pos += len; pos += len;
} }
/**
* Append one single byte.
*
* @param x the value
*/
public void writeByte(byte x) { public void writeByte(byte x) {
data[pos++] = x; data[pos++] = x;
} }
/**
* Read one single byte.
*
* @return the value
*/
public int readByte() { public int readByte() {
return data[pos++]; return data[pos++];
} }
/**
* Read a long value. This method reads two int values and combines them.
*
* @return the long value
*/
public long readLong() { public long readLong() {
return ((long) (readInt()) << 32) + (readInt() & 0xffffffffL); 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) { public void writeLong(long x) {
writeInt((int) (x >>> 32)); writeInt((int) (x >>> 32));
writeInt((int) x); writeInt((int) x);
} }
/**
* Append a value.
*
* @param v the value
*/
public void writeValue(Value v) throws SQLException { public void writeValue(Value v) throws SQLException {
if (SysProperties.CHECK) { if (SysProperties.CHECK) {
checkCapacity(8); checkCapacity(8);
...@@ -454,6 +506,11 @@ public abstract class DataPage { ...@@ -454,6 +506,11 @@ public abstract class DataPage {
} }
} }
/**
* Read a value.
*
* @return the value
*/
public Value readValue() throws SQLException { public Value readValue() throws SQLException {
int dataType = data[pos++]; int dataType = data[pos++];
if (dataType == '-') { if (dataType == '-') {
...@@ -555,6 +612,11 @@ public abstract class DataPage { ...@@ -555,6 +612,11 @@ public abstract class DataPage {
fill(MathUtils.roundUp(pos + 2, Constants.FILE_BLOCK_SIZE)); 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) { public void setPos(int pos) {
this.pos = pos; this.pos = pos;
} }
......
...@@ -701,7 +701,7 @@ public class DiskFile implements CacheWriter { ...@@ -701,7 +701,7 @@ public class DiskFile implements CacheWriter {
* Set the owner of a page. * Set the owner of a page.
* *
* @param page the page id * @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 { public void setPageOwner(int page, int storageId) throws SQLException {
int old = pageOwners.get(page); int old = pageOwners.get(page);
......
...@@ -16,10 +16,25 @@ import java.sql.SQLException; ...@@ -16,10 +16,25 @@ import java.sql.SQLException;
*/ */
public abstract class FileSystem { public abstract class FileSystem {
public static final String MEMORY_PREFIX = "memFS:"; /**
public static final String MEMORY_PREFIX_LZF = "memLZF:"; * The prefix used for an in-memory file system.
public static final String DB_PREFIX = "jdbc:"; */
public static final String ZIP_PREFIX = "zip:"; 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. * Get the file system object.
...@@ -30,16 +45,16 @@ public abstract class FileSystem { ...@@ -30,16 +45,16 @@ public abstract class FileSystem {
public static FileSystem getInstance(String fileName) { public static FileSystem getInstance(String fileName) {
if (isInMemory(fileName)) { if (isInMemory(fileName)) {
return FileSystemMemory.getInstance(); return FileSystemMemory.getInstance();
} else if (fileName.startsWith(DB_PREFIX)) { } else if (fileName.startsWith(PREFIX_DB)) {
return FileSystemDatabase.getInstance(fileName); return FileSystemDatabase.getInstance(fileName);
} else if (fileName.startsWith(ZIP_PREFIX)) { } else if (fileName.startsWith(PREFIX_ZIP)) {
return FileSystemZip.getInstance(); return FileSystemZip.getInstance();
} }
return FileSystemDisk.getInstance(); return FileSystemDisk.getInstance();
} }
private static boolean isInMemory(String fileName) { 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 { ...@@ -247,4 +262,14 @@ public abstract class FileSystem {
* @return the input stream * @return the input stream
*/ */
public abstract InputStream openFileInputStream(String fileName) throws IOException; 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 { ...@@ -95,6 +95,9 @@ public class FileSystemDatabase extends FileSystem {
} }
} }
/**
* Close the underlying database.
*/
public void close() { public void close() {
JdbcUtils.closeSilently(conn); JdbcUtils.closeSilently(conn);
} }
......
...@@ -204,7 +204,7 @@ public class FileSystemMemory extends FileSystem { ...@@ -204,7 +204,7 @@ public class FileSystemMemory extends FileSystem {
synchronized (MEMORY_FILES) { synchronized (MEMORY_FILES) {
FileObjectMemory m = (FileObjectMemory) MEMORY_FILES.get(fileName); FileObjectMemory m = (FileObjectMemory) MEMORY_FILES.get(fileName);
if (m == null) { if (m == null) {
boolean compress = fileName.startsWith(FileSystem.MEMORY_PREFIX_LZF); boolean compress = fileName.startsWith(FileSystem.PREFIX_MEMORY_LZF);
m = new FileObjectMemory(fileName, compress); m = new FileObjectMemory(fileName, compress);
MEMORY_FILES.put(fileName, m); MEMORY_FILES.put(fileName, m);
} }
......
...@@ -221,8 +221,8 @@ public class FileSystemZip extends FileSystem { ...@@ -221,8 +221,8 @@ public class FileSystemZip extends FileSystem {
} }
private String translateFileName(String fileName) { private String translateFileName(String fileName) {
if (fileName.startsWith(FileSystem.ZIP_PREFIX)) { if (fileName.startsWith(FileSystem.PREFIX_ZIP)) {
fileName = fileName.substring(FileSystem.ZIP_PREFIX.length()); fileName = fileName.substring(FileSystem.PREFIX_ZIP.length());
} }
int idx = fileName.indexOf('!'); int idx = fileName.indexOf('!');
if (idx >= 0) { if (idx >= 0) {
......
...@@ -574,7 +574,7 @@ public class MetaTable extends Table { ...@@ -574,7 +574,7 @@ public class MetaTable extends Table {
storageType = "LOCAL TEMPORARY"; storageType = "LOCAL TEMPORARY";
} }
} else { } else {
storageType = table.isPersistent() ? "CACHED" : "MEMORY"; storageType = table.getPersistent() ? "CACHED" : "MEMORY";
} }
add(rows, new String[] { add(rows, new String[] {
// TABLE_CATALOG // TABLE_CATALOG
...@@ -683,7 +683,7 @@ public class MetaTable extends Table { ...@@ -683,7 +683,7 @@ public class MetaTable extends Table {
// TABLE_NAME // TABLE_NAME
tableName, tableName,
// NON_UNIQUE // NON_UNIQUE
index.getIndexType().isUnique() ? "FALSE" : "TRUE", index.getIndexType().getUnique() ? "FALSE" : "TRUE",
// INDEX_NAME // INDEX_NAME
identifier(index.getName()), identifier(index.getName()),
// ORDINAL_POSITION // ORDINAL_POSITION
...@@ -693,11 +693,11 @@ public class MetaTable extends Table { ...@@ -693,11 +693,11 @@ public class MetaTable extends Table {
// CARDINALITY // CARDINALITY
"0", "0",
// PRIMARY_KEY // PRIMARY_KEY
index.getIndexType().isPrimaryKey() ? "TRUE" : "FALSE", index.getIndexType().getPrimaryKey() ? "TRUE" : "FALSE",
// INDEX_TYPE_NAME // INDEX_TYPE_NAME
index.getIndexType().getSQL(), index.getIndexType().getSQL(),
// IS_GENERATED // IS_GENERATED
index.getIndexType().belongsToConstraint() ? "TRUE" : "FALSE", index.getIndexType().getBelongsToConstraint() ? "TRUE" : "FALSE",
// INDEX_TYPE // INDEX_TYPE
"" + DatabaseMetaData.tableIndexOther, "" + DatabaseMetaData.tableIndexOther,
// ASC_OR_DESC // ASC_OR_DESC
...@@ -1486,7 +1486,7 @@ public class MetaTable extends Table { ...@@ -1486,7 +1486,7 @@ public class MetaTable extends Table {
// SESSION_ID // SESSION_ID
"" + s.getId(), "" + s.getId(),
// LOCK_TYPE // LOCK_TYPE
table.isLockExclusive(s) ? "WRITE" : "READ", table.isLockedExclusivelyBy(s) ? "WRITE" : "READ",
}); });
} }
} }
......
...@@ -9,6 +9,8 @@ package org.h2.table; ...@@ -9,6 +9,8 @@ package org.h2.table;
import java.sql.SQLException; import java.sql.SQLException;
import java.util.Comparator; import java.util.Comparator;
import java.util.HashSet; import java.util.HashSet;
import java.util.Iterator;
import org.h2.api.DatabaseEventListener; import org.h2.api.DatabaseEventListener;
import org.h2.constant.ErrorCode; import org.h2.constant.ErrorCode;
import org.h2.constant.SysProperties; import org.h2.constant.SysProperties;
...@@ -82,6 +84,13 @@ public class TableData extends Table implements RecordReader { ...@@ -82,6 +84,13 @@ public class TableData extends Table implements RecordReader {
} }
} }
/**
* Read the given row.
*
* @param session the session
* @param key the position of the row in the file
* @return the row
*/
public Row getRow(Session session, int key) throws SQLException { public Row getRow(Session session, int key) throws SQLException {
return scanIndex.getRow(session, key); return scanIndex.getRow(session, key);
} }
...@@ -133,7 +142,7 @@ public class TableData extends Table implements RecordReader { ...@@ -133,7 +142,7 @@ public class TableData extends Table implements RecordReader {
public Index getUniqueIndex() { public Index getUniqueIndex() {
for (int i = 0; i < indexes.size(); i++) { for (int i = 0; i < indexes.size(); i++) {
Index idx = (Index) indexes.get(i); Index idx = (Index) indexes.get(i);
if (idx.getIndexType().isUnique()) { if (idx.getIndexType().getUnique()) {
return idx; return idx;
} }
} }
...@@ -146,7 +155,7 @@ public class TableData extends Table implements RecordReader { ...@@ -146,7 +155,7 @@ public class TableData extends Table implements RecordReader {
public Index addIndex(Session session, String indexName, int indexId, IndexColumn[] cols, IndexType indexType, public Index addIndex(Session session, String indexName, int indexId, IndexColumn[] cols, IndexType indexType,
int headPos, String indexComment) throws SQLException { int headPos, String indexComment) throws SQLException {
if (indexType.isPrimaryKey()) { if (indexType.getPrimaryKey()) {
for (int i = 0; i < cols.length; i++) { for (int i = 0; i < cols.length; i++) {
Column column = cols[i].column; Column column = cols[i].column;
if (column.getNullable()) { if (column.getNullable()) {
...@@ -156,10 +165,10 @@ public class TableData extends Table implements RecordReader { ...@@ -156,10 +165,10 @@ public class TableData extends Table implements RecordReader {
} }
} }
Index index; Index index;
if (isPersistent() && indexType.isPersistent()) { if (getPersistent() && indexType.getPersistent()) {
index = new BtreeIndex(session, this, indexId, indexName, cols, indexType, headPos); index = new BtreeIndex(session, this, indexId, indexName, cols, indexType, headPos);
} else { } else {
if (indexType.isHash()) { if (indexType.getHash()) {
index = new HashIndex(this, indexId, indexName, cols, indexType); index = new HashIndex(this, indexId, indexName, cols, indexType);
} else { } else {
index = new TreeIndex(this, indexId, indexName, cols, indexType); index = new TreeIndex(this, indexId, indexName, cols, indexType);
...@@ -214,7 +223,7 @@ public class TableData extends Table implements RecordReader { ...@@ -214,7 +223,7 @@ public class TableData extends Table implements RecordReader {
// Need to update, because maybe the index is rebuilt at startup, // Need to update, because maybe the index is rebuilt at startup,
// and so the head pos may have changed, which needs to be stored now. // and so the head pos may have changed, which needs to be stored now.
// addSchemaObject doesn't update the sys table at startup // addSchemaObject doesn't update the sys table at startup
if (index.getIndexType().isPersistent() && !database.getReadOnly() if (index.getIndexType().getPersistent() && !database.getReadOnly()
&& !database.getLog().containsInDoubtTransactions()) { && !database.getLog().containsInDoubtTransactions()) {
// can not save anything in the log file if it contains in-doubt transactions // can not save anything in the log file if it contains in-doubt transactions
database.update(session, index); database.update(session, index);
...@@ -319,7 +328,7 @@ public class TableData extends Table implements RecordReader { ...@@ -319,7 +328,7 @@ public class TableData extends Table implements RecordReader {
rowCount = 0; rowCount = 0;
} }
public boolean isLockExclusive(Session session) { boolean isLockedExclusivelyBy(Session session) {
return lockExclusive == session; return lockExclusive == session;
} }
...@@ -328,7 +337,6 @@ public class TableData extends Table implements RecordReader { ...@@ -328,7 +337,6 @@ public class TableData extends Table implements RecordReader {
if (lockMode == Constants.LOCK_MODE_OFF) { if (lockMode == Constants.LOCK_MODE_OFF) {
return; return;
} }
long max = System.currentTimeMillis() + session.getLockTimeout();
if (!force && database.isMultiVersion()) { if (!force && database.isMultiVersion()) {
// MVCC: update, delete, and insert use a shared lock // MVCC: update, delete, and insert use a shared lock
// select doesn't lock // select doesn't lock
...@@ -339,6 +347,17 @@ public class TableData extends Table implements RecordReader { ...@@ -339,6 +347,17 @@ public class TableData extends Table implements RecordReader {
} }
} }
synchronized (database) { synchronized (database) {
try {
doLock(session, lockMode, exclusive);
} finally {
session.setWaitForLock(null);
}
}
}
private void doLock(Session session, int lockMode, boolean exclusive) throws SQLException {
long max = System.currentTimeMillis() + session.getLockTimeout();
boolean checkDeadlock = false;
while (true) { while (true) {
if (lockExclusive == session) { if (lockExclusive == session) {
return; return;
...@@ -372,6 +391,16 @@ public class TableData extends Table implements RecordReader { ...@@ -372,6 +391,16 @@ public class TableData extends Table implements RecordReader {
return; return;
} }
} }
session.setWaitForLock(this);
if (checkDeadlock) {
ObjectArray sessions = checkDeadlock(session, null);
if (sessions != null) {
throw Message.getSQLException(ErrorCode.DEADLOCK_1, getDeadlockDetails(sessions));
}
} else {
// check for deadlocks from now on
checkDeadlock = true;
}
long now = System.currentTimeMillis(); long now = System.currentTimeMillis();
if (now >= max) { if (now >= max) {
traceLock(session, exclusive, "timeout after " + session.getLockTimeout()); traceLock(session, exclusive, "timeout after " + session.getLockTimeout());
...@@ -389,12 +418,83 @@ public class TableData extends Table implements RecordReader { ...@@ -389,12 +418,83 @@ public class TableData extends Table implements RecordReader {
} }
} }
} }
database.wait(max - now); // don't wait too long so that deadlocks are detected early
long sleep = Math.min(Constants.DEADLOCK_CHECK, (max - now) / 4);
if (sleep == 0) {
sleep = 1;
}
database.wait(sleep);
} catch (InterruptedException e) { } catch (InterruptedException e) {
// ignore // ignore
} }
} }
} }
private String getDeadlockDetails(ObjectArray sessions) {
StringBuffer buff = new StringBuffer();
for (int i = 0; i < sessions.size(); i++) {
buff.append('\n');
Session s = (Session) sessions.get(i);
Table lock = s.getWaitForLock();
buff.append("Session ").append(s).append(" is waiting to lock ").append(lock);
buff.append(" while locking ");
Table[] locks = s.getLocks();
for (int j = 0; j < locks.length; j++) {
if (j > 0) {
buff.append(", ");
}
Table t = locks[j];
buff.append(t);
if (t instanceof TableData) {
if (((TableData) t).lockExclusive == s) {
buff.append(" (exclusive)");
} else {
buff.append(" (shared)");
}
}
}
buff.append('.');
}
return buff.toString();
}
public ObjectArray checkDeadlock(Session session, Session clash) {
// only one deadlock check at any given time
synchronized (TableData.class) {
if (clash == null) {
// verification is started
clash = session;
} else if (clash == session) {
// we found a circle
return new ObjectArray();
}
ObjectArray error = null;
for (Iterator it = lockShared.iterator(); it.hasNext();) {
Session s = (Session) it.next();
if (s == session) {
// it doesn't matter if we have locked the object already
continue;
}
Table t = s.getWaitForLock();
if (t != null) {
error = t.checkDeadlock(s, clash);
if (error != null) {
error.add(session);
break;
}
}
}
if (error == null && lockExclusive != null) {
Table t = lockExclusive.getWaitForLock();
if (t != null) {
error = t.checkDeadlock(lockExclusive, clash);
if (error != null) {
error.add(session);
}
}
}
return error;
}
} }
private void traceLock(Session session, boolean exclusive, String s) { private void traceLock(Session session, boolean exclusive, String s) {
...@@ -417,7 +517,7 @@ public class TableData extends Table implements RecordReader { ...@@ -417,7 +517,7 @@ public class TableData extends Table implements RecordReader {
buff.append("LOCAL "); buff.append("LOCAL ");
} }
buff.append("TEMPORARY "); buff.append("TEMPORARY ");
} else if (isPersistent()) { } else if (getPersistent()) {
buff.append("CACHED "); buff.append("CACHED ");
} else { } else {
buff.append("MEMORY "); buff.append("MEMORY ");
...@@ -473,6 +573,11 @@ public class TableData extends Table implements RecordReader { ...@@ -473,6 +573,11 @@ public class TableData extends Table implements RecordReader {
return row; return row;
} }
/**
* Set the row count of this table.
*
* @param count the row count
*/
public void setRowCount(int count) { public void setRowCount(int count) {
this.rowCount = count; this.rowCount = count;
} }
...@@ -503,6 +608,10 @@ public class TableData extends Table implements RecordReader { ...@@ -503,6 +608,10 @@ public class TableData extends Table implements RecordReader {
invalidate(); invalidate();
} }
public String toString() {
return getSQL();
}
public void checkRename() { public void checkRename() {
// ok // ok
} }
...@@ -542,7 +651,7 @@ public class TableData extends Table implements RecordReader { ...@@ -542,7 +651,7 @@ public class TableData extends Table implements RecordReader {
return lastModificationId; return lastModificationId;
} }
public boolean isClustered() { boolean getClustered() {
return clustered; return clustered;
} }
......
...@@ -66,6 +66,15 @@ public class TableFilter implements ColumnResolver { ...@@ -66,6 +66,15 @@ public class TableFilter implements ColumnResolver {
private boolean foundOne; private boolean foundOne;
private Expression fullCondition; 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) public TableFilter(Session session, Table table, String alias, boolean rightsChecked, Select select)
throws SQLException { throws SQLException {
this.session = session; this.session = session;
...@@ -85,6 +94,13 @@ public class TableFilter implements ColumnResolver { ...@@ -85,6 +94,13 @@ public class TableFilter implements ColumnResolver {
return table; 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 { public void lock(Session session, boolean exclusive, boolean force) throws SQLException {
table.lock(session, exclusive, force); table.lock(session, exclusive, force);
if (join != null) { if (join != null) {
...@@ -92,6 +108,12 @@ public class TableFilter implements ColumnResolver { ...@@ -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 { public PlanItem getBestPlanItem(Session session) throws SQLException {
PlanItem item; PlanItem item;
if (indexConditions.size() == 0) { if (indexConditions.size() == 0) {
...@@ -135,6 +157,11 @@ public class TableFilter implements ColumnResolver { ...@@ -135,6 +157,11 @@ public class TableFilter implements ColumnResolver {
} while (join != null); } while (join != null);
} }
/**
* Set what plan item (index, cost) to use use.
*
* @param item the plan item
*/
public void setPlanItem(PlanItem item) { public void setPlanItem(PlanItem item) {
setIndex(item.getIndex()); setIndex(item.getIndex());
if (join != null) { if (join != null) {
...@@ -144,6 +171,10 @@ public class TableFilter implements ColumnResolver { ...@@ -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 { public void prepare() throws SQLException {
// forget all unused index conditions // forget all unused index conditions
for (int i = 0; i < indexConditions.size(); i++) { for (int i = 0; i < indexConditions.size(); i++) {
...@@ -170,7 +201,12 @@ public class TableFilter implements ColumnResolver { ...@@ -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; this.session = session;
scanCount = 0; scanCount = 0;
if (join != null) { if (join != null) {
...@@ -178,6 +214,9 @@ public class TableFilter implements ColumnResolver { ...@@ -178,6 +214,9 @@ public class TableFilter implements ColumnResolver {
} }
} }
/**
* Reset to the current position.
*/
public void reset() { public void reset() {
if (join != null) { if (join != null) {
join.reset(); join.reset();
...@@ -186,6 +225,11 @@ public class TableFilter implements ColumnResolver { ...@@ -186,6 +225,11 @@ public class TableFilter implements ColumnResolver {
foundOne = false; foundOne = false;
} }
/**
* Check if there are more rows to read.
*
* @return true if there are
*/
public boolean next() throws SQLException { public boolean next() throws SQLException {
boolean alwaysFalse = false; boolean alwaysFalse = false;
if (state == AFTER_LAST) { if (state == AFTER_LAST) {
...@@ -307,9 +351,14 @@ public class TableFilter implements ColumnResolver { ...@@ -307,9 +351,14 @@ public class TableFilter implements ColumnResolver {
return Boolean.TRUE.equals(condition.getBooleanValue(session)); return Boolean.TRUE.equals(condition.getBooleanValue(session));
} }
/**
* Get the current row.
*
* @return the current row, or null
*/
public Row get() throws SQLException { public Row get() throws SQLException {
if (current == null && currentSearchRow != null) { if (current == null && currentSearchRow != null) {
if (table.isClustered()) { if (table.getClustered()) {
current = table.getTemplateRow(); current = table.getTemplateRow();
for (int i = 0; i < currentSearchRow.getColumnCount(); i++) { for (int i = 0; i < currentSearchRow.getColumnCount(); i++) {
current.setValue(i, currentSearchRow.getValue(i)); current.setValue(i, currentSearchRow.getValue(i));
...@@ -321,6 +370,11 @@ public class TableFilter implements ColumnResolver { ...@@ -321,6 +370,11 @@ public class TableFilter implements ColumnResolver {
return current; return current;
} }
/**
* Set the current row.
*
* @param current the current row
*/
public void set(Row current) { public void set(Row current) {
// this is currently only used so that check constraints work - to set // this is currently only used so that check constraints work - to set
// the current (new) row // the current (new) row
...@@ -328,6 +382,12 @@ public class TableFilter implements ColumnResolver { ...@@ -328,6 +382,12 @@ public class TableFilter implements ColumnResolver {
this.currentSearchRow = current; 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() { public String getTableAlias() {
if (alias != null) { if (alias != null) {
return alias; return alias;
...@@ -335,10 +395,21 @@ public class TableFilter implements ColumnResolver { ...@@ -335,10 +395,21 @@ public class TableFilter implements ColumnResolver {
return table.getName(); return table.getName();
} }
/**
* Add an index condition.
*
* @param condition the index condition
*/
public void addIndexCondition(IndexCondition condition) { public void addIndexCondition(IndexCondition condition) {
indexConditions.add(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) { public void addFilterCondition(Expression condition, boolean join) {
if (join) { if (join) {
if (joinCondition == null) { if (joinCondition == null) {
...@@ -355,6 +426,13 @@ public class TableFilter implements ColumnResolver { ...@@ -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 { public void addJoin(TableFilter filter, boolean outer, Expression on) throws SQLException {
if (on != null) { if (on != null) {
on.mapColumns(this, 0); on.mapColumns(this, 0);
...@@ -383,10 +461,21 @@ public class TableFilter implements ColumnResolver { ...@@ -383,10 +461,21 @@ public class TableFilter implements ColumnResolver {
return join; return join;
} }
/**
* Check if this is an outer joined table.
*
* @return true if it is
*/
public boolean isJoinOuter() { public boolean isJoinOuter() {
return outerJoin; 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) { public String getPlanSQL(boolean join) {
StringBuffer buff = new StringBuffer(); StringBuffer buff = new StringBuffer();
if (join) { if (join) {
...@@ -472,6 +561,9 @@ public class TableFilter implements ColumnResolver { ...@@ -472,6 +561,9 @@ public class TableFilter implements ColumnResolver {
this.session = session; this.session = session;
} }
/**
* Remove the joined table
*/
public void removeJoin() { public void removeJoin() {
this.join = null; this.join = null;
} }
...@@ -480,6 +572,9 @@ public class TableFilter implements ColumnResolver { ...@@ -480,6 +572,9 @@ public class TableFilter implements ColumnResolver {
return joinCondition; return joinCondition;
} }
/**
* Remove the join condition.
*/
public void removeJoinCondition() { public void removeJoinCondition() {
this.joinCondition = null; this.joinCondition = null;
} }
...@@ -488,6 +583,9 @@ public class TableFilter implements ColumnResolver { ...@@ -488,6 +583,9 @@ public class TableFilter implements ColumnResolver {
return filterCondition; return filterCondition;
} }
/**
* Remove the filter condition.
*/
public void removeFilterCondition() { public void removeFilterCondition() {
this.filterCondition = null; this.filterCondition = null;
} }
...@@ -499,6 +597,12 @@ public class TableFilter implements ColumnResolver { ...@@ -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) { void optimizeFullCondition(boolean fromOuterJoin) {
if (fullCondition != null) { if (fullCondition != null) {
fullCondition.addFilterConditions(this, fromOuterJoin || outerJoin); fullCondition.addFilterConditions(this, fromOuterJoin || outerJoin);
...@@ -508,6 +612,13 @@ public class TableFilter implements ColumnResolver { ...@@ -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) { public void setEvaluatable(TableFilter filter, boolean b) {
if (filterCondition != null) { if (filterCondition != null) {
filterCondition.setEvaluatable(filter, b); filterCondition.setEvaluatable(filter, b);
...@@ -528,6 +639,13 @@ public class TableFilter implements ColumnResolver { ...@@ -528,6 +639,13 @@ public class TableFilter implements ColumnResolver {
return table.getColumns(); 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() { public Column[] getSystemColumns() {
if (!session.getDatabase().getMode().systemColumns) { if (!session.getDatabase().getMode().systemColumns) {
return null; return null;
......
...@@ -299,6 +299,13 @@ public class TableLink extends Table { ...@@ -299,6 +299,13 @@ public class TableLink extends Table {
return qualifiedTableName; 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 { public PreparedStatement getPreparedStatement(String sql) throws SQLException {
if (conn == null) { if (conn == null) {
throw connectException; throw connectException;
...@@ -362,7 +369,7 @@ public class TableLink extends Table { ...@@ -362,7 +369,7 @@ public class TableLink extends Table {
public Index getUniqueIndex() { public Index getUniqueIndex() {
for (int i = 0; i < indexes.size(); i++) { for (int i = 0; i < indexes.size(); i++) {
Index idx = (Index) indexes.get(i); Index idx = (Index) indexes.get(i);
if (idx.getIndexType().isUnique()) { if (idx.getIndexType().getUnique()) {
return idx; return idx;
} }
} }
......
...@@ -116,6 +116,11 @@ public class TableView extends Table { ...@@ -116,6 +116,11 @@ public class TableView extends Table {
setColumns(cols); setColumns(cols);
} }
/**
* Check if this view is currently invalid.
*
* @return true if it is
*/
public boolean getInvalid() { public boolean getInvalid() {
return createException != null; return createException != null;
} }
...@@ -252,6 +257,11 @@ public class TableView extends Table { ...@@ -252,6 +257,11 @@ public class TableView extends Table {
return null; return null;
} }
/**
* Re-compile the view query.
*
* @param session the session
*/
public void recompile(Session session) throws SQLException { public void recompile(Session session) throws SQLException {
for (int i = 0; i < tables.size(); i++) { for (int i = 0; i < tables.size(); i++) {
Table t = (Table) tables.get(i); Table t = (Table) tables.get(i);
...@@ -308,11 +318,19 @@ public class TableView extends Table { ...@@ -308,11 +318,19 @@ public class TableView extends Table {
return owner; return owner;
} }
public static TableView createTempView(Session s, User owner, Query query) throws SQLException { /**
String tempViewName = s.getNextTempViewName(); * Create a temporary view out of the given query.
Schema mainSchema = s.getDatabase().getSchema(Constants.SCHEMA_MAIN); *
* @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(); 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); false);
if (v.createException != null) { if (v.createException != null) {
throw v.createException; throw v.createException;
......
...@@ -19,6 +19,7 @@ import org.h2.test.db.TestCheckpoint; ...@@ -19,6 +19,7 @@ import org.h2.test.db.TestCheckpoint;
import org.h2.test.db.TestCluster; import org.h2.test.db.TestCluster;
import org.h2.test.db.TestCompatibility; import org.h2.test.db.TestCompatibility;
import org.h2.test.db.TestCsv; import org.h2.test.db.TestCsv;
import org.h2.test.db.TestDeadlock;
import org.h2.test.db.TestEncryptedDb; import org.h2.test.db.TestEncryptedDb;
import org.h2.test.db.TestExclusive; import org.h2.test.db.TestExclusive;
import org.h2.test.db.TestFullText; import org.h2.test.db.TestFullText;
...@@ -168,29 +169,9 @@ java org.h2.test.TestAll timer ...@@ -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 C:\download\Data Concurrency and Consistency.pdf
Console says English but is German
detect deadlock alarm detect deadlock: alarm
not tested: not tested:
PreparedProcedure PREPARE <name>(column,...) AS ... PreparedProcedure PREPARE <name>(column,...) AS ...
...@@ -472,6 +453,7 @@ Roadmap: ...@@ -472,6 +453,7 @@ Roadmap:
new TestCluster().runTest(this); new TestCluster().runTest(this);
new TestCompatibility().runTest(this); new TestCompatibility().runTest(this);
new TestCsv().runTest(this); new TestCsv().runTest(this);
new TestDeadlock().runTest(this);
new TestEncryptedDb().runTest(this); new TestEncryptedDb().runTest(this);
new TestExclusive().runTest(this); new TestExclusive().runTest(this);
new TestFullText().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 { ...@@ -81,7 +81,7 @@ public class TestRandomSQL extends TestBase {
private void deleteDb() throws SQLException { private void deleteDb() throws SQLException {
String name = getDatabaseName(); String name = getDatabaseName();
if (name.startsWith(FileSystem.MEMORY_PREFIX)) { if (name.startsWith(FileSystem.PREFIX_MEMORY)) {
DeleteDbFiles.execute("memFS:/", name, true); DeleteDbFiles.execute("memFS:/", name, true);
} else { } else {
DeleteDbFiles.execute(baseDir, name, true); DeleteDbFiles.execute(baseDir, name, true);
......
...@@ -30,10 +30,10 @@ public class TestFileSystem extends TestBase { ...@@ -30,10 +30,10 @@ public class TestFileSystem extends TestBase {
testDatabaseInMemFileSys(); testDatabaseInMemFileSys();
testDatabaseInJar(); testDatabaseInJar();
testFileSystem(baseDir + "/fs"); 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;TRACE_LEVEL_FILE=3");
testFileSystem("jdbc:h2:mem:fs"); testFileSystem("jdbc:h2:mem:fs");
testFileSystem(FileSystem.MEMORY_PREFIX_LZF); testFileSystem(FileSystem.PREFIX_MEMORY_LZF);
testUserHome(); testUserHome();
} }
...@@ -162,10 +162,10 @@ public class TestFileSystem extends TestBase { ...@@ -162,10 +162,10 @@ public class TestFileSystem extends TestBase {
assertTrue(fs.tryDelete(fsBase + "/test2")); assertTrue(fs.tryDelete(fsBase + "/test2"));
fs.delete(fsBase + "/test"); 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"); fs.createDirs(fsBase + "/testDir/test");
assertTrue(fs.isDirectory(fsBase + "/testDir")); assertTrue(fs.isDirectory(fsBase + "/testDir"));
if (!fsBase.startsWith(FileSystem.DB_PREFIX)) { if (!fsBase.startsWith(FileSystem.PREFIX_DB)) {
fs.deleteRecursive("/testDir"); fs.deleteRecursive("/testDir");
assertTrue(!fs.exists("/testDir")); assertTrue(!fs.exists("/testDir"));
} }
......
...@@ -516,4 +516,5 @@ placing refer informational unlocks memo unlimited unmounted keeping hints ...@@ -516,4 +516,5 @@ placing refer informational unlocks memo unlimited unmounted keeping hints
hides heterogeneous construction rutema prepending rowscn overrides jconsole hides heterogeneous construction rutema prepending rowscn overrides jconsole
mbean explicit directs leaves printing holds covariant redirector piped alarm mbean explicit directs leaves printing holds covariant redirector piped alarm
indicate timezone unmounting beep ignoring gary tong extending respective indicate timezone unmounting beep ignoring gary tong extending respective
overloaded overloaded decide clash involve verification dining recursively originating
\ No newline at end of file philosophers
\ No newline at end of file
Markdown 格式
0%
您添加了 0 到此讨论。请谨慎行事。
请先完成此评论的编辑!
注册 或者 后发表评论