Skip to content

Commit a8221ae

Browse files
committed
#895 Fix negative buffer size on Firebird 2.5 if information response is 32KiB or greater
1 parent e8690a7 commit a8221ae

File tree

3 files changed

+72
-1
lines changed

3 files changed

+72
-1
lines changed

src/docs/asciidoc/release_notes.adoc

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,18 @@ Changes per Jaybird 6 release.
3333
See also <<whats-new-in-jaybird-6>>.
3434
For known issues, consult <<known-issues>>.
3535

36+
=== Jaybird 6.0.4
37+
38+
The following was fixed or changed since Jaybird 6.0.3:
39+
40+
* Fixed: Negative buffer size on Firebird 2.5 if information response is 32KiB or greater (https://github.com/FirebirdSQL/jaybird/issues/895[#895])
41+
+
42+
The error occurs when preparing or executing statements with a lot of columns, especially select statements with columns that have long names and/or aliases for columns, tables and table owners.
43+
Symptoms include unexpected closed connections, and errors like "`__Unsupported or unexpected operation code <number> in processOperation__`" (with varying numbers) and "`__java.lang.NegativeArraySizeException: -1__`" (and other negative numbers).
44+
+
45+
This fix was frontported from Jaybird 5.0.10.
46+
Only Firebird 2.5 (not supported) is affected.
47+
3648
=== Jaybird 6.0.3
3749

3850
The following was fixed or changed since Jaybird 6.0.2:

src/main/org/firebirdsql/gds/impl/wire/XdrInputStream.java

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -100,7 +100,13 @@ public void skipPadding(int len) throws IOException {
100100
* if an error occurs while reading from the underlying input stream
101101
*/
102102
public byte[] readBuffer() throws IOException {
103-
return readBuffer(readInt());
103+
return readBuffer(fixupLength(readInt()));
104+
}
105+
106+
private static int fixupLength(int len) {
107+
// Older Firebird versions may return a 32-bit value that is a sign-extended 16-bit value.
108+
// Firebird does something similar in remote/protocol.cpp (xdr_cstring_with_limit).
109+
return len >>> 16 == 0xFFFF ? len & 0xFFFF : len;
104110
}
105111

106112
/**

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

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919
package org.firebirdsql.jdbc;
2020

2121
import org.firebirdsql.common.DataGenerator;
22+
import org.firebirdsql.common.extension.DatabaseUserExtension;
2223
import org.firebirdsql.common.extension.UsesDatabaseExtension;
2324
import org.firebirdsql.gds.ISCConstants;
2425
import org.firebirdsql.jaybird.props.PropertyNames;
@@ -49,6 +50,7 @@
4950
import java.util.concurrent.ScheduledExecutorService;
5051
import java.util.concurrent.TimeUnit;
5152
import java.util.concurrent.atomic.AtomicBoolean;
53+
import java.util.stream.IntStream;
5254
import java.util.stream.Stream;
5355

5456
import static org.firebirdsql.common.DdlHelper.executeCreateTable;
@@ -72,6 +74,8 @@ class FBPreparedStatementTest {
7274

7375
@RegisterExtension
7476
static final UsesDatabaseExtension.UsesDatabaseForAll usesDatabase = UsesDatabaseExtension.usesDatabaseForAll();
77+
@RegisterExtension
78+
final DatabaseUserExtension databaseUsers = DatabaseUserExtension.withDatabaseUser();
7579

7680
private static final String DROP_GENERATOR = "DROP GENERATOR test_generator";
7781
private static final String CREATE_GENERATOR = "CREATE GENERATOR test_generator";
@@ -1531,6 +1535,55 @@ void setObject_InputStream_scaleOrLength() throws Exception {
15311535
}
15321536
}
15331537

1538+
/**
1539+
* Prepare a statement where the info size exceeds 32KiB.
1540+
* <p>
1541+
* Rationale: <a href="https://github.com/FirebirdSQL/jaybird/issues/894">#894</a>
1542+
* </p>
1543+
*/
1544+
@Test
1545+
void prepareStatementWithInfoExceeding32K() throws Exception {
1546+
final String owner = "U234567890123456789012345678901";
1547+
databaseUsers.createUser(owner, owner,
1548+
getDefaultSupportInfo().supportsAuthenticationPlugin("Srp") ? "Srp" : null);
1549+
if (getDefaultSupportInfo().isVersionEqualOrAbove(3, 0)) {
1550+
try (var stmt = con.createStatement()) {
1551+
stmt.execute("grant create table to user " + owner);
1552+
}
1553+
}
1554+
// On Firebird 3+, this results in an info response of ~256KiB;
1555+
// on Firebird 2.5 it requires multiple round trips
1556+
final int columnCount = 1500;
1557+
try (var connection = getConnectionViaDriverManager(Map.of("user", owner, "password", owner))) {
1558+
final String tableName = "T234567890123456789012345678901";
1559+
try (var stmt = connection.createStatement()) {
1560+
var sb = new StringBuilder(50 + columnCount * 41);
1561+
sb.append("create table ").append(tableName).append('(');
1562+
IntStream.rangeClosed(1, columnCount).forEach(idx ->
1563+
sb.append(String.format("C%030d smallint,", idx)));
1564+
// remove last comma
1565+
sb.setLength(sb.length() - 1);
1566+
sb.append(')');
1567+
stmt.execute(sb.toString());
1568+
}
1569+
1570+
try (var pstmt = connection.prepareStatement("select * from " + tableName)) {
1571+
ResultSetMetaData rsmd = pstmt.getMetaData();
1572+
assertEquals(columnCount, rsmd.getColumnCount(), "columnCount");
1573+
IntStream.rangeClosed(1, columnCount).forEach(idx -> {
1574+
try {
1575+
assertEquals(tableName, rsmd.getTableName(idx), "tableName");
1576+
final String expectedName = String.format("C%030d", idx);
1577+
assertEquals(expectedName, rsmd.getColumnName(idx), "columnName");
1578+
assertEquals(expectedName, rsmd.getColumnLabel(idx), "columnLabel");
1579+
} catch (SQLException e) {
1580+
fail(e);
1581+
}
1582+
});
1583+
}
1584+
}
1585+
}
1586+
15341587
private void prepareTestData() throws SQLException {
15351588
con.setAutoCommit(false);
15361589
try (var pstmt = con.prepareStatement(INSERT_DATA)) {

0 commit comments

Comments
 (0)