Skip to content

Commit cf0a085

Browse files
committed
Add nullability to FBCachedBlob
1 parent b31a496 commit cf0a085

3 files changed

Lines changed: 23 additions & 61 deletions

File tree

src/docs/asciidoc/release_notes.adoc

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -868,6 +868,8 @@ use constructor `RowDescriptor(List<FieldDescriptor>, DatatypeCoder)`
868868
* `RowDescriptorBuilder`
869869
** `toRowDescriptor()` can no longer build an incomplete row descriptor, and throws an `IllegalStateException` if one or more fields have not been defined.
870870
There is no replacement to build incomplete row descriptors.
871+
* `FBCachedBlob`
872+
** Constructor `FBCachedBlob(byte[])` no longer accepts `null`.
871873
872874
[#breaking-changes-unlikely]
873875
=== Unlikely breaking changes

src/main/org/firebirdsql/jdbc/FBCachedBlob.java

Lines changed: 17 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,22 @@
11
/*
22
SPDX-FileCopyrightText: Copyright 2002-2003 Blas Rodriguez Somoza
33
SPDX-FileCopyrightText: Copyright 2003-2007 Roman Rokytskyy
4-
SPDX-FileCopyrightText: Copyright 2014-2024 Mark Rotteveel
4+
SPDX-FileCopyrightText: Copyright 2014-2026 Mark Rotteveel
55
SPDX-License-Identifier: LGPL-2.1-or-later
66
*/
77
package org.firebirdsql.jdbc;
88

99
import org.firebirdsql.gds.JaybirdErrorCodes;
1010
import org.firebirdsql.gds.ng.FbExceptionBuilder;
1111
import org.firebirdsql.util.InternalApi;
12+
import org.jspecify.annotations.NullMarked;
1213

1314
import java.sql.SQLException;
1415
import java.sql.Blob;
1516
import java.io.*;
1617

18+
import static java.util.Objects.requireNonNull;
19+
1720
/**
1821
* This class represents a cached blob field.
1922
* <p>
@@ -22,6 +25,7 @@
2225
* </p>
2326
*/
2427
@InternalApi
28+
@NullMarked
2529
public final class FBCachedBlob implements FirebirdBlob {
2630

2731
// NOTE: Do not assign ByteArrayHelper.empty(), as this must be a unique instance
@@ -35,15 +39,15 @@ public final class FBCachedBlob implements FirebirdBlob {
3539
* Create an instance using the cached data.
3640
*
3741
* @param data
38-
* array of bytes containing the cached data.
42+
* array of bytes containing the cached data
3943
*/
4044
public FBCachedBlob(byte[] data) {
41-
blobData = data;
45+
blobData = requireNonNull(data, "data");
4246
}
4347

4448
@Override
4549
public FirebirdBlob detach() throws SQLException {
46-
checkClosed();
50+
checkOpen();
4751
return new FBCachedBlob(blobData);
4852
}
4953

@@ -55,23 +59,22 @@ public FirebirdBlob detach() throws SQLException {
5559
*/
5660
@Override
5761
public boolean isSegmented() throws SQLException {
58-
checkClosed();
62+
checkOpen();
5963
return false;
6064
}
6165

6266
/**
6367
* Get the length of the cached blob field.
6468
*
65-
* @return length of the cached blob field or -1 if the field is null.
69+
* @return length of the cached blob field
6670
*/
6771
@Override
6872
public long length() throws SQLException {
69-
checkClosed();
70-
return blobData != null ? blobData.length : -1;
73+
checkOpen();
74+
return blobData.length;
7175
}
7276

7377
@Override
74-
@SuppressWarnings("java:S1168")
7578
public byte[] getBytes(long pos, int length) throws SQLException {
7679
if (pos < 1) {
7780
throw new SQLException("Expected value of pos > 0, got " + pos,
@@ -81,8 +84,7 @@ public byte[] getBytes(long pos, int length) throws SQLException {
8184
throw new SQLException("Expected value of length >= 0, got " + length,
8285
SQLStateConstants.SQL_STATE_INVALID_STRING_LENGTH);
8386
}
84-
checkClosed();
85-
if (blobData == null) return null;
87+
checkOpen();
8688

8789
// TODO What if pos or length are beyond blobData
8890
byte[] result = new byte[length];
@@ -92,8 +94,8 @@ public byte[] getBytes(long pos, int length) throws SQLException {
9294

9395
@Override
9496
public byte[] getBytes() throws SQLException {
95-
checkClosed();
96-
return blobData != null ? blobData.clone() : null;
97+
checkOpen();
98+
return blobData.clone();
9799
}
98100

99101
/**
@@ -120,9 +122,7 @@ public long position(Blob pattern, long start) throws SQLException {
120122

121123
@Override
122124
public InputStream getBinaryStream() throws SQLException {
123-
checkClosed();
124-
if (blobData == null) return null;
125-
125+
checkOpen();
126126
return new ByteArrayInputStream(blobData);
127127
}
128128

@@ -180,7 +180,7 @@ public void free() throws SQLException {
180180
blobData = FREED_MARKER;
181181
}
182182

183-
private void checkClosed() throws SQLException {
183+
private void checkOpen() throws SQLException {
184184
if (blobData == FREED_MARKER) {
185185
throw FbExceptionBuilder.toException(JaybirdErrorCodes.jb_blobClosed);
186186
}

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

Lines changed: 4 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
// SPDX-FileCopyrightText: Copyright 2014-2023 Mark Rotteveel
1+
// SPDX-FileCopyrightText: Copyright 2014-2026 Mark Rotteveel
22
// SPDX-License-Identifier: LGPL-2.1-or-later
33
package org.firebirdsql.jdbc;
44

@@ -45,14 +45,10 @@ void testIsSegmented() throws Exception {
4545
assertFalse(blob.isSegmented(), "FBCachedBlob.isSegmented() should return false");
4646
}
4747

48-
/**
49-
* Test if {@link FBCachedBlob#length()} returns {@code -1} if data is {@code null}.
50-
*/
5148
@Test
52-
void testLength_null() throws Exception {
53-
FBCachedBlob blob = new FBCachedBlob(null);
54-
55-
assertEquals(-1, blob.length(), "Unexpected length for null data");
49+
@SuppressWarnings("DataFlowIssue")
50+
void createBlobWithNull_notAllowed() {
51+
assertThrows(NullPointerException.class, () -> new FBCachedBlob(null));
5652
}
5753

5854
/**
@@ -120,19 +116,6 @@ void testGetBytes_length0() throws Exception {
120116
assertEquals(0, data.length, "Expected empty (zero-length) array");
121117
}
122118

123-
/**
124-
* Test if {@link FBCachedBlob#getBytes(long, int)} returns {@code null} if data is null.
125-
* <p>
126-
* TODO: Not certain if this behavior is allowed!
127-
* </p>
128-
*/
129-
@Test
130-
void testGetBytes_long_int_null() throws Exception {
131-
FBCachedBlob blob = new FBCachedBlob(null);
132-
133-
assertNull(blob.getBytes(1, 1));
134-
}
135-
136119
/**
137120
* Test {@link FBCachedBlob#getBytes(long, int)}.
138121
*/
@@ -157,19 +140,6 @@ void testGetBytes() throws Exception {
157140
assertNotSame(input, data, "Expected copy of input, not same object");
158141
}
159142

160-
/**
161-
* Test if {@link FBCachedBlob#getBytes()} returns {@code null} if data is null.
162-
* <p>
163-
* TODO: Not certain if this behavior is allowed!
164-
* </p>
165-
*/
166-
@Test
167-
void testGetBytes_null() throws Exception {
168-
var blob = new FBCachedBlob(null);
169-
170-
assertNull(blob.getBytes());
171-
}
172-
173143
/**
174144
* Test if {@link FBCachedBlob#position(byte[], long)} throws SQLFeatureNotSupportedException.
175145
*/
@@ -190,16 +160,6 @@ void testPosition_Blob_long() {
190160
assertThrows(SQLFeatureNotSupportedException.class, () -> blob.position(blob, 1));
191161
}
192162

193-
/**
194-
* Test if {@link FBCachedBlob#getBinaryStream()} returns {@code null} when data is {@code null}.
195-
*/
196-
@Test
197-
void testGetBinaryStream_null() throws Exception {
198-
FBCachedBlob blob = new FBCachedBlob(null);
199-
200-
assertNull(blob.getBinaryStream());
201-
}
202-
203163
/**
204164
* Test if {@link FBCachedBlob#getBinaryStream()} returns stream with all data.
205165
*/

0 commit comments

Comments
 (0)