提交 98442f5d authored 作者: Thomas Mueller's avatar Thomas Mueller

--no commit message

--no commit message
上级 180841cb
...@@ -253,7 +253,7 @@ ...@@ -253,7 +253,7 @@
<mkdir dir="docs/javadoc"/> <mkdir dir="docs/javadoc"/>
<javadoc <javadoc
sourcepath="src/main" sourcepath="src/main"
packagenames="org.h2.jdbc.*,org.h2.tools.*,org.h2.api.*" packagenames="org.h2.jdbc.*,org.h2.jdbcx.*,org.h2.tools.*,org.h2.api.*"
doclet="org.h2.tools.doclet.Doclet" doclet="org.h2.tools.doclet.Doclet"
docletpath="bin" docletpath="bin"
/> />
......
...@@ -155,7 +155,7 @@ When using the isolation level 'serializable', dirty reads, non-repeatable reads ...@@ -155,7 +155,7 @@ When using the isolation level 'serializable', dirty reads, non-repeatable reads
<h3>Table Level Locking</h3> <h3>Table Level Locking</h3>
<p> <p>
The database allows multiple concurrent connections to the same database. The database allows multiple concurrent connections to the same database.
To make sure all connections only see consistent data, table level locking is used. To make sure all connections only see consistent data, table level locking is used by default.
This mechanism does not allow high concurrency, but is very fast. This mechanism does not allow high concurrency, but is very fast.
Shared locks and exclusive locks are supported. Shared locks and exclusive locks are supported.
Before reading from a table, the database tries to add a shared lock to the table Before reading from a table, the database tries to add a shared lock to the table
...@@ -929,7 +929,7 @@ INFORMATION_SCHEMA.SETTINGS ...@@ -929,7 +929,7 @@ INFORMATION_SCHEMA.SETTINGS
<tr><td>h2.overflowExceptions</td><td>true</td><td>Throw an exception on integer overflows</td></tr> <tr><td>h2.overflowExceptions</td><td>true</td><td>Throw an exception on integer overflows</td></tr>
<tr><td>h2.recompileAlways</td><td>false</td><td>Always recompile prepared statements</td></tr> <tr><td>h2.recompileAlways</td><td>false</td><td>Always recompile prepared statements</td></tr>
<tr><td>h2.redoBufferSize</td><td>262144</td><td>Size of the redo buffer (used at startup when recovering)</td></tr> <tr><td>h2.redoBufferSize</td><td>262144</td><td>Size of the redo buffer (used at startup when recovering)</td></tr>
<tr><td>h2.runFinalizers</td><td>true</td><td>Run finalizers to detect unclosed connections</td></tr> <tr><td>h2.runFinalize</td><td>true</td><td>Run finalizers to detect unclosed connections</td></tr>
<tr><td>h2.scriptDirectory</td><td></td><td>Relative or absolute directory where the script files are stored to or read from</td></tr> <tr><td>h2.scriptDirectory</td><td></td><td>Relative or absolute directory where the script files are stored to or read from</td></tr>
<tr><td>h2.serverCachedObjects</td><td>64</td><td>TCP Server: number of cached objects per session</td></tr> <tr><td>h2.serverCachedObjects</td><td>64</td><td>TCP Server: number of cached objects per session</td></tr>
<tr><td>h2.serverSmallResultSetSize</td><td>100</td><td>TCP Server: result sets below this size are sent in one block</td></tr> <tr><td>h2.serverSmallResultSetSize</td><td>100</td><td>TCP Server: result sets below this size are sent in one block</td></tr>
......
...@@ -15,6 +15,8 @@ Features ...@@ -15,6 +15,8 @@ Features
<a href="#feature_list"> <a href="#feature_list">
Feature List</a><br /> Feature List</a><br />
<a href="#limitations">
Limitations</a><br />
<a href="#comparison"> <a href="#comparison">
Comparison to Other Database Engines</a><br /> Comparison to Other Database Engines</a><br />
<a href="#products_work_with"> <a href="#products_work_with">
...@@ -43,8 +45,8 @@ Features ...@@ -43,8 +45,8 @@ Features
Database File Layout</a><br /> Database File Layout</a><br />
<a href="#logging_recovery"> <a href="#logging_recovery">
Logging and Recovery</a><br /> Logging and Recovery</a><br />
<a href="#compatibility_modes"> <a href="#compatibility">
Compatibility Modes</a><br /> Compatibility</a><br />
<a href="#trace_options"> <a href="#trace_options">
Using the Trace Options</a><br /> Using the Trace Options</a><br />
<a href="#read_only"> <a href="#read_only">
...@@ -138,6 +140,13 @@ Features ...@@ -138,6 +140,13 @@ Features
</li><li>Well tested (high code coverage, randomized stress tests) </li><li>Well tested (high code coverage, randomized stress tests)
</li></ul> </li></ul>
<br /><a name="limitations"></a>
<h2>Limitations</h2>
For the list of limitations, please have a look at the road map page at:
<a href="http://groups.google.com/group/h2-database/web/roadmap">
http://groups.google.com/group/h2-database/web/roadmap
</a>.
<br /><a name="comparison"></a> <br /><a name="comparison"></a>
<h2>Comparison to Other Database Engines</h2> <h2>Comparison to Other Database Engines</h2>
...@@ -793,11 +802,20 @@ must contain RECOVER=1, as in jdbc:h2:~/test;RECOVER=1. Indexes are rebuilt in t ...@@ -793,11 +802,20 @@ must contain RECOVER=1, as in jdbc:h2:~/test;RECOVER=1. Indexes are rebuilt in t
the summary (object allocation table) is not read in this case, so opening the database takes longer. the summary (object allocation table) is not read in this case, so opening the database takes longer.
</p> </p>
<br /><a name="compatibility_modes"></a> <br /><a name="compatibility"></a>
<h2>Compatibility Modes</h2> <h2>Compatibility</h2>
<p>
All database engines behave a little bit different. Where possible, H2 supports the ANSI SQL standard,
and tries to be compatible to other databases. There are still a few differences however:
</p>
<p> <p>
All database engines behave a little bit different. For certain features, In MySQL text columns are case insensitive by default, while in H2 they are case sensitive. However
this database can emulate the behavior of specific databases. Not all features or differences of those H2 supports case insensitive columns as well. To create the tables with case insensitive texts, append
IGNORECASE=TRUE to the database URL (example: jdbc:h2:test;IGNORECASE=TRUE).
</p>
<h3>Compatibility Modes</h3>
For certain features, this database can emulate the behavior of specific databases. Not all features or differences of those
databases are implemented. Currently, this feature is mainly used for randomized comparative testing databases are implemented. Currently, this feature is mainly used for randomized comparative testing
(where random statements are executed against multiple databases and the results are compared). (where random statements are executed against multiple databases and the results are compared).
The mode can be changed by specifying the mode in the database URL, or using the SQL statement SET MODE. The mode can be changed by specifying the mode in the database URL, or using the SQL statement SET MODE.
...@@ -829,6 +847,10 @@ Here is the list of currently supported modes and the difference to the regular ...@@ -829,6 +847,10 @@ Here is the list of currently supported modes and the difference to the regular
Usually, the scale is converted and 0s are added if required. Usually, the scale is converted and 0s are added if required.
</td></tr> </td></tr>
</table> </table>
<p>
</p>
<br /><a name="trace_options"></a> <br /><a name="trace_options"></a>
<h2>Using the Trace Options</h2> <h2>Using the Trace Options</h2>
......
...@@ -3657,6 +3657,12 @@ public class Parser { ...@@ -3657,6 +3657,12 @@ public class Parser {
boolean value = readBooleanSetting(); boolean value = readBooleanSetting();
int setting = value ? TransactionCommand.AUTOCOMMIT_TRUE : TransactionCommand.AUTOCOMMIT_FALSE; int setting = value ? TransactionCommand.AUTOCOMMIT_TRUE : TransactionCommand.AUTOCOMMIT_FALSE;
return new TransactionCommand(session, setting); return new TransactionCommand(session, setting);
} else if (readIf("MVCC")) {
readIfEqualOrTo();
boolean value = readBooleanSetting();
Set command = new Set(session, SetTypes.MVCC);
command.setInt(value ? 1 : 0);
return command;
} else if (readIf("IGNORECASE")) { } else if (readIf("IGNORECASE")) {
readIfEqualOrTo(); readIfEqualOrTo();
boolean value = readBooleanSetting(); boolean value = readBooleanSetting();
...@@ -3759,10 +3765,6 @@ public class Parser { ...@@ -3759,10 +3765,6 @@ public class Parser {
readIfEqualOrTo(); readIfEqualOrTo();
read(); read();
return new NoOperation(session); return new NoOperation(session);
} else if (readIf("MVCC")) {
readIfEqualOrTo();
read();
return new NoOperation(session);
} else if (readIf("ACCESS_MODE_LOG")) { } else if (readIf("ACCESS_MODE_LOG")) {
readIfEqualOrTo(); readIfEqualOrTo();
read(); read();
......
...@@ -86,6 +86,9 @@ public class AlterUser extends DefineCommand { ...@@ -86,6 +86,9 @@ public class AlterUser extends DefineCommand {
break; break;
case ADMIN: case ADMIN:
session.getUser().checkAdmin(); session.getUser().checkAdmin();
if (!admin) {
user.checkNoSchemas();
}
user.setAdmin(admin); user.setAdmin(admin);
break; break;
default: default:
......
...@@ -42,6 +42,7 @@ public class DropUser extends DefineCommand { ...@@ -42,6 +42,7 @@ public class DropUser extends DefineCommand {
if (user == session.getUser()) { if (user == session.getUser()) {
throw Message.getSQLException(ErrorCode.CANNOT_DROP_CURRENT_USER); throw Message.getSQLException(ErrorCode.CANNOT_DROP_CURRENT_USER);
} }
user.checkNoSchemas();
db.removeDatabaseObject(session, user); db.removeDatabaseObject(session, user);
} }
return 0; return 0;
......
...@@ -13,10 +13,10 @@ import org.h2.expression.Expression; ...@@ -13,10 +13,10 @@ import org.h2.expression.Expression;
import org.h2.log.UndoLogRecord; import org.h2.log.UndoLogRecord;
import org.h2.result.LocalResult; import org.h2.result.LocalResult;
import org.h2.result.Row; import org.h2.result.Row;
import org.h2.result.RowList;
import org.h2.table.PlanItem; import org.h2.table.PlanItem;
import org.h2.table.Table; import org.h2.table.Table;
import org.h2.table.TableFilter; import org.h2.table.TableFilter;
import org.h2.util.ObjectArray;
import org.h2.util.StringUtils; import org.h2.util.StringUtils;
/** /**
...@@ -46,38 +46,37 @@ public class Delete extends Prepared { ...@@ -46,38 +46,37 @@ public class Delete extends Prepared {
session.getUser().checkRight(table, Right.DELETE); session.getUser().checkRight(table, Right.DELETE);
table.fireBefore(session); table.fireBefore(session);
table.lock(session, true, false); table.lock(session, true, false);
ObjectArray rows = new ObjectArray(); RowList rows = new RowList(session);
setCurrentRowNumber(0); try {
while (tableFilter.next()) { setCurrentRowNumber(0);
checkCancelled(); while (tableFilter.next()) {
setCurrentRowNumber(rows.size() + 1);
if (condition == null || Boolean.TRUE.equals(condition.getBooleanValue(session))) {
Row row = tableFilter.get();
rows.add(row);
}
}
if (table.fireRow()) {
for (int i = 0; i < rows.size(); i++) {
checkCancelled(); checkCancelled();
Row row = (Row) rows.get(i); setCurrentRowNumber(rows.size() + 1);
table.fireBeforeRow(session, row, null); if (condition == null || Boolean.TRUE.equals(condition.getBooleanValue(session))) {
Row row = tableFilter.get();
if (table.fireRow()) {
table.fireBeforeRow(session, row, null);
}
rows.add(row);
}
} }
} for (rows.reset(); rows.hasNext();) {
for (int i = 0; i < rows.size(); i++) {
checkCancelled();
Row row = (Row) rows.get(i);
table.removeRow(session, row);
session.log(table, UndoLogRecord.DELETE, row);
}
if (table.fireRow()) {
for (int i = 0; i < rows.size(); i++) {
checkCancelled(); checkCancelled();
Row row = (Row) rows.get(i); Row row = rows.next();
table.fireAfterRow(session, row, null); table.removeRow(session, row);
session.log(table, UndoLogRecord.DELETE, row);
}
if (table.fireRow()) {
for (rows.reset(); rows.hasNext();) {
Row row = rows.next();
table.fireAfterRow(session, row, null);
}
} }
table.fireAfter(session);
return rows.size();
} finally {
rows.close();
} }
table.fireAfter(session);
return rows.size();
} }
public String getPlanSQL() { public String getPlanSQL() {
......
...@@ -261,6 +261,18 @@ public class Set extends Prepared { ...@@ -261,6 +261,18 @@ public class Set extends Prepared {
database.setReferentialIntegrity(value == 1); database.setReferentialIntegrity(value == 1);
break; break;
} }
case SetTypes.MVCC: {
if (database.isMultiVersion() != (getIntValue() == 1)) {
throw Message.getSQLException(ErrorCode.CANNOT_CHANGE_SETTING_WHEN_OPEN_1, "MVCC");
}
break;
}
case SetTypes.MAX_OPERATION_MEMORY: {
session.getUser().checkAdmin();
int value = getIntValue();
database.setMaxOperationMemory(value);
break;
}
default: default:
throw Message.getInternalError("type="+type); throw Message.getInternalError("type="+type);
} }
......
...@@ -20,7 +20,7 @@ public class SetTypes { ...@@ -20,7 +20,7 @@ public class SetTypes {
public static final int LOG = 19, THROTTLE = 20, MAX_MEMORY_UNDO = 21, MAX_LENGTH_INPLACE_LOB = 22; public static final int LOG = 19, THROTTLE = 20, MAX_MEMORY_UNDO = 21, MAX_LENGTH_INPLACE_LOB = 22;
public static final int COMPRESS_LOB = 23, ALLOW_LITERALS = 24, MULTI_THREADED = 25, SCHEMA = 26; public static final int COMPRESS_LOB = 23, ALLOW_LITERALS = 24, MULTI_THREADED = 25, SCHEMA = 26;
public static final int OPTIMIZE_REUSE_RESULTS = 27, SCHEMA_SEARCH_PATH = 28, UNDO_LOG = 29; public static final int OPTIMIZE_REUSE_RESULTS = 27, SCHEMA_SEARCH_PATH = 28, UNDO_LOG = 29;
public static final int REFERENTIAL_INTEGRITY = 30; public static final int REFERENTIAL_INTEGRITY = 30, MVCC = 31, MAX_OPERATION_MEMORY = 32;
private static ObjectArray types = new ObjectArray(); private static ObjectArray types = new ObjectArray();
...@@ -55,6 +55,8 @@ public class SetTypes { ...@@ -55,6 +55,8 @@ public class SetTypes {
setType(SCHEMA_SEARCH_PATH, "SCHEMA_SEARCH_PATH"); setType(SCHEMA_SEARCH_PATH, "SCHEMA_SEARCH_PATH");
setType(UNDO_LOG, "UNDO_LOG"); setType(UNDO_LOG, "UNDO_LOG");
setType(REFERENTIAL_INTEGRITY, "REFERENTIAL_INTEGRITY"); setType(REFERENTIAL_INTEGRITY, "REFERENTIAL_INTEGRITY");
setType(MVCC, "MVCC");
setType(MAX_OPERATION_MEMORY, "MAX_OPERATION_MEMORY");
} }
private static void setType(int type, String name) { private static void setType(int type, String name) {
......
...@@ -14,11 +14,11 @@ import org.h2.expression.Expression; ...@@ -14,11 +14,11 @@ import org.h2.expression.Expression;
import org.h2.message.Message; import org.h2.message.Message;
import org.h2.result.LocalResult; import org.h2.result.LocalResult;
import org.h2.result.Row; import org.h2.result.Row;
import org.h2.result.RowList;
import org.h2.table.Column; import org.h2.table.Column;
import org.h2.table.PlanItem; import org.h2.table.PlanItem;
import org.h2.table.Table; import org.h2.table.Table;
import org.h2.table.TableFilter; import org.h2.table.TableFilter;
import org.h2.util.ObjectArray;
import org.h2.util.StringUtils; import org.h2.util.StringUtils;
import org.h2.value.Value; import org.h2.value.Value;
...@@ -58,62 +58,61 @@ public class Update extends Prepared { ...@@ -58,62 +58,61 @@ public class Update extends Prepared {
public int update() throws SQLException { public int update() throws SQLException {
tableFilter.startQuery(session); tableFilter.startQuery(session);
tableFilter.reset(); tableFilter.reset();
// TODO optimization: update old / new list: maybe use a linked list (to avoid array allocation) RowList rows = new RowList(session);
ObjectArray oldRows = new ObjectArray(); try {
ObjectArray newRows = new ObjectArray(); Table table = tableFilter.getTable();
Table table = tableFilter.getTable(); session.getUser().checkRight(table, Right.UPDATE);
session.getUser().checkRight(table, Right.UPDATE); table.fireBefore(session);
table.fireBefore(session); table.lock(session, true, false);
table.lock(session, true, false); int columnCount = table.getColumns().length;
int columnCount = table.getColumns().length; // get the old rows, compute the new rows
// get the old rows, compute the new rows setCurrentRowNumber(0);
setCurrentRowNumber(0); int count = 0;
while (tableFilter.next()) { while (tableFilter.next()) {
checkCancelled(); checkCancelled();
setCurrentRowNumber(oldRows.size()+1); setCurrentRowNumber(count+1);
if (condition == null || Boolean.TRUE.equals(condition.getBooleanValue(session))) { if (condition == null || Boolean.TRUE.equals(condition.getBooleanValue(session))) {
Row oldRow = tableFilter.get(); Row oldRow = tableFilter.get();
Row newRow = table.getTemplateRow(); Row newRow = table.getTemplateRow();
for (int i = 0; i < columnCount; i++) { for (int i = 0; i < columnCount; i++) {
Expression newExpr = expressions[i]; Expression newExpr = expressions[i];
Value newValue; Value newValue;
if (newExpr == null) { if (newExpr == null) {
newValue = oldRow.getValue(i); newValue = oldRow.getValue(i);
} else { } else {
Column column = table.getColumn(i); Column column = table.getColumn(i);
newValue = newExpr.getValue(session).convertTo(column.getType()); newValue = newExpr.getValue(session).convertTo(column.getType());
}
newRow.setValue(i, newValue);
} }
newRow.setValue(i, newValue); table.validateConvertUpdateSequence(session, newRow);
if (table.fireRow()) {
table.fireBeforeRow(session, oldRow, newRow);
}
rows.add(oldRow);
rows.add(newRow);
count++;
} }
table.validateConvertUpdateSequence(session, newRow);
oldRows.add(oldRow);
newRows.add(newRow);
}
}
// TODO performance: loop only if required
// TODO self referencing referential integrity constraints don't work if update is multi-row and 'inversed' the condition!
// probably need multi-row triggers with 'deleted' and 'inserted' at the same time. anyway good for sql compatibility
// TODO update in-place (but if the position changes, we need to update all indexes)
// before row triggers
if (table.fireRow()) {
for (int i = 0; i < oldRows.size(); i++) {
checkCancelled();
Row o = (Row) oldRows.get(i);
Row n = (Row) newRows.get(i);
table.fireBeforeRow(session, o, n);
} }
} // TODO performance: loop only if required
table.updateRows(this, session, oldRows, newRows); // TODO self referencing referential integrity constraints don't work if update is multi-row and 'inversed' the condition!
if (table.fireRow()) { // probably need multi-row triggers with 'deleted' and 'inserted' at the same time. anyway good for sql compatibility
for (int i = 0; i < newRows.size(); i++) { // TODO update in-place (but if the position changes, we need to update all indexes)
checkCancelled(); // before row triggers
Row n = (Row) newRows.get(i); table.updateRows(this, session, rows);
Row o = (Row) oldRows.get(i); if (table.fireRow()) {
table.fireAfterRow(session, o, n); for (rows.reset(); rows.hasNext();) {
checkCancelled();
Row o = rows.next();
Row n = rows.next();
table.fireAfterRow(session, o, n);
}
} }
table.fireAfter(session);
return count;
} finally {
rows.close();
} }
table.fireAfter(session);
return newRows.size();
} }
public String getPlanSQL() { public String getPlanSQL() {
......
...@@ -313,6 +313,7 @@ public class ErrorCode { ...@@ -313,6 +313,7 @@ public class ErrorCode {
public static final int METHOD_NOT_ALLOWED_FOR_PREPARED_STATEMENT = 90130; public static final int METHOD_NOT_ALLOWED_FOR_PREPARED_STATEMENT = 90130;
public static final int CONCURRENT_UPDATE_1 = 90131; public static final int CONCURRENT_UPDATE_1 = 90131;
public static final int AGGREGATE_NOT_FOUND_1 = 90132; public static final int AGGREGATE_NOT_FOUND_1 = 90132;
public static final int CANNOT_CHANGE_SETTING_WHEN_OPEN_1 = 90133;
/** /**
* INTERNAL * INTERNAL
......
...@@ -61,6 +61,10 @@ public class SysProperties { ...@@ -61,6 +61,10 @@ public class SysProperties {
public static final boolean LOB_FILES_IN_DIRECTORIES = getBooleanSetting("h2.lobFilesInDirectories", false); public static final boolean LOB_FILES_IN_DIRECTORIES = getBooleanSetting("h2.lobFilesInDirectories", false);
public static final int LOB_FILES_PER_DIRECTORY = getIntSetting("h2.lobFilesPerDirectory", 256); public static final int LOB_FILES_PER_DIRECTORY = getIntSetting("h2.lobFilesPerDirectory", 256);
public static final boolean NEW_DISPLAY_SIZE = getBooleanSetting("h2.newDisplaySize", true); public static final boolean NEW_DISPLAY_SIZE = getBooleanSetting("h2.newDisplaySize", true);
private int test;
public static final int DEFAULT_MAX_OPERERATION_MEMORY = getIntSetting("h2.defaultMaxOperationMemory", 0);
// public static final int DEFAULT_MAX_OPERERATION_MEMORY = getIntSetting("h2.defaultMaxOperationMemory", 100000);
// public static final int DEFAULT_MAX_OPERERATION_MEMORY = getIntSetting("h2.defaultMaxOperationMemory", 1);
private static boolean getBooleanSetting(String name, boolean defaultValue) { private static boolean getBooleanSetting(String name, boolean defaultValue) {
String s = System.getProperty(name); String s = System.getProperty(name);
......
...@@ -288,6 +288,7 @@ public class ConstraintReferential extends Constraint { ...@@ -288,6 +288,7 @@ public class ConstraintReferential extends Constraint {
} }
private boolean found(Session session, Index index, SearchRow check) throws SQLException { private boolean found(Session session, Index index, SearchRow check) throws SQLException {
index.getTable().lock(session, false, false);
Cursor cursor = index.find(session, check, check); Cursor cursor = index.find(session, check, check);
while (cursor.next()) { while (cursor.next()) {
SearchRow found; SearchRow found;
......
...@@ -42,7 +42,7 @@ public class ConnectionInfo { ...@@ -42,7 +42,7 @@ public class ConnectionInfo {
// TODO document these settings // TODO document these settings
String[] connectionTime = new String[] { "ACCESS_MODE_LOG", "ACCESS_MODE_DATA", "AUTOCOMMIT", "CIPHER", String[] connectionTime = new String[] { "ACCESS_MODE_LOG", "ACCESS_MODE_DATA", "AUTOCOMMIT", "CIPHER",
"CREATE", "CACHE_TYPE", "DB_CLOSE_ON_EXIT", "FILE_LOCK", "IGNORE_UNKNOWN_SETTINGS", "IFEXISTS", "CREATE", "CACHE_TYPE", "DB_CLOSE_ON_EXIT", "FILE_LOCK", "IGNORE_UNKNOWN_SETTINGS", "IFEXISTS",
"PASSWORD", "RECOVER", "STORAGE", "USER", "MVCC", "DATABASE_EVENT_LISTENER_OBJECT" }; "PASSWORD", "RECOVER", "STORAGE", "USER", "DATABASE_EVENT_LISTENER_OBJECT" };
for (int i = 0; i < connectionTime.length; i++) { for (int i = 0; i < connectionTime.length; i++) {
String key = connectionTime[i]; String key = connectionTime[i];
if (SysProperties.CHECK && KNOWN_SETTINGS.contains(key)) { if (SysProperties.CHECK && KNOWN_SETTINGS.contains(key)) {
......
...@@ -145,6 +145,7 @@ public class Database implements DataHandler { ...@@ -145,6 +145,7 @@ public class Database implements DataHandler {
private DatabaseCloser closeOnExit; private DatabaseCloser closeOnExit;
private Mode mode = Mode.getInstance(Mode.REGULAR); private Mode mode = Mode.getInstance(Mode.REGULAR);
private boolean multiThreaded; private boolean multiThreaded;
private int maxOperationMemory = SysProperties.DEFAULT_MAX_OPERERATION_MEMORY;
public Database(String name, ConnectionInfo ci, String cipher) throws SQLException { public Database(String name, ConnectionInfo ci, String cipher) throws SQLException {
this.compareMode = new CompareMode(null, null); this.compareMode = new CompareMode(null, null);
...@@ -468,8 +469,7 @@ public class Database implements DataHandler { ...@@ -468,8 +469,7 @@ public class Database implements DataHandler {
roles.put(Constants.PUBLIC_ROLE_NAME, publicRole); roles.put(Constants.PUBLIC_ROLE_NAME, publicRole);
systemUser.setAdmin(true); systemUser.setAdmin(true);
systemSession = new Session(this, systemUser, ++nextSessionId); systemSession = new Session(this, systemUser, ++nextSessionId);
// TODO storage: antivir scans .script files, maybe other scanners scan // TODO storage: antivir scans .script files, maybe other scanners scan .db files?
// .db files?
ObjectArray cols = new ObjectArray(); ObjectArray cols = new ObjectArray();
Column columnId = new Column("ID", Value.INT); Column columnId = new Column("ID", Value.INT);
columnId.setNullable(false); columnId.setNullable(false);
...@@ -567,6 +567,22 @@ public class Database implements DataHandler { ...@@ -567,6 +567,22 @@ public class Database implements DataHandler {
} }
} }
} while (recompileSuccessful); } while (recompileSuccessful);
// when opening a database, views are initialized before indexes, so they may not have the optimal plan yet
// this is not a problem, it is just nice to see the newest plan
ObjectArray list = getAllSchemaObjects(DbObject.TABLE_OR_VIEW);
for (int i = 0; i < list.size(); i++) {
DbObject obj = (DbObject) list.get(i);
if (obj instanceof TableView) {
TableView view = (TableView) obj;
if (!view.getInvalid()) {
try {
view.recompile(systemSession);
} catch (SQLException e) {
// ignore
}
}
}
}
} }
private void removeUnusedStorages(Session session) throws SQLException { private void removeUnusedStorages(Session session) throws SQLException {
...@@ -824,7 +840,7 @@ public class Database implements DataHandler { ...@@ -824,7 +840,7 @@ public class Database implements DataHandler {
closing = true; closing = true;
} }
try { try {
if (persistent && fileData != null) { if (systemSession != null) {
ObjectArray tablesAndViews = getAllSchemaObjects(DbObject.TABLE_OR_VIEW); ObjectArray tablesAndViews = getAllSchemaObjects(DbObject.TABLE_OR_VIEW);
for (int i = 0; i < tablesAndViews.size(); i++) { for (int i = 0; i < tablesAndViews.size(); i++) {
Table table = (Table) tablesAndViews.get(i); Table table = (Table) tablesAndViews.get(i);
...@@ -1594,5 +1610,13 @@ public class Database implements DataHandler { ...@@ -1594,5 +1610,13 @@ public class Database implements DataHandler {
public void setMultiThreaded(boolean multiThreaded) { public void setMultiThreaded(boolean multiThreaded) {
this.multiThreaded = multiThreaded; this.multiThreaded = multiThreaded;
} }
public void setMaxOperationMemory(int maxOperationMemory) {
this.maxOperationMemory = maxOperationMemory;
}
public int getMaxOperationMemory() {
return maxOperationMemory;
}
} }
...@@ -9,6 +9,7 @@ import java.sql.SQLException; ...@@ -9,6 +9,7 @@ import java.sql.SQLException;
import org.h2.constant.ErrorCode; import org.h2.constant.ErrorCode;
import org.h2.message.Message; import org.h2.message.Message;
import org.h2.message.Trace; import org.h2.message.Trace;
import org.h2.schema.Schema;
import org.h2.security.SHA256; import org.h2.security.SHA256;
import org.h2.table.MetaTable; import org.h2.table.MetaTable;
import org.h2.table.RangeTable; import org.h2.table.RangeTable;
...@@ -138,14 +139,21 @@ public class User extends RightOwner { ...@@ -138,14 +139,21 @@ public class User extends RightOwner {
public ObjectArray getChildren() { public ObjectArray getChildren() {
ObjectArray all = database.getAllRights(); ObjectArray all = database.getAllRights();
ObjectArray rights = new ObjectArray(); ObjectArray children = new ObjectArray();
for (int i = 0; i < all.size(); i++) { for (int i = 0; i < all.size(); i++) {
Right right = (Right) all.get(i); Right right = (Right) all.get(i);
if (right.getGrantee() == this) { if (right.getGrantee() == this) {
rights.add(right); children.add(right);
} }
} }
return rights; all = database.getAllSchemas();
for (int i = 0; i < all.size(); i++) {
Schema schema = (Schema) all.get(i);
if (schema.getOwner() == this) {
children.add(schema);
}
}
return children;
} }
public void removeChildrenAndResources(Session session) throws SQLException { public void removeChildrenAndResources(Session session) throws SQLException {
...@@ -166,4 +174,14 @@ public class User extends RightOwner { ...@@ -166,4 +174,14 @@ public class User extends RightOwner {
// ok // ok
} }
public void checkNoSchemas() throws SQLException {
ObjectArray schemas = database.getAllSchemas();
for (int i = 0; i < schemas.size(); i++) {
Schema s = (Schema) schemas.get(i);
if (this == s.getOwner()) {
throw Message.getSQLException(ErrorCode.CANNOT_DROP_2, new String[]{ getName(), s.getName() });
}
}
}
} }
...@@ -28,12 +28,12 @@ import org.h2.engine.Constants; ...@@ -28,12 +28,12 @@ import org.h2.engine.Constants;
import org.h2.engine.SessionInterface; import org.h2.engine.SessionInterface;
import org.h2.engine.SessionRemote; import org.h2.engine.SessionRemote;
import org.h2.expression.ParameterInterface; import org.h2.expression.ParameterInterface;
import org.h2.jdbcx.JdbcConnectionListener;
import org.h2.message.Message; import org.h2.message.Message;
import org.h2.message.Trace; import org.h2.message.Trace;
import org.h2.message.TraceObject; import org.h2.message.TraceObject;
import org.h2.result.ResultInterface; import org.h2.result.ResultInterface;
import org.h2.util.ClassUtils; import org.h2.util.ClassUtils;
import org.h2.util.JdbcConnectionListener;
import org.h2.util.TempFileDeleter; import org.h2.util.TempFileDeleter;
import org.h2.value.Value; import org.h2.value.Value;
import org.h2.value.ValueInt; import org.h2.value.ValueInt;
......
...@@ -225,27 +225,53 @@ implements XADataSource, DataSource, ConnectionPoolDataSource, Serializable, Ref ...@@ -225,27 +225,53 @@ implements XADataSource, DataSource, ConnectionPoolDataSource, Serializable, Ref
//#endif //#endif
/** /**
* Open a new XA connection using the current URL, user name and password.
* *
* @return the connection
*/ */
//#ifdef JDK14 //#ifdef JDK14
public XAConnection getXAConnection() throws SQLException { public XAConnection getXAConnection() throws SQLException {
int document;
debugCodeCall("getXAConnection"); debugCodeCall("getXAConnection");
int id = getNextId(XA_DATA_SOURCE); int id = getNextId(XA_DATA_SOURCE);
return new JdbcXAConnection(factory, id, url, user, password); return new JdbcXAConnection(factory, id, url, user, password);
} }
//#endif
/**
* Open a new XA connection using the current URL and the specified user name and password.
*
* @param the user name
* @param the password
* @return the connection
*/
//#ifdef JDK14
public XAConnection getXAConnection(String user, String password) throws SQLException { public XAConnection getXAConnection(String user, String password) throws SQLException {
debugCode("getXAConnection("+quote(user)+", "+quote(password)+");"); debugCode("getXAConnection("+quote(user)+", "+quote(password)+");");
int id = getNextId(XA_DATA_SOURCE); int id = getNextId(XA_DATA_SOURCE);
return new JdbcXAConnection(factory, id, url, user, password); return new JdbcXAConnection(factory, id, url, user, password);
} }
//#endif
/**
* Open a new XA connection using the current URL, user name and password.
*
* @return the connection
*/
//#ifdef JDK14
public PooledConnection getPooledConnection() throws SQLException { public PooledConnection getPooledConnection() throws SQLException {
debugCodeCall("getPooledConnection"); debugCodeCall("getPooledConnection");
return getXAConnection(); return getXAConnection();
} }
//#endif
/**
* Open a new XA connection using the current URL and the specified user name and password.
*
* @param the user name
* @param the password
* @return the connection
*/
//#ifdef JDK14
public PooledConnection getPooledConnection(String user, String password) throws SQLException { public PooledConnection getPooledConnection(String user, String password) throws SQLException {
debugCode("getPooledConnection("+quote(user)+", "+quote(password)+");"); debugCode("getPooledConnection("+quote(user)+", "+quote(password)+");");
return getXAConnection(user, password); return getXAConnection(user, password);
......
...@@ -18,6 +18,7 @@ import javax.transaction.xa.XAException; ...@@ -18,6 +18,7 @@ import javax.transaction.xa.XAException;
import javax.transaction.xa.XAResource; import javax.transaction.xa.XAResource;
import javax.transaction.xa.Xid; import javax.transaction.xa.Xid;
import org.h2.util.ByteUtils; import org.h2.util.ByteUtils;
import org.h2.util.JdbcConnectionListener;
import org.h2.util.JdbcUtils; import org.h2.util.JdbcUtils;
import org.h2.jdbc.JdbcConnection; import org.h2.jdbc.JdbcConnection;
//#endif //#endif
......
...@@ -153,6 +153,7 @@ ...@@ -153,6 +153,7 @@
90130=This method is not allowed for a prepared statement; use a regular statement instead. 90130=This method is not allowed for a prepared statement; use a regular statement instead.
90131=Concurrent update in table {0}\: another transaction has updated or deleted the same row 90131=Concurrent update in table {0}\: another transaction has updated or deleted the same row
90132=Aggregate {0} not found 90132=Aggregate {0} not found
90133=Cannot change the setting {0} when the database is already open
HY000=General error\: {0} HY000=General error\: {0}
HY004=Unknown data type\: {0} HY004=Unknown data type\: {0}
HYC00=Feature not supported HYC00=Feature not supported
......
...@@ -941,6 +941,18 @@ Admin rights are required to execute this command. ...@@ -941,6 +941,18 @@ Admin rights are required to execute this command.
SET MAX_MEMORY_UNDO 1000 SET MAX_MEMORY_UNDO 1000
" "
"Commands (Other)","SET MAX_OPERATION_MEMORY","
SET MAX_OPERATION_MEMORY int
","
Sets the maximum memory size used for large operations (delete and insert).
Operations that use more memory are buffered to disk, slowing down the operation.
The default max size is 100000. 0 means no limit.
This setting is not persistent.
Admin rights are required to execute this command.
","
SET MAX_OPERATION_MEMORY 0
"
"Commands (Other)","SET MODE"," "Commands (Other)","SET MODE","
SET MODE {REGULAR | HSQLDB | POSTGRESQL | MYSQL} SET MODE {REGULAR | HSQLDB | POSTGRESQL | MYSQL}
"," ","
......
...@@ -115,12 +115,12 @@ public class LocalResult implements ResultInterface { ...@@ -115,12 +115,12 @@ public class LocalResult implements ResultInterface {
} else { } else {
this.maxMemoryRows = session.getDatabase().getMaxMemoryRows(); this.maxMemoryRows = session.getDatabase().getMaxMemoryRows();
} }
this.expressions = new Expression[cols.size()];
cols.toArray(expressions);
this.displaySizes = new int[cols.size()];
rows = new ObjectArray(); rows = new ObjectArray();
this.visibleColumnCount = visibleColumnCount; this.visibleColumnCount = visibleColumnCount;
rowId = -1; rowId = -1;
this.expressions = new Expression[cols.size()];
cols.toArray(expressions);
this.displaySizes = new int[cols.size()];
} }
public void setSortOrder(SortOrder sort) { public void setSortOrder(SortOrder sort) {
......
...@@ -66,22 +66,22 @@ public class Row extends Record implements SearchRow { ...@@ -66,22 +66,22 @@ public class Row extends Record implements SearchRow {
return blockCount * (DiskFile.BLOCK_SIZE / 16) + memory * 4; return blockCount * (DiskFile.BLOCK_SIZE / 16) + memory * 4;
} }
// public String toString() { public String toString() {
// int testing; StringBuffer buff = new StringBuffer(data.length * 5);
// StringBuffer buff = new StringBuffer(data.length*5); buff.append("( /* pos:");
// buff.append('('); buff.append(getPos());
// for(int i=0; i<data.length; i++) { if (getDeleted()) {
// if(i>0) { buff.append(" deleted");
// buff.append(", "); }
// } buff.append(" */ ");
// buff.append(data[i].getSQL()); for (int i = 0; i < data.length; i++) {
// } if (i > 0) {
// buff.append(')'); buff.append(", ");
// buff.append(" /* pos: " + getPos() + "*/ "); }
// if(getDeleted()) { buff.append(data[i].getSQL());
// buff.append(" /* deleted /*"); }
// } buff.append(')');
// return buff.toString(); return buff.toString();
// } }
} }
/*
* Copyright 2004-2007 H2 Group. Licensed under the H2 License, Version 1.0 (http://h2database.com/html/license.html).
* Initial Developer: H2 Group
*/
package org.h2.result;
import java.sql.SQLException;
import org.h2.constant.SysProperties;
import org.h2.engine.Constants;
import org.h2.engine.Database;
import org.h2.engine.Session;
import org.h2.store.DataPage;
import org.h2.store.FileStore;
import org.h2.util.Cache;
import org.h2.util.CacheObject;
import org.h2.util.ObjectArray;
import org.h2.value.Value;
import org.h2.value.ValueLob;
public class RowList {
private final Session session;
private ObjectArray list = new ObjectArray();
private int size;
private int index, listIndex;
private FileStore file;
private DataPage rowBuff;
private Cache cache;
private ObjectArray lobs;
private int memory, maxMemory;
private boolean written;
public RowList(Session session) {
this.session = session;
if (SysProperties.DEFAULT_MAX_OPERERATION_MEMORY > 0 && session.getDatabase().isPersistent()) {
maxMemory = session.getDatabase().getMaxOperationMemory();
}
}
private void writeRow(DataPage buff, Row r) throws SQLException {
buff.checkCapacity(1 + buff.getIntLen() * 6);
buff.writeByte((byte) 1);
buff.writeInt(r.getMemorySize());
buff.writeInt(r.getColumnCount());
buff.writeInt(r.getPos());
buff.writeInt(r.getDeleted() ? 1 : 0);
buff.writeInt(r.getSessionId());
buff.writeInt(r.getStorageId());
for (int i = 0; i < r.getColumnCount(); i++) {
Value v = r.getValue(i);
if (v.getType() == Value.CLOB || v.getType() == Value.BLOB) {
// need to keep a reference to temporary lobs,
// otherwise the temp file is deleted
ValueLob lob = (ValueLob) v;
if (lob.getSmall() == null && lob.getTableId() == 0) {
if (lobs == null) {
lobs = new ObjectArray();
}
lobs.add(lob);
}
}
buff.checkCapacity(buff.getValueLen(v));
buff.writeValue(v);
}
}
private void writeAllRows() throws SQLException {
if (file == null) {
Database db = session.getDatabase();
this.cache = db.getDataFile().getCache();
String fileName = db.createTempFile();
file = db.openFile(fileName, "rw", false);
file.autoDelete();
file.seek(FileStore.HEADER_LENGTH);
rowBuff = DataPage.create(db, Constants.DEFAULT_DATA_PAGE_SIZE);
file.seek(FileStore.HEADER_LENGTH);
}
DataPage buff = rowBuff;
initBuffer(buff);
for (int i = 0; i < list.size(); i++) {
if (i > 0 && buff.length() > Constants.IO_BUFFER_SIZE) {
flushBuffer(buff);
initBuffer(buff);
}
Row r = (Row) list.get(i);
writeRow(buff, r);
}
flushBuffer(buff);
list.clear();
memory = 0;
}
private void initBuffer(DataPage buff) {
buff.reset();
buff.writeInt(0);
}
private void flushBuffer(DataPage buff) throws SQLException {
buff.checkCapacity(1);
buff.writeByte((byte) 0);
buff.fillAligned();
buff.setInt(0, buff.length() / Constants.FILE_BLOCK_SIZE);
buff.updateChecksum();
file.write(buff.getBytes(), 0, buff.length());
}
public void add(Row r) throws SQLException {
int test;
//print(" add " + r);
list.add(r);
memory += r.getMemorySize();
if (maxMemory > 0 && memory > maxMemory) {
writeAllRows();
}
size++;
}
public void reset() throws SQLException {
int test;
//print(" reset");
index = 0;
if (file != null) {
listIndex = 0;
if (!written) {
writeAllRows();
written = true;
}
list.clear();
file.seek(FileStore.HEADER_LENGTH);
}
}
public boolean hasNext() {
int test;
//print(" hasNext " + (index < size) + " index:" + index + " size:" + size);
return index < size;
}
private Row readRow(DataPage buff) throws SQLException {
if (buff.readByte() == 0) {
return null;
}
int memory = buff.readInt();
int columnCount = buff.readInt();
int pos = buff.readInt();
boolean deleted = buff.readInt() == 1;
int sessionId = buff.readInt();
int storageId = buff.readInt();
Value[] values = new Value[columnCount];
for (int i = 0; i < columnCount; i++) {
values[i] = buff.readValue();
}
if (pos != 0) {
CacheObject found = cache.find(pos);
if (found != null) {
return (Row) found;
}
}
Row row = new Row(values, memory);
row.setPos(pos);
row.setDeleted(deleted);
row.setSessionId(sessionId);
row.setStorageId(storageId);
return row;
}
public Row next() throws SQLException {
Row r;
if (file == null) {
r = (Row) list.get(index++);
} else {
if (listIndex >= list.size()) {
list.clear();
listIndex = 0;
DataPage buff = rowBuff;
buff.reset();
int min = Constants.FILE_BLOCK_SIZE;
file.readFully(buff.getBytes(), 0, min);
int len = buff.readInt() * Constants.FILE_BLOCK_SIZE;
buff.checkCapacity(len);
if (len - min > 0) {
file.readFully(buff.getBytes(), min, len - min);
}
buff.check(len);
for (int i = 0;; i++) {
r = readRow(buff);
if (r == null) {
break;
}
list.add(r);
}
}
index++;
r = (Row) list.get(listIndex++);
}
int test;
//print(" get " + r + " index:" + index + " listIndex:" + listIndex);
return r;
}
public int size() {
return size;
}
public void close() {
if (file != null) {
file.closeAndDeleteSilently();
file = null;
rowBuff = null;
}
}
// private void print(String s) {
// int test;
// System.out.println(this + s);
// }
}
...@@ -227,16 +227,19 @@ public class PageParser { ...@@ -227,16 +227,19 @@ public class PageParser {
return "&nbsp;"; return "&nbsp;";
} }
StringBuffer buff = new StringBuffer(s.length()); StringBuffer buff = new StringBuffer(s.length());
boolean leadingSpace = true; boolean convertSpace = true;
for (int i = 0; i < s.length(); i++) { for (int i = 0; i < s.length(); i++) {
char c = s.charAt(i); char c = s.charAt(i);
if (leadingSpace) { if (c == ' ') {
if (c == ' ') { if (convertSpace) {
buff.append("&nbsp;"); buff.append("&nbsp;");
continue;
} else { } else {
leadingSpace = false; buff.append(' ');
convertSpace = true;
} }
continue;
} else {
convertSpace = false;
} }
switch (c) { switch (c) {
case '$': case '$':
...@@ -261,7 +264,7 @@ public class PageParser { ...@@ -261,7 +264,7 @@ public class PageParser {
case '\n': case '\n':
if (convertBreak) { if (convertBreak) {
buff.append("<br />"); buff.append("<br />");
leadingSpace = true; convertSpace = true;
} else { } else {
buff.append(c); buff.append(c);
} }
......
...@@ -105,6 +105,7 @@ public class WebServer implements Service { ...@@ -105,6 +105,7 @@ public class WebServer implements Service {
private String url; private String url;
private ShutdownHandler shutdownHandler; private ShutdownHandler shutdownHandler;
private Thread listenerThread; private Thread listenerThread;
private boolean ifExists;
byte[] getFile(String file) throws IOException { byte[] getFile(String file) throws IOException {
trace("getFile <" + file + ">"); trace("getFile <" + file + ">");
...@@ -180,15 +181,18 @@ public class WebServer implements Service { ...@@ -180,15 +181,18 @@ public class WebServer implements Service {
ssl = FileUtils.getBooleanProperty(prop, "webSSL", Constants.DEFAULT_HTTP_SSL); ssl = FileUtils.getBooleanProperty(prop, "webSSL", Constants.DEFAULT_HTTP_SSL);
allowOthers = FileUtils.getBooleanProperty(prop, "webAllowOthers", Constants.DEFAULT_HTTP_ALLOW_OTHERS); allowOthers = FileUtils.getBooleanProperty(prop, "webAllowOthers", Constants.DEFAULT_HTTP_ALLOW_OTHERS);
for (int i = 0; args != null && i < args.length; i++) { for (int i = 0; args != null && i < args.length; i++) {
if ("-webPort".equals(args[i])) { String a = args[i];
if ("-webPort".equals(a)) {
port = MathUtils.decodeInt(args[++i]); port = MathUtils.decodeInt(args[++i]);
} else if ("-webSSL".equals(args[i])) { } else if ("-webSSL".equals(a)) {
ssl = Boolean.valueOf(args[++i]).booleanValue(); ssl = Boolean.valueOf(args[++i]).booleanValue();
} else if ("-webAllowOthers".equals(args[i])) { } else if ("-webAllowOthers".equals(a)) {
allowOthers = Boolean.valueOf(args[++i]).booleanValue(); allowOthers = Boolean.valueOf(args[++i]).booleanValue();
} else if ("-baseDir".equals(args[i])) { } else if ("-baseDir".equals(a)) {
String baseDir = args[++i]; String baseDir = args[++i];
SysProperties.setBaseDir(baseDir); SysProperties.setBaseDir(baseDir);
} else if ("-ifExists".equals(a)) {
ifExists = Boolean.valueOf(args[++i]).booleanValue();
} }
} }
// if(driverList != null) { // if(driverList != null) {
...@@ -464,7 +468,14 @@ public class WebServer implements Service { ...@@ -464,7 +468,14 @@ public class WebServer implements Service {
p.setProperty("user", user.trim()); p.setProperty("user", user.trim());
p.setProperty("password", password.trim()); p.setProperty("password", password.trim());
if (url.startsWith("jdbc:h2:")) { if (url.startsWith("jdbc:h2:")) {
if (ifExists) {
url += ";IFEXISTS=TRUE";
}
p.put("DATABASE_EVENT_LISTENER_OBJECT", listener); p.put("DATABASE_EVENT_LISTENER_OBJECT", listener);
// PostgreSQL would throw a NullPointerException
// if it is loaded before the H2 driver
// because it can't deal with non-String objects in the connection Properties
return org.h2.Driver.load().connect(url, p);
} }
// try { // try {
// Driver dr = (Driver) urlClassLoader.loadClass(driver).newInstance(); // Driver dr = (Driver) urlClassLoader.loadClass(driver).newInstance();
......
...@@ -222,14 +222,19 @@ public abstract class DataPage { ...@@ -222,14 +222,19 @@ public abstract class DataPage {
lob.convertToFileIfRequired(handler); lob.convertToFileIfRequired(handler);
byte[] small = lob.getSmall(); byte[] small = lob.getSmall();
if (small == null) { if (small == null) {
// TODO lob: currently use -2 for historical reasons (-1 didn't // -2 for historical reasons (-1 didn't store precision)
// store precision) int type = -2;
writeInt(-2); if (!lob.isLinked()) {
type = -3;
}
writeInt(type);
writeInt(lob.getTableId()); writeInt(lob.getTableId());
writeInt(lob.getObjectId()); writeInt(lob.getObjectId());
writeLong(lob.getPrecision()); writeLong(lob.getPrecision());
writeByte((byte) (lob.useCompression() ? 1 : 0)); // compression writeByte((byte) (lob.useCompression() ? 1 : 0));
// flag if (type == -3) {
writeString(lob.getFileName());
}
} else { } else {
writeInt(small.length); writeInt(small.length);
write(small, 0, small.length); write(small, 0, small.length);
...@@ -304,6 +309,9 @@ public abstract class DataPage { ...@@ -304,6 +309,9 @@ public abstract class DataPage {
len += getIntLen() + small.length; len += getIntLen() + small.length;
} else { } else {
len += getIntLen() + getIntLen() + getIntLen() + getLongLen(lob.getPrecision()) + 1; len += getIntLen() + getIntLen() + getIntLen() + getLongLen(lob.getPrecision()) + 1;
if (!lob.isLinked()) {
len += getStringLen(lob.getFileName());
}
} }
return len; return len;
} }
...@@ -385,13 +393,16 @@ public abstract class DataPage { ...@@ -385,13 +393,16 @@ public abstract class DataPage {
int objectId = readInt(); int objectId = readInt();
long precision = 0; long precision = 0;
boolean compression = false; boolean compression = false;
// TODO lob: -2 is for historical reasons (-1 didn't store // -2 is for historical reasons (-1 didn't store precision)
// precision) if (smallLen == -2 || smallLen == -3) {
if (smallLen == -2) {
precision = readLong(); precision = readLong();
compression = readByte() == 1; compression = readByte() == 1;
} }
return ValueLob.open(type, handler, tableId, objectId, precision, compression); ValueLob lob = ValueLob.open(type, handler, tableId, objectId, precision, compression);
if (smallLen == -3) {
lob.setFileName(readString());
}
return lob;
} }
} }
case Value.ARRAY: { case Value.ARRAY: {
......
...@@ -687,6 +687,7 @@ public class MetaTable extends Table { ...@@ -687,6 +687,7 @@ public class MetaTable extends Table {
add(rows, new String[] { "property." + s, prop.getProperty(s, "") }); add(rows, new String[] { "property." + s, prop.getProperty(s, "") });
} }
} }
add(rows, new String[] { "MVCC", database.isMultiVersion() ? "TRUE" : "FALSE" });
add(rows, new String[] { "MODE", database.getMode().getName() }); add(rows, new String[] { "MODE", database.getMode().getName() });
add(rows, new String[] { "MULTI_THREADED", database.getMultiThreaded() ? "1" : "0"}); add(rows, new String[] { "MULTI_THREADED", database.getMultiThreaded() ? "1" : "0"});
DiskFile dataFile = database.getDataFile(); DiskFile dataFile = database.getDataFile();
......
...@@ -6,6 +6,7 @@ package org.h2.table; ...@@ -6,6 +6,7 @@ package org.h2.table;
import java.sql.SQLException; import java.sql.SQLException;
import java.util.HashMap; import java.util.HashMap;
import org.h2.command.Prepared; import org.h2.command.Prepared;
import org.h2.constant.ErrorCode; import org.h2.constant.ErrorCode;
import org.h2.constraint.Constraint; import org.h2.constraint.Constraint;
...@@ -19,6 +20,7 @@ import org.h2.log.UndoLogRecord; ...@@ -19,6 +20,7 @@ import org.h2.log.UndoLogRecord;
import org.h2.message.Message; import org.h2.message.Message;
import org.h2.message.Trace; import org.h2.message.Trace;
import org.h2.result.Row; import org.h2.result.Row;
import org.h2.result.RowList;
import org.h2.result.SearchRow; import org.h2.result.SearchRow;
import org.h2.result.SimpleRow; import org.h2.result.SimpleRow;
import org.h2.result.SimpleRowValue; import org.h2.result.SimpleRowValue;
...@@ -115,7 +117,16 @@ public abstract class Table extends SchemaObjectBase { ...@@ -115,7 +117,16 @@ public abstract class Table extends SchemaObjectBase {
memoryPerRow = memory; memoryPerRow = memory;
} }
public void renameColumn(Column column, String newName) { public void renameColumn(Column column, String newName) throws SQLException {
for (int i = 0; i < columns.length; i++) {
Column c = columns[i];
if (c == column) {
continue;
}
if (c.getName().equals(newName)) {
throw Message.getSQLException(ErrorCode.DUPLICATE_COLUMN_NAME_1, newName);
}
}
columnMap.remove(column.getName()); columnMap.remove(column.getName());
column.rename(newName); column.rename(newName);
columnMap.put(newName, column); columnMap.put(newName, column);
...@@ -150,19 +161,21 @@ public abstract class Table extends SchemaObjectBase { ...@@ -150,19 +161,21 @@ public abstract class Table extends SchemaObjectBase {
public abstract long getMaxDataModificationId(); public abstract long getMaxDataModificationId();
public void updateRows(Prepared prepared, Session session, ObjectArray oldRows, ObjectArray newRows) public void updateRows(Prepared prepared, Session session, RowList rows)
throws SQLException { throws SQLException {
// remove the old rows // remove the old rows
for (int i = 0; i < oldRows.size(); i++) { for (rows.reset(); rows.hasNext();) {
prepared.checkCancelled(); prepared.checkCancelled();
Row o = (Row) oldRows.get(i); Row o = rows.next();
rows.next();
removeRow(session, o); removeRow(session, o);
session.log(this, UndoLogRecord.DELETE, o); session.log(this, UndoLogRecord.DELETE, o);
} }
// add the new rows // add the new rows
for (int i = 0; i < newRows.size(); i++) { for (rows.reset(); rows.hasNext();) {
prepared.checkCancelled(); prepared.checkCancelled();
Row n = (Row) newRows.get(i); rows.next();
Row n = rows.next();
addRow(session, n); addRow(session, n);
session.log(this, UndoLogRecord.INSERT, n); session.log(this, UndoLogRecord.INSERT, n);
} }
......
...@@ -22,6 +22,7 @@ import org.h2.index.LinkedIndex; ...@@ -22,6 +22,7 @@ import org.h2.index.LinkedIndex;
import org.h2.log.UndoLogRecord; import org.h2.log.UndoLogRecord;
import org.h2.message.Message; import org.h2.message.Message;
import org.h2.result.Row; import org.h2.result.Row;
import org.h2.result.RowList;
import org.h2.schema.Schema; import org.h2.schema.Schema;
import org.h2.util.JdbcUtils; import org.h2.util.JdbcUtils;
import org.h2.util.MathUtils; import org.h2.util.MathUtils;
...@@ -351,14 +352,14 @@ public class TableLink extends Table { ...@@ -351,14 +352,14 @@ public class TableLink extends Table {
return null; return null;
} }
public void updateRows(Prepared prepared, Session session, ObjectArray oldRows, ObjectArray newRows) public void updateRows(Prepared prepared, Session session, RowList rows)
throws SQLException { throws SQLException {
boolean deleteInsert; boolean deleteInsert;
if (emitUpdates) { if (emitUpdates) {
for (int i = 0; i < oldRows.size(); i++) { for (rows.reset(); rows.hasNext();) {
session.checkCancelled(); session.checkCancelled();
Row oldRow = (Row) oldRows.get(i); Row oldRow = rows.next();
Row newRow = (Row) newRows.get(i); Row newRow = rows.next();
linkedIndex.update(session, oldRow, newRow); linkedIndex.update(session, oldRow, newRow);
session.log(this, UndoLogRecord.DELETE, oldRow); session.log(this, UndoLogRecord.DELETE, oldRow);
session.log(this, UndoLogRecord.INSERT, newRow); session.log(this, UndoLogRecord.INSERT, newRow);
...@@ -368,7 +369,7 @@ public class TableLink extends Table { ...@@ -368,7 +369,7 @@ public class TableLink extends Table {
deleteInsert = true; deleteInsert = true;
} }
if (deleteInsert) { if (deleteInsert) {
super.updateRows(prepared, session, oldRows, newRows); super.updateRows(prepared, session, rows);
} }
} }
......
...@@ -47,17 +47,22 @@ public class TableView extends Table { ...@@ -47,17 +47,22 @@ public class TableView extends Table {
index = new ViewIndex(this, querySQL, params, recursive); index = new ViewIndex(this, querySQL, params, recursive);
initColumnsAndTables(session); initColumnsAndTables(session);
} }
public Query recompileQuery(Session session) throws SQLException {
Prepared p = session.prepare(querySQL);
if (!(p instanceof Query)) {
throw Message.getSyntaxError(querySQL, 0);
}
Query query = (Query) p;
querySQL = query.getPlanSQL();
return query;
}
private void initColumnsAndTables(Session session) throws SQLException { private void initColumnsAndTables(Session session) throws SQLException {
Column[] cols; Column[] cols;
removeViewFromTables(); removeViewFromTables();
try { try {
Prepared p = session.prepare(querySQL); Query query = recompileQuery(session);
if (!(p instanceof Query)) {
throw Message.getSyntaxError(querySQL, 0);
}
Query query = (Query) p;
querySQL = query.getPlanSQL();
tables = new ObjectArray(query.getTables()); tables = new ObjectArray(query.getTables());
ObjectArray expressions = query.getExpressions(); ObjectArray expressions = query.getExpressions();
ObjectArray list = new ObjectArray(); ObjectArray list = new ObjectArray();
......
...@@ -34,7 +34,7 @@ public class Backup { ...@@ -34,7 +34,7 @@ public class Backup {
/** /**
* The command line interface for this tool. * The command line interface for this tool.
* The options must be split into strings like this: "-db", "test",... * The options must be split into strings like this: "-db", "test",...
* The following options are supported: * Options are case sensitive. The following options are supported:
* <ul> * <ul>
* <li>-help or -? (print the list of options) * <li>-help or -? (print the list of options)
* </li><li>-file filename (the default is backup.zip) * </li><li>-file filename (the default is backup.zip)
......
...@@ -37,7 +37,7 @@ public class ChangePassword { ...@@ -37,7 +37,7 @@ public class ChangePassword {
/** /**
* The command line interface for this tool. * The command line interface for this tool.
* The options must be split into strings like this: "-db", "test",... * The options must be split into strings like this: "-db", "test",...
* The following options are supported: * Options are case sensitive. The following options are supported:
* <ul> * <ul>
* <li>-help or -? (print the list of options) * <li>-help or -? (print the list of options)
* </li><li>-dir database directory (the default is the current directory) * </li><li>-dir database directory (the default is the current directory)
...@@ -75,6 +75,9 @@ public class ChangePassword { ...@@ -75,6 +75,9 @@ public class ChangePassword {
encrypt = getFileEncryptionKey(args[++i].toCharArray()); encrypt = getFileEncryptionKey(args[++i].toCharArray());
} else if (args[i].equals("-quiet")) { } else if (args[i].equals("-quiet")) {
quiet = true; quiet = true;
} else {
showUsage();
return;
} }
} }
if (encrypt == null && decrypt == null) { if (encrypt == null && decrypt == null) {
......
...@@ -55,7 +55,9 @@ ShutdownHandler { ...@@ -55,7 +55,9 @@ ShutdownHandler {
/** /**
* The command line interface for this tool. * The command line interface for this tool.
* The command line options are the same as in the Server tool. * The command line options are the same as in the Server tool,
* but this tool will always start the TCP, TCP and PG server.
* Options are case sensitive.
* *
* @param args the command line arguments * @param args the command line arguments
* @throws Exception * @throws Exception
......
...@@ -22,11 +22,11 @@ public class ConvertTraceFile { ...@@ -22,11 +22,11 @@ public class ConvertTraceFile {
System.out.println("java "+getClass().getName() System.out.println("java "+getClass().getName()
+ " [-traceFile <trace file name>] [-javaClass <java class name>] [-script <sql script file>]"); + " [-traceFile <trace file name>] [-javaClass <java class name>] [-script <sql script file>]");
} }
/** /**
* The command line interface for this tool. * The command line interface for this tool.
* The options must be split into strings like this: "-traceFile", "test.trace.db",... * The options must be split into strings like this: "-traceFile", "test.trace.db",...
* The following options are supported: * Options are case sensitive. The following options are supported:
* <ul> * <ul>
* <li>-help or -? (print the list of options) * <li>-help or -? (print the list of options)
* </li><li>-traceFile filename (the default is test.trace.db) * </li><li>-traceFile filename (the default is test.trace.db)
......
...@@ -28,7 +28,7 @@ public class CreateCluster { ...@@ -28,7 +28,7 @@ public class CreateCluster {
/** /**
* The command line interface for this tool. * The command line interface for this tool.
* The options must be split into strings like this: "-urlSource", "jdbc:h2:test",... * The options must be split into strings like this: "-urlSource", "jdbc:h2:test",...
* The following options are supported: * Options are case sensitive. The following options are supported:
* <ul> * <ul>
* <li>-help or -? (print the list of options) * <li>-help or -? (print the list of options)
* </li><li>-urlSource jdbc:h2:... (the database URL of the source database) * </li><li>-urlSource jdbc:h2:... (the database URL of the source database)
......
...@@ -26,7 +26,7 @@ public class DeleteDbFiles { ...@@ -26,7 +26,7 @@ public class DeleteDbFiles {
/** /**
* The command line interface for this tool. * The command line interface for this tool.
* The options must be split into strings like this: "-db", "test",... * The options must be split into strings like this: "-db", "test",...
* The following options are supported: * Options are case sensitive. The following options are supported:
* <ul> * <ul>
* <li>-help or -? (print the list of options) * <li>-help or -? (print the list of options)
* </li><li>-dir database directory (the default is the current directory) * </li><li>-dir database directory (the default is the current directory)
......
...@@ -76,7 +76,7 @@ public class Recover implements DataHandler { ...@@ -76,7 +76,7 @@ public class Recover implements DataHandler {
/** /**
* The command line interface for this tool. * The command line interface for this tool.
* The options must be split into strings like this: "-db", "test",... * The options must be split into strings like this: "-db", "test",...
* The following options are supported: * Options are case sensitive. The following options are supported:
* <ul> * <ul>
* <li>-help or -? (print the list of options) * <li>-help or -? (print the list of options)
* </li><li>-dir database directory (the default is the current directory) * </li><li>-dir database directory (the default is the current directory)
......
...@@ -32,7 +32,7 @@ public class Restore { ...@@ -32,7 +32,7 @@ public class Restore {
/** /**
* The command line interface for this tool. * The command line interface for this tool.
* The options must be split into strings like this: "-db", "test",... * The options must be split into strings like this: "-db", "test",...
* The following options are supported: * Options are case sensitive. The following options are supported:
* <ul> * <ul>
* <li>-help or -? (print the list of options) * <li>-help or -? (print the list of options)
* </li><li>-file filename (the default is backup.zip) * </li><li>-file filename (the default is backup.zip)
......
...@@ -39,8 +39,8 @@ public class RunScript { ...@@ -39,8 +39,8 @@ public class RunScript {
} }
/** /**
* The command line interface for this tool. The options must be split into strings like this: "-user", "sa",... The * The command line interface for this tool. The options must be split into strings like this: "-user", "sa",...
* following options are supported: * Options are case sensitive. The following options are supported:
* <ul> * <ul>
* <li>-help or -? (print the list of options) * <li>-help or -? (print the list of options)
* </li><li>-url jdbc:h2:... (database URL) * </li><li>-url jdbc:h2:... (database URL)
......
...@@ -32,7 +32,7 @@ public class Script { ...@@ -32,7 +32,7 @@ public class Script {
/** /**
* The command line interface for this tool. * The command line interface for this tool.
* The options must be split into strings like this: "-user", "sa",... * The options must be split into strings like this: "-user", "sa",...
* The following options are supported: * Options are case sensitive. The following options are supported:
* <ul> * <ul>
* <li>-help or -? (print the list of options) * <li>-help or -? (print the list of options)
* </li><li>-url jdbc:h2:... (database URL) * </li><li>-url jdbc:h2:... (database URL)
......
...@@ -36,7 +36,8 @@ public class Server implements Runnable, ShutdownHandler { ...@@ -36,7 +36,8 @@ public class Server implements Runnable, ShutdownHandler {
private void showUsage() { private void showUsage() {
System.out.println("java "+getClass().getName() + " [options]"); System.out.println("java "+getClass().getName() + " [options]");
System.out.println("By default, -tcp, -web, -browser and -pg are started"); System.out.println("By default, -tcp, -web, -browser and -pg are started.");
System.out.println("Options are case sensitive. Options:");
System.out.println("-tcp (start the TCP Server)"); System.out.println("-tcp (start the TCP Server)");
System.out.println("-tcpPort <port> (default: " + TcpServer.DEFAULT_PORT+")"); System.out.println("-tcpPort <port> (default: " + TcpServer.DEFAULT_PORT+")");
System.out.println("-tcpSSL [true|false]"); System.out.println("-tcpSSL [true|false]");
...@@ -62,9 +63,9 @@ public class Server implements Runnable, ShutdownHandler { ...@@ -62,9 +63,9 @@ public class Server implements Runnable, ShutdownHandler {
System.out.println("-ftpWrite <writeUserName> (default: " + FtpServer.DEFAULT_WRITE+")"); System.out.println("-ftpWrite <writeUserName> (default: " + FtpServer.DEFAULT_WRITE+")");
System.out.println("-ftpWritePassword <password> (default: " + FtpServer.DEFAULT_WRITE_PASSWORD+")"); System.out.println("-ftpWritePassword <password> (default: " + FtpServer.DEFAULT_WRITE_PASSWORD+")");
System.out.println("-log [true|false]"); System.out.println("-log [true|false] (for all servers)");
System.out.println("-baseDir <directory> (sets the base directory for H2 databases)"); System.out.println("-baseDir <directory> (sets the base directory for H2 databases, for all servers)");
System.out.println("-ifExists [true|false] (only existing databases may be opened)"); System.out.println("-ifExists [true|false] (only existing databases may be opened, for all servers)");
} }
private Server() { private Server() {
...@@ -75,6 +76,7 @@ public class Server implements Runnable, ShutdownHandler { ...@@ -75,6 +76,7 @@ public class Server implements Runnable, ShutdownHandler {
* The options must be split into strings like this: "-baseDir", "/temp/data",... * The options must be split into strings like this: "-baseDir", "/temp/data",...
* By default, -tcp, -web, -browser and -pg are started. * By default, -tcp, -web, -browser and -pg are started.
* If there is a problem starting a service, the program terminates with an exit code of 1. * If there is a problem starting a service, the program terminates with an exit code of 1.
* Options are case sensitive.
* The following options are supported: * The following options are supported:
* <ul> * <ul>
* <li>-help or -? (print the list of options) * <li>-help or -? (print the list of options)
...@@ -152,6 +154,9 @@ public class Server implements Runnable, ShutdownHandler { ...@@ -152,6 +154,9 @@ public class Server implements Runnable, ShutdownHandler {
} else if ("-browser".equals(a)) { } else if ("-browser".equals(a)) {
startDefaultServers = false; startDefaultServers = false;
browserStart = true; browserStart = true;
} else {
showUsage();
return EXIT_ERROR;
} }
} }
int exitCode = 0; int exitCode = 0;
......
...@@ -2,7 +2,7 @@ ...@@ -2,7 +2,7 @@
* Copyright 2004-2007 H2 Group. Licensed under the H2 License, Version 1.0 (http://h2database.com/html/license.html). * Copyright 2004-2007 H2 Group. Licensed under the H2 License, Version 1.0 (http://h2database.com/html/license.html).
* Initial Developer: H2 Group * Initial Developer: H2 Group
*/ */
package org.h2.jdbcx; package org.h2.util;
import java.sql.SQLException; import java.sql.SQLException;
......
...@@ -44,7 +44,11 @@ public class TempFileDeleter { ...@@ -44,7 +44,11 @@ public class TempFileDeleter {
} }
public static void deleteUnused() { public static void deleteUnused() {
while (true) { // Mystery: I don't know how QUEUE could get null, but two independent
// people reported NullPointerException here - if somebody understands
// how it could happend please report it!
// Setup: webapp under Tomcat, exception occurs during undeploy
while (QUEUE != null) {
Reference ref = QUEUE.poll(); Reference ref = QUEUE.poll();
if (ref == null) { if (ref == null) {
break; break;
......
...@@ -637,4 +637,8 @@ public class ValueLob extends Value { ...@@ -637,4 +637,8 @@ public class ValueLob extends Value {
} }
} }
public void setFileName(String fileName) {
this.fileName = fileName;
}
} }
...@@ -150,16 +150,23 @@ java org.h2.test.TestAll timer ...@@ -150,16 +150,23 @@ java org.h2.test.TestAll timer
/* /*
support large updates/deletes
Known Problems: Known Problems:
link to history page, bug page link to history page, bug page
Add a link to the google code bug page Add a link to the google code bug page
History:
MVCC: now an exception is thrown when an application tries to change the MVCC setting while the database is already open.
Referential integrity checks didn't lock the referenced table, and thus could read uncommitted rows of other connections. In that way the referential contraints could get violated (except when using MVCC).
Renaming or dropping a user with a schema, or removing the admin property of that user made the schema inaccessible after re-opening the database. Fixed.
The H2 Console now also support the command line option -ifExists when started from the Server tool, but only when connecting to H2 databases.
Duplicate column names were not detected when renaming columns. Fixed.
The console did not display multiple embedded spaces in text correctly. Fixed.
Google Android support: use 'ant codeswitchAndroid' to switch the source code to Android. Google Android support: use 'ant codeswitchAndroid' to switch the source code to Android.
implement & test: checkpoint commits running transactions implement & test: checkpoint commits running transactions
start writing javadocs for jdbcx package
test DbStarter test DbStarter
create table test(id int, name varchar); create table test(id int, name varchar);
......
...@@ -18,10 +18,22 @@ public class TestBigResult extends TestBase { ...@@ -18,10 +18,22 @@ public class TestBigResult extends TestBase {
if (config.memory) { if (config.memory) {
return; return;
} }
testLargeUpdateDelete();
testCloseConnectionDelete(); testCloseConnectionDelete();
testOrderGroup(); testOrderGroup();
testLimitBufferedResult(); testLimitBufferedResult();
} }
private void testLargeUpdateDelete() throws Exception {
deleteDb("bigResult");
Connection conn = getConnection("bigResult");
Statement stat = conn.createStatement();
int len = getSize(10000, 100000);
stat.execute("CREATE TABLE TEST AS SELECT * FROM SYSTEM_RANGE(1, " + len + ")");
stat.execute("UPDATE TEST SET X=X+1");
stat.execute("DELETE FROM TEST");
conn.close();
}
private void testCloseConnectionDelete() throws Exception { private void testCloseConnectionDelete() throws Exception {
deleteDb("bigResult"); deleteDb("bigResult");
......
...@@ -18,6 +18,7 @@ public class TestRights extends TestBase { ...@@ -18,6 +18,7 @@ public class TestRights extends TestBase {
public void test() throws Exception { public void test() throws Exception {
// testLowerCaseUser(); // testLowerCaseUser();
testSchemaRenameUser();
testAccessRights(); testAccessRights();
} }
...@@ -35,6 +36,39 @@ public class TestRights extends TestBase { ...@@ -35,6 +36,39 @@ public class TestRights extends TestBase {
// conn.close(); // conn.close();
// } // }
public void testSchemaRenameUser() throws Exception {
if (config.memory) {
return;
}
deleteDb("rights");
Connection conn = getConnection("rights");
stat = conn.createStatement();
stat.execute("create user test password '' admin");
stat.execute("create schema b authorization test");
stat.execute("create table b.test(id int)");
stat.execute("alter user test rename to test1");
conn.close();
conn = getConnection("rights");
stat = conn.createStatement();
stat.execute("select * from b.test");
try {
stat.execute("alter user test1 admin false");
error("Unexpected success");
} catch (SQLException e) {
checkNotGeneralException(e);
}
try {
stat.execute("drop user test1");
error("Unexpected success");
} catch (SQLException e) {
checkNotGeneralException(e);
}
stat.execute("drop schema b");
stat.execute("alter user test1 admin false");
stat.execute("drop user test1");
conn.close();
}
public void testAccessRights() throws Exception { public void testAccessRights() throws Exception {
if (config.memory) { if (config.memory) {
return; return;
......
...@@ -18,9 +18,35 @@ import org.h2.test.TestBase; ...@@ -18,9 +18,35 @@ import org.h2.test.TestBase;
public class TestTransaction extends TestBase { public class TestTransaction extends TestBase {
public void test() throws Exception { public void test() throws Exception {
testReferential();
testSavepoint(); testSavepoint();
testIsolation(); testIsolation();
} }
private void testReferential() throws Exception {
deleteDb("transaction");
Connection c1 = getConnection("transaction");
c1.setAutoCommit(false);
Statement s1 = c1.createStatement();
s1.execute("drop table if exists a");
s1.execute("drop table if exists b");
s1.execute("create table a (id integer identity not null, code varchar(10) not null, primary key(id))");
s1.execute("create table b (name varchar(100) not null, a integer, primary key(name), foreign key(a) references a(id))");
Connection c2 = getConnection("transaction");
c2.setAutoCommit(false);
s1.executeUpdate("insert into A(code) values('un cod')");
Statement s2 = c2.createStatement();
try {
s2.executeUpdate("insert into B values('un B', 1)");
error("Unexpected success");
} catch (SQLException e) {
checkNotGeneralException(e);
}
c2.commit();
c1.rollback();
c1.close();
c2.close();
}
public void testSavepoint() throws Exception { public void testSavepoint() throws Exception {
deleteDb("transaction"); deleteDb("transaction");
......
...@@ -19,12 +19,37 @@ public class TestMVCC extends TestBase { ...@@ -19,12 +19,37 @@ public class TestMVCC extends TestBase {
Connection c1, c2; Connection c1, c2;
Statement s1, s2; Statement s1, s2;
public void test() throws Exception { public void test() throws Exception {
testSetMode();
testCases();
}
private void testSetMode() throws Exception {
DeleteDbFiles.execute(null, "test", true);
Class.forName("org.h2.Driver");
c1 = DriverManager.getConnection("jdbc:h2:test", "sa", "sa");
Statement stat = c1.createStatement();
ResultSet rs = stat.executeQuery("select * from information_schema.settings where name='MVCC'");
rs.next();
check("FALSE", rs.getString("VALUE"));
try {
stat.execute("SET MVCC TRUE");
error("Unexpected success");
} catch (SQLException e) {
check(ErrorCode.CANNOT_CHANGE_SETTING_WHEN_OPEN_1, e.getErrorCode());
}
rs = stat.executeQuery("select * from information_schema.settings where name='MVCC'");
rs.next();
check("FALSE", rs.getString("VALUE"));
c1.close();
}
private void testCases() throws Exception {
if (!config.mvcc) { if (!config.mvcc) {
return; return;
} }
// TODO Prio 1: make unit test work // TODO Prio 1: make unit test work (remaining problem: optimization for select min/max)
// TODO Prio 1: document: exclusive table lock still used when altering tables, adding indexes, select ... for update; table level locks are checked // TODO Prio 1: document: exclusive table lock still used when altering tables, adding indexes, select ... for update; table level locks are checked
// TODO Prio 1: free up disk space (for deleted rows and old versions of updated rows) on commit // TODO Prio 1: free up disk space (for deleted rows and old versions of updated rows) on commit
// TODO Prio 1: ScanIndex: never remove uncommitted data from cache (lost sessionId) // TODO Prio 1: ScanIndex: never remove uncommitted data from cache (lost sessionId)
...@@ -61,6 +86,21 @@ public class TestMVCC extends TestBase { ...@@ -61,6 +86,21 @@ public class TestMVCC extends TestBase {
s2.execute("drop table test"); s2.execute("drop table test");
c2.rollback(); c2.rollback();
// referential integrity problem
s1.execute("create table a (id integer identity not null, code varchar(10) not null, primary key(id))");
s1.execute("create table b (name varchar(100) not null, a integer, primary key(name), foreign key(a) references a(id))");
s1.execute("insert into a(code) values('un cod')");
try {
s2.execute("insert into b values('un B', 1)");
error("Unexpected success");
} catch (SQLException e) {
checkNotGeneralException(e);
}
c2.commit();
c1.rollback();
s1.execute("drop table a, b");
c2.commit();
// select for update should do an exclusive lock, even with mvcc // select for update should do an exclusive lock, even with mvcc
s1.execute("create table test(id int primary key, name varchar(255))"); s1.execute("create table test(id int primary key, name varchar(255))");
s1.execute("insert into test values(1, 'y')"); s1.execute("insert into test values(1, 'y')");
......
...@@ -28,7 +28,7 @@ public class TestWeb extends TestBase { ...@@ -28,7 +28,7 @@ public class TestWeb extends TestBase {
result = client.get(url, "index.do?language=en"); result = client.get(url, "index.do?language=en");
result = client.get(url, "login.jsp"); result = client.get(url, "login.jsp");
check(result.indexOf("Einstellung") < 0); check(result.indexOf("Einstellung") < 0);
result = client.get(url, "test.do?driver=abc&url=jdbc:h2:mem:web&user=sa&password=sa&name=_test_"); result = client.get(url, "test.do?driver=abc&url=jdbc:abc:mem:web&user=sa&password=sa&name=_test_");
check(result.indexOf("Exception") >= 0); check(result.indexOf("Exception") >= 0);
result = client.get(url, "test.do?driver=org.h2.Driver&url=jdbc:h2:mem:web&user=sa&password=sa&name=_test_"); result = client.get(url, "test.do?driver=org.h2.Driver&url=jdbc:h2:mem:web&user=sa&password=sa&name=_test_");
check(result.indexOf("Exception") < 0); check(result.indexOf("Exception") < 0);
......
--- special grammar and test cases --------------------------------------------------------------------------------------------- --- special grammar and test cases ---------------------------------------------------------------------------------------------
CREATE TABLE TEST(ID INTEGER NOT NULL, ID2 INTEGER DEFAULT 0);
> ok
ALTER TABLE test ALTER COLUMN ID2 RENAME TO ID;
> exception
drop table test;
> ok
create table test(id int primary key, data array); create table test(id int primary key, data array);
> ok > ok
......
Markdown 格式
0%
您添加了 0 到此讨论。请谨慎行事。
请先完成此评论的编辑!
注册 或者 后发表评论