提交 271f9943 authored 作者: Thomas Mueller Graf's avatar Thomas Mueller Graf

Temporary CLOB and BLOB objects are now removed while the database is open

上级 735f3467
...@@ -21,6 +21,9 @@ Change Log ...@@ -21,6 +21,9 @@ Change Log
<h2>Next Version (unreleased)</h2> <h2>Next Version (unreleased)</h2>
<ul> <ul>
<li>Temporary CLOB and BLOB objects are now removed while the database is open
(and not just when closing the database).
</li>
<li>MVStore CLOB and BLOB larger than about 25 MB: An exception could be thrown <li>MVStore CLOB and BLOB larger than about 25 MB: An exception could be thrown
when using the MVStore storage. when using the MVStore storage.
</li> </li>
......
...@@ -236,7 +236,7 @@ public class CommandRemote implements CommandInterface { ...@@ -236,7 +236,7 @@ public class CommandRemote implements CommandInterface {
for (ParameterInterface p : parameters) { for (ParameterInterface p : parameters) {
Value v = p.getParamValue(); Value v = p.getParamValue();
if (v != null) { if (v != null) {
v.close(); v.remove();
} }
} }
} catch (DbException e) { } catch (DbException e) {
......
...@@ -86,7 +86,7 @@ public class Session extends SessionWithState { ...@@ -86,7 +86,7 @@ public class Session extends SessionWithState {
private String currentSchemaName; private String currentSchemaName;
private String[] schemaSearchPath; private String[] schemaSearchPath;
private Trace trace; private Trace trace;
private HashMap<String, Value> unlinkLobMap; private HashMap<String, Value> removeLobMap;
private int systemIdentifier; private int systemIdentifier;
private HashMap<String, Procedure> procedures; private HashMap<String, Procedure> procedures;
private boolean undoLogEnabled = true; private boolean undoLogEnabled = true;
...@@ -173,14 +173,13 @@ public class Session extends SessionWithState { ...@@ -173,14 +173,13 @@ public class Session extends SessionWithState {
old = variables.remove(name); old = variables.remove(name);
} else { } else {
// link LOB values, to make sure we have our own object // link LOB values, to make sure we have our own object
value = value.link(database, value = value.copy(database,
LobStorageFrontend.TABLE_ID_SESSION_VARIABLE); LobStorageFrontend.TABLE_ID_SESSION_VARIABLE);
old = variables.put(name, value); old = variables.put(name, value);
} }
if (old != null) { if (old != null) {
// close the old value (in case it is a lob) // remove the old value (in case it is a lob)
old.unlink(database); old.remove();
old.close();
} }
} }
...@@ -547,8 +546,8 @@ public class Session extends SessionWithState { ...@@ -547,8 +546,8 @@ public class Session extends SessionWithState {
private void removeTemporaryLobs(boolean onTimeout) { private void removeTemporaryLobs(boolean onTimeout) {
if (temporaryLobs != null) { if (temporaryLobs != null) {
for (Value v : temporaryLobs) { for (Value v : temporaryLobs) {
if (!v.isLinked()) { if (!v.isLinkedToTable()) {
v.close(); v.remove();
} }
} }
temporaryLobs.clear(); temporaryLobs.clear();
...@@ -562,8 +561,8 @@ public class Session extends SessionWithState { ...@@ -562,8 +561,8 @@ public class Session extends SessionWithState {
break; break;
} }
Value v = temporaryResultLobs.removeFirst().value; Value v = temporaryResultLobs.removeFirst().value;
if (!v.isLinked()) { if (!v.isLinkedToTable()) {
v.close(); v.remove();
} }
} }
} }
...@@ -576,17 +575,16 @@ public class Session extends SessionWithState { ...@@ -576,17 +575,16 @@ public class Session extends SessionWithState {
} }
private void endTransaction() { private void endTransaction() {
if (unlinkLobMap != null && unlinkLobMap.size() > 0) { if (removeLobMap != null && removeLobMap.size() > 0) {
if (database.getMvStore() == null) { if (database.getMvStore() == null) {
// need to flush the transaction log, because we can't unlink // need to flush the transaction log, because we can't unlink
// lobs if the commit record is not written // lobs if the commit record is not written
database.flush(); database.flush();
} }
for (Value v : unlinkLobMap.values()) { for (Value v : removeLobMap.values()) {
v.unlink(database); v.remove();
v.close();
} }
unlinkLobMap = null; removeLobMap = null;
} }
unlockAll(); unlockAll();
} }
...@@ -1128,29 +1126,24 @@ public class Session extends SessionWithState { ...@@ -1128,29 +1126,24 @@ public class Session extends SessionWithState {
} }
/** /**
* Remember that the given LOB value must be un-linked (disconnected from * Remember that the given LOB value must be removed at commit.
* the table) at commit.
* *
* @param v the value * @param v the value
*/ */
public void unlinkAtCommit(Value v) { public void removeAtCommit(Value v) {
if (SysProperties.CHECK && !v.isLinked()) { if (SysProperties.CHECK && !v.isLinkedToTable()) {
DbException.throwInternalError(); DbException.throwInternalError();
} }
if (unlinkLobMap == null) {
unlinkLobMap = New.hashMap();
}
unlinkLobMap.put(v.toString(), v);
} }
/** /**
* Do not unlink this LOB value at commit any longer. * Do not remove this LOB value at commit any longer.
* *
* @param v the value * @param v the value
*/ */
public void unlinkAtCommitStop(Value v) { public void removeAtCommitStop(Value v) {
if (unlinkLobMap != null) { if (removeLobMap != null) {
unlinkLobMap.remove(v.toString()); removeLobMap.remove(v.toString());
} }
} }
...@@ -1481,7 +1474,11 @@ public class Session extends SessionWithState { ...@@ -1481,7 +1474,11 @@ public class Session extends SessionWithState {
@Override @Override
public void addTemporaryLob(Value v) { public void addTemporaryLob(Value v) {
if (v.getTableId() == LobStorageFrontend.TABLE_RESULT) { if (v.getType() != Value.CLOB && v.getType() != Value.BLOB) {
return;
}
if (v.getTableId() == LobStorageFrontend.TABLE_RESULT ||
v.getTableId() == LobStorageFrontend.TABLE_TEMP) {
if (temporaryResultLobs == null) { if (temporaryResultLobs == null) {
temporaryResultLobs = new LinkedList<TimeoutValue>(); temporaryResultLobs = new LinkedList<TimeoutValue>();
} }
......
...@@ -1592,6 +1592,7 @@ public class Function extends Expression implements FunctionCall { ...@@ -1592,6 +1592,7 @@ public class Function extends Expression implements FunctionCall {
} }
result = database.getLobStorage().createClob(reader, -1); result = database.getLobStorage().createClob(reader, -1);
} }
session.addTemporaryLob(result);
} catch (IOException e) { } catch (IOException e) {
throw DbException.convertIOException(e, fileName); throw DbException.convertIOException(e, fileName);
} }
......
...@@ -32,7 +32,7 @@ public class ParameterRemote implements ParameterInterface { ...@@ -32,7 +32,7 @@ public class ParameterRemote implements ParameterInterface {
@Override @Override
public void setValue(Value newValue, boolean closeOld) { public void setValue(Value newValue, boolean closeOld) {
if (closeOld && value != null) { if (closeOld && value != null) {
value.close(); value.remove();
} }
value = newValue; value = newValue;
} }
......
...@@ -116,9 +116,9 @@ public class PageDataIndex extends PageIndex { ...@@ -116,9 +116,9 @@ public class PageDataIndex extends PageIndex {
if (tableData.getContainsLargeObject()) { if (tableData.getContainsLargeObject()) {
for (int i = 0, len = row.getColumnCount(); i < len; i++) { for (int i = 0, len = row.getColumnCount(); i < len; i++) {
Value v = row.getValue(i); Value v = row.getValue(i);
Value v2 = v.link(database, getId()); Value v2 = v.copy(database, getId());
if (v2.isLinked()) { if (v2.isLinkedToTable()) {
session.unlinkAtCommitStop(v2); session.removeAtCommitStop(v2);
} }
if (v != v2) { if (v != v2) {
row.setValue(i, v2); row.setValue(i, v2);
...@@ -327,8 +327,8 @@ public class PageDataIndex extends PageIndex { ...@@ -327,8 +327,8 @@ public class PageDataIndex extends PageIndex {
if (tableData.getContainsLargeObject()) { if (tableData.getContainsLargeObject()) {
for (int i = 0, len = row.getColumnCount(); i < len; i++) { for (int i = 0, len = row.getColumnCount(); i < len; i++) {
Value v = row.getValue(i); Value v = row.getValue(i);
if (v.isLinked()) { if (v.isLinkedToTable()) {
session.unlinkAtCommit(v); session.removeAtCommitStop(v);
} }
} }
} }
......
...@@ -118,9 +118,9 @@ public class MVPrimaryIndex extends BaseIndex { ...@@ -118,9 +118,9 @@ public class MVPrimaryIndex extends BaseIndex {
if (mvTable.getContainsLargeObject()) { if (mvTable.getContainsLargeObject()) {
for (int i = 0, len = row.getColumnCount(); i < len; i++) { for (int i = 0, len = row.getColumnCount(); i < len; i++) {
Value v = row.getValue(i); Value v = row.getValue(i);
Value v2 = v.link(database, getId()); Value v2 = v.copy(database, getId());
if (v2.isLinked()) { if (v2.isLinkedToTable()) {
session.unlinkAtCommitStop(v2); session.removeAtCommitStop(v2);
} }
if (v != v2) { if (v != v2) {
row.setValue(i, v2); row.setValue(i, v2);
...@@ -153,8 +153,8 @@ public class MVPrimaryIndex extends BaseIndex { ...@@ -153,8 +153,8 @@ public class MVPrimaryIndex extends BaseIndex {
if (mvTable.getContainsLargeObject()) { if (mvTable.getContainsLargeObject()) {
for (int i = 0, len = row.getColumnCount(); i < len; i++) { for (int i = 0, len = row.getColumnCount(); i < len; i++) {
Value v = row.getValue(i); Value v = row.getValue(i);
if (v.isLinked()) { if (v.isLinkedToTable()) {
session.unlinkAtCommit(v); session.removeAtCommit(v);
} }
} }
} }
......
...@@ -181,11 +181,11 @@ public class RowList { ...@@ -181,11 +181,11 @@ public class RowList {
v = null; v = null;
} else { } else {
v = buff.readValue(); v = buff.readValue();
if (v.isLinked()) { if (v.isLinkedToTable()) {
// the table id is 0 if it was linked when writing // the table id is 0 if it was linked when writing
// a temporary entry // a temporary entry
if (v.getTableId() == 0) { if (v.getTableId() == 0) {
session.unlinkAtCommit(v); session.removeAtCommit(v);
} }
} }
} }
......
...@@ -623,7 +623,7 @@ public class Data { ...@@ -623,7 +623,7 @@ public class Data {
byte[] small = lob.getSmall(); byte[] small = lob.getSmall();
if (small == null) { if (small == null) {
int t = -1; int t = -1;
if (!lob.isLinked()) { if (!lob.isLinkedToTable()) {
t = -2; t = -2;
} }
writeVarInt(t); writeVarInt(t);
...@@ -1044,7 +1044,7 @@ public class Data { ...@@ -1044,7 +1044,7 @@ public class Data {
byte[] small = lob.getSmall(); byte[] small = lob.getSmall();
if (small == null) { if (small == null) {
int t = -1; int t = -1;
if (!lob.isLinked()) { if (!lob.isLinkedToTable()) {
t = -2; t = -2;
} }
len += getVarIntLen(t); len += getVarIntLen(t);
......
...@@ -691,8 +691,7 @@ public class Recover extends Tool implements DataHandler { ...@@ -691,8 +691,7 @@ public class Recover extends Tool implements DataHandler {
MVMap<Long, byte[]> lobData = mv.openMap("lobData"); MVMap<Long, byte[]> lobData = mv.openMap("lobData");
StreamStore streamStore = new StreamStore(lobData); StreamStore streamStore = new StreamStore(lobData);
MVMap<Long, Object[]> lobMap = mv.openMap("lobMap"); MVMap<Long, Object[]> lobMap = mv.openMap("lobMap");
writer.println("-- LOB (lobMap.id=0x" + Integer.toHexString(lobMap.getId()) + writer.println("-- LOB");
", lobData.id=0x" + Integer.toHexString(lobData.getId()) + ")");
writer.println("CREATE TABLE IF NOT EXISTS " + writer.println("CREATE TABLE IF NOT EXISTS " +
"INFORMATION_SCHEMA.LOB_BLOCKS(" + "INFORMATION_SCHEMA.LOB_BLOCKS(" +
"LOB_ID BIGINT, SEQ INT, DATA BINARY, " + "LOB_ID BIGINT, SEQ INT, DATA BINARY, " +
...@@ -722,6 +721,9 @@ public class Recover extends Tool implements DataHandler { ...@@ -722,6 +721,9 @@ public class Recover extends Tool implements DataHandler {
hasErrors = true; hasErrors = true;
} }
} }
writer.println("-- lobMap.size: " + lobMap.sizeAsLong());
writer.println("-- lobData.size: " + lobData.sizeAsLong());
if (hasErrors) { if (hasErrors) {
writer.println("-- lobMap"); writer.println("-- lobMap");
for (Long k : lobMap.keyList()) { for (Long k : lobMap.keyList()) {
......
...@@ -607,6 +607,9 @@ public class DataType { ...@@ -607,6 +607,9 @@ public class DataType {
createClob(new BufferedReader(in), -1); createClob(new BufferedReader(in), -1);
} }
} }
if (session != null) {
session.addTemporaryLob(v);
}
break; break;
} }
case Value.BLOB: { case Value.BLOB: {
...@@ -618,6 +621,9 @@ public class DataType { ...@@ -618,6 +621,9 @@ public class DataType {
InputStream in = rs.getBinaryStream(columnIndex); InputStream in = rs.getBinaryStream(columnIndex);
v = (in == null) ? (Value) ValueNull.INSTANCE : v = (in == null) ? (Value) ValueNull.INSTANCE :
session.getDataHandler().getLobStorage().createBlob(in, -1); session.getDataHandler().getLobStorage().createBlob(in, -1);
if (session != null) {
session.addTemporaryLob(v);
}
break; break;
} }
case Value.JAVA_OBJECT: { case Value.JAVA_OBJECT: {
...@@ -956,6 +962,15 @@ public class DataType { ...@@ -956,6 +962,15 @@ public class DataType {
*/ */
public static Value convertToValue(SessionInterface session, Object x, public static Value convertToValue(SessionInterface session, Object x,
int type) { int type) {
Value v = convertToValue1(session, x, type);
if (session != null) {
session.addTemporaryLob(v);
}
return v;
}
private static Value convertToValue1(SessionInterface session, Object x,
int type) {
if (x == null) { if (x == null) {
return ValueNull.INSTANCE; return ValueNull.INSTANCE;
} }
......
...@@ -1021,14 +1021,14 @@ public abstract class Value { ...@@ -1021,14 +1021,14 @@ public abstract class Value {
} }
/** /**
* Link a large value to a given table. For values that are kept fully in * Copy a large value, to be used in the given table. For values that are
* memory this method has no effect. * kept fully in memory this method has no effect.
* *
* @param handler the data handler * @param handler the data handler
* @param tableId the table to link to * @param tableId the table where this object is used
* @return the new value or itself * @return the new value or itself
*/ */
public Value link(DataHandler handler, int tableId) { public Value copy(DataHandler handler, int tableId) {
return this; return this;
} }
...@@ -1038,25 +1038,15 @@ public abstract class Value { ...@@ -1038,25 +1038,15 @@ public abstract class Value {
* *
* @return true if it is * @return true if it is
*/ */
public boolean isLinked() { public boolean isLinkedToTable() {
return false; return false;
} }
/** /**
* Mark any underlying resource as 'not linked to any table'. For values * Remove the underlying resource, if any. For values that are kept fully in
* that are kept fully in memory this method has no effect.
*
* @param handler the data handler
*/
public void unlink(DataHandler handler) {
// nothing to do
}
/**
* Close the underlying resource, if any. For values that are kept fully in
* memory this method has no effect. * memory this method has no effect.
*/ */
public void close() { public void remove() {
// nothing to do // nothing to do
} }
......
...@@ -464,7 +464,7 @@ public class ValueLob extends Value { ...@@ -464,7 +464,7 @@ public class ValueLob extends Value {
} }
@Override @Override
public boolean isLinked() { public boolean isLinkedToTable() {
return linked; return linked;
} }
...@@ -478,7 +478,7 @@ public class ValueLob extends Value { ...@@ -478,7 +478,7 @@ public class ValueLob extends Value {
} }
@Override @Override
public void close() { public void remove() {
if (fileName != null) { if (fileName != null) {
if (tempFile != null) { if (tempFile != null) {
tempFile.stopAutoDelete(); tempFile.stopAutoDelete();
...@@ -489,26 +489,7 @@ public class ValueLob extends Value { ...@@ -489,26 +489,7 @@ public class ValueLob extends Value {
} }
@Override @Override
public void unlink(DataHandler handler) { public Value copy(DataHandler h, int tabId) {
if (linked && fileName != null) {
String temp;
// synchronize on the database, to avoid concurrent temp file
// creation / deletion / backup
synchronized (handler) {
temp = getFileName(handler, -1, objectId);
deleteFile(handler, temp);
renameFile(handler, fileName, temp);
tempFile = FileStore.open(handler, temp, "rw");
tempFile.autoDelete();
tempFile.closeSilently();
fileName = temp;
linked = false;
}
}
}
@Override
public Value link(DataHandler h, int tabId) {
if (fileName == null) { if (fileName == null) {
this.tableId = tabId; this.tableId = tabId;
return this; return this;
...@@ -747,7 +728,7 @@ public class ValueLob extends Value { ...@@ -747,7 +728,7 @@ public class ValueLob extends Value {
createFromReader( createFromReader(
new char[len], 0, getReader(), Long.MAX_VALUE, h); new char[len], 0, getReader(), Long.MAX_VALUE, h);
} }
Value v2 = link(h, tabId); Value v2 = copy(h, tabId);
if (SysProperties.CHECK && v2 != this) { if (SysProperties.CHECK && v2 != this) {
DbException.throwInternalError(); DbException.throwInternalError();
} }
......
...@@ -13,6 +13,7 @@ import java.io.InputStream; ...@@ -13,6 +13,7 @@ import java.io.InputStream;
import java.io.Reader; import java.io.Reader;
import java.sql.PreparedStatement; import java.sql.PreparedStatement;
import java.sql.SQLException; import java.sql.SQLException;
import org.h2.engine.Constants; import org.h2.engine.Constants;
import org.h2.engine.SysProperties; import org.h2.engine.SysProperties;
import org.h2.message.DbException; import org.h2.message.DbException;
...@@ -205,10 +206,9 @@ public class ValueLobDb extends Value implements Value.ValueClob, ...@@ -205,10 +206,9 @@ public class ValueLobDb extends Value implements Value.ValueClob,
} }
@Override @Override
public boolean isLinked() { public boolean isLinkedToTable() {
return tableId != LobStorageFrontend.TABLE_ID_SESSION_VARIABLE && return small == null &&
tableId != LobStorageFrontend.TABLE_RESULT && tableId >= 0;
small == null;
} }
public boolean isStored() { public boolean isStored() {
...@@ -216,7 +216,7 @@ public class ValueLobDb extends Value implements Value.ValueClob, ...@@ -216,7 +216,7 @@ public class ValueLobDb extends Value implements Value.ValueClob,
} }
@Override @Override
public void close() { public void remove() {
if (fileName != null) { if (fileName != null) {
if (tempFile != null) { if (tempFile != null) {
tempFile.stopAutoDelete(); tempFile.stopAutoDelete();
...@@ -233,24 +233,10 @@ public class ValueLobDb extends Value implements Value.ValueClob, ...@@ -233,24 +233,10 @@ public class ValueLobDb extends Value implements Value.ValueClob,
} }
@Override @Override
public void unlink(DataHandler database) { public Value copy(DataHandler database, int tableId) {
if (small == null &&
tableId != LobStorageFrontend.TABLE_ID_SESSION_VARIABLE) {
database.getLobStorage().setTable(this,
LobStorageFrontend.TABLE_ID_SESSION_VARIABLE);
tableId = LobStorageFrontend.TABLE_ID_SESSION_VARIABLE;
}
}
@Override
public Value link(DataHandler database, int tabId) {
if (small == null) { if (small == null) {
if (tableId == LobStorageFrontend.TABLE_TEMP) { Value v2 = handler.getLobStorage().copyLob(this, tableId, getPrecision());
database.getLobStorage().setTable(this, tabId); return v2;
this.tableId = tabId;
} else {
return handler.getLobStorage().copyLob(this, tabId, getPrecision());
}
} else if (small.length > database.getMaxLengthInplaceLob()) { } else if (small.length > database.getMaxLengthInplaceLob()) {
LobStorageInterface s = database.getLobStorage(); LobStorageInterface s = database.getLobStorage();
Value v; Value v;
...@@ -259,7 +245,9 @@ public class ValueLobDb extends Value implements Value.ValueClob, ...@@ -259,7 +245,9 @@ public class ValueLobDb extends Value implements Value.ValueClob,
} else { } else {
v = s.createClob(getReader(), getPrecision()); v = s.createClob(getReader(), getPrecision());
} }
return v.link(database, tabId); Value v2 = v.copy(database, tableId);
v.remove();
return v2;
} }
return this; return this;
} }
......
...@@ -104,7 +104,7 @@ public class ValueResultSet extends Value { ...@@ -104,7 +104,7 @@ public class ValueResultSet extends Value {
for (int j = 0; j < columnCount; j++) { for (int j = 0; j < columnCount; j++) {
buff.appendExceptFirst(", "); buff.appendExceptFirst(", ");
int t = DataType.getValueTypeFromResultSet(meta, j + 1); int t = DataType.getValueTypeFromResultSet(meta, j + 1);
Value v = DataType.readValue(null, result, j+1, t); Value v = DataType.readValue(null, result, j + 1, t);
buff.append(v.getString()); buff.append(v.getString());
} }
buff.append(')'); buff.append(')');
......
Markdown 格式
0%
您添加了 0 到此讨论。请谨慎行事。
请先完成此评论的编辑!
注册 或者 后发表评论