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

Close lost instances of ResultTempTable on garbage collection

上级 d54db840
...@@ -5,6 +5,7 @@ ...@@ -5,6 +5,7 @@
*/ */
package org.h2.result; package org.h2.result;
import java.lang.ref.Reference;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Arrays; import java.util.Arrays;
import java.util.BitSet; import java.util.BitSet;
...@@ -21,6 +22,7 @@ import org.h2.schema.Schema; ...@@ -21,6 +22,7 @@ import org.h2.schema.Schema;
import org.h2.table.Column; import org.h2.table.Column;
import org.h2.table.IndexColumn; import org.h2.table.IndexColumn;
import org.h2.table.Table; import org.h2.table.Table;
import org.h2.util.TempFileDeleter;
import org.h2.value.Value; import org.h2.value.Value;
import org.h2.value.ValueNull; import org.h2.value.ValueNull;
...@@ -29,6 +31,53 @@ import org.h2.value.ValueNull; ...@@ -29,6 +31,53 @@ import org.h2.value.ValueNull;
*/ */
public class ResultTempTable implements ResultExternal { 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 static final String COLUMN_NAME = "DATA";
private final boolean distinct; private final boolean distinct;
private final SortOrder sort; private final SortOrder sort;
...@@ -42,7 +91,21 @@ public class ResultTempTable implements ResultExternal { ...@@ -42,7 +91,21 @@ public class ResultTempTable implements ResultExternal {
private final ResultTempTable parent; private final ResultTempTable parent;
private boolean closed; private boolean closed;
private int childCount; 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) { ResultTempTable(Session session, Expression[] expressions, boolean distinct, SortOrder sort) {
this.session = session; this.session = session;
...@@ -51,17 +114,15 @@ public class ResultTempTable implements ResultExternal { ...@@ -51,17 +114,15 @@ public class ResultTempTable implements ResultExternal {
this.columnCount = expressions.length; this.columnCount = expressions.length;
Schema schema = session.getDatabase().getSchema(Constants.SCHEMA_MAIN); Schema schema = session.getDatabase().getSchema(Constants.SCHEMA_MAIN);
CreateTableData data = new CreateTableData(); CreateTableData data = new CreateTableData();
boolean b = false; boolean containsLob = false;
for (int i = 0; i < expressions.length; i++) { for (int i = 0; i < expressions.length; i++) {
int type = expressions[i].getType(); int type = expressions[i].getType();
Column col = new Column(COLUMN_NAME + i, Column col = new Column(COLUMN_NAME + i, type);
type);
if (type == Value.CLOB || type == Value.BLOB) { if (type == Value.CLOB || type == Value.BLOB) {
b = true; containsLob = true;
} }
data.columns.add(col); data.columns.add(col);
} }
containsLob = b;
data.id = session.getDatabase().allocateObjectId(); data.id = session.getDatabase().allocateObjectId();
data.tableName = "TEMP_RESULT_SET_" + data.id; data.tableName = "TEMP_RESULT_SET_" + data.id;
data.temporary = true; data.temporary = true;
...@@ -70,10 +131,26 @@ public class ResultTempTable implements ResultExternal { ...@@ -70,10 +131,26 @@ public class ResultTempTable implements ResultExternal {
data.create = true; data.create = true;
data.session = session; data.session = session;
table = schema.createTable(data); 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) { if (sort != null || distinct) {
getIndex(); getIndex();
} }
parent = null;
} }
private ResultTempTable(ResultTempTable parent) { private ResultTempTable(ResultTempTable parent) {
...@@ -84,8 +161,9 @@ public class ResultTempTable implements ResultExternal { ...@@ -84,8 +161,9 @@ public class ResultTempTable implements ResultExternal {
this.table = parent.table; this.table = parent.table;
this.rowCount = parent.rowCount; this.rowCount = parent.rowCount;
this.sort = parent.sort; this.sort = parent.sort;
this.containsLob = parent.containsLob; this.tempFileDeleter = null;
reset(); this.closeable = null;
this.fileRef = null;
} }
private Index getIndex() { private Index getIndex() {
...@@ -129,12 +207,14 @@ public class ResultTempTable implements ResultExternal { ...@@ -129,12 +207,14 @@ public class ResultTempTable implements ResultExternal {
indexCols[i] = createIndexColumn(i); indexCols[i] = createIndexColumn(i);
} }
} }
String indexName = table.getSchema().getUniqueIndexName(session, String indexName = table.getSchema().getUniqueIndexName(session, table, Constants.PREFIX_INDEX);
table, Constants.PREFIX_INDEX);
int indexId = session.getDatabase().allocateObjectId(); int indexId = session.getDatabase().allocateObjectId();
IndexType indexType = IndexType.createNonUnique(true); IndexType indexType = IndexType.createNonUnique(true);
return index = table.addIndex(session, indexName, indexId, indexCols, index = table.addIndex(session, indexName, indexId, indexCols, indexType, true, null);
indexType, true, null); if (closeable != null) {
closeable.index = index;
}
return index;
} }
private IndexColumn createIndexColumn(int index) { private IndexColumn createIndexColumn(int index) {
...@@ -203,7 +283,7 @@ public class ResultTempTable implements ResultExternal { ...@@ -203,7 +283,7 @@ public class ResultTempTable implements ResultExternal {
private synchronized void closeChild() { private synchronized void closeChild() {
if (--childCount == 0 && closed) { if (--childCount == 0 && closed) {
dropTable(); delete();
} }
} }
...@@ -217,55 +297,14 @@ public class ResultTempTable implements ResultExternal { ...@@ -217,55 +297,14 @@ public class ResultTempTable implements ResultExternal {
parent.closeChild(); parent.closeChild();
} else { } else {
if (childCount == 0) { if (childCount == 0) {
dropTable(); delete();
} }
} }
} }
private void dropTable() { private void delete() {
if (table == null) { if (tempFileDeleter != null) {
return; tempFileDeleter.deleteFile(fileRef, closeable);
}
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;
} }
} }
...@@ -329,4 +368,3 @@ public class ResultTempTable implements ResultExternal { ...@@ -329,4 +368,3 @@ public class ResultTempTable implements ResultExternal {
} }
} }
Markdown 格式
0%
您添加了 0 到此讨论。请谨慎行事。
请先完成此评论的编辑!
注册 或者 后发表评论