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

--no commit message

--no commit message
上级 180841cb
......@@ -253,7 +253,7 @@
<mkdir dir="docs/javadoc"/>
<javadoc
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"
docletpath="bin"
/>
......
......@@ -155,7 +155,7 @@ When using the isolation level 'serializable', dirty reads, non-repeatable reads
<h3>Table Level Locking</h3>
<p>
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.
Shared locks and exclusive locks are supported.
Before reading from a table, the database tries to add a shared lock to the table
......@@ -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.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.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.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>
......
......@@ -15,6 +15,8 @@ Features
<a href="#feature_list">
Feature List</a><br />
<a href="#limitations">
Limitations</a><br />
<a href="#comparison">
Comparison to Other Database Engines</a><br />
<a href="#products_work_with">
......@@ -43,8 +45,8 @@ Features
Database File Layout</a><br />
<a href="#logging_recovery">
Logging and Recovery</a><br />
<a href="#compatibility_modes">
Compatibility Modes</a><br />
<a href="#compatibility">
Compatibility</a><br />
<a href="#trace_options">
Using the Trace Options</a><br />
<a href="#read_only">
......@@ -138,6 +140,13 @@ Features
</li><li>Well tested (high code coverage, randomized stress tests)
</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>
<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
the summary (object allocation table) is not read in this case, so opening the database takes longer.
</p>
<br /><a name="compatibility_modes"></a>
<h2>Compatibility Modes</h2>
<br /><a name="compatibility"></a>
<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>
All database engines behave a little bit different. For certain features,
this database can emulate the behavior of specific databases. Not all features or differences of those
In MySQL text columns are case insensitive by default, while in H2 they are case sensitive. However
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
(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.
......@@ -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.
</td></tr>
</table>
<p>
</p>
<br /><a name="trace_options"></a>
<h2>Using the Trace Options</h2>
......
......@@ -3657,6 +3657,12 @@ public class Parser {
boolean value = readBooleanSetting();
int setting = value ? TransactionCommand.AUTOCOMMIT_TRUE : TransactionCommand.AUTOCOMMIT_FALSE;
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")) {
readIfEqualOrTo();
boolean value = readBooleanSetting();
......@@ -3759,10 +3765,6 @@ public class Parser {
readIfEqualOrTo();
read();
return new NoOperation(session);
} else if (readIf("MVCC")) {
readIfEqualOrTo();
read();
return new NoOperation(session);
} else if (readIf("ACCESS_MODE_LOG")) {
readIfEqualOrTo();
read();
......
......@@ -86,6 +86,9 @@ public class AlterUser extends DefineCommand {
break;
case ADMIN:
session.getUser().checkAdmin();
if (!admin) {
user.checkNoSchemas();
}
user.setAdmin(admin);
break;
default:
......
......@@ -42,6 +42,7 @@ public class DropUser extends DefineCommand {
if (user == session.getUser()) {
throw Message.getSQLException(ErrorCode.CANNOT_DROP_CURRENT_USER);
}
user.checkNoSchemas();
db.removeDatabaseObject(session, user);
}
return 0;
......
......@@ -13,10 +13,10 @@ import org.h2.expression.Expression;
import org.h2.log.UndoLogRecord;
import org.h2.result.LocalResult;
import org.h2.result.Row;
import org.h2.result.RowList;
import org.h2.table.PlanItem;
import org.h2.table.Table;
import org.h2.table.TableFilter;
import org.h2.util.ObjectArray;
import org.h2.util.StringUtils;
/**
......@@ -46,38 +46,37 @@ public class Delete extends Prepared {
session.getUser().checkRight(table, Right.DELETE);
table.fireBefore(session);
table.lock(session, true, false);
ObjectArray rows = new ObjectArray();
setCurrentRowNumber(0);
while (tableFilter.next()) {
checkCancelled();
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++) {
RowList rows = new RowList(session);
try {
setCurrentRowNumber(0);
while (tableFilter.next()) {
checkCancelled();
Row row = (Row) rows.get(i);
table.fireBeforeRow(session, row, null);
setCurrentRowNumber(rows.size() + 1);
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 (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++) {
for (rows.reset(); rows.hasNext();) {
checkCancelled();
Row row = (Row) rows.get(i);
table.fireAfterRow(session, row, null);
Row row = rows.next();
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() {
......
......@@ -261,6 +261,18 @@ public class Set extends Prepared {
database.setReferentialIntegrity(value == 1);
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:
throw Message.getInternalError("type="+type);
}
......
......@@ -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 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 REFERENTIAL_INTEGRITY = 30;
public static final int REFERENTIAL_INTEGRITY = 30, MVCC = 31, MAX_OPERATION_MEMORY = 32;
private static ObjectArray types = new ObjectArray();
......@@ -55,6 +55,8 @@ public class SetTypes {
setType(SCHEMA_SEARCH_PATH, "SCHEMA_SEARCH_PATH");
setType(UNDO_LOG, "UNDO_LOG");
setType(REFERENTIAL_INTEGRITY, "REFERENTIAL_INTEGRITY");
setType(MVCC, "MVCC");
setType(MAX_OPERATION_MEMORY, "MAX_OPERATION_MEMORY");
}
private static void setType(int type, String name) {
......
......@@ -14,11 +14,11 @@ import org.h2.expression.Expression;
import org.h2.message.Message;
import org.h2.result.LocalResult;
import org.h2.result.Row;
import org.h2.result.RowList;
import org.h2.table.Column;
import org.h2.table.PlanItem;
import org.h2.table.Table;
import org.h2.table.TableFilter;
import org.h2.util.ObjectArray;
import org.h2.util.StringUtils;
import org.h2.value.Value;
......@@ -58,62 +58,61 @@ public class Update extends Prepared {
public int update() throws SQLException {
tableFilter.startQuery(session);
tableFilter.reset();
// TODO optimization: update old / new list: maybe use a linked list (to avoid array allocation)
ObjectArray oldRows = new ObjectArray();
ObjectArray newRows = new ObjectArray();
Table table = tableFilter.getTable();
session.getUser().checkRight(table, Right.UPDATE);
table.fireBefore(session);
table.lock(session, true, false);
int columnCount = table.getColumns().length;
// get the old rows, compute the new rows
setCurrentRowNumber(0);
while (tableFilter.next()) {
checkCancelled();
setCurrentRowNumber(oldRows.size()+1);
if (condition == null || Boolean.TRUE.equals(condition.getBooleanValue(session))) {
Row oldRow = tableFilter.get();
Row newRow = table.getTemplateRow();
for (int i = 0; i < columnCount; i++) {
Expression newExpr = expressions[i];
Value newValue;
if (newExpr == null) {
newValue = oldRow.getValue(i);
} else {
Column column = table.getColumn(i);
newValue = newExpr.getValue(session).convertTo(column.getType());
RowList rows = new RowList(session);
try {
Table table = tableFilter.getTable();
session.getUser().checkRight(table, Right.UPDATE);
table.fireBefore(session);
table.lock(session, true, false);
int columnCount = table.getColumns().length;
// get the old rows, compute the new rows
setCurrentRowNumber(0);
int count = 0;
while (tableFilter.next()) {
checkCancelled();
setCurrentRowNumber(count+1);
if (condition == null || Boolean.TRUE.equals(condition.getBooleanValue(session))) {
Row oldRow = tableFilter.get();
Row newRow = table.getTemplateRow();
for (int i = 0; i < columnCount; i++) {
Expression newExpr = expressions[i];
Value newValue;
if (newExpr == null) {
newValue = oldRow.getValue(i);
} else {
Column column = table.getColumn(i);
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);
}
}
table.updateRows(this, session, oldRows, newRows);
if (table.fireRow()) {
for (int i = 0; i < newRows.size(); i++) {
checkCancelled();
Row n = (Row) newRows.get(i);
Row o = (Row) oldRows.get(i);
table.fireAfterRow(session, o, n);
// 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
table.updateRows(this, session, rows);
if (table.fireRow()) {
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() {
......
......@@ -313,6 +313,7 @@ public class ErrorCode {
public static final int METHOD_NOT_ALLOWED_FOR_PREPARED_STATEMENT = 90130;
public static final int CONCURRENT_UPDATE_1 = 90131;
public static final int AGGREGATE_NOT_FOUND_1 = 90132;
public static final int CANNOT_CHANGE_SETTING_WHEN_OPEN_1 = 90133;
/**
* INTERNAL
......
......@@ -61,6 +61,10 @@ public class SysProperties {
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 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) {
String s = System.getProperty(name);
......
......@@ -288,6 +288,7 @@ public class ConstraintReferential extends Constraint {
}
private boolean found(Session session, Index index, SearchRow check) throws SQLException {
index.getTable().lock(session, false, false);
Cursor cursor = index.find(session, check, check);
while (cursor.next()) {
SearchRow found;
......
......@@ -42,7 +42,7 @@ public class ConnectionInfo {
// TODO document these settings
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",
"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++) {
String key = connectionTime[i];
if (SysProperties.CHECK && KNOWN_SETTINGS.contains(key)) {
......
......@@ -145,6 +145,7 @@ public class Database implements DataHandler {
private DatabaseCloser closeOnExit;
private Mode mode = Mode.getInstance(Mode.REGULAR);
private boolean multiThreaded;
private int maxOperationMemory = SysProperties.DEFAULT_MAX_OPERERATION_MEMORY;
public Database(String name, ConnectionInfo ci, String cipher) throws SQLException {
this.compareMode = new CompareMode(null, null);
......@@ -468,8 +469,7 @@ public class Database implements DataHandler {
roles.put(Constants.PUBLIC_ROLE_NAME, publicRole);
systemUser.setAdmin(true);
systemSession = new Session(this, systemUser, ++nextSessionId);
// TODO storage: antivir scans .script files, maybe other scanners scan
// .db files?
// TODO storage: antivir scans .script files, maybe other scanners scan .db files?
ObjectArray cols = new ObjectArray();
Column columnId = new Column("ID", Value.INT);
columnId.setNullable(false);
......@@ -567,6 +567,22 @@ public class Database implements DataHandler {
}
}
} 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 {
......@@ -824,7 +840,7 @@ public class Database implements DataHandler {
closing = true;
}
try {
if (persistent && fileData != null) {
if (systemSession != null) {
ObjectArray tablesAndViews = getAllSchemaObjects(DbObject.TABLE_OR_VIEW);
for (int i = 0; i < tablesAndViews.size(); i++) {
Table table = (Table) tablesAndViews.get(i);
......@@ -1594,5 +1610,13 @@ public class Database implements DataHandler {
public void setMultiThreaded(boolean 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;
import org.h2.constant.ErrorCode;
import org.h2.message.Message;
import org.h2.message.Trace;
import org.h2.schema.Schema;
import org.h2.security.SHA256;
import org.h2.table.MetaTable;
import org.h2.table.RangeTable;
......@@ -138,14 +139,21 @@ public class User extends RightOwner {
public ObjectArray getChildren() {
ObjectArray all = database.getAllRights();
ObjectArray rights = new ObjectArray();
ObjectArray children = new ObjectArray();
for (int i = 0; i < all.size(); i++) {
Right right = (Right) all.get(i);
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 {
......@@ -166,4 +174,14 @@ public class User extends RightOwner {
// 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;
import org.h2.engine.SessionInterface;
import org.h2.engine.SessionRemote;
import org.h2.expression.ParameterInterface;
import org.h2.jdbcx.JdbcConnectionListener;
import org.h2.message.Message;
import org.h2.message.Trace;
import org.h2.message.TraceObject;
import org.h2.result.ResultInterface;
import org.h2.util.ClassUtils;
import org.h2.util.JdbcConnectionListener;
import org.h2.util.TempFileDeleter;
import org.h2.value.Value;
import org.h2.value.ValueInt;
......
......@@ -225,27 +225,53 @@ implements XADataSource, DataSource, ConnectionPoolDataSource, Serializable, Ref
//#endif
/**
* Open a new XA connection using the current URL, user name and password.
*
* @return the connection
*/
//#ifdef JDK14
public XAConnection getXAConnection() throws SQLException {
int document;
debugCodeCall("getXAConnection");
int id = getNextId(XA_DATA_SOURCE);
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 {
debugCode("getXAConnection("+quote(user)+", "+quote(password)+");");
int id = getNextId(XA_DATA_SOURCE);
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 {
debugCodeCall("getPooledConnection");
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 {
debugCode("getPooledConnection("+quote(user)+", "+quote(password)+");");
return getXAConnection(user, password);
......
......@@ -18,6 +18,7 @@ import javax.transaction.xa.XAException;
import javax.transaction.xa.XAResource;
import javax.transaction.xa.Xid;
import org.h2.util.ByteUtils;
import org.h2.util.JdbcConnectionListener;
import org.h2.util.JdbcUtils;
import org.h2.jdbc.JdbcConnection;
//#endif
......
......@@ -153,6 +153,7 @@
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
90132=Aggregate {0} not found
90133=Cannot change the setting {0} when the database is already open
HY000=General error\: {0}
HY004=Unknown data type\: {0}
HYC00=Feature not supported
......
......@@ -941,6 +941,18 @@ Admin rights are required to execute this command.
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","
SET MODE {REGULAR | HSQLDB | POSTGRESQL | MYSQL}
","
......
......@@ -115,12 +115,12 @@ public class LocalResult implements ResultInterface {
} else {
this.maxMemoryRows = session.getDatabase().getMaxMemoryRows();
}
this.expressions = new Expression[cols.size()];
cols.toArray(expressions);
this.displaySizes = new int[cols.size()];
rows = new ObjectArray();
this.visibleColumnCount = visibleColumnCount;
rowId = -1;
this.expressions = new Expression[cols.size()];
cols.toArray(expressions);
this.displaySizes = new int[cols.size()];
}
public void setSortOrder(SortOrder sort) {
......
......@@ -66,22 +66,22 @@ public class Row extends Record implements SearchRow {
return blockCount * (DiskFile.BLOCK_SIZE / 16) + memory * 4;
}
// public String toString() {
// int testing;
// StringBuffer buff = new StringBuffer(data.length*5);
// buff.append('(');
// for(int i=0; i<data.length; i++) {
// if(i>0) {
// buff.append(", ");
// }
// buff.append(data[i].getSQL());
// }
// buff.append(')');
// buff.append(" /* pos: " + getPos() + "*/ ");
// if(getDeleted()) {
// buff.append(" /* deleted /*");
// }
// return buff.toString();
// }
public String toString() {
StringBuffer buff = new StringBuffer(data.length * 5);
buff.append("( /* pos:");
buff.append(getPos());
if (getDeleted()) {
buff.append(" deleted");
}
buff.append(" */ ");
for (int i = 0; i < data.length; i++) {
if (i > 0) {
buff.append(", ");
}
buff.append(data[i].getSQL());
}
buff.append(')');
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 {
return "&nbsp;";
}
StringBuffer buff = new StringBuffer(s.length());
boolean leadingSpace = true;
boolean convertSpace = true;
for (int i = 0; i < s.length(); i++) {
char c = s.charAt(i);
if (leadingSpace) {
if (c == ' ') {
if (c == ' ') {
if (convertSpace) {
buff.append("&nbsp;");
continue;
} else {
leadingSpace = false;
buff.append(' ');
convertSpace = true;
}
continue;
} else {
convertSpace = false;
}
switch (c) {
case '$':
......@@ -261,7 +264,7 @@ public class PageParser {
case '\n':
if (convertBreak) {
buff.append("<br />");
leadingSpace = true;
convertSpace = true;
} else {
buff.append(c);
}
......
......@@ -105,6 +105,7 @@ public class WebServer implements Service {
private String url;
private ShutdownHandler shutdownHandler;
private Thread listenerThread;
private boolean ifExists;
byte[] getFile(String file) throws IOException {
trace("getFile <" + file + ">");
......@@ -180,15 +181,18 @@ public class WebServer implements Service {
ssl = FileUtils.getBooleanProperty(prop, "webSSL", Constants.DEFAULT_HTTP_SSL);
allowOthers = FileUtils.getBooleanProperty(prop, "webAllowOthers", Constants.DEFAULT_HTTP_ALLOW_OTHERS);
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]);
} else if ("-webSSL".equals(args[i])) {
} else if ("-webSSL".equals(a)) {
ssl = Boolean.valueOf(args[++i]).booleanValue();
} else if ("-webAllowOthers".equals(args[i])) {
} else if ("-webAllowOthers".equals(a)) {
allowOthers = Boolean.valueOf(args[++i]).booleanValue();
} else if ("-baseDir".equals(args[i])) {
} else if ("-baseDir".equals(a)) {
String baseDir = args[++i];
SysProperties.setBaseDir(baseDir);
} else if ("-ifExists".equals(a)) {
ifExists = Boolean.valueOf(args[++i]).booleanValue();
}
}
// if(driverList != null) {
......@@ -464,7 +468,14 @@ public class WebServer implements Service {
p.setProperty("user", user.trim());
p.setProperty("password", password.trim());
if (url.startsWith("jdbc:h2:")) {
if (ifExists) {
url += ";IFEXISTS=TRUE";
}
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 {
// Driver dr = (Driver) urlClassLoader.loadClass(driver).newInstance();
......
......@@ -222,14 +222,19 @@ public abstract class DataPage {
lob.convertToFileIfRequired(handler);
byte[] small = lob.getSmall();
if (small == null) {
// TODO lob: currently use -2 for historical reasons (-1 didn't
// store precision)
writeInt(-2);
// -2 for historical reasons (-1 didn't store precision)
int type = -2;
if (!lob.isLinked()) {
type = -3;
}
writeInt(type);
writeInt(lob.getTableId());
writeInt(lob.getObjectId());
writeLong(lob.getPrecision());
writeByte((byte) (lob.useCompression() ? 1 : 0)); // compression
// flag
writeByte((byte) (lob.useCompression() ? 1 : 0));
if (type == -3) {
writeString(lob.getFileName());
}
} else {
writeInt(small.length);
write(small, 0, small.length);
......@@ -304,6 +309,9 @@ public abstract class DataPage {
len += getIntLen() + small.length;
} else {
len += getIntLen() + getIntLen() + getIntLen() + getLongLen(lob.getPrecision()) + 1;
if (!lob.isLinked()) {
len += getStringLen(lob.getFileName());
}
}
return len;
}
......@@ -385,13 +393,16 @@ public abstract class DataPage {
int objectId = readInt();
long precision = 0;
boolean compression = false;
// TODO lob: -2 is for historical reasons (-1 didn't store
// precision)
if (smallLen == -2) {
// -2 is for historical reasons (-1 didn't store precision)
if (smallLen == -2 || smallLen == -3) {
precision = readLong();
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: {
......
......@@ -687,6 +687,7 @@ public class MetaTable extends Table {
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[] { "MULTI_THREADED", database.getMultiThreaded() ? "1" : "0"});
DiskFile dataFile = database.getDataFile();
......
......@@ -6,6 +6,7 @@ package org.h2.table;
import java.sql.SQLException;
import java.util.HashMap;
import org.h2.command.Prepared;
import org.h2.constant.ErrorCode;
import org.h2.constraint.Constraint;
......@@ -19,6 +20,7 @@ import org.h2.log.UndoLogRecord;
import org.h2.message.Message;
import org.h2.message.Trace;
import org.h2.result.Row;
import org.h2.result.RowList;
import org.h2.result.SearchRow;
import org.h2.result.SimpleRow;
import org.h2.result.SimpleRowValue;
......@@ -115,7 +117,16 @@ public abstract class Table extends SchemaObjectBase {
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());
column.rename(newName);
columnMap.put(newName, column);
......@@ -150,19 +161,21 @@ public abstract class Table extends SchemaObjectBase {
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 {
// remove the old rows
for (int i = 0; i < oldRows.size(); i++) {
for (rows.reset(); rows.hasNext();) {
prepared.checkCancelled();
Row o = (Row) oldRows.get(i);
Row o = rows.next();
rows.next();
removeRow(session, o);
session.log(this, UndoLogRecord.DELETE, o);
}
// add the new rows
for (int i = 0; i < newRows.size(); i++) {
for (rows.reset(); rows.hasNext();) {
prepared.checkCancelled();
Row n = (Row) newRows.get(i);
rows.next();
Row n = rows.next();
addRow(session, n);
session.log(this, UndoLogRecord.INSERT, n);
}
......
......@@ -22,6 +22,7 @@ import org.h2.index.LinkedIndex;
import org.h2.log.UndoLogRecord;
import org.h2.message.Message;
import org.h2.result.Row;
import org.h2.result.RowList;
import org.h2.schema.Schema;
import org.h2.util.JdbcUtils;
import org.h2.util.MathUtils;
......@@ -351,14 +352,14 @@ public class TableLink extends Table {
return null;
}
public void updateRows(Prepared prepared, Session session, ObjectArray oldRows, ObjectArray newRows)
public void updateRows(Prepared prepared, Session session, RowList rows)
throws SQLException {
boolean deleteInsert;
if (emitUpdates) {
for (int i = 0; i < oldRows.size(); i++) {
for (rows.reset(); rows.hasNext();) {
session.checkCancelled();
Row oldRow = (Row) oldRows.get(i);
Row newRow = (Row) newRows.get(i);
Row oldRow = rows.next();
Row newRow = rows.next();
linkedIndex.update(session, oldRow, newRow);
session.log(this, UndoLogRecord.DELETE, oldRow);
session.log(this, UndoLogRecord.INSERT, newRow);
......@@ -368,7 +369,7 @@ public class TableLink extends Table {
deleteInsert = true;
}
if (deleteInsert) {
super.updateRows(prepared, session, oldRows, newRows);
super.updateRows(prepared, session, rows);
}
}
......
......@@ -47,17 +47,22 @@ public class TableView extends Table {
index = new ViewIndex(this, querySQL, params, recursive);
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 {
Column[] cols;
removeViewFromTables();
try {
Prepared p = session.prepare(querySQL);
if (!(p instanceof Query)) {
throw Message.getSyntaxError(querySQL, 0);
}
Query query = (Query) p;
querySQL = query.getPlanSQL();
Query query = recompileQuery(session);
tables = new ObjectArray(query.getTables());
ObjectArray expressions = query.getExpressions();
ObjectArray list = new ObjectArray();
......
......@@ -34,7 +34,7 @@ public class Backup {
/**
* The command line interface for this tool.
* 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>
* <li>-help or -? (print the list of options)
* </li><li>-file filename (the default is backup.zip)
......
......@@ -37,7 +37,7 @@ public class ChangePassword {
/**
* The command line interface for this tool.
* 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>
* <li>-help or -? (print the list of options)
* </li><li>-dir database directory (the default is the current directory)
......@@ -75,6 +75,9 @@ public class ChangePassword {
encrypt = getFileEncryptionKey(args[++i].toCharArray());
} else if (args[i].equals("-quiet")) {
quiet = true;
} else {
showUsage();
return;
}
}
if (encrypt == null && decrypt == null) {
......
......@@ -55,7 +55,9 @@ ShutdownHandler {
/**
* 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
* @throws Exception
......
......@@ -22,11 +22,11 @@ public class ConvertTraceFile {
System.out.println("java "+getClass().getName()
+ " [-traceFile <trace file name>] [-javaClass <java class name>] [-script <sql script file>]");
}
/**
* The command line interface for this tool.
* 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>
* <li>-help or -? (print the list of options)
* </li><li>-traceFile filename (the default is test.trace.db)
......
......@@ -28,7 +28,7 @@ public class CreateCluster {
/**
* The command line interface for this tool.
* 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>
* <li>-help or -? (print the list of options)
* </li><li>-urlSource jdbc:h2:... (the database URL of the source database)
......
......@@ -26,7 +26,7 @@ public class DeleteDbFiles {
/**
* The command line interface for this tool.
* 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>
* <li>-help or -? (print the list of options)
* </li><li>-dir database directory (the default is the current directory)
......
......@@ -76,7 +76,7 @@ public class Recover implements DataHandler {
/**
* The command line interface for this tool.
* 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>
* <li>-help or -? (print the list of options)
* </li><li>-dir database directory (the default is the current directory)
......
......@@ -32,7 +32,7 @@ public class Restore {
/**
* The command line interface for this tool.
* 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>
* <li>-help or -? (print the list of options)
* </li><li>-file filename (the default is backup.zip)
......
......@@ -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
* following options are supported:
* The command line interface for this tool. The options must be split into strings like this: "-user", "sa",...
* Options are case sensitive. The following options are supported:
* <ul>
* <li>-help or -? (print the list of options)
* </li><li>-url jdbc:h2:... (database URL)
......
......@@ -32,7 +32,7 @@ public class Script {
/**
* The command line interface for this tool.
* 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>
* <li>-help or -? (print the list of options)
* </li><li>-url jdbc:h2:... (database URL)
......
......@@ -36,7 +36,8 @@ public class Server implements Runnable, ShutdownHandler {
private void showUsage() {
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("-tcpPort <port> (default: " + TcpServer.DEFAULT_PORT+")");
System.out.println("-tcpSSL [true|false]");
......@@ -62,9 +63,9 @@ public class Server implements Runnable, ShutdownHandler {
System.out.println("-ftpWrite <writeUserName> (default: " + FtpServer.DEFAULT_WRITE+")");
System.out.println("-ftpWritePassword <password> (default: " + FtpServer.DEFAULT_WRITE_PASSWORD+")");
System.out.println("-log [true|false]");
System.out.println("-baseDir <directory> (sets the base directory for H2 databases)");
System.out.println("-ifExists [true|false] (only existing databases may be opened)");
System.out.println("-log [true|false] (for all servers)");
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, for all servers)");
}
private Server() {
......@@ -75,6 +76,7 @@ public class Server implements Runnable, ShutdownHandler {
* The options must be split into strings like this: "-baseDir", "/temp/data",...
* 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.
* Options are case sensitive.
* The following options are supported:
* <ul>
* <li>-help or -? (print the list of options)
......@@ -152,6 +154,9 @@ public class Server implements Runnable, ShutdownHandler {
} else if ("-browser".equals(a)) {
startDefaultServers = false;
browserStart = true;
} else {
showUsage();
return EXIT_ERROR;
}
}
int exitCode = 0;
......
......@@ -2,7 +2,7 @@
* 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.jdbcx;
package org.h2.util;
import java.sql.SQLException;
......
......@@ -44,7 +44,11 @@ public class TempFileDeleter {
}
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();
if (ref == null) {
break;
......
......@@ -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
/*
support large updates/deletes
Known Problems:
link to history page, 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.
implement & test: checkpoint commits running transactions
start writing javadocs for jdbcx package
test DbStarter
create table test(id int, name varchar);
......
......@@ -18,10 +18,22 @@ public class TestBigResult extends TestBase {
if (config.memory) {
return;
}
testLargeUpdateDelete();
testCloseConnectionDelete();
testOrderGroup();
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 {
deleteDb("bigResult");
......
......@@ -18,6 +18,7 @@ public class TestRights extends TestBase {
public void test() throws Exception {
// testLowerCaseUser();
testSchemaRenameUser();
testAccessRights();
}
......@@ -35,6 +36,39 @@ public class TestRights extends TestBase {
// 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 {
if (config.memory) {
return;
......
......@@ -18,9 +18,35 @@ import org.h2.test.TestBase;
public class TestTransaction extends TestBase {
public void test() throws Exception {
testReferential();
testSavepoint();
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 {
deleteDb("transaction");
......
......@@ -19,12 +19,37 @@ public class TestMVCC extends TestBase {
Connection c1, c2;
Statement s1, s2;
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) {
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: 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)
......@@ -61,6 +86,21 @@ public class TestMVCC extends TestBase {
s2.execute("drop table test");
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
s1.execute("create table test(id int primary key, name varchar(255))");
s1.execute("insert into test values(1, 'y')");
......
......@@ -28,7 +28,7 @@ public class TestWeb extends TestBase {
result = client.get(url, "index.do?language=en");
result = client.get(url, "login.jsp");
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);
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);
......
--- 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);
> ok
......
Markdown 格式
0%
您添加了 0 到此讨论。请谨慎行事。
请先完成此评论的编辑!
注册 或者 后发表评论