提交 ad08cb5e authored 作者: Noel Grandin's avatar Noel Grandin 提交者: avp1983

Issue #324: Deadlock when sending BLOBs over TCP

上级 add3db91
......@@ -21,6 +21,8 @@ Change Log
<h2>Next Version (unreleased)</h2>
<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>
<li>Issue #266: Spatial index not updating, fixed by merging PR #267
......
......@@ -1602,10 +1602,11 @@ public class Function extends Expression implements FunctionCall {
String fileName = v0.getString();
boolean blob = args.length == 1;
try {
long fileLength = FileUtils.size(fileName);
InputStream in = new AutoCloseInputStream(
FileUtils.newInputStream(fileName));
if (blob) {
result = database.getLobStorage().createBlob(in, -1);
result = database.getLobStorage().createBlob(in, fileLength);
} else {
Reader reader;
if (v1 == ValueNull.INSTANCE) {
......@@ -1613,7 +1614,7 @@ public class Function extends Expression implements FunctionCall {
} else {
reader = new InputStreamReader(in, v1.getString());
}
result = database.getLobStorage().createClob(reader, -1);
result = database.getLobStorage().createClob(reader, fileLength);
}
session.addTemporaryLob(result);
} catch (IOException e) {
......
......@@ -15,7 +15,7 @@
23506=Нарушение ссылочной целостности: {0}
23507=Для поля {0} не установлено значение по умолчанию
23513=Нарушение ограничения: {0}
23514=#Check constraint invalid: {0}
23514=Неправильное ограничение CHECK: {0}
28000=Неверное имя пользователя или пароль
40001=Обнаружена взаимная блокировка потоков. Текущая транзакция была откачена. Детали: {0}
42000=Синтаксическая ошибка в выражении SQL {0}
......@@ -33,12 +33,12 @@
90002=Метод разрешен только для запросов. Используйте execute или executeUpdate вместо executeQuery
90003=Шестнадцатиричная строка содержит нечетное количество символов: {0}
90004=Шестнадцатиричная строка содержит нешестнадцатиричные символы: {0}
90006=#Sequence {0} has run out of numbers
90006=Последовательность {0} вышла за границы (MINVALUE, MAXVALUE)
90007=Объект уже закрыт
90008=Недопустимое значение {0} для параметра {1}
90009=#Unable to create or alter sequence {0} because of invalid attributes (start value {1}, min value {2}, max value {3}, increment {4})
90010=#Invalid TO_CHAR format {0}
90011=#A file path that is implicitly relative to the current working directory is not allowed in the database URL {0}. Use an absolute path, ~/name, ./name, or the baseDir setting instead.
90009=Невозможно создать или изменить последовательность {0} из-за неправильных атрибутов (START/RESTART {1}, MINVALUE {2}, MAXVALUE {3}, INCREMENT {4})
90010=Неправильный формат TO_CHAR {0}
90011=Путь к файлу, который неявен относительно текущего рабочего каталога не допускается в URL базы данных {0}. Используйте абсолютный путь, ~/name, ./name, или настройку baseDir.
90012=Параметр {0} не установлен
90013=База данных {0} не найдена
90014=Ошибка при разборе {0}
......@@ -48,7 +48,7 @@
90018=Незакрытое приложением соединение уничтожено сборщиком мусора
90019=Невозможно удалить текущего пользователя
90020=База данных уже используется: {0}. Возможные решения: закрыть все другие соединения; использовать режим сервера
90021=#This combination of database settings is not supported: {0}
90021=Такое сочетание настроек базы данных не поддерживается: {0}
90022=Функция {0} не найдена
90023=Поле {0} не должно поддерживать значение NULL
90024=Ошибка при переименовании файла {0} в {1}
......@@ -66,7 +66,7 @@
90036=Последовательность {0} не найдена
90037=Представление {0} не найдено
90038=Представление {0} уже существует
90039=#This CLOB or BLOB reference timed out: {0}
90039=Этот CLOB или BLOB объект закрыт по таймауту: {0}
90040=Для выполнения данной операции необходимы права администратора
90041=Триггер {0} уже существует
90042=Триггер {0} не найден
......@@ -78,12 +78,12 @@
90048=Неподдерживаемая версия файлов базы данных или некорректный заголовок в файле {0}
90049=Ошибка шифрования в файле {0}
90050=Некорректный формат пароля, должен быть: пароль файла <пробел> пароль пользователя
90051=#Scale(${0}) must not be bigger than precision({1})
90051=Количество цифр после разделителя (scale) (${0}) не должно быть больше общего количества цифр (precision) ({1})
90052=Подзапрос выбирает более одного столбца
90053=Подзапрос выбирает более одной строки
90054=Некорректное использование агрегирующей функции {0}
90055=Метод шифрования {0} не поддерживается
90056=#Function {0}: Invalid date format: {1}
90056=Функция {0}: Неверный формат даты: {1}
90057=Ограничение {0} не найдено
90058=Commit или rollback внутри триггера не допускается
90059=Неоднозначное имя столбца {0}
......@@ -165,8 +165,8 @@
90138=Недопустимое имя базы данных: {0}
90139=public static Java метод не найден: {0}
90140=Набор записей не является обновляемым. Возможно необходимо использовать conn.createStatement(.., ResultSet.CONCUR_UPDATABLE).
90141=#Serializer cannot be changed because there is a data table: {0}
90142=#Step size must not be zero
90141=Serializer не может быть изменен, потому что есть таблица данных: {0}
90142=Размер шага не должен быть равен нулю
HY000=Внутренняя ошибка: {0}
HY004=Неизвестный тип данных: {0}
HYC00=Данная функция не поддерживается: {0}
......
......@@ -12,11 +12,10 @@ import java.nio.ByteBuffer;
import java.nio.CharBuffer;
import java.nio.charset.CharsetEncoder;
import java.nio.charset.CodingErrorAction;
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 {
......
......@@ -5,15 +5,12 @@
*/
package org.h2.store;
import java.io.BufferedInputStream;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.Reader;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Map.Entry;
import org.h2.api.ErrorCode;
import org.h2.engine.Constants;
import org.h2.engine.Database;
......@@ -145,26 +142,21 @@ public class LobStorageMap implements LobStorageInterface {
public Value createBlob(InputStream in, long maxLength) {
init();
int type = Value.BLOB;
if (maxLength < 0) {
maxLength = Long.MAX_VALUE;
}
int max = (int) Math.min(maxLength, database.getMaxLengthInplaceLob());
try {
if (max != 0 && max < Integer.MAX_VALUE) {
BufferedInputStream b = new BufferedInputStream(in, max);
b.mark(max);
byte[] small = new byte[max];
int len = IOUtils.readFully(b, small, max);
if (len < max) {
if (maxLength != -1
&& maxLength <= database.getMaxLengthInplaceLob()) {
byte[] small = new byte[(int) maxLength];
int len = IOUtils.readFully(in, small, (int) maxLength);
if (len > maxLength) {
throw new IllegalStateException(
"len > blobLength, " + len + " > " + maxLength);
}
if (len < small.length) {
small = Arrays.copyOf(small, len);
}
return ValueLobDb.createSmallLob(type, small);
}
b.reset();
in = b;
}
if (maxLength != Long.MAX_VALUE) {
if (maxLength != -1) {
in = new LimitInputStream(in, maxLength);
}
return createLob(in, type);
......@@ -179,32 +171,34 @@ public class LobStorageMap implements LobStorageInterface {
public Value createClob(Reader reader, long maxLength) {
init();
int type = Value.CLOB;
if (maxLength < 0) {
maxLength = Long.MAX_VALUE;
}
int max = (int) Math.min(maxLength, database.getMaxLengthInplaceLob());
try {
if (max != 0 && max < Integer.MAX_VALUE) {
BufferedReader b = new BufferedReader(reader, max);
b.mark(max);
char[] small = new char[max];
int len = IOUtils.readFully(b, small, max);
if (len < max) {
if (len < small.length) {
small = Arrays.copyOf(small, len);
// we multiple by 3 here to get the worst-case size in bytes
if (maxLength != -1
&& maxLength * 3 <= database.getMaxLengthInplaceLob()) {
char[] small = new char[(int) maxLength];
int len = IOUtils.readFully(reader, small, (int) maxLength);
if (len > maxLength) {
throw new IllegalStateException(
"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);
}
b.reset();
reader = b;
if (maxLength < 0) {
maxLength = Long.MAX_VALUE;
}
CountingReaderInputStream in =
new CountingReaderInputStream(reader, maxLength);
CountingReaderInputStream in = new CountingReaderInputStream(reader,
maxLength);
ValueLobDb lob = createLob(in, type);
// the length is not correct
lob = ValueLobDb.create(type, database,
lob.getTableId(), lob.getLobId(), null, in.getLength());
lob = ValueLobDb.create(type, database, lob.getTableId(),
lob.getLobId(), null, in.getLength());
return lob;
} catch (IllegalStateException e) {
throw DbException.get(ErrorCode.OBJECT_CLOSED, e);
......
......@@ -1046,10 +1046,10 @@ public class DataType {
createClob(r, -1);
} else if (x instanceof java.sql.Clob) {
try {
Reader r = new BufferedReader(
((java.sql.Clob) x).getCharacterStream());
java.sql.Clob clob = (java.sql.Clob) x;
Reader r = new BufferedReader(clob.getCharacterStream());
return session.getDataHandler().getLobStorage().
createClob(r, -1);
createClob(r, clob.length());
} catch (SQLException e) {
throw DbException.convert(e);
}
......@@ -1058,8 +1058,9 @@ public class DataType {
createBlob((java.io.InputStream) x, -1);
} else if (x instanceof java.sql.Blob) {
try {
java.sql.Blob blob = (java.sql.Blob) x;
return session.getDataHandler().getLobStorage().
createBlob(((java.sql.Blob) x).getBinaryStream(), -1);
createBlob(blob.getBinaryStream(), blob.length());
} catch (SQLException e) {
throw DbException.convert(e);
}
......
......@@ -23,7 +23,6 @@ import java.sql.SQLException;
import java.sql.Savepoint;
import java.sql.Statement;
import java.util.Random;
import org.h2.api.ErrorCode;
import org.h2.engine.SysProperties;
import org.h2.jdbc.JdbcConnection;
......@@ -84,6 +83,7 @@ public class TestLob extends TestBase {
testDelete();
testLobServerMemory();
testUpdatingLobRow();
testBufferedInputStreamBug();
if (config.memory) {
return;
}
......@@ -1505,6 +1505,17 @@ public class TestLob extends TestBase {
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) {
return new CharArrayReader(getRandomChars(len, seed));
}
......
Markdown 格式
0%
您添加了 0 到此讨论。请谨慎行事。
请先完成此评论的编辑!
注册 或者 后发表评论