Skip to content

Commit

Permalink
#770 Fix FBStringField.fixPadding to work with codepoints instead of …
Browse files Browse the repository at this point in the history
…char - backport #760 to Jaybird 5.0.3
  • Loading branch information
mrotteveel committed Oct 27, 2023
1 parent d8abe29 commit 2e1e241
Show file tree
Hide file tree
Showing 3 changed files with 46 additions and 2 deletions.
3 changes: 3 additions & 0 deletions src/docs/asciidoc/release_notes.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,9 @@ Previously, Jaybird rejected attempts to read blobs with blob id `0` (not on all
Formally, blob id `0` is not a valid blob id, but in practice they can occur (e.g. due to bugs, or access components/drivers explicitly setting a blob column to id `0`).
Other drivers and tools simply send the requests for blob id `0` to the server, which then treats it as an empty blob.
For consistency, we decided to let Jaybird handle it the same.
* Fixed: on `CHAR` fields, a too short value could be returned if the string contained one or more codepoints represented by surrogate pairs and the string length in `char` exceeded the maximum string length -- backported from Jaybird 6 (https://github.com/FirebirdSQL/jaybird/issues/770[#770])
+
We now truncate the returned string if the codepoint count exceeds the maximum string length.
[#jaybird-5-0-2-changelog]
=== Jaybird 5.0.2
Expand Down
5 changes: 3 additions & 2 deletions src/main/org/firebirdsql/jdbc/field/FBStringField.java
Original file line number Diff line number Diff line change
Expand Up @@ -197,8 +197,9 @@ private String fixPadding(String result) {
// NOTE: For Firebird 3.0 and earlier, this prevents access to oversized CHAR(n) CHARACTER SET UNICODE_FSS.
// We accept that limitation because the workaround is to cast to VARCHAR, and because Firebird 4.0 no longer
// allows storing oversized UNICODE_FSS values
if (result.length() > possibleCharLength) {
return result.substring(0, possibleCharLength);
if (result != null && result.length() > possibleCharLength
&& result.codePointCount(0, result.length()) > possibleCharLength) {
return result.substring(0, result.offsetByCodePoints(0, possibleCharLength));
}
return result;
}
Expand Down
40 changes: 40 additions & 0 deletions src/test/org/firebirdsql/jdbc/field/FBStringFieldTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -790,6 +790,46 @@ void getObject_java_time_ZonedDateTime() throws SQLException {
assertEquals(expectedZonedDateTime, field.getObject(ZonedDateTime.class));
}

@Test
void setString_surrogatePairs_maxLength() throws Exception {
fieldDescriptor = rowDescriptorBuilder
.setType(ISCConstants.SQL_TEXT)
.setLength(4 /* bytes */)
.setSubType(4 /* UTF8 */)
.toFieldDescriptor();
field = new FBStringField(fieldDescriptor, fieldData, Types.CHAR);
datatypeCoder = fieldDescriptor.getDatatypeCoder();
Encoding encoding = datatypeCoder.getEncoding();
assertEquals("UTF-8", encoding.getCharsetName(), "Unexpected charset for field");

// character Smiling Face with Open Mouth; uses surrogate pairs
String surrogatePairsValue = Character.toString(0x1f603);
assertEquals(2, surrogatePairsValue.length(), "Expected string with 2 characters (surrogate pairs)");
field.setString(surrogatePairsValue);

verifySetString(surrogatePairsValue, encoding);
}

@Test
void getString_surrogatePairs_maxLength() throws Exception {
fieldDescriptor = rowDescriptorBuilder
.setType(ISCConstants.SQL_TEXT)
.setLength(4 /* bytes */)
.setSubType(4 /* UTF8 */)
.toFieldDescriptor();
field = new FBStringField(fieldDescriptor, fieldData, Types.CHAR);
datatypeCoder = fieldDescriptor.getDatatypeCoder();
Encoding encoding = datatypeCoder.getEncoding();
assertEquals("UTF-8", encoding.getCharsetName(), "Unexpected charset for field");

// character Smiling Face with Open Mouth; uses surrogate pairs
String surrogatePairsValue = Character.toString(0x1f603);
assertEquals(2, surrogatePairsValue.length(), "Expected string with 2 characters (surrogate pairs)");
toReturnStringExpectations(surrogatePairsValue, encoding);

assertEquals(surrogatePairsValue, field.getString(), "Unexpected value for string with surrogate pairs");
}

@Override
String getNonNullObject() {
return TEST_STRING_SHORT;
Expand Down

0 comments on commit 2e1e241

Please sign in to comment.