提交 47128795 authored 作者: Thomas Mueller's avatar Thomas Mueller

This database no longer uses finalize() except for temporary files. The system…

This database no longer uses finalize() except for temporary files. The system property "h2.runFinalize" is no longer supported. Unclosed connections are still detected, but the opening stack trace is now collected only if connections are not closed repeatedly (starting with the second unclosed connection).
上级 33633d37
......@@ -16,9 +16,24 @@ Change Log
<!-- } -->
<h1>Change Log</h1>
<h2>Next Version (unreleased)</h2>
<ul><li>The built-in connection pool is now a bit faster, specially if the system property
"h2.runFinalize" is set to "false".
<ul><li>This database no longer uses finalize() except for temporary files.
The system property "h2.runFinalize" is no longer supported.
Unclosed connections are still detected, but the opening stack trace is now collected
only if connections are not closed repeatedly (starting with the second unclosed connection).
To detect unclosed connections, a PhantomReference is used.
</li><li>Issue 322: Left joins didn't work when used as subquery in the form clause.
</li><li>A prepared statement if type CALL that returned a result set could
throw a NullPointerExecption if executed multiple times.
</li><li>The following built-in functions were marked as deterministic and therefore
not re-executed when using in a prepared statement:
LOCK_TIMEOUT, LINK_SCHEMA, CANCEL_SESSION, FILE_READ.
</li><li>Count on a column that can not be null is now optimized to COUNT(*).
Thanks a lot to Ross Judson for providing a patch and test case!
</li><li>The following prepared statements are now cached as well:
insert, update, delete, call, merge, and transactional commands.
</li><li>The built-in connection pool is now a bit faster.
</li><li>The scale was not set correctly in some cases when using
CREATE TABLE AS SELECT if there was no explicit column definition.
</li><li>When trying to connect to a server using TCP/IP failed, it will retry at most
......
......@@ -71,6 +71,7 @@ See also <a href="build.html#providing_patches">Providing Patches</a>.
</li><li>Drop with invalidate views (so that source code is not lost). Check what other databases do exactly.
</li><li>Index usage for (ID, NAME)=(1, 'Hi'); document.
</li><li>Set a connection read only (Connection.setReadOnly) or using a connection parameter.
</li><li>Support GRANT SELECT, UPDATE ON [schemaName.] *.
</li><li>Access rights: finer grained access control (grant access for specific functions).
</li><li>ROW_NUMBER() OVER([PARTITION BY columnName][ORDER BY columnName]).
</li><li>Version check: docs / web console (using Javascript), and maybe in the library (using TCP/IP).
......@@ -85,6 +86,7 @@ See also <a href="build.html#providing_patches">Providing Patches</a>.
</li><li>Linked schema using CSV files: one schema for a directory of files; support indexes for CSV files.
</li><li>iReport to support H2.
</li><li>Include SMPT (mail) client (alert on cluster failure, low disk space,...).
</li><li>Option for SCRIPT to only process one or a set of schemas or tables, and append to a file.
</li><li>JSON parser and functions.
</li><li>Copy database: tool with config GUI and batch mode, extensible (example: compare).
</li><li>Document, implement tool for long running transactions using user-defined compensation statements.
......@@ -113,7 +115,6 @@ See also <a href="build.html#providing_patches">Providing Patches</a>.
</li><li>Max memory rows / max undo log size: use block count / row size not row count.
</li><li>Support 123L syntax as in Java; example: SELECT (2000000000*2).
</li><li>Implement point-in-time recovery.
</li><li>Option for SCRIPT to only process one or a set of schemas or tables, and append to a file.
</li><li>LIKE: improved version for larger texts (currently using naive search).
</li><li>Throw an exception when the application calls getInt on a Long (optional).
</li><li>Default date format for input and output (local date constants).
......@@ -156,7 +157,6 @@ See also <a href="build.html#providing_patches">Providing Patches</a>.
</li><li>Support updatable views with join on primary keys (to extend a table).
</li><li>Public interface for functions (not public static).
</li><li>Support reading the transaction log.
</li><li>Support GRANT SELECT, UPDATE ON *.
</li><li>Feature matrix as in <a href="http://www.inetsoftware.de/products/jdbc/mssql/features/default.asp">i-net software</a>.
</li><li>Updatable result set on table without primary key or unique index.
</li><li>Compatibility with Derby and PostgreSQL: VALUES(1), (2); SELECT * FROM (VALUES (1), (2)) AS myTable(c1). Issue 221.
......
......@@ -71,7 +71,6 @@ public class Session extends SessionWithState {
private int firstUncommittedLog = Session.LOG_WRITTEN;
private int firstUncommittedPos = Session.LOG_WRITTEN;
private HashMap<String, Integer> savepoints;
private Exception openStackTrace = new Exception();
private HashMap<String, Table> localTempTables;
private HashMap<String, Index> localTempTableIndexes;
private HashMap<String, Constraint> localTempTableConstraints;
......@@ -341,15 +340,6 @@ public class Session extends SessionWithState {
}
}
protected void finalize() {
if (!SysProperties.runFinalize) {
return;
}
if (!closed) {
throw new RuntimeException("Not closed", openStackTrace);
}
}
public boolean getAutoCommit() {
return autoCommit;
}
......
......@@ -6,6 +6,7 @@
*/
package org.h2.engine;
import java.io.Closeable;
import org.h2.command.CommandInterface;
import org.h2.message.Trace;
import org.h2.store.DataHandler;
......@@ -13,7 +14,7 @@ import org.h2.store.DataHandler;
/**
* A local or remote session. A session represents a database connection.
*/
public interface SessionInterface {
public interface SessionInterface extends Closeable {
/**
* Parse a command and prepare it for execution.
......
......@@ -34,6 +34,7 @@ import org.h2.message.DbException;
import org.h2.message.Trace;
import org.h2.message.TraceObject;
import org.h2.result.ResultInterface;
import org.h2.util.CloseWatcher;
import org.h2.util.Utils;
import org.h2.value.CompareMode;
import org.h2.value.Value;
......@@ -65,10 +66,7 @@ import java.util.concurrent.Executor;
*/
public class JdbcConnection extends TraceObject implements Connection {
/**
* The stack trace of when the connection was created.
*/
protected Exception openStackTrace;
private static boolean keepOpenStackTrace;
private String url;
private String user;
......@@ -84,10 +82,10 @@ public class JdbcConnection extends TraceObject implements Connection {
private int savepointId;
private Trace trace;
private boolean isInternal;
private String catalog;
private Statement executingStatement;
private CompareMode compareMode = CompareMode.getInstance(null, 0);
private CloseWatcher watcher;
/**
* INTERNAL
......@@ -120,9 +118,8 @@ public class JdbcConnection extends TraceObject implements Connection {
+ ", " + quote(user) + ", \"\");");
}
this.url = ci.getURL();
if (SysProperties.runFinalize) {
openStackTrace = new Exception("Stack Trace");
}
closeOld();
watcher = CloseWatcher.register(this, session, keepOpenStackTrace);
} catch (Exception e) {
throw logAndConvert(e);
}
......@@ -138,13 +135,19 @@ public class JdbcConnection extends TraceObject implements Connection {
setTrace(trace, TraceObject.CONNECTION, id);
this.user = clone.user;
this.url = clone.url;
this.catalog = clone.catalog;
this.commit = clone.commit;
this.getGeneratedKeys = clone.getGeneratedKeys;
this.getLockMode = clone.getLockMode;
this.getQueryTimeout = clone.getQueryTimeout;
this.getReadOnly = clone.getReadOnly;
this.rollback = clone.rollback;
}
/**
* INTERNAL
*/
public JdbcConnection(SessionInterface session, String user, String url) {
isInternal = true;
this.session = session;
trace = session.getTrace();
int id = getNextId(TraceObject.CONNECTION);
......@@ -153,6 +156,26 @@ public class JdbcConnection extends TraceObject implements Connection {
this.url = url;
}
private void closeOld() {
while (true) {
CloseWatcher w = CloseWatcher.pollUnclosed();
if (w == null) {
break;
}
try {
w.getCloseable().close();
} catch (Exception e) {
trace.error(e, "closing session");
}
// there was an unclosed object -
// keep the stack trace from now on
keepOpenStackTrace = true;
String s = w.getOpenStackTrace();
Exception ex = DbException.get(ErrorCode.TRACE_CONNECTION_NOT_CLOSED);
trace.error(ex, s);
}
}
/**
* Creates a new statement.
*
......@@ -303,10 +326,10 @@ public class JdbcConnection extends TraceObject implements Connection {
public synchronized void close() throws SQLException {
try {
debugCodeCall("close");
openStackTrace = null;
if (session == null) {
return;
}
CloseWatcher.unregister(watcher);
session.cancel();
if (executingStatement != null) {
try {
......@@ -1392,24 +1415,6 @@ public class JdbcConnection extends TraceObject implements Connection {
return user;
}
protected void finalize() {
if (!SysProperties.runFinalize) {
return;
}
if (isInternal) {
return;
}
if (session != null && openStackTrace != null) {
Exception ex = DbException.get(ErrorCode.TRACE_CONNECTION_NOT_CLOSED);
trace.error(openStackTrace, ex.getMessage());
try {
close();
} catch (SQLException e) {
trace.debug(e, "finalize");
}
}
}
private void rollbackInternal() {
rollback = prepareCommand("ROLLBACK", rollback);
rollback.executeUpdate();
......
......@@ -15,7 +15,6 @@ import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.HashMap;
import org.h2.constant.ErrorCode;
import org.h2.constant.SysProperties;
import org.h2.engine.Constants;
import org.h2.jdbc.JdbcSQLException;
import org.h2.util.IOUtils;
......@@ -339,13 +338,6 @@ public class TraceSystem implements TraceWriter {
closed = true;
}
protected void finalize() {
if (!SysProperties.runFinalize) {
return;
}
close();
}
public void setName(String name) {
// nothing to do (the file name is already set)
}
......
......@@ -8,7 +8,6 @@ package org.h2.result;
import java.io.ByteArrayOutputStream;
import java.util.ArrayList;
import org.h2.constant.SysProperties;
import org.h2.engine.Constants;
import org.h2.engine.Database;
import org.h2.engine.Session;
......@@ -252,13 +251,6 @@ class ResultDiskBuffer implements ResultExternal {
return sort.compare(va, vb);
}
protected void finalize() {
if (!SysProperties.runFinalize) {
return;
}
close();
}
private synchronized void closeChild() {
if (--childCount == 0 && closed) {
file.closeAndDeleteSilently();
......
......@@ -8,7 +8,6 @@ package org.h2.store;
import java.io.IOException;
import java.io.InputStream;
import org.h2.constant.SysProperties;
import org.h2.engine.Constants;
import org.h2.message.DbException;
import org.h2.tools.CompressTool;
......@@ -136,13 +135,6 @@ public class FileStoreInputStream extends InputStream {
}
}
protected void finalize() {
if (!SysProperties.runFinalize) {
return;
}
close();
}
public int read() throws IOException {
fillBuffer();
if (endOfFile) {
......
/*
* Copyright 2004-2011 H2 Group. Multiple-Licensed under the H2 License,
* Version 1.0, and under the Eclipse Public License, Version 1.0
* (http://h2database.com/html/license.html).
* Initial Developer: H2 Group
* Iso8601:
* Initial Developer: Robert Rathsack (firstName dot lastName at gmx dot de)
*/
package org.h2.util;
import java.io.Closeable;
import java.io.PrintWriter;
import java.io.StringWriter;
import java.lang.ref.PhantomReference;
import java.lang.ref.ReferenceQueue;
import java.util.HashSet;
/**
* A phantom reference to watch for unclosed objects.
*/
public class CloseWatcher extends PhantomReference<Object> {
/**
* The queue (might be set to null at any time).
*/
public static ReferenceQueue<Object> queue = new ReferenceQueue<Object>();
/**
* The reference set. Must keep it, otherwise the references are garbage
* collected first and thus never enqueued.
*/
public static HashSet<CloseWatcher> refs = New.hashSet();
/**
* The stack trace of when the object was created. It is converted to a
* string early on to avoid classloader problems (a classloader can't be
* garbage collected if there is a static reference to one of its classes).
*/
private String openStackTrace;
/**
* The closeable object.
*/
private Closeable closeable;
public CloseWatcher(Object referent, ReferenceQueue<Object> q, Closeable closeable) {
super(referent, q);
this.closeable = closeable;
}
/**
* Check for an collected object.
*
* @return the first watcher
*/
public static CloseWatcher pollUnclosed() {
ReferenceQueue<Object> q = queue;
if (q == null) {
return null;
}
while (true) {
CloseWatcher cw = (CloseWatcher) q.poll();
if (cw == null) {
return null;
}
if (refs != null) {
refs.remove(cw);
}
if (cw.closeable != null) {
return cw;
}
}
}
/**
* Register an object. Before calling this method, pollUnclosed() should be
* called in a loop to remove old references.
*
* @param o the object
* @param stackTrace whether the stack trace should be registered (this is
* relatively slow)
*/
public static CloseWatcher register(Object o, Closeable closeable, boolean stackTrace) {
ReferenceQueue<Object> q = queue;
if (q == null) {
q = new ReferenceQueue<Object>();
queue = q;
}
CloseWatcher cw = new CloseWatcher(o, q, closeable);
if (stackTrace) {
Exception e = new Exception("Open Stack Trace");
StringWriter s = new StringWriter();
e.printStackTrace(new PrintWriter(s));
cw.openStackTrace = s.toString();
}
if (refs == null) {
refs = New.hashSet();
}
refs.add(cw);
return cw;
}
/**
* Unregister an object, so it is no longer tracked.
*
* @param w the reference
*/
public static void unregister(CloseWatcher w) {
w.closeable = null;
refs.remove(w);
}
/**
* Get the open stack trace or null if none.
*
* @return the open stack trace
*/
public String getOpenStackTrace() {
return openStackTrace;
}
public Closeable getCloseable() {
return closeable;
}
}
......@@ -30,6 +30,8 @@ public class TestClearReferences extends TestBase {
"org.h2.jdbcx.JdbcDataSourceFactory.cachedTraceSystem",
"org.h2.store.fs.FileSystem.tempRandom",
"org.h2.tools.CompressTool.cachedBuffer",
"org.h2.util.CloseWatcher.queue",
"org.h2.util.CloseWatcher.refs",
"org.h2.util.DateTimeUtils.cachedCalendar",
"org.h2.util.MathUtils.cachedSecureRandom",
"org.h2.util.NetUtils.cachedLocalAddress",
......
Markdown 格式
0%
您添加了 0 到此讨论。请谨慎行事。
请先完成此评论的编辑!
注册 或者 后发表评论