Skip to content

Commit bf0265f

Browse files
committed
Complete nullability info in org.firebirdsql.jdbc.field
1 parent 6f34fc5 commit bf0265f

9 files changed

Lines changed: 53 additions & 37 deletions

File tree

src/main/org/firebirdsql/encodings/DefaultEncodingSet.java

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010

1111
import static org.firebirdsql.gds.ISCConstants.CS_BINARY;
1212
import static org.firebirdsql.gds.ISCConstants.CS_NONE;
13+
import static org.firebirdsql.gds.ISCConstants.CS_UTF8;
1314

1415
/**
1516
* The default encoding set for Jaybird.
@@ -44,7 +45,7 @@ private static List<EncodingDefinition> createEncodingDefinitions() {
4445
new DefaultEncodingDefinition("OCTETS", NO_CHARSET, 1, CS_BINARY, false),
4546
new DefaultEncodingDefinition("ASCII", StandardCharsets.US_ASCII, 1, 2, false),
4647
new DefaultEncodingDefinition("UNICODE_FSS", StandardCharsets.UTF_8, 3, 3, true),
47-
new DefaultEncodingDefinition("UTF8", StandardCharsets.UTF_8, 4, 4, false),
48+
new DefaultEncodingDefinition("UTF8", StandardCharsets.UTF_8, 4, CS_UTF8, false),
4849
new DefaultEncodingDefinition("SJIS_0208", "MS932", 2, 5, false),
4950
new DefaultEncodingDefinition("EUCJ_0208", "EUC_JP", 2, 6, false),
5051
new DefaultEncodingDefinition("DOS737", "Cp737", 1, 9, false),

src/main/org/firebirdsql/gds/ISCConstants.java

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2406,7 +2406,8 @@ public interface ISCConstants {
24062406

24072407
// Other stuff
24082408
int CS_NONE = 0; /* No Character Set */
2409-
int CS_BINARY = 1; /* BINARY BYTES */
2409+
int CS_BINARY = 1;
2410+
int CS_UTF8 = 4;
24102411
int CS_dynamic = 127; // Pseudo number for runtime charset (see intl\charsets.h and references to it in Firebird)
24112412

24122413
int BLOB_SUB_TYPE_BINARY = StandardBlobTypes.isc_blob_untyped;

src/main/org/firebirdsql/jdbc/field/FBBigDecimalField.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -290,6 +290,7 @@ private enum FieldDataSize {
290290
protected @Nullable BigDecimal decode(FieldDescriptor fieldDescriptor, byte @Nullable [] fieldData) {
291291
if (fieldData == null) return null;
292292
BigInteger int128Value = fieldDescriptor.getDatatypeCoder().decodeInt128(fieldData);
293+
//noinspection DataFlowIssue : already covered by fieldData == null
293294
return new BigDecimal(int128Value, -1 * fieldDescriptor.getScale());
294295
}
295296

src/main/org/firebirdsql/jdbc/field/FBDecfloatField.java

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

@@ -13,6 +13,7 @@
1313
import org.firebirdsql.jdbc.FBDriverNotCapableException;
1414
import org.jspecify.annotations.NonNull;
1515
import org.jspecify.annotations.NullMarked;
16+
import org.jspecify.annotations.Nullable;
1617

1718
import java.math.BigDecimal;
1819
import java.math.BigInteger;
@@ -32,7 +33,7 @@ final class FBDecfloatField<T extends Decimal<T>> extends FBField {
3233
private static final BigDecimal BD_MIN_LONG = BigDecimal.valueOf(Long.MIN_VALUE);
3334

3435
private final @NonNull Class<T> decimalType;
35-
private final @NonNull DecimalHandling<T> decimalHandling;
36+
private final @NonNull DecimalHandling<@NonNull T> decimalHandling;
3637

3738
@NullMarked
3839
FBDecfloatField(FieldDescriptor fieldDescriptor, FieldDataProvider dataProvider, int requiredType,
@@ -210,23 +211,24 @@ public void setString(String value) throws SQLException {
210211

211212
@SuppressWarnings("unchecked")
212213
@NullMarked
213-
private DecimalHandling<T> getDecimalHandling(FieldDescriptor fieldDescriptor, Class<T> decimalType)
214+
private DecimalHandling<@NonNull T> getDecimalHandling(FieldDescriptor fieldDescriptor, Class<T> decimalType)
214215
throws FBDriverNotCapableException {
215216
if (decimalType == Decimal64.class && fieldDescriptor.isFbType(ISCConstants.SQL_DEC16)) {
216-
return (DecimalHandling<T>) Decimal64Handling.INSTANCE;
217+
return (DecimalHandling<@NonNull T>) Decimal64Handling.INSTANCE;
217218
} else if (decimalType == Decimal128.class && fieldDescriptor.isFbType(ISCConstants.SQL_DEC34)) {
218-
return (DecimalHandling<T>) Decimal128Handling.INSTANCE;
219+
return (DecimalHandling<@NonNull T>) Decimal128Handling.INSTANCE;
219220
} else {
220221
throw new FBDriverNotCapableException("Unsupported type " + decimalType.getName() + " and/or field type " +
221222
fieldDescriptor.getType());
222223
}
223224
}
224225

226+
@NullMarked
225227
private interface DecimalHandling<T extends Decimal<T>> {
226228

227-
byte[] encode(FieldDescriptor fieldDescriptor, T value);
229+
byte @Nullable [] encode(FieldDescriptor fieldDescriptor, @Nullable T value);
228230

229-
T decode(FieldDescriptor fieldDescriptor, byte[] fieldData);
231+
@Nullable T decode(FieldDescriptor fieldDescriptor, byte @Nullable [] fieldData);
230232

231233
T valueOf(double value);
232234

@@ -236,17 +238,18 @@ private interface DecimalHandling<T extends Decimal<T>> {
236238

237239
}
238240

241+
@NullMarked
239242
private static final class Decimal64Handling implements DecimalHandling<Decimal64> {
240243

241244
private static final Decimal64Handling INSTANCE = new Decimal64Handling();
242245

243246
@Override
244-
public byte[] encode(FieldDescriptor fieldDescriptor, Decimal64 value) {
247+
public byte @Nullable[] encode(FieldDescriptor fieldDescriptor, @Nullable Decimal64 value) {
245248
return fieldDescriptor.getDatatypeCoder().encodeDecimal64(value);
246249
}
247250

248251
@Override
249-
public Decimal64 decode(FieldDescriptor fieldDescriptor, byte[] fieldData) {
252+
public @Nullable Decimal64 decode(FieldDescriptor fieldDescriptor, byte @Nullable [] fieldData) {
250253
return fieldDescriptor.getDatatypeCoder().decodeDecimal64(fieldData);
251254
}
252255

@@ -267,17 +270,18 @@ public Decimal64 valueOf(BigDecimal value) {
267270

268271
}
269272

273+
@NullMarked
270274
private static final class Decimal128Handling implements DecimalHandling<Decimal128> {
271275

272276
private static final Decimal128Handling INSTANCE = new Decimal128Handling();
273277

274278
@Override
275-
public byte[] encode(FieldDescriptor fieldDescriptor, Decimal128 value) {
279+
public byte @Nullable [] encode(FieldDescriptor fieldDescriptor, @Nullable Decimal128 value) {
276280
return fieldDescriptor.getDatatypeCoder().encodeDecimal128(value);
277281
}
278282

279283
@Override
280-
public Decimal128 decode(FieldDescriptor fieldDescriptor, byte[] fieldData) {
284+
public @Nullable Decimal128 decode(FieldDescriptor fieldDescriptor, byte @Nullable [] fieldData) {
281285
return fieldDescriptor.getDatatypeCoder().decodeDecimal128(fieldData);
282286
}
283287

src/main/org/firebirdsql/jdbc/field/FBField.java

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
SPDX-FileCopyrightText: Copyright 2002-2003 Blas Rodriguez Somoza
44
SPDX-FileCopyrightText: Copyright 2002 David Jencks
55
SPDX-FileCopyrightText: Copyright 2007 Gabriel Reid
6-
SPDX-FileCopyrightText: Copyright 2011-2024 Mark Rotteveel
6+
SPDX-FileCopyrightText: Copyright 2011-2026 Mark Rotteveel
77
SPDX-FileCopyrightText: Copyright 2020 Vasiliy Yashkov
88
SPDX-License-Identifier: LGPL-2.1-or-later
99
*/
@@ -18,7 +18,6 @@
1818
import org.firebirdsql.jdbc.*;
1919
import org.jspecify.annotations.NonNull;
2020
import org.jspecify.annotations.NullMarked;
21-
import org.jspecify.annotations.NullUnmarked;
2221
import org.jspecify.annotations.Nullable;
2322

2423
import java.io.InputStream;
@@ -42,7 +41,6 @@
4241
* @author Roman Rokytskyy
4342
* @author Mark Rotteveel
4443
*/
45-
@NullUnmarked
4644
public abstract class FBField {
4745

4846
static final String SQL_TYPE_NOT_SUPPORTED = "SQL type for this field is not yet supported";
@@ -81,8 +79,8 @@ public abstract class FBField {
8179
protected GDSHelper gdsHelper;
8280
protected int requiredType;
8381

84-
FBField(@NonNull FieldDescriptor fieldDescriptor, @NonNull FieldDataProvider dataProvider, int requiredType)
85-
throws SQLException {
82+
@NullMarked
83+
FBField(FieldDescriptor fieldDescriptor, FieldDataProvider dataProvider, int requiredType) throws SQLException {
8684
if (fieldDescriptor == null) {
8785
throw new SQLNonTransientException("Cannot create FBField instance with fieldDescriptor null",
8886
SQL_STATE_INVALID_USE_NULL);
@@ -730,6 +728,7 @@ final SQLException invalidGetConversion(Class<?> requestedType) {
730728
}
731729

732730
@NullMarked
731+
@SuppressWarnings("SameParameterValue")
733732
final SQLException invalidGetConversion(Class<?> requestedType, @Nullable String reason) {
734733
return invalidGetConversion(requestedType, reason, null);
735734
}

src/main/org/firebirdsql/jdbc/field/FBStringField.java

Lines changed: 16 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
SPDX-FileCopyrightText: Copyright 2002-2003 Blas Rodriguez Somoza
44
SPDX-FileCopyrightText: Copyright 2002 David Jencks
55
SPDX-FileCopyrightText: Copyright 2003 Ryan Baldwin
6-
SPDX-FileCopyrightText: Copyright 2012-2024 Mark Rotteveel
6+
SPDX-FileCopyrightText: Copyright 2012-2026 Mark Rotteveel
77
SPDX-License-Identifier: LGPL-2.1-or-later
88
*/
99
package org.firebirdsql.jdbc.field;
@@ -29,6 +29,8 @@
2929
import java.util.Calendar;
3030
import java.util.function.Function;
3131

32+
import static org.firebirdsql.gds.ISCConstants.CS_UTF8;
33+
3234
/**
3335
* Field implementation for {@code CHAR} and {@code VARCHAR}.
3436
*
@@ -302,25 +304,29 @@ public void setBoolean(boolean value) throws SQLException {
302304
@Override
303305
public void setString(String value) throws SQLException {
304306
if (setWhenNull(value)) return;
307+
byte[] data = encodeString(value);
308+
if (data.length > fieldDescriptor.getLength()) {
309+
// NOTE: This doesn't catch truncation errors for oversized strings with multibyte character sets that
310+
// still fit, those are handled by the server on execute. For UTF8, the earlier check should handle this.
311+
throw new DataTruncation(fieldDescriptor.getPosition() + 1, true, false, data.length,
312+
fieldDescriptor.getLength());
313+
}
314+
setFieldData(data);
315+
}
316+
317+
private byte[] encodeString(String value) throws DataTruncation {
305318
DatatypeCoder datatypeCoder = getDatatypeCoder();
306319
EncodingDefinition encodingDefinition = datatypeCoder.getEncodingDefinition();
307320
// Special rules for UTF8 (but not UNICODE_FSS), compare by codepoint count
308-
if (encodingDefinition.getFirebirdCharacterSetId() == 4 /* UTF8 */ && value.length() > possibleCharLength) {
321+
if (encodingDefinition.getFirebirdCharacterSetId() == CS_UTF8 && value.length() > possibleCharLength) {
309322
int codePointCount = value.codePointCount(0, value.length());
310323
if (codePointCount > possibleCharLength) {
311324
// NOTE: We're reporting the codepoint lengths, not the maximum size in bytes
312325
throw new DataTruncation(fieldDescriptor.getPosition() + 1, true, false, codePointCount,
313326
possibleCharLength);
314327
}
315328
}
316-
byte[] data = datatypeCoder.encodeString(value);
317-
if (data.length > fieldDescriptor.getLength()) {
318-
// NOTE: This doesn't catch truncation errors for oversized strings with multibyte character sets that
319-
// still fit, those are handled by the server on execute. For UTF8, the earlier check should handle this.
320-
throw new DataTruncation(fieldDescriptor.getPosition() + 1, true, false, data.length,
321-
fieldDescriptor.getLength());
322-
}
323-
setFieldData(data);
329+
return datatypeCoder.encodeString(value);
324330
}
325331

326332
//----- setXXXStream code

src/main/org/firebirdsql/jdbc/field/TrimmableField.java

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

55
import org.firebirdsql.util.InternalApi;
66
import org.jspecify.annotations.NullMarked;
77
import org.jspecify.annotations.Nullable;
88

9+
import static org.firebirdsql.jaybird.util.StringUtils.isNullOrEmpty;
10+
911
/**
1012
* Trim behaviour of {@code getString} in string fields.
1113
*
@@ -41,9 +43,7 @@ public interface TrimmableField {
4143
* @return value without trailing spaces, {@code null} if {@code value} was null
4244
*/
4345
static @Nullable String trimTrailing(@Nullable String value) {
44-
if (value == null || value.isEmpty()) {
45-
return value;
46-
}
46+
if (isNullOrEmpty(value)) return value;
4747
for (int idx = value.length() - 1; idx >= 0; idx--) {
4848
if (value.charAt(idx) != ' ') {
4949
return value.substring(0, idx + 1);

src/main/org/firebirdsql/jdbc/field/TypeConversionException.java

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2,22 +2,23 @@
22
SPDX-FileCopyrightText: Copyright 2002-2003 Roman Rokytskyy
33
SPDX-FileCopyrightText: Copyright 2002 David Jencks
44
SPDX-FileCopyrightText: Copyright 2003 Blas Rodriguez Somoza
5-
SPDX-FileCopyrightText: Copyright 2011-2024 Mark Rotteveel
5+
SPDX-FileCopyrightText: Copyright 2011-2026 Mark Rotteveel
66
SPDX-License-Identifier: LGPL-2.1-or-later
77
*/
88
package org.firebirdsql.jdbc.field;
99

1010
import org.firebirdsql.jdbc.SQLStateConstants;
11+
import org.jspecify.annotations.NullMarked;
1112

1213
import java.io.Serial;
1314
import java.sql.SQLNonTransientException;
1415

1516
/**
16-
* This exception is thrown when the requested type conversion cannot be
17-
* performed.
17+
* This exception is thrown when the requested type conversion cannot be performed.
18+
*
1819
* @author Roman Rokytskyy
19-
* @version 1.0
2020
*/
21+
@NullMarked
2122
public class TypeConversionException extends SQLNonTransientException {
2223

2324
@Serial

src/main/org/firebirdsql/jdbc/field/package-info.java

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,9 @@
44
* Implementation of fields for getting or setting result set columns or prepared statement parameters.
55
*/
66
@InternalApi
7+
// Most methods accept and/or return null, so annotating those that are non-null is less invasive
8+
@NullUnmarked
79
package org.firebirdsql.jdbc.field;
810

9-
import org.firebirdsql.util.InternalApi;
11+
import org.firebirdsql.util.InternalApi;
12+
import org.jspecify.annotations.NullUnmarked;

0 commit comments

Comments
 (0)