提交 63182126 authored 作者: Evgenij Ryazanov's avatar Evgenij Ryazanov

Close lost instances of ResultTempTable on garbage collection

上级 d54db840
......@@ -5,6 +5,7 @@
*/
package org.h2.result;
import java.lang.ref.Reference;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.BitSet;
......@@ -21,6 +22,7 @@ import org.h2.schema.Schema;
import org.h2.table.Column;
import org.h2.table.IndexColumn;
import org.h2.table.Table;
import org.h2.util.TempFileDeleter;
import org.h2.value.Value;
import org.h2.value.ValueNull;
......@@ -29,6 +31,53 @@ import org.h2.value.ValueNull;
*/
public class ResultTempTable implements ResultExternal {
private static final class CloseImpl implements AutoCloseable {
private final Session session;
private final Table table;
Index index;
CloseImpl(Session session, Table table) {
this.session = session;
this.table = table;
}
@Override
public void close() throws Exception {
Database database = session.getDatabase();
// Need to lock because not all of the code-paths
// that reach here have already taken this lock,
// notably via the close() paths.
synchronized (session) {
synchronized (database) {
table.truncate(session);
}
}
// This session may not lock the sys table (except if it already has
// locked it) because it must be committed immediately, otherwise
// other threads can not access the sys table. If the table is not
// removed now, it will be when the database is opened the next
// time. (the table is truncated, so this is just one record)
if (!database.isSysTableLocked()) {
Session sysSession = database.getSystemSession();
table.removeChildrenAndResources(sysSession);
if (index != null) {
// need to explicitly do this,
// as it's not registered in the system session
session.removeLocalTempTableIndex(index);
}
// the transaction must be committed immediately
// TODO this synchronization cascade is very ugly
synchronized (session) {
synchronized (sysSession) {
synchronized (database) {
sysSession.commit(false);
}
}
}
}
}
}
private static final String COLUMN_NAME = "DATA";
private final boolean distinct;
private final SortOrder sort;
......@@ -42,7 +91,21 @@ public class ResultTempTable implements ResultExternal {
private final ResultTempTable parent;
private boolean closed;
private int childCount;
private final boolean containsLob;
/**
* Temporary file deleter.
*/
private final TempFileDeleter tempFileDeleter;
/**
* Closeable to close the storage.
*/
private final CloseImpl closeable;
/**
* Reference to the record in the temporary file deleter.
*/
private final Reference<?> fileRef;
ResultTempTable(Session session, Expression[] expressions, boolean distinct, SortOrder sort) {
this.session = session;
......@@ -51,17 +114,15 @@ public class ResultTempTable implements ResultExternal {
this.columnCount = expressions.length;
Schema schema = session.getDatabase().getSchema(Constants.SCHEMA_MAIN);
CreateTableData data = new CreateTableData();
boolean b = false;
boolean containsLob = false;
for (int i = 0; i < expressions.length; i++) {
int type = expressions[i].getType();
Column col = new Column(COLUMN_NAME + i,
type);
Column col = new Column(COLUMN_NAME + i, type);
if (type == Value.CLOB || type == Value.BLOB) {
b = true;
containsLob = true;
}
data.columns.add(col);
}
containsLob = b;
data.id = session.getDatabase().allocateObjectId();
data.tableName = "TEMP_RESULT_SET_" + data.id;
data.temporary = true;
......@@ -70,10 +131,26 @@ public class ResultTempTable implements ResultExternal {
data.create = true;
data.session = session;
table = schema.createTable(data);
parent = null;
if (containsLob) {
// contains BLOB or CLOB: can not truncate now,
// otherwise the BLOB and CLOB entries are removed
tempFileDeleter = null;
closeable = null;
fileRef = null;
} else {
tempFileDeleter = session.getDatabase().getTempFileDeleter();
closeable = new CloseImpl(session, table);
fileRef = tempFileDeleter.addFile(closeable, this);
}
/*
* If ORDER BY or DISTINCT is specified create the index immediately. If
* they are not specified index still may be created later if required
* for IN (SELECT ...) etc.
*/
if (sort != null || distinct) {
getIndex();
}
parent = null;
}
private ResultTempTable(ResultTempTable parent) {
......@@ -84,8 +161,9 @@ public class ResultTempTable implements ResultExternal {
this.table = parent.table;
this.rowCount = parent.rowCount;
this.sort = parent.sort;
this.containsLob = parent.containsLob;
reset();
this.tempFileDeleter = null;
this.closeable = null;
this.fileRef = null;
}
private Index getIndex() {
......@@ -129,12 +207,14 @@ public class ResultTempTable implements ResultExternal {
indexCols[i] = createIndexColumn(i);
}
}
String indexName = table.getSchema().getUniqueIndexName(session,
table, Constants.PREFIX_INDEX);
String indexName = table.getSchema().getUniqueIndexName(session, table, Constants.PREFIX_INDEX);
int indexId = session.getDatabase().allocateObjectId();
IndexType indexType = IndexType.createNonUnique(true);
return index = table.addIndex(session, indexName, indexId, indexCols,
indexType, true, null);
index = table.addIndex(session, indexName, indexId, indexCols, indexType, true, null);
if (closeable != null) {
closeable.index = index;
}
return index;
}
private IndexColumn createIndexColumn(int index) {
......@@ -203,7 +283,7 @@ public class ResultTempTable implements ResultExternal {
private synchronized void closeChild() {
if (--childCount == 0 && closed) {
dropTable();
delete();
}
}
......@@ -217,55 +297,14 @@ public class ResultTempTable implements ResultExternal {
parent.closeChild();
} else {
if (childCount == 0) {
dropTable();
delete();
}
}
}
private void dropTable() {
if (table == null) {
return;
}
if (containsLob) {
// contains BLOB or CLOB: can not truncate now,
// otherwise the BLOB and CLOB entries are removed
return;
}
try {
Database database = session.getDatabase();
// Need to lock because not all of the code-paths
// that reach here have already taken this lock,
// notably via the close() paths.
synchronized (session) {
synchronized (database) {
table.truncate(session);
}
}
// This session may not lock the sys table (except if it already has
// locked it) because it must be committed immediately, otherwise
// other threads can not access the sys table. If the table is not
// removed now, it will be when the database is opened the next
// time. (the table is truncated, so this is just one record)
if (!database.isSysTableLocked()) {
Session sysSession = database.getSystemSession();
table.removeChildrenAndResources(sysSession);
if (index != null) {
// need to explicitly do this,
// as it's not registered in the system session
session.removeLocalTempTableIndex(index);
}
// the transaction must be committed immediately
// TODO this synchronization cascade is very ugly
synchronized (session) {
synchronized (sysSession) {
synchronized (database) {
sysSession.commit(false);
}
}
}
}
} finally {
table = null;
private void delete() {
if (tempFileDeleter != null) {
tempFileDeleter.deleteFile(fileRef, closeable);
}
}
......@@ -329,4 +368,3 @@ public class ResultTempTable implements ResultExternal {
}
}
Markdown 格式
0%
您添加了 0 到此讨论。请谨慎行事。
请先完成此评论的编辑!
注册 或者 后发表评论