提交 39376f76 authored 作者: Thomas Mueller's avatar Thomas Mueller

--no commit message

--no commit message
上级 c427908d
......@@ -273,6 +273,7 @@
</target>
<target name="warConsole" depends="compileServlet, jar">
<fail unless="servlet.jar.present" message="Servlet API jar not found"/>
<war destfile="bin/h2console.war" webxml="src/tools/WEB-INF/web.xml" basedir="src/tools/WEB-INF" includes="console.html">
<lib file="bin/h2.jar" />
</war>
......
......@@ -92,20 +92,46 @@ This may not have the desired effect if a default value in the target table is o
<br /><a name="transaction_isolation"></a>
<h2>Transaction Isolation</h2>
This database supports the transaction isolation level 'serializable', in which dirty reads, non-repeatable
reads and phantom reads are prohibited.
<p>
This database supports the following transaction isolation levels:
</p>
<ul>
<li><b>Serializable</b><br />
This is the default level.<br />
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 />
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 />
or append ;LOCK_MODE=3 to the database URL: jdbc:h2:~/test;LOCK_MODE=3
</li><li><b>Read Uncommitted</b><br />
This level means that transaction isolation is disabled.<br />
To enable, execute the SQL statement 'SET LOCK_MODE 0'<br />
or append ;LOCK_MODE=0 to the database URL: jdbc:h2:~/test;LOCK_MODE=0
</li>
</ul>
<p>
When using the isolation level 'serializable', dirty reads, non-repeatable reads, and phantom reads are prohibited.
</p>
<ul>
<li><b>Dirty Reads</b><br />
Means a connection can read uncommitted changes made by another connection.
Means a connection can read uncommitted changes made by another connection.<br />
Possible with: read uncommitted
</li><li><b>Non-Repeatable Reads</b><br />
A connection reads a row, another connection changes a row and commits,
and the first connection re-reads the same row and gets the new result.
and the first connection re-reads the same row and gets the new result.<br />
Possible with: read uncommitted, read committed
</li><li><b>Phantom Reads</b><br />
A connection reads a set of rows using a condition, another connection
inserts a row that falls in this condition and commits, then the first connection
re-reads using the same condition and gets the new row.
</li></ul>
re-reads using the same condition and gets the new row.<br />
Possible with: read uncommitted, read committed
</li>
</ul>
<h3>Table Level Locking</h3>
The database allows multiple concurrent connections to the same database.
......
......@@ -824,13 +824,13 @@ Hypersonic SQL or HSQLDB. H2 is built from scratch.
<h3>Priority 1</h3>
<ul>
<li>RECOVER=1 should automatically recover, =2 should run the recovery tool if required
<li>MVCC (Multi Version Concurrency Control)
</li><li>RECOVER=1 should automatically recover, =2 should run the recovery tool if required
</li><li>More tests with MULTI_THREADED=1
</li><li>Improve performance for create table (if this is possible)
</li><li>Test with Spatial DB in a box / JTS (http://docs.codehaus.org/display/GEOS/SpatialDBBox)
</li><li>Document how to use H2 with PHP (generic database API)
</li><li>Optimization: result set caching (like MySQL)
</li><li>MVCC (Multi Version Concurrency Control)
</li><li>Server side cursors
</li><li>Row level locking
</li><li>Read-only databases inside a jar (splitting large files to speed up random access)
......
......@@ -763,25 +763,27 @@ public class Database implements DataHandler {
}
}
synchronized void close(boolean fromShutdownHook) {
this.closing = true;
if(sessions.size() > 0) {
if(!fromShutdownHook) {
return;
}
traceSystem.getTrace(Trace.DATABASE).info("closing " + databaseName + " from shutdown hook");
Session[] all = new Session[sessions.size()];
sessions.toArray(all);
for(int i=0; i<all.length; i++) {
Session s = all[i];
try {
s.close();
} catch(SQLException e) {
traceSystem.getTrace(Trace.SESSION).error("disconnecting #" + s.getId(), e);
}
}
}
traceSystem.getTrace(Trace.DATABASE).info("closing " + databaseName);
void close(boolean fromShutdownHook) {
synchronized(this) {
this.closing = true;
if(sessions.size() > 0) {
if(!fromShutdownHook) {
return;
}
traceSystem.getTrace(Trace.DATABASE).info("closing " + databaseName + " from shutdown hook");
Session[] all = new Session[sessions.size()];
sessions.toArray(all);
for(int i=0; i<all.length; i++) {
Session s = all[i];
try {
s.close();
} catch(SQLException e) {
traceSystem.getTrace(Trace.SESSION).error("disconnecting #" + s.getId(), e);
}
}
}
}
traceSystem.getTrace(Trace.DATABASE).info("closing " + databaseName);
if(eventListener != null) {
eventListener.closingDatabase();
eventListener = null;
......
......@@ -61,7 +61,7 @@ public class WebServer implements Service {
"Generic Derby (Embedded)|org.apache.derby.jdbc.EmbeddedDriver|jdbc:derby:test;create=true|sa",
"Generic Derby (Server)|org.apache.derby.jdbc.ClientDriver|jdbc:derby://localhost:1527/test;create=true|sa",
"Generic HSQLDB|org.hsqldb.jdbcDriver|jdbc:hsqldb:test;hsqldb.default_table_type=cached|sa" ,
"Generic H2|org.h2.Driver|jdbc:h2:test|sa",
"Generic H2|org.h2.Driver|jdbc:h2:~/test|sa",
};
// private URLClassLoader urlClassLoader;
......@@ -247,7 +247,11 @@ public class WebServer implements Service {
void trace(String s) {
// System.out.println(s);
}
public void traceError(Exception e) {
e.printStackTrace();
}
public boolean supportsLanguage(String language) {
return languages.contains(language);
}
......
......@@ -50,6 +50,8 @@ class WebThread extends Thread {
private InputStream input;
private String ifModifiedSince;
String mimeType;
boolean cache;
// TODO web: support online data editing like http://numsum.com/
......@@ -60,45 +62,63 @@ class WebThread extends Thread {
}
void setSession(WebSession session, Properties attributes) {
int todoRefactor;
this.session = session;
this.attributes = attributes;
}
protected String getComboBox(String[] elements, String selected) {
StringBuffer buff = new StringBuffer();
for(int i=0; i<elements.length; i++) {
String value = elements[i];
buff.append("<option value=\"");
buff.append(PageParser.escapeHtml(value));
buff.append("\"");
if(value.equals(selected)) {
buff.append(" selected");
}
buff.append(">");
buff.append(PageParser.escapeHtml(value));
buff.append("</option>");
private String getAllowedFile(String requestedFile) {
if(!allow()) {
return "notAllowed.jsp";
}
return buff.toString();
if(requestedFile.length() == 0) {
return "index.do";
}
return requestedFile;
}
protected String getComboBox(String[][] elements, String selected) {
StringBuffer buff = new StringBuffer();
for(int i=0; i<elements.length; i++) {
String[] n = elements[i];
buff.append("<option value=\"");
buff.append(PageParser.escapeHtml(n[0]));
buff.append("\"");
if(n[0].equals(selected)) {
buff.append(" selected");
public String processRequest(String file, String hostname) {
int index = file.lastIndexOf('.');
String suffix;
if(index >= 0) {
suffix = file.substring(index+1);
} else {
suffix = "";
}
if(suffix.equals("ico")) {
mimeType = "image/x-icon";
cache=true;
} else if(suffix.equals("gif")) {
mimeType = "image/gif";
cache=true;
} else if(suffix.equals("css")) {
cache=true;
mimeType = "text/css";
} else if(suffix.equals("html") || suffix.equals("do") || suffix.equals("jsp")) {
cache=false;
mimeType = "text/html";
if (session == null) {
session = server.createNewSession(hostname);
if (!file.equals("notAllowed.jsp")) {
file = "index.do";
}
}
buff.append(">");
buff.append(PageParser.escapeHtml(n[1]));
buff.append("</option>");
} else if(suffix.equals("js")) {
cache=true;
mimeType = "text/javascript";
} else {
cache = false;
mimeType = "text/html";
file = "error.jsp";
server.trace("unknown mime type, file "+file);
}
return buff.toString();
server.trace("mimeType="+mimeType);
server.trace(file);
if(file.endsWith(".do")) {
file = process(file);
}
return file;
}
public void run() {
try {
input = socket.getInputStream();
......@@ -106,13 +126,8 @@ class WebThread extends Thread {
if(head.startsWith("GET ") || head.startsWith("POST ")) {
int begin = head.indexOf('/'), end = head.lastIndexOf(' ');
String file = head.substring(begin+1, end).trim();
if(file.length() == 0) {
file = "index.do";
}
if(!allow()) {
file = "notAllowed.jsp";
}
server.trace(head + " :" + file);
server.trace(head + ": " + file);
file = getAllowedFile(file);
attributes = new Properties();
int paramIndex = file.indexOf("?");
session = null;
......@@ -123,50 +138,11 @@ class WebThread extends Thread {
file = file.substring(0, paramIndex);
session = server.getSession(sessionId);
}
// TODO web: support errors
String mimeType;
boolean cache;
int index = file.lastIndexOf('.');
String suffix;
if(index >= 0) {
suffix = file.substring(index+1);
} else {
suffix = "";
}
if(suffix.equals("ico")) {
mimeType = "image/x-icon";
cache=true;
} else if(suffix.equals("gif")) {
mimeType = "image/gif";
cache=true;
} else if(suffix.equals("css")) {
cache=true;
mimeType = "text/css";
} else if(suffix.equals("html") || suffix.equals("do") || suffix.equals("jsp")) {
cache=false;
mimeType = "text/html";
if (session == null) {
String hostname = socket.getInetAddress().getHostName();
session = server.createNewSession(hostname);
if (!file.equals("notAllowed.jsp")) {
file = "index.do";
}
}
} else if(suffix.equals("js")) {
cache=true;
mimeType = "text/javascript";
} else {
cache = false;
mimeType = "text/html";
file = "error.jsp";
server.trace("unknown mime type, file "+file);
}
server.trace("mimeType="+mimeType);
parseHeader();
server.trace(file);
if(file.endsWith(".do")) {
file = process(file);
}
String hostname = socket.getInetAddress().getHostName();
file = processRequest(file, hostname);
String message;
byte[] bytes;
if(cache && ifModifiedSince!=null && ifModifiedSince.equals(server.getStartDateTime())) {
......@@ -179,7 +155,13 @@ class WebThread extends Thread {
bytes = StringUtils.utf8Encode("File not found: "+file);
} else {
if(session != null && file.endsWith(".jsp")) {
bytes = StringUtils.utf8Encode(fill(StringUtils.utf8Decode(bytes)));
String page = StringUtils.utf8Decode(bytes);
page = PageParser.parse(server, page, session.map);
try {
bytes = StringUtils.utf8Encode(page);
} catch(SQLException e) {
server.traceError(e);
}
}
message = "HTTP/1.1 200 OK\n";
message += "Content-Type: "+mimeType+"\n";
......@@ -202,7 +184,6 @@ class WebThread extends Thread {
}
output.flush();
output.close();
output.close();
socket.close();
return;
}
......@@ -211,6 +192,40 @@ class WebThread extends Thread {
}
}
protected String getComboBox(String[] elements, String selected) {
StringBuffer buff = new StringBuffer();
for(int i=0; i<elements.length; i++) {
String value = elements[i];
buff.append("<option value=\"");
buff.append(PageParser.escapeHtml(value));
buff.append("\"");
if(value.equals(selected)) {
buff.append(" selected");
}
buff.append(">");
buff.append(PageParser.escapeHtml(value));
buff.append("</option>");
}
return buff.toString();
}
protected String getComboBox(String[][] elements, String selected) {
StringBuffer buff = new StringBuffer();
for(int i=0; i<elements.length; i++) {
String[] n = elements[i];
buff.append("<option value=\"");
buff.append(PageParser.escapeHtml(n[0]));
buff.append("\"");
if(n[0].equals(selected)) {
buff.append(" selected");
}
buff.append(">");
buff.append(PageParser.escapeHtml(n[1]));
buff.append("</option>");
}
return buff.toString();
}
private String readHeaderLine() throws IOException {
StringBuffer buff=new StringBuffer();
while (true) {
......@@ -310,10 +325,6 @@ class WebThread extends Thread {
}
}
private String fill(String page) {
return PageParser.parse(server, page, session.map);
}
String process(String file) {
server.trace("process " + file);
while(file.endsWith(".do")) {
......@@ -1505,5 +1516,17 @@ class WebThread extends Thread {
}
return NetUtils.isLoopbackAddress(socket);
}
public String getMimeType() {
return mimeType;
}
public boolean getCache() {
return cache;
}
public WebSession getSession() {
return session;
}
}
......@@ -306,7 +306,6 @@ public class FileStore {
public void sync() {
try {
file.getFD().sync();
} catch(IOException e) {
// TODO log exception
......
......@@ -493,7 +493,7 @@ public class FileUtils {
return new File(fileName).canWrite();
}
private static void trace(String method, String fileName, Object o) {
static void trace(String method, String fileName, Object o) {
if(Constants.TRACE_IO) {
System.out.println("FileUtils." + method + " " + fileName + " " + o);
}
......
......@@ -21,13 +21,13 @@ import org.h2.message.Message;
public class ReaderInputStream extends InputStream {
private Reader reader;
private final Reader reader;
private final char[] chars;
private final ByteArrayOutputStream out;
private final OutputStreamWriter writer;
private int pos;
private int remaining;
private char[] chars;
private byte[] buffer;
private ByteArrayOutputStream out;
private OutputStreamWriter writer;
public ReaderInputStream(Reader reader) throws SQLException {
chars = new char[Constants.IO_BUFFER_SIZE];
......
......@@ -18,6 +18,7 @@ public class TempFileDeleter {
private static HashMap refMap = new HashMap();
public static synchronized Reference addFile(String fileName, Object file) {
FileUtils.trace("TempFileDeleter.addFile", fileName, file);
PhantomReference ref = new PhantomReference(file, queue);
refMap.put(ref, fileName);
deleteUnused();
......@@ -33,6 +34,7 @@ public class TempFileDeleter {
}
if(fileName != null && FileUtils.exists(fileName)) {
try {
FileUtils.trace("TempFileDeleter.deleteFile", fileName, null);
FileUtils.delete(fileName);
} catch(Exception e) {
// TODO log such errors?
......@@ -52,6 +54,7 @@ public class TempFileDeleter {
}
public static void stopAutoDelete(Reference ref, String fileName) {
FileUtils.trace("TempFileDeleter.stopAutoDelete", fileName, ref);
if(ref != null) {
String f2 = (String) refMap.remove(ref);
if(Constants.CHECK && (f2 == null || !f2.equals(fileName))) {
......
......@@ -33,7 +33,7 @@ public class ValueLob extends Value {
// TODO lob: concatenate function for blob and clob (to create a large blob from pieces)
// and a getpart function (to get it in pieces) and make sure a file is created!
private int type;
private final int type;
private long precision;
private DataHandler handler;
private int tableId;
......
......@@ -95,6 +95,10 @@ 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)
......
db1 = H2, org.h2.Driver, jdbc:h2:data/test;LOCK_TIMEOUT=10000, sa, sa
xdb2 = H2 (XTEA), org.h2.Driver, jdbc:h2:data/test_xtea;LOCK_TIMEOUT=10000;CIPHER=XTEA, sa, sa 123
xdb3 = H2 (AES), org.h2.Driver, jdbc:h2:data/test_aes;LOCK_TIMEOUT=10000;CIPHER=AES, sa, sa 123
xdb4 = H2, org.h2.Driver, jdbc:h2:data/test;LOCK_TIMEOUT=10000;write_mode_log=rws;write_delay=0, sa, sa
db1 = H2, org.h2.Driver, jdbc:h2:data/test;LOCK_TIMEOUT=10000;LOCK_MODE=3, sa, sa
xdb2 = H2 (XTEA), org.h2.Driver, jdbc:h2:data/test_xtea;LOCK_TIMEOUT=10000;LOCK_MODE=3;CIPHER=XTEA, sa, sa 123
xdb3 = H2 (AES), org.h2.Driver, jdbc:h2:data/test_aes;LOCK_TIMEOUT=10000;LOCK_MODE=3;CIPHER=AES, sa, sa 123
xdb4 = H2, org.h2.Driver, jdbc:h2:data/test;LOCK_TIMEOUT=10000;LOCK_MODE=3;write_mode_log=rws;write_delay=0, sa, sa
db2 = HSQLDB, org.hsqldb.jdbcDriver, jdbc:hsqldb:data/test;hsqldb.default_table_type=cached;sql.enforce_size=true, sa
db3 = Derby, org.apache.derby.jdbc.EmbeddedDriver, jdbc:derby:data/test;create=true, sa, sa
db4 = H2, org.h2.Driver, jdbc:h2:tcp://localhost/data/testServer;LOCK_TIMEOUT=10000, sa, sa
db4 = H2, org.h2.Driver, jdbc:h2:tcp://localhost/data/testServer;LOCK_TIMEOUT=10000;LOCK_MODE=3, sa, sa
db5 = HSQLDB, org.hsqldb.jdbcDriver, jdbc:hsqldb:hsql://localhost/xdb, sa
db6 = Derby, org.apache.derby.jdbc.ClientDriver, jdbc:derby://localhost/data/testServer;create=true, sa, sa
db7 = PostgreSQL, org.postgresql.Driver, jdbc:postgresql:test, sa, sa
......
......@@ -26,8 +26,6 @@ public class WebServlet extends HttpServlet {
private static final long serialVersionUID = 9171446624885086692L;
private WebServer server;
private int todoRefactorRemoveDuplicateCode;
private int todoRemoveSystem_out;
private int todoTestWithTomcat;
private int todoTestWithJetty;
......@@ -58,7 +56,7 @@ public class WebServlet extends HttpServlet {
public void destroy() {
}
boolean allow(HttpServletRequest req) {
private boolean allow(HttpServletRequest req) {
if(server.getAllowOthers()) {
return true;
}
......@@ -72,6 +70,16 @@ public class WebServlet extends HttpServlet {
return address.isLoopbackAddress();
}
private String getAllowedFile(HttpServletRequest req, String requestedFile) {
if(!allow(req)) {
return "notAllowed.jsp";
}
if(requestedFile.length() == 0) {
return "index.do";
}
return requestedFile;
}
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws IOException {
String file = req.getPathInfo();
if(file == null) {
......@@ -80,12 +88,7 @@ public class WebServlet extends HttpServlet {
} else if(file.startsWith("/")) {
file = file.substring(1);
}
if(file.length() == 0) {
file = "index.do";
}
if(!allow(req)) {
file = "notAllowed.jsp";
}
file = getAllowedFile(req, file);
byte[] bytes = null;
Properties attributes = new Properties();
Enumeration en = req.getAttributeNames();
......@@ -105,54 +108,17 @@ public class WebServlet extends HttpServlet {
if(sessionId != null) {
session = server.getSession(sessionId);
}
String mimeType;
boolean cache;
int index = file.lastIndexOf('.');
String suffix;
if(index >= 0) {
suffix = file.substring(index+1);
} else {
suffix = "";
}
if(suffix.equals("ico")) {
mimeType = "image/x-icon";
cache=true;
} else if(suffix.equals("gif")) {
mimeType = "image/gif";
cache=true;
} else if(suffix.equals("css")) {
cache=true;
mimeType = "text/css";
} else if(suffix.equals("html") || suffix.equals("do") || suffix.equals("jsp")) {
cache = false;
mimeType = "text/html";
if (session == null) {
int todoTest;
String hostname = req.getRemoteHost();
session = server.createNewSession(hostname);
if (!file.equals("notAllowed.jsp")) {
file = "index.do";
}
}
} else if(suffix.equals("js")) {
cache = true;
mimeType = "text/javascript";
} else {
cache = false;
mimeType = "text/html";
file = "error.jsp";
server.trace("unknown mime type, file "+file);
}
server.trace("mimeType="+mimeType);
// parseHeader();
String ifModifiedSince = req.getHeader("if-modified-since");
server.trace(file);
WebThread app = new WebThread(null, server);
app.setSession(session, attributes);
String ifModifiedSince = req.getHeader("if-modified-since");
if(file.endsWith(".do")) {
app.setSession(session, attributes);
file = app.process(file);
}
String hostname = req.getRemoteHost();
file = app.processRequest(file, hostname);
session = app.getSession();
String mimeType = app.getMimeType();
boolean cache = app.getCache();
if(cache && server.getStartDateTime().equals(ifModifiedSince)) {
resp.setStatus(HttpServletResponse.SC_NOT_MODIFIED);
return;
......@@ -164,7 +130,7 @@ public class WebServlet extends HttpServlet {
try {
bytes = StringUtils.utf8Encode("File not found: "+file);
} catch(SQLException e) {
int todoNotIgnore;
server.traceError(e);
}
} else {
if(session != null && file.endsWith(".jsp")) {
......@@ -173,7 +139,7 @@ public class WebServlet extends HttpServlet {
try {
bytes = StringUtils.utf8Encode(page);
} catch(SQLException e) {
int todoNotIgnore;
server.traceError(e);
}
}
resp.setContentType(mimeType);
......
Markdown 格式
0%
您添加了 0 到此讨论。请谨慎行事。
请先完成此评论的编辑!
注册 或者 后发表评论