提交 f3ec805a authored 作者: Noel Grandin's avatar Noel Grandin

Issue #324: Deadlock when sending BLOBs over TCP

上级 add3db91
...@@ -21,6 +21,8 @@ Change Log ...@@ -21,6 +21,8 @@ Change Log
<h2>Next Version (unreleased)</h2> <h2>Next Version (unreleased)</h2>
<ul> <ul>
<li>Issue #324: Deadlock when sending BLOBs over TCP
</li>
<li>Fix for creating and accessing views in MULTITHREADED mode, test-case courtesy of Daniel Rosenbaum <li>Fix for creating and accessing views in MULTITHREADED mode, test-case courtesy of Daniel Rosenbaum
</li> </li>
<li>Issue #266: Spatial index not updating, fixed by merging PR #267 <li>Issue #266: Spatial index not updating, fixed by merging PR #267
......
...@@ -1602,10 +1602,11 @@ public class Function extends Expression implements FunctionCall { ...@@ -1602,10 +1602,11 @@ public class Function extends Expression implements FunctionCall {
String fileName = v0.getString(); String fileName = v0.getString();
boolean blob = args.length == 1; boolean blob = args.length == 1;
try { try {
long fileLength = FileUtils.size(fileName);
InputStream in = new AutoCloseInputStream( InputStream in = new AutoCloseInputStream(
FileUtils.newInputStream(fileName)); FileUtils.newInputStream(fileName));
if (blob) { if (blob) {
result = database.getLobStorage().createBlob(in, -1); result = database.getLobStorage().createBlob(in, fileLength);
} else { } else {
Reader reader; Reader reader;
if (v1 == ValueNull.INSTANCE) { if (v1 == ValueNull.INSTANCE) {
...@@ -1613,7 +1614,7 @@ public class Function extends Expression implements FunctionCall { ...@@ -1613,7 +1614,7 @@ public class Function extends Expression implements FunctionCall {
} else { } else {
reader = new InputStreamReader(in, v1.getString()); reader = new InputStreamReader(in, v1.getString());
} }
result = database.getLobStorage().createClob(reader, -1); result = database.getLobStorage().createClob(reader, fileLength);
} }
session.addTemporaryLob(result); session.addTemporaryLob(result);
} catch (IOException e) { } catch (IOException e) {
......
...@@ -12,11 +12,10 @@ import java.nio.ByteBuffer; ...@@ -12,11 +12,10 @@ import java.nio.ByteBuffer;
import java.nio.CharBuffer; import java.nio.CharBuffer;
import java.nio.charset.CharsetEncoder; import java.nio.charset.CharsetEncoder;
import java.nio.charset.CodingErrorAction; import java.nio.charset.CodingErrorAction;
import org.h2.engine.Constants; import org.h2.engine.Constants;
/** /**
* An input stream that reads the data from a reader. * An input stream that reads the data from a reader and limits the number of bytes that can be read.
*/ */
public class CountingReaderInputStream extends InputStream { public class CountingReaderInputStream extends InputStream {
......
...@@ -5,15 +5,12 @@ ...@@ -5,15 +5,12 @@
*/ */
package org.h2.store; package org.h2.store;
import java.io.BufferedInputStream;
import java.io.BufferedReader;
import java.io.IOException; import java.io.IOException;
import java.io.InputStream; import java.io.InputStream;
import java.io.Reader; import java.io.Reader;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Arrays; import java.util.Arrays;
import java.util.Map.Entry; import java.util.Map.Entry;
import org.h2.api.ErrorCode; import org.h2.api.ErrorCode;
import org.h2.engine.Constants; import org.h2.engine.Constants;
import org.h2.engine.Database; import org.h2.engine.Database;
...@@ -145,26 +142,21 @@ public class LobStorageMap implements LobStorageInterface { ...@@ -145,26 +142,21 @@ public class LobStorageMap implements LobStorageInterface {
public Value createBlob(InputStream in, long maxLength) { public Value createBlob(InputStream in, long maxLength) {
init(); init();
int type = Value.BLOB; int type = Value.BLOB;
if (maxLength < 0) {
maxLength = Long.MAX_VALUE;
}
int max = (int) Math.min(maxLength, database.getMaxLengthInplaceLob());
try { try {
if (max != 0 && max < Integer.MAX_VALUE) { if (maxLength != -1
BufferedInputStream b = new BufferedInputStream(in, max); && maxLength <= database.getMaxLengthInplaceLob()) {
b.mark(max); byte[] small = new byte[(int) maxLength];
byte[] small = new byte[max]; int len = IOUtils.readFully(in, small, (int) maxLength);
int len = IOUtils.readFully(b, small, max); if (len > maxLength) {
if (len < max) { throw new IllegalStateException(
"len > blobLength, " + len + " > " + maxLength);
}
if (len < small.length) { if (len < small.length) {
small = Arrays.copyOf(small, len); small = Arrays.copyOf(small, len);
} }
return ValueLobDb.createSmallLob(type, small); return ValueLobDb.createSmallLob(type, small);
} }
b.reset(); if (maxLength != -1) {
in = b;
}
if (maxLength != Long.MAX_VALUE) {
in = new LimitInputStream(in, maxLength); in = new LimitInputStream(in, maxLength);
} }
return createLob(in, type); return createLob(in, type);
...@@ -179,32 +171,34 @@ public class LobStorageMap implements LobStorageInterface { ...@@ -179,32 +171,34 @@ public class LobStorageMap implements LobStorageInterface {
public Value createClob(Reader reader, long maxLength) { public Value createClob(Reader reader, long maxLength) {
init(); init();
int type = Value.CLOB; int type = Value.CLOB;
if (maxLength < 0) {
maxLength = Long.MAX_VALUE;
}
int max = (int) Math.min(maxLength, database.getMaxLengthInplaceLob());
try { try {
if (max != 0 && max < Integer.MAX_VALUE) { // we multiple by 3 here to get the worst-case size in bytes
BufferedReader b = new BufferedReader(reader, max); if (maxLength != -1
b.mark(max); && maxLength * 3 <= database.getMaxLengthInplaceLob()) {
char[] small = new char[max]; char[] small = new char[(int) maxLength];
int len = IOUtils.readFully(b, small, max); int len = IOUtils.readFully(reader, small, (int) maxLength);
if (len < max) { if (len > maxLength) {
if (len < small.length) { throw new IllegalStateException(
small = Arrays.copyOf(small, len); "len > blobLength, " + len + " > " + maxLength);
}
byte[] utf8 = new String(small, 0, len)
.getBytes(Constants.UTF8);
if (utf8.length > database.getMaxLengthInplaceLob()) {
throw new IllegalStateException(
"len > maxinplace, " + utf8.length + " > "
+ database.getMaxLengthInplaceLob());
} }
byte[] utf8 = new String(small, 0, len).getBytes(Constants.UTF8);
return ValueLobDb.createSmallLob(type, utf8); return ValueLobDb.createSmallLob(type, utf8);
} }
b.reset(); if (maxLength < 0) {
reader = b; maxLength = Long.MAX_VALUE;
} }
CountingReaderInputStream in = CountingReaderInputStream in = new CountingReaderInputStream(reader,
new CountingReaderInputStream(reader, maxLength); maxLength);
ValueLobDb lob = createLob(in, type); ValueLobDb lob = createLob(in, type);
// the length is not correct // the length is not correct
lob = ValueLobDb.create(type, database, lob = ValueLobDb.create(type, database, lob.getTableId(),
lob.getTableId(), lob.getLobId(), null, in.getLength()); lob.getLobId(), null, in.getLength());
return lob; return lob;
} catch (IllegalStateException e) { } catch (IllegalStateException e) {
throw DbException.get(ErrorCode.OBJECT_CLOSED, e); throw DbException.get(ErrorCode.OBJECT_CLOSED, e);
......
...@@ -1046,10 +1046,10 @@ public class DataType { ...@@ -1046,10 +1046,10 @@ public class DataType {
createClob(r, -1); createClob(r, -1);
} else if (x instanceof java.sql.Clob) { } else if (x instanceof java.sql.Clob) {
try { try {
Reader r = new BufferedReader( java.sql.Clob clob = (java.sql.Clob) x;
((java.sql.Clob) x).getCharacterStream()); Reader r = new BufferedReader(clob.getCharacterStream());
return session.getDataHandler().getLobStorage(). return session.getDataHandler().getLobStorage().
createClob(r, -1); createClob(r, clob.length());
} catch (SQLException e) { } catch (SQLException e) {
throw DbException.convert(e); throw DbException.convert(e);
} }
...@@ -1058,8 +1058,9 @@ public class DataType { ...@@ -1058,8 +1058,9 @@ public class DataType {
createBlob((java.io.InputStream) x, -1); createBlob((java.io.InputStream) x, -1);
} else if (x instanceof java.sql.Blob) { } else if (x instanceof java.sql.Blob) {
try { try {
java.sql.Blob blob = (java.sql.Blob) x;
return session.getDataHandler().getLobStorage(). return session.getDataHandler().getLobStorage().
createBlob(((java.sql.Blob) x).getBinaryStream(), -1); createBlob(blob.getBinaryStream(), blob.length());
} catch (SQLException e) { } catch (SQLException e) {
throw DbException.convert(e); throw DbException.convert(e);
} }
......
...@@ -23,7 +23,6 @@ import java.sql.SQLException; ...@@ -23,7 +23,6 @@ import java.sql.SQLException;
import java.sql.Savepoint; import java.sql.Savepoint;
import java.sql.Statement; import java.sql.Statement;
import java.util.Random; import java.util.Random;
import org.h2.api.ErrorCode; import org.h2.api.ErrorCode;
import org.h2.engine.SysProperties; import org.h2.engine.SysProperties;
import org.h2.jdbc.JdbcConnection; import org.h2.jdbc.JdbcConnection;
...@@ -84,6 +83,7 @@ public class TestLob extends TestBase { ...@@ -84,6 +83,7 @@ public class TestLob extends TestBase {
testDelete(); testDelete();
testLobServerMemory(); testLobServerMemory();
testUpdatingLobRow(); testUpdatingLobRow();
testBufferedInputStreamBug();
if (config.memory) { if (config.memory) {
return; return;
} }
...@@ -1505,6 +1505,17 @@ public class TestLob extends TestBase { ...@@ -1505,6 +1505,17 @@ public class TestLob extends TestBase {
conn.close(); conn.close();
} }
/** test a bug where the usage of BufferedInputStream in LobStorageMap was causing a deadlock */
private void testBufferedInputStreamBug() throws SQLException {
deleteDb("lob");
JdbcConnection conn = (JdbcConnection) getConnection("lob");
conn.createStatement().execute("CREATE TABLE TEST(test BLOB)");
PreparedStatement ps = conn.prepareStatement("INSERT INTO TEST(test) VALUES(?)");
ps.setBlob(1, new ByteArrayInputStream(new byte[257]));
ps.executeUpdate();
conn.close();
}
private static Reader getRandomReader(int len, int seed) { private static Reader getRandomReader(int len, int seed) {
return new CharArrayReader(getRandomChars(len, seed)); return new CharArrayReader(getRandomChars(len, seed));
} }
......
Markdown 格式
0%
您添加了 0 到此讨论。请谨慎行事。
请先完成此评论的编辑!
注册 或者 后发表评论