From 2e1e241bd96cf63f1302d7c908306c265615d605 Mon Sep 17 00:00:00 2001 From: Mark Rotteveel Date: Fri, 27 Oct 2023 12:45:19 +0200 Subject: [PATCH] #770 Fix FBStringField.fixPadding to work with codepoints instead of char - backport #760 to Jaybird 5.0.3 --- src/docs/asciidoc/release_notes.adoc | 3 ++ .../firebirdsql/jdbc/field/FBStringField.java | 5 ++- .../jdbc/field/FBStringFieldTest.java | 40 +++++++++++++++++++ 3 files changed, 46 insertions(+), 2 deletions(-) diff --git a/src/docs/asciidoc/release_notes.adoc b/src/docs/asciidoc/release_notes.adoc index 031ae7027..6c7556ec0 100644 --- a/src/docs/asciidoc/release_notes.adoc +++ b/src/docs/asciidoc/release_notes.adoc @@ -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 diff --git a/src/main/org/firebirdsql/jdbc/field/FBStringField.java b/src/main/org/firebirdsql/jdbc/field/FBStringField.java index 515e78dac..d6bd7ffb9 100644 --- a/src/main/org/firebirdsql/jdbc/field/FBStringField.java +++ b/src/main/org/firebirdsql/jdbc/field/FBStringField.java @@ -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; } diff --git a/src/test/org/firebirdsql/jdbc/field/FBStringFieldTest.java b/src/test/org/firebirdsql/jdbc/field/FBStringFieldTest.java index 9670f34f3..a64381af7 100644 --- a/src/test/org/firebirdsql/jdbc/field/FBStringFieldTest.java +++ b/src/test/org/firebirdsql/jdbc/field/FBStringFieldTest.java @@ -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;