提交 1b68cfa3 authored 作者: Thomas Mueller's avatar Thomas Mueller

--no commit message

--no commit message
上级 32b6da0e
......@@ -162,7 +162,7 @@
<!--
<attribute name="Bundle-Activator" value="org.h2.osgi.Activator"/>
-->
<attribute name="Main-Class" value="org.h2.SysTray"/>
<attribute name="Main-Class" value="org.h2.tools.Console"/>
</manifest>
<jar jarfile="bin/h2.jar" basedir="bin" manifest="bin/META-INF/MANIFEST.MF">
<include name="**/*.*"/>
......
......@@ -102,6 +102,7 @@ This database supports the following transaction isolation levels:
To enable, execute the SQL statement 'SET LOCK_MODE 0'<br />
or append ;LOCK_MODE=1 to the database URL: jdbc:h2:~/test;LOCK_MODE=1
</li><li><b>Read Committed</b><br />
Read locks are released immediately.
Higer concurrency is possible when using this level.<br />
This is the isolation level used for many database systems.<br />
To enable, execute the SQL statement 'SET LOCK_MODE 0'<br />
......
......@@ -80,13 +80,15 @@ To connect to a database using JDBC, use the following code:
</p>
<pre>
Class.forName("org.h2.Driver");
Connection conn = DriverManager.getConnection("jdbc:h2:test", "sa", "");
Connection conn = DriverManager.getConnection("jdbc:h2:~/test", "sa", "");
</pre>
<br /><a name="database_files"></a>
<h3>Where are the Database Files Stored?</h3>
<p>
If the base directory is not set, the database files are stored in the directory where the application is started
When using database URLs like jdbc:h2:~/test, the database is stored in the user directory.
For Windows, this is usually C:\Documents and Settings\&lt;userName&gt;.
If the base directory is not set (as in jdbc:h2:test), the database files are stored in the directory where the application is started
(the current working directory). When using the H2 Console application from the start menu, this is [Installation Directory]/bin.
The base directory can be set in the database URL. A fixed or relative path can be used. When using the URL
jdbc:h2:file:data/sample, the database is stored in the directory data (relative to the current working directory).
......
......@@ -359,7 +359,7 @@ This is achieved using different database URLs. The settings in the URLs are not
<td>Embedded (local) connection</td>
<td>
jdbc:h2:[file:][&lt;path&gt;]&lt;databaseName&gt;<br />
jdbc:h2:test<br />
jdbc:h2:~/test<br />
jdbc:h2:file:/data/sample<br />
jdbc:h2:file:C:/data/sample (Windows only)<br />
</td>
......@@ -502,7 +502,7 @@ and the user password; the file password itself may not contain spaces. File pas
encrypted database:
<pre>
Class.forName("org.h2.Driver");
String url = "jdbc:h2:test;CIPHER=AES";
String url = "jdbc:h2:~/test;CIPHER=AES";
String user = "sa";
String pwds = "filepwd userpwd";
conn = DriverManager.
......@@ -530,13 +530,13 @@ in this case it is up to the application to protect the database files.
To open the database with a different file locking method, use the parameter 'FILE_LOCK'.
The following code opens the database with the 'socket' locking method:
<pre>
String url = "jdbc:h2:test;FILE_LOCK=SOCKET";
String url = "jdbc:h2:~/test;FILE_LOCK=SOCKET";
</pre>
The following code forces the database to not create a lock file at all. Please note that
this is unsafe as another process is able to open the same database, possibly leading to
data corruption:
<pre>
String url = "jdbc:h2:test;FILE_LOCK=NO";
String url = "jdbc:h2:~/test;FILE_LOCK=NO";
</pre>
For more information about the algorithms please see in Advanced Topics under
File Locking Protocol.
......@@ -570,7 +570,7 @@ SET DB_CLOSE_DELAY 10
The value -1 means the database is never closed automatically.
The value 0 is the default and means the database is closed when the last connection is closed.
This setting is persistent and can be set by an administrator only.
It is possible to set the value in the database URL: <code>jdbc:h2:test;DB_CLOSE_DELAY=10</code>.
It is possible to set the value in the database URL: <code>jdbc:h2:~/test;DB_CLOSE_DELAY=10</code>.
<h3>Don't Close the Database when the VM Exits</h3>
By default, a database is closed when the last connection is closed. However, if it is never closed,
......@@ -582,7 +582,7 @@ This can be done in the database URL. The first connection (the one that is open
set the option in the database URL (it is not possible to change the setting afterwards).
The database URL to disable database closing on exit is:
<pre>
String url = "jdbc:h2:test;DB_CLOSE_ON_EXIT=FALSE";
String url = "jdbc:h2:~/test;DB_CLOSE_ON_EXIT=FALSE";
</pre>
<br /><a name="log_index_changes"></a>
......@@ -595,7 +595,7 @@ In some situations, for example when using very large databases (over a few hund
re-creating the index file takes very long.
In these situations it may be better to log changes to the index file,
so that recovery from a corrupted index file is fast.
To enable log index changes, add LOG=2 to the URL, as in jdbc:h2:test;LOG=2
To enable log index changes, add LOG=2 to the URL, as in jdbc:h2:~/test;LOG=2
This setting should be specified when connecting.
The update performance of the database will be reduced when using this option.
......@@ -625,7 +625,7 @@ The access mode used for log files is set via ACCESS_MODE_LOG; for
data and index files use ACCESS_MODE_DATA.
These settings must be specified in the database URL:
<pre>
String url = "jdbc:h2:test;ACCESS_MODE_LOG=rws;ACCESS_MODE_DATA=rws";
String url = "jdbc:h2:~/test;ACCESS_MODE_LOG=rws;ACCESS_MODE_DATA=rws";
</pre>
For more information see <a href="advanced.html#durability_problems">Durability Problems</a>.
On many operating systems the access mode 'rws' does not guarantee that the data is written to the disk.
......@@ -843,7 +843,7 @@ If the database file is corrupted, because the checksum of a record does not mat
file was edited with another application), the database can be opened in recovery mode. In this case,
errors in the database are logged but not thrown. The database should be backed up to a script
and re-built as soon as possible. To open the database in the recovery mode, use a database URL
must contain RECOVER=1, as in jdbc:h2:test;RECOVER=1. Indexes are rebuilt in this case, and
must contain RECOVER=1, as in jdbc:h2:~/test;RECOVER=1. Indexes are rebuilt in this case, and
the summary (object allocation table) is not read in this case, so opening the database takes longer.
</p>
......@@ -855,7 +855,7 @@ this database can emulate the behavior of specific databases. Not all features o
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.
To use the HSQLDB mode, you can use the database URL <code>jdbc:h2:test;MODE=HSQLDB</code>
To use the HSQLDB mode, you can use the database URL <code>jdbc:h2:~/test;MODE=HSQLDB</code>
or the SQL statement <code>SET MODE HSQLDB</code>.
Here is the list of currently supported modes and the difference to the regular mode:
</p>
......@@ -903,7 +903,7 @@ and one for file tracing (TRACE_LEVEL_FILE).
The trace levels are 0 for OFF, 1 for ERROR (the default), 2 for INFO and 3 for DEBUG.
A database URL with both levels set to DEBUG is:
<pre>
jdbc:h2:test;TRACE_LEVEL_FILE=3;TRACE_LEVEL_SYSTEM_OUT=3
jdbc:h2:~/test;TRACE_LEVEL_FILE=3;TRACE_LEVEL_SYSTEM_OUT=3
</pre>
The trace level can be changed at runtime by executing the SQL command
<code>SET TRACE_LEVEL_SYSTEM_OUT level</code> (for System.out tracing)
......@@ -989,7 +989,7 @@ for example to debug the database engine.
If a database already exists, the storage format is recognized automatically.
New databases are created in the binary storage format by default.
To create a new database in the text storage format, the database URL must contain
the parameter STORAGE=TEXT. Example URL: jdbc:h2:test;STORAGE=TEXT
the parameter STORAGE=TEXT. Example URL: jdbc:h2:~/test;STORAGE=TEXT
<br /><a name="low_disk_space"></a>
<h2>Graceful Handling of Low Disk Space Situations</h2>
......@@ -1007,7 +1007,7 @@ It is possible to install a database event listener to detect low disk space sit
(when only 1 MB if space is available). To do this, use the SQL statement
SET DATABASE_EVENT_LISTENER.
The listener can also be set at connection time, using an URL of the form
jdbc:h2:test;DATABASE_EVENT_LISTENER='com.acme.DbListener'
jdbc:h2:~/test;DATABASE_EVENT_LISTENER='com.acme.DbListener'
(the quotes around the class name are required).
See also the DatabaseEventListener API.
</p>
......@@ -1117,12 +1117,12 @@ However, Java Swing supports a way to get passwords using a char array (JPasswor
Instead of passing the user name as a separate parameter as in
<code>
Connection conn = DriverManager.
getConnection("jdbc:h2:test", "sa", "123");
getConnection("jdbc:h2:~/test", "sa", "123");
</code>
the user name (and/or password) can be supplied in the URL itself:
<code>
Connection conn = DriverManager.
getConnection("jdbc:h2:test;USER=sa;PASSWORD=123");
getConnection("jdbc:h2:~/test;USER=sa;PASSWORD=123");
</code>
The settings in the URL override the settings passed as a separate parameter.
......@@ -1273,7 +1273,7 @@ of a database and re-build the database from the script.
The database keeps most frequently used data and index pages in the main memory.
The amount of memory used for caching can be changed using the setting
CACHE_SIZE. This setting can be set in the database connection URL
(jdbc:h2:test;CACHE_SIZE=200000), or it can be changed at runtime using
(jdbc:h2:~/test;CACHE_SIZE=200000), or it can be changed at runtime using
SET CACHE_SIZE size.
</p><p>
This database supports two cache page replacement algorithms: LRU (the default) and
......@@ -1281,7 +1281,7 @@ This database supports two cache page replacement algorithms: LRU (the default)
cache if it becomes full. The 2Q algorithm is a bit more complicated, basically two
queues are used. The 2Q algorithm is more resistant to table scans, however the overhead
is a bit higher compared to the LRU. To use the cache algorithm 2Q, use a database URL
of the form jdbc:h2:test;CACHE_TYPE=TQ. The cache algorithm can not be changed
of the form jdbc:h2:~/test;CACHE_TYPE=TQ. The cache algorithm can not be changed
once the database is open.
</p><p>
To get information about page reads and writes, and the current caching algorithm in use,
......
......@@ -37,7 +37,17 @@ Hypersonic SQL or HSQLDB. H2 is built from scratch.
<h3>Version 1.0 (Current)</h3>
<h3>Version 1.0 / TODO (Build TODO)</h3><ul>
<li>The default trace level for JdbcDataSourceFactory was DEBUG, so when using data sources a trace file
<li>The default database name in the documentation is now jdbc:h2:~/test. Like this, the database
is stored in the user home directory (system property user.home). Of course storing the database
in the current working directory (as in jdbc:h2:test) still works. Using the user directory avoids
the problem that the database is not found if the application runs in another directory than
the console.
</li><li>The SQL statement SET ASSERT has been deprecated, because Constants.CHECK is now final.
To disable assertions (and improve performance), the system property h2.check can be used.
Executing SET ASSERT still works, but has no effect.
</li><li>In the READ_COMMITTED mode, when the multi-threaded kernel is enabled,
read locks are now acquired but released immediately after a query.
</li><li>The default trace level for JdbcDataSourceFactory was DEBUG, so when using data sources a trace file
was always created. Now the default trace level is ERROR, and the file is only created if necessary.
</li><li>PooledConnection.getConnection took a long time if only one connection was open at any time. Fixed.
</li><li>Referential integrity violation: Two different SQL states are now used for missing parent / existing child.
......@@ -58,6 +68,7 @@ Hypersonic SQL or HSQLDB. H2 is built from scratch.
or it can be embedded as a servlet into any existing web application. To build the
'H2 Console' web application, execute 'ant warConsole'.
See src/tools/org/h2/server/web and src/tools/WEB-INF for details.
This web application has been tested with Tomcat and Jetty.
</li><li>Deleting database files didn't work for Windows if the database was on the root directory of a drive.
</li><li>The Polish translation is available. Thanks a lot to Tomek!
</li><li>Windows service: the CLASSPATH was not included when starting the service. Fixed.
......
......@@ -201,7 +201,7 @@ public class Test {
throws Exception {
Class.forName("org.h2.Driver");
Connection conn = DriverManager.
getConnection("jdbc:h2:test", "sa", "");
getConnection("jdbc:h2:~/test", "sa", "");
// add application code here
}
}
......@@ -386,7 +386,7 @@ However, this is not recommended while the database is in use. Also, the databas
and quite large. The recommended way to backup a database is to create a compressed SQL script file.
This can be done using the backup tool:
<pre>
java org.h2.tools.Backup -url jdbc:h2:test -user sa -script test.zip -options compression zip
java org.h2.tools.Backup -url jdbc:h2:~/test -user sa -script test.zip -options compression zip
</pre>
For more information about the options, see the SQL command SCRIPT.
The backup can be done remotely, however the file will be created on the server side.
......@@ -396,7 +396,7 @@ It is also possible to use the SQL command SCRIPT to create the backup of the da
<h3>Restore</h3>
To restore a database from a SQL script file, you can use the RunScript tool:
<pre>
java org.h2.tools.RunScript -url jdbc:h2:test -user sa -script test.zip -options compression zip
java org.h2.tools.RunScript -url jdbc:h2:~/test -user sa -script test.zip -options compression zip
</pre>
For more information about the options, see the SQL command RUNSCRIPT.
The restore can be done remotely, however the file needs to be on the server side.
......
......@@ -85,6 +85,8 @@ public abstract class Command implements CommandInterface {
session.commit(true);
} else if (session.getAutoCommit()) {
session.commit(false);
} else if (Constants.MULTI_THREADED_KERNEL && session.getDatabase().getLockMode() == Constants.LOCK_MODE_READ_COMMITTED) {
session.unlockReadLocks();
}
if (trace.info()) {
long time = System.currentTimeMillis() - startTime;
......
......@@ -3473,6 +3473,9 @@ public class Parser {
} else if(readIf("ACCESS_MODE_LOG")) {
read();
return new NoOperation(session);
} else if(readIf("ASSERT")) {
read();
return new NoOperation(session);
} else if(readIf("ACCESS_MODE_DATA")) {
read();
return new NoOperation(session);
......
......@@ -165,11 +165,6 @@ public class Set extends Prepared {
addOrUpdateSetting(name, null, getIntValue());
break;
}
case SetTypes.ASSERT: {
session.getUser().checkAdmin();
Constants.CHECK = (getIntValue() == 1);
break;
}
case SetTypes.MULTI_THREADED: {
session.getUser().checkAdmin();
Constants.MULTI_THREADED_KERNEL = (getIntValue() == 1);
......
......@@ -13,10 +13,10 @@ public class SetTypes {
public static final int CACHE_SIZE = 8;
public static final int TRACE_LEVEL_SYSTEM_OUT = 9, TRACE_LEVEL_FILE = 10, TRACE_MAX_FILE_SIZE = 11;
public static final int COLLATION = 12, CLUSTER = 13, WRITE_DELAY = 14, DATABASE_EVENT_LISTENER = 15;
public static final int MAX_MEMORY_ROWS = 16, LOCK_MODE = 17, ASSERT = 18, DB_CLOSE_DELAY = 19;
public static final int LOG = 20, THROTTLE = 21, MAX_MEMORY_UNDO = 22, MAX_LENGTH_INPLACE_LOB = 23;
public static final int COMPRESS_LOB = 24, ALLOW_LITERALS = 25, MULTI_THREADED = 26, SCHEMA = 27;
public static final int OPTIMIZE_REUSE_RESULTS = 28;
public static final int MAX_MEMORY_ROWS = 16, LOCK_MODE = 17, DB_CLOSE_DELAY = 18;
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;
private static ObjectArray types = new ObjectArray();
static {
......@@ -37,7 +37,6 @@ public class SetTypes {
setType(DATABASE_EVENT_LISTENER, "DATABASE_EVENT_LISTENER");
setType(MAX_MEMORY_ROWS, "MAX_MEMORY_ROWS");
setType(LOCK_MODE, "LOCK_MODE");
setType(ASSERT, "ASSERT");
setType(DB_CLOSE_DELAY, "DB_CLOSE_DELAY");
setType(LOG, "LOG");
setType(THROTTLE, "THROTTLE");
......
......@@ -69,8 +69,8 @@ import org.h2.message.TraceSystem;
*/
public class Constants {
public static final int BUILD_ID = 51;
private static final String BUILD = "2007-06-25";
public static final int BUILD_ID = 52;
private static final String BUILD = "2007-06-30";
public static final int VERSION_MAJOR = 1;
public static final int VERSION_MINOR = 0;
......@@ -224,7 +224,7 @@ public class Constants {
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 boolean CHECK = getBooleanSetting("h2.check", true);
public static final boolean CHECK = getBooleanSetting("h2.check", true);
public static boolean MULTI_THREADED_KERNEL = getBooleanSetting("h2.multiThreadedKernel", false);
public static boolean RUN_FINALIZE = getBooleanSetting("h2.runFinalize", true);
public static String SCRIPT_DIRECTORY = getStringSetting("h2.scriptDirectory", "");
......
......@@ -274,6 +274,17 @@ public class Session implements SessionInterface {
}
undoLog.add(log);
}
public void unlockReadLocks() {
for(int i=0; i<locks.size(); i++) {
Table t = (Table)locks.get(i);
if(!t.isLockedExclusively()) {
t.unlock(this);
locks.remove(i);
i--;
}
}
}
private void unlockAll() throws SQLException {
if(Constants.CHECK) {
......
......@@ -849,15 +849,4 @@ public class JdbcCallableStatement extends JdbcPreparedStatement implements Call
throw Message.getUnsupportedException();
}
// public void finalize() {
// if(!Constants.RUN_FINALIZE) {
// return;
// }
// try {
// close();
// } catch (SQLException e) {
// // TODO log exception
// }
// }
}
......@@ -888,15 +888,5 @@ public class JdbcStatement extends TraceObject implements Statement {
}
}
// public void finalize() {
// if(!Constants.RUN_FINALIZE) {
// return;
// }
// closeOld();
// if(conn != null) {
// conn = null;
// }
// }
}
......@@ -683,19 +683,6 @@ This setting can be appended to the database URL: jdbc:h2:test;ALLOW_LITERALS=NO
SET ALLOW_LITERALS NONE
"
"Commands (Other)","SET ASSERT","
SET ASSERT int
","
Sets the assertion mode.
0: no assertions (for higher performance)
1: assertions are switched on (default)
This setting is not persistent.
Admin rights are required to execute this command.
This setting can be appended to the database URL: jdbc:h2:test;ASSERT=0
","
SET ASSERT 0
"
"Commands (Other)","SET AUTOCOMMIT","
SET AUTOCOMMIT {TRUE | ON | FALSE | OFF}
","
......@@ -831,7 +818,7 @@ Sets the lock mode.
0: No locking (should only be used for testing). Also known as READ_UNCOMMITTED.
1: Table level locking (default). Also known as SERIALIZABLE.
2: Table level locking with garbage collection (if the application does not close all connections).
3: Table level locking, but only when writing (no read locks). Also known as READ_COMMITTED.
3: Table level locking, but read locks are released immediately. Also known as READ_COMMITTED.
This setting is not persistent.
Admin rights are required to execute this command.
This setting can be appended to the database URL: jdbc:h2:test;LOCK_MODE=3
......
......@@ -120,16 +120,10 @@ public class WebSession {
return commandHistory;
}
public HashMap getMainInfo() {
int todoRefactorMergeWithGetInfo;
public HashMap getInfo() {
HashMap m = new HashMap();
m.putAll(map);
m.put("lastAccess", new Timestamp(lastAccess).toString());
return m;
}
public HashMap getInfo() {
HashMap m = getMainInfo();
try {
m.put("url", conn == null ? "not connected" : conn.getMetaData().getURL());
m.put("user", conn == null ? "-" : conn.getMetaData().getUserName());
......
......@@ -539,19 +539,6 @@ public class DiskFile implements CacheWriter {
deleted.setRange(pos, blockCount, false);
}
// public void finalize() {
// if (!Constants.RUN_FINALIZE) {
// return;
// }
// if (file != null) {
// try {
// file.close();
// } catch (Exception e) {
// // ignore
// }
// }
// }
public synchronized void delete() throws SQLException {
try {
cache.clear();
......
......@@ -298,8 +298,9 @@ public class TableData extends Table implements RecordReader {
}
} else {
if (lockExclusive == null) {
if(lockMode == Constants.LOCK_MODE_READ_COMMITTED) {
// READ_COMMITTED means 'wait until no write locks', but no read lock is added
if(lockMode == Constants.LOCK_MODE_READ_COMMITTED && !Constants.MULTI_THREADED_KERNEL) {
// READ_COMMITTED read locks are acquired but they are released immediately
// when allowing only one thread, no read locks are required
return;
} else if(!lockShared.contains(session)) {
traceLock(session, exclusive, "ok");
......
......@@ -848,6 +848,7 @@ public class Recover implements DataHandler {
* INTERNAL
*/
public void handleInvalidChecksum() throws SQLException {
throw new SQLException("Invalid Checksum");
}
/**
......
......@@ -242,6 +242,9 @@ public class FileUtils {
if(file.exists()) {
for(int i=0; i<Constants.MAX_FILE_RETRY; i++) {
trace("delete", fileName, null);
if(fileName.indexOf("1459.146") >= 0) {
new Error(fileName).printStackTrace();
}
boolean ok = file.delete();
if(ok) {
return;
......
......@@ -42,15 +42,21 @@ public class ObjectArray {
data[i] = it.next();
}
}
private void throwException(int index) {
throw new ArrayIndexOutOfBoundsException("i=" + index + " size=" + size);
}
public void add(Object value) {
ensureCapacity(size);
if(size >= data.length) {
ensureCapacity(size);
}
data[size++] = value;
}
public Object get(int i) {
if (Constants.CHECK && i >= size) {
throw new ArrayIndexOutOfBoundsException("i=" + i + " size=" + size);
throwException(i);
}
return data[i];
}
......@@ -58,7 +64,7 @@ public class ObjectArray {
public Object remove(int i) {
// TODO performance: the app should (where possible) remove from end to start, to avoid O(n^2)
if (Constants.CHECK && i >= size) {
throw new ArrayIndexOutOfBoundsException("i=" + i + " size=" + size);
throwException(i);
}
Object value = data[i];
System.arraycopy(data, i + 1, data, i, size - i - 1);
......@@ -94,7 +100,7 @@ public class ObjectArray {
public void add(int i, Object value) {
if (Constants.CHECK && i > size) {
throw new ArrayIndexOutOfBoundsException("i=" + i + " size=" + size);
throwException(i);
}
ensureCapacity(size);
if (i == size) {
......@@ -108,7 +114,7 @@ public class ObjectArray {
public void set(int i, Object value) {
if (Constants.CHECK && i >= size) {
throw new ArrayIndexOutOfBoundsException("i=" + i + " size=" + size);
throwException(i);
}
data[i] = value;
}
......
......@@ -95,14 +95,12 @@ java -Xmx512m -Xrunhprof:cpu=samples,depth=8 org.h2.tools.RunScript -url jdbc:h2
/*
read committed: read locks are acquired but they are released immediately
make read-committed the default
------------------------------------
make sure INDEX_LOOKUP_NEW = is true by default.
Test Console (batch, javaw, different platforms)
test with openoffice (metadata changes)
set read-committed as the default
testHalt
java org.h2.test.TestAll halt
......@@ -170,6 +168,14 @@ make static member variables final (this helps find forgotten initializers)
Merge more from diff.zip (Pavel Ganelin)
inlining: try to shorten methods that are used very often
store dates as 'local'. Problem: existing files use GMT (use escape syntax)
drop table test;
CREATE TABLE TEST( ID BIGINT PRIMARY KEY, CREATEDON TIMESTAMP);
INSERT INTO TEST VALUES(1, '2007-01-01 00:00:00');
SELECT * FROM TEST;
*/
/*
......
package org.h2.test.poweroff;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.PrintWriter;
import java.util.Date;
import java.security.SecureRandom;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.Statement;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.List;
import java.util.Random;
import java.util.zip.ZipEntry;
import java.util.zip.ZipOutputStream;
import org.h2.util.IOUtils;
public class TestRecover {
private Random random;
private static final String RELATIVE = System.getProperty("h2.testRecoverPath", "db");
private static final String TEST_DIRECTORY = "/temp/"+RELATIVE+"/data";
private static final String BACKUP_DIRECTORY = "/temp/"+RELATIVE+"/last";
public static void main(String[] args) throws Exception {
new TestRecover().runTest(args);
}
private void runTest(String[] args) throws Exception {
System.out.println("backup...");
new File(TEST_DIRECTORY).mkdirs();
File backup = backup(TEST_DIRECTORY, BACKUP_DIRECTORY, "data", 10);
System.out.println("check consistency...");
if(!testConsistency()) {
System.out.println("error! renaming file");
backup.renameTo(new File(backup.getParentFile(), "error-"+ backup.getName()));
}
System.out.println("deleting old run...");
deleteRecursive(new File(TEST_DIRECTORY));
System.out.println("testing...");
testLoop();
}
static File backup(String sourcePath, String targetPath, String basePath, int max) throws Exception {
File root = new File(targetPath);
if(!root.exists()) {
root.mkdirs();
}
while(true) {
File[] list = root.listFiles();
File oldest = null;
int count = 0;
for(int i=0; i<list.length; i++) {
File f = list[i];
String name = f.getName();
if(f.isFile() && name.startsWith("backup") && name.endsWith(".zip")) {
count++;
if(oldest == null || f.lastModified() < oldest.lastModified()) {
oldest = f;
}
}
}
if(count < max) {
break;
}
oldest.delete();
}
SimpleDateFormat sd = new SimpleDateFormat("yyMMdd-HHmmss");
String date = sd.format(new Date());
File zipFile = new File(root, "backup-" + date + ".zip");
ArrayList list = new ArrayList();
File base = new File(sourcePath);
listRecursive(list, base);
if(list.size() == 0) {
FileOutputStream out = new FileOutputStream(zipFile);
out.close();
} else {
OutputStream out = null;
try {
out = new FileOutputStream(zipFile);
ZipOutputStream zipOut = new ZipOutputStream(out);
String baseName = base.getAbsolutePath();
for(int i=0; i<list.size(); i++) {
File f = (File) list.get(i);
String fileName = f.getAbsolutePath();
String entryName = fileName;
if(fileName.startsWith(baseName)) {
entryName = entryName.substring(baseName.length());
}
if(entryName.startsWith("\\")) {
entryName = entryName.substring(1);
}
if(!entryName.startsWith("/")) {
entryName = "/" + entryName;
}
ZipEntry entry = new ZipEntry(basePath + entryName);
zipOut.putNextEntry(entry);
InputStream in = null;
try {
in = new FileInputStream(fileName);
IOUtils.copyAndCloseInput(in, zipOut);
} finally {
IOUtils.closeSilently(in);
}
zipOut.closeEntry();
}
zipOut.closeEntry();
zipOut.close();
} finally {
IOUtils.closeSilently(out);
}
}
return zipFile;
}
static void listRecursive(List list, File file) throws IOException {
File[] l = file.listFiles();
for(int i=0; l != null && i<l.length; i++) {
File f = l[i];
if(f.isDirectory()) {
listRecursive(list, f);
} else {
list.add(f);
}
}
}
static void deleteRecursive(File file) throws IOException {
if(file.isDirectory()) {
File[] list = file.listFiles();
for(int i=0; i<list.length; i++) {
deleteRecursive(list[i]);
}
}
if(file.exists() && !file.delete()) {
throw new IOException("Could not delete " + file.getAbsolutePath());
}
}
private void testLoop() throws Exception {
random = new SecureRandom();
while(true) {
runOneTest(random.nextInt());
}
}
Connection openConnection() throws Exception {
Class.forName("org.h2.Driver");
Connection conn = DriverManager.getConnection("jdbc:h2:" + TEST_DIRECTORY + "/test", "sa", "sa");
Statement stat = conn.createStatement();
stat.execute("CREATE TABLE IF NOT EXISTS TEST(ID IDENTITY, NAME VARCHAR)");
return conn;
}
private void runOneTest(int i) throws Exception {
Random random = new Random(i);
Connection conn = openConnection();
PreparedStatement prep = null;
while (true) {
boolean rollback = random.nextInt(10) == 1;
int len;
if (random.nextInt(10) == 1) {
len = random.nextInt(8000) * 2;
} else {
len = random.nextInt(2) * 2;
}
if (rollback && random.nextBoolean()) {
// make the length odd
len++;
}
byte[] data = new byte[len];
random.nextBytes(data);
int op = random.nextInt();
if (op % 100 == 0) {
conn.close();
conn = openConnection();
prep = null;
}
if(prep == null) {
prep = conn.prepareStatement("INSERT INTO TEST(NAME) VALUES(?)");
conn.setAutoCommit(false);
}
prep.setString(1, "" + len);
prep.execute();
if (rollback) {
conn.rollback();
} else {
conn.commit();
}
}
}
private boolean testConsistency() {
FileOutputStream out = null;
PrintWriter p = null;
try {
out = new FileOutputStream(TEST_DIRECTORY + "/result.txt");
p = new PrintWriter(out);
p.println("Results");
p.flush();
} catch(Throwable t) {
t.printStackTrace();
System.exit(0);
}
Connection conn = null;
try {
conn = openConnection();
ResultSet rs = conn.createStatement().executeQuery("SELECT * FROM TEST");
while(rs.next()) {
String name = rs.getString("NAME");
int value = Integer.parseInt(name);
if(value % 2 == 1) {
throw new Exception("unexpected odd entry " + rs.getInt("ID"));
}
}
conn.close();
return true;
} catch(Throwable t) {
t.printStackTrace();
t.printStackTrace(p);
return false;
} finally {
if(conn != null) {
try {
conn.close();
} catch(Throwable t2) {
t2.printStackTrace();
t2.printStackTrace(p);
}
}
p.flush();
p.close();
IOUtils.closeSilently(out);
}
}
}
......@@ -26,9 +26,6 @@ public class WebServlet extends HttpServlet {
private static final long serialVersionUID = 9171446624885086692L;
private WebServer server;
private int todoTestWithTomcat;
private int todoTestWithJetty;
public void init() throws ServletException {
ServletConfig config = getServletConfig();
Enumeration en = config.getInitParameterNames();
......
Markdown 格式
0%
您添加了 0 到此讨论。请谨慎行事。
请先完成此评论的编辑!
注册 或者 后发表评论