Skip to content

Commit c4f6ab2

Browse files
committed
#888 Fix batch error "Repeated blob id 0:0 in registerBlob()"
1 parent 3677b8b commit c4f6ab2

2 files changed

Lines changed: 46 additions & 0 deletions

File tree

src/main/org/firebirdsql/gds/ng/wire/version16/V16Statement.java

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,8 +25,10 @@
2525

2626
import java.io.ByteArrayOutputStream;
2727
import java.io.IOException;
28+
import java.nio.ByteBuffer;
2829
import java.sql.SQLException;
2930
import java.util.Collection;
31+
import java.util.HashSet;
3032
import java.util.List;
3133

3234
import static org.firebirdsql.gds.impl.wire.WireProtocolConstants.op_batch_cancel;
@@ -154,10 +156,14 @@ private void registerBlobs(XdrOutputStream xdrOut, RowDescriptor parameterDescri
154156
List<Integer> blobPositions = blobPositions(parameterDescriptor);
155157
if (blobPositions.isEmpty()) return;
156158
FbWireDatabase db = getDatabase();
159+
var blobsRegistered = new HashSet<>((int) (rowValues.size() * blobPositions.size() / 0.75f + 1));
157160
for (RowValue rowValue : rowValues) {
158161
for (int position : blobPositions) {
159162
byte[] fieldData = rowValue.getFieldData(position);
160163
if (fieldData == null) continue;
164+
// Do not register a blob multiple times
165+
if (!blobsRegistered.add(toLong(fieldData))) continue;
166+
161167
xdrOut.writeInt(WireProtocolConstants.op_batch_regblob);
162168
xdrOut.writeInt(getHandle()); // p_batch_statement
163169
// register as itself
@@ -169,6 +175,12 @@ private void registerBlobs(XdrOutputStream xdrOut, RowDescriptor parameterDescri
169175
xdrOut.flush();
170176
}
171177

178+
// This is not necessarily the correct endianness, we just want the id bytes expressed as a long.
179+
private static long toLong(byte[] bytes) {
180+
assert bytes.length == 8 : "expected 8 bytes";
181+
return ByteBuffer.wrap(bytes).getLong();
182+
}
183+
172184
private List<Integer> blobPositions(RowDescriptor parameterDescriptor) {
173185
return parameterDescriptor.getFieldDescriptors().stream()
174186
.filter(f -> f.isFbType(ISCConstants.SQL_BLOB))

src/test/org/firebirdsql/jdbc/BatchUpdatesTest.java

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919
import java.time.LocalDateTime;
2020
import java.time.LocalTime;
2121
import java.util.Arrays;
22+
import java.util.List;
2223
import java.util.Properties;
2324
import java.util.stream.IntStream;
2425

@@ -27,6 +28,9 @@
2728
import static org.firebirdsql.common.FBTestProperties.getDefaultPropertiesForConnection;
2829
import static org.firebirdsql.common.FBTestProperties.getUrl;
2930
import static org.firebirdsql.common.FbAssumptions.assumeServerBatchSupport;
31+
import static org.firebirdsql.common.assertions.ResultSetAssertions.assertNextRow;
32+
import static org.firebirdsql.common.assertions.ResultSetAssertions.assertNoNextRow;
33+
import static org.firebirdsql.common.assertions.ResultSetAssertions.assertRowEquals;
3034
import static org.firebirdsql.common.matchers.SQLExceptionMatchers.message;
3135
import static org.hamcrest.MatcherAssert.assertThat;
3236
import static org.hamcrest.Matchers.containsString;
@@ -384,6 +388,36 @@ void testPreparedStatementBatch_65Blobs() throws SQLException {
384388
}
385389
}
386390

391+
/**
392+
* Rationale: see <a href="https://github.com/FirebirdSQL/jaybird/issues/888">#888</a>.
393+
*/
394+
@ParameterizedTest(name = "[{index}] useServerBatch = {0}")
395+
@ValueSource(booleans = { true, false })
396+
void testBatchMultipleEmptyStringsInBlob(boolean useServerBatch) throws Exception {
397+
try (var connection = createConnection(useServerBatch);
398+
var stmt = connection.createStatement()) {
399+
stmt.execute(RECREATE_BATCH_UPDATES_TABLE);
400+
connection.setAutoCommit(false);
401+
try (var ps = connection.prepareStatement("INSERT INTO batch_updates(id, clob_value) VALUES (?, ?)")) {
402+
ps.setInt(1, 1);
403+
ps.setString(2, "");
404+
ps.addBatch();
405+
ps.setInt(1, 2);
406+
ps.setString(2, "");
407+
ps.addBatch();
408+
assertDoesNotThrow(ps::executeBatch);
409+
}
410+
411+
try (var rs = stmt.executeQuery("select ID, CLOB_VALUE from BATCH_UPDATES order by ID")) {
412+
assertNextRow(rs);
413+
assertRowEquals(rs, List.of(1, ""));
414+
assertNextRow(rs);
415+
assertRowEquals(rs, List.of(2, ""));
416+
assertNoNextRow(rs);
417+
}
418+
}
419+
}
420+
387421
private static Connection createConnection(boolean useServerBatch) throws SQLException {
388422
Properties props = getDefaultPropertiesForConnection();
389423
props.setProperty(PropertyNames.useServerBatch, String.valueOf(useServerBatch));

0 commit comments

Comments
 (0)