diff --git a/src/integrationTest/java/integration/tests/PreparedStatementTest.java b/src/integrationTest/java/integration/tests/PreparedStatementTest.java index 779ab50a9..84f44423d 100644 --- a/src/integrationTest/java/integration/tests/PreparedStatementTest.java +++ b/src/integrationTest/java/integration/tests/PreparedStatementTest.java @@ -435,6 +435,26 @@ void shouldInsertAndRetrieveUrl() throws SQLException, MalformedURLException { } } + @Test + void shouldFetchSpecialCharacters() throws SQLException, MalformedURLException { + try (Connection connection = createConnection()) { + try (PreparedStatement statement = connection + .prepareStatement("SELECT ? as a, ? as b, ? as c, ? as d")) { + statement.setString(1, "ї"); + statement.setString(2, "\n"); + statement.setString(3, "\\"); + statement.setString(4, "don't"); + statement.execute(); + ResultSet rs = statement.getResultSet(); + assertTrue(rs.next()); + assertEquals("ї", rs.getString(1)); + assertEquals("\n", rs.getString(2)); + assertEquals("\\", rs.getString(3)); + assertEquals("don't", rs.getString(4)); + } + } + } + @Builder @Value @EqualsAndHashCode diff --git a/src/main/java/com/firebolt/jdbc/connection/FireboltConnection.java b/src/main/java/com/firebolt/jdbc/connection/FireboltConnection.java index 1129935f8..1b5976229 100644 --- a/src/main/java/com/firebolt/jdbc/connection/FireboltConnection.java +++ b/src/main/java/com/firebolt/jdbc/connection/FireboltConnection.java @@ -18,11 +18,13 @@ import com.firebolt.jdbc.statement.FireboltStatement; import com.firebolt.jdbc.statement.preparedstatement.FireboltPreparedStatement; import com.firebolt.jdbc.type.FireboltDataType; +import com.firebolt.jdbc.type.ParserVersion; import com.firebolt.jdbc.type.array.FireboltArray; import com.firebolt.jdbc.type.lob.FireboltBlob; import com.firebolt.jdbc.type.lob.FireboltClob; import com.firebolt.jdbc.util.PropertyUtil; import lombok.CustomLog; +import lombok.Getter; import lombok.NonNull; import okhttp3.OkHttpClient; @@ -83,12 +85,16 @@ public abstract class FireboltConnection extends JdbcBase implements Connection, //Properties that are used at the beginning of the connection for authentication protected final FireboltProperties loginProperties; private final Collection cacheListeners = Collections.newSetFromMap(new IdentityHashMap<>()); + // Parameter parser is determined by the version we're running on + @Getter + public final ParserVersion parserVersion; protected FireboltConnection(@NonNull String url, Properties connectionSettings, FireboltAuthenticationService fireboltAuthenticationService, FireboltStatementService fireboltStatementService, - String protocolVersion) { + String protocolVersion, + ParserVersion parserVersion) { this.loginProperties = extractFireboltProperties(url, connectionSettings); this.fireboltAuthenticationService = fireboltAuthenticationService; @@ -99,11 +105,13 @@ protected FireboltConnection(@NonNull String url, this.connectionTimeout = loginProperties.getConnectionTimeoutMillis(); this.networkTimeout = loginProperties.getSocketTimeoutMillis(); this.protocolVersion = protocolVersion; + this.parserVersion = parserVersion; } // This code duplication between constructors is done because of back reference: dependent services require reference to current instance of FireboltConnection that prevents using constructor chaining or factory method. @ExcludeFromJacocoGeneratedReport - protected FireboltConnection(@NonNull String url, Properties connectionSettings, String protocolVersion) throws SQLException { + protected FireboltConnection(@NonNull String url, Properties connectionSettings, String protocolVersion, + ParserVersion parserVersion) throws SQLException { this.loginProperties = extractFireboltProperties(url, connectionSettings); OkHttpClient httpClient = getHttpClient(loginProperties); @@ -115,6 +123,7 @@ protected FireboltConnection(@NonNull String url, Properties connectionSettings, this.connectionTimeout = loginProperties.getConnectionTimeoutMillis(); this.networkTimeout = loginProperties.getSocketTimeoutMillis(); this.protocolVersion = protocolVersion; + this.parserVersion = parserVersion; } protected abstract FireboltAuthenticationClient createFireboltAuthenticationClient(OkHttpClient httpClient); @@ -125,8 +134,10 @@ public static FireboltConnection create(@NonNull String url, Properties connecti private static FireboltConnection createConnectionInstance(@NonNull String url, Properties connectionSettings) throws SQLException { switch(getUrlVersion(url, connectionSettings)) { - case 1: return new FireboltConnectionUserPassword(url, connectionSettings); - case 2: return new FireboltConnectionServiceSecret(url, connectionSettings); + case 1: + return new FireboltConnectionUserPassword(url, connectionSettings, ParserVersion.LEGACY); + case 2: + return new FireboltConnectionServiceSecret(url, connectionSettings, ParserVersion.CURRENT); default: throw new IllegalArgumentException(format("Cannot distinguish version from url %s", url)); } } diff --git a/src/main/java/com/firebolt/jdbc/connection/FireboltConnectionServiceSecret.java b/src/main/java/com/firebolt/jdbc/connection/FireboltConnectionServiceSecret.java index a99335f59..ccc5dcd74 100644 --- a/src/main/java/com/firebolt/jdbc/connection/FireboltConnectionServiceSecret.java +++ b/src/main/java/com/firebolt/jdbc/connection/FireboltConnectionServiceSecret.java @@ -16,6 +16,7 @@ import com.firebolt.jdbc.service.FireboltEngineVersion2Service; import com.firebolt.jdbc.service.FireboltGatewayUrlService; import com.firebolt.jdbc.service.FireboltStatementService; +import com.firebolt.jdbc.type.ParserVersion; import com.firebolt.jdbc.util.PropertyUtil; import lombok.NonNull; import okhttp3.OkHttpClient; @@ -42,16 +43,19 @@ public class FireboltConnectionServiceSecret extends FireboltConnection { FireboltGatewayUrlService fireboltGatewayUrlService, FireboltStatementService fireboltStatementService, FireboltEngineInformationSchemaService fireboltEngineService, - FireboltAccountIdService fireboltAccountIdService) throws SQLException { - super(url, connectionSettings, fireboltAuthenticationService, fireboltStatementService, PROTOCOL_VERSION); + FireboltAccountIdService fireboltAccountIdService, + ParserVersion parserVersion) throws SQLException { + super(url, connectionSettings, fireboltAuthenticationService, fireboltStatementService, PROTOCOL_VERSION, + parserVersion); this.fireboltGatewayUrlService = fireboltGatewayUrlService; this.fireboltEngineService = fireboltEngineService; connect(); } @ExcludeFromJacocoGeneratedReport - FireboltConnectionServiceSecret(@NonNull String url, Properties connectionSettings) throws SQLException { - super(url, connectionSettings, PROTOCOL_VERSION); + FireboltConnectionServiceSecret(@NonNull String url, Properties connectionSettings, ParserVersion parserVersion) + throws SQLException { + super(url, connectionSettings, PROTOCOL_VERSION, parserVersion); OkHttpClient httpClient = getHttpClient(loginProperties); this.fireboltGatewayUrlService = new FireboltGatewayUrlService(createFireboltAccountRetriever(httpClient, GatewayUrlResponse.class)); connect(); diff --git a/src/main/java/com/firebolt/jdbc/connection/FireboltConnectionUserPassword.java b/src/main/java/com/firebolt/jdbc/connection/FireboltConnectionUserPassword.java index e36845c1b..84058571b 100644 --- a/src/main/java/com/firebolt/jdbc/connection/FireboltConnectionUserPassword.java +++ b/src/main/java/com/firebolt/jdbc/connection/FireboltConnectionUserPassword.java @@ -11,6 +11,7 @@ import com.firebolt.jdbc.service.FireboltEngineInformationSchemaService; import com.firebolt.jdbc.service.FireboltEngineService; import com.firebolt.jdbc.service.FireboltStatementService; +import com.firebolt.jdbc.type.ParserVersion; import lombok.NonNull; import okhttp3.OkHttpClient; @@ -27,15 +28,18 @@ public class FireboltConnectionUserPassword extends FireboltConnection { Properties connectionSettings, FireboltAuthenticationService fireboltAuthenticationService, FireboltStatementService fireboltStatementService, - FireboltEngineInformationSchemaService fireboltEngineService) throws SQLException { - super(url, connectionSettings, fireboltAuthenticationService, fireboltStatementService, PROTOCOL_VERSION); + FireboltEngineInformationSchemaService fireboltEngineService, + ParserVersion parserVersion) throws SQLException { + super(url, connectionSettings, fireboltAuthenticationService, fireboltStatementService, PROTOCOL_VERSION, + parserVersion); this.fireboltEngineService = fireboltEngineService; connect(); } @ExcludeFromJacocoGeneratedReport - FireboltConnectionUserPassword(@NonNull String url, Properties connectionSettings) throws SQLException { - super(url, connectionSettings, PROTOCOL_VERSION); + FireboltConnectionUserPassword(@NonNull String url, Properties connectionSettings, ParserVersion parserVersion) + throws SQLException { + super(url, connectionSettings, PROTOCOL_VERSION, parserVersion); OkHttpClient httpClient = getHttpClient(loginProperties); this.fireboltEngineService = new FireboltEngineApiService(new FireboltAccountClient(httpClient, this, loginProperties.getUserDrivers(), loginProperties.getUserClients())); connect(); diff --git a/src/main/java/com/firebolt/jdbc/statement/preparedstatement/FireboltPreparedStatement.java b/src/main/java/com/firebolt/jdbc/statement/preparedstatement/FireboltPreparedStatement.java index 41ce33462..12e479b04 100644 --- a/src/main/java/com/firebolt/jdbc/statement/preparedstatement/FireboltPreparedStatement.java +++ b/src/main/java/com/firebolt/jdbc/statement/preparedstatement/FireboltPreparedStatement.java @@ -12,6 +12,7 @@ import com.firebolt.jdbc.statement.StatementInfoWrapper; import com.firebolt.jdbc.statement.StatementUtil; import com.firebolt.jdbc.statement.rawstatement.RawStatementWrapper; +import com.firebolt.jdbc.type.ParserVersion; import com.firebolt.jdbc.type.JavaTypeToFireboltSQLString; import com.firebolt.jdbc.util.InputStreamUtil; import lombok.CustomLog; @@ -57,6 +58,7 @@ public class FireboltPreparedStatement extends FireboltStatement implements Prep private final RawStatementWrapper rawStatement; private final List> rows; private Map providedParameters; + private final ParserVersion parserVersion; public FireboltPreparedStatement(FireboltStatementService statementService, FireboltConnection connection, String sql) { this(statementService, connection.getSessionProperties(), connection, sql); @@ -70,6 +72,7 @@ public FireboltPreparedStatement(FireboltStatementService statementService, Fire this.rawStatement = StatementUtil.parseToRawStatementWrapper(sql); rawStatement.getSubStatements().forEach(statement -> createValidator(statement, connection).validate(statement)); this.rows = new ArrayList<>(); + this.parserVersion = connection.getParserVersion(); } @Override @@ -155,7 +158,7 @@ public void setBigDecimal(int parameterIndex, BigDecimal x) throws SQLException public void setString(int parameterIndex, String x) throws SQLException { validateStatementIsNotClosed(); validateParamIndex(parameterIndex); - providedParameters.put(parameterIndex, JavaTypeToFireboltSQLString.STRING.transform(x)); + providedParameters.put(parameterIndex, JavaTypeToFireboltSQLString.STRING.transform(x, parserVersion)); } @Override @@ -198,7 +201,8 @@ public void setObject(int parameterIndex, Object x, int targetSqlType) throws SQ validateStatementIsNotClosed(); validateParamIndex(parameterIndex); try { - providedParameters.put(parameterIndex, JavaTypeToFireboltSQLString.transformAny(x, targetSqlType)); + providedParameters.put(parameterIndex, + JavaTypeToFireboltSQLString.transformAny(x, targetSqlType, parserVersion)); } catch (FireboltException fbe) { if (ExceptionType.TYPE_NOT_SUPPORTED.equals(fbe.getType())) { throw new SQLFeatureNotSupportedException(fbe.getMessage(), fbe); diff --git a/src/main/java/com/firebolt/jdbc/type/BaseType.java b/src/main/java/com/firebolt/jdbc/type/BaseType.java index c2b4caf97..e99e0838f 100644 --- a/src/main/java/com/firebolt/jdbc/type/BaseType.java +++ b/src/main/java/com/firebolt/jdbc/type/BaseType.java @@ -135,7 +135,7 @@ private static String checkInfinity(String s) { } public static boolean isNull(String value) { - return NULL_VALUE.equalsIgnoreCase(value); + return NULL_VALUE.equals(value); } private static boolean isNan(String value) { diff --git a/src/main/java/com/firebolt/jdbc/type/JavaTypeToFireboltSQLString.java b/src/main/java/com/firebolt/jdbc/type/JavaTypeToFireboltSQLString.java index 2229f622a..cb3db7ba9 100644 --- a/src/main/java/com/firebolt/jdbc/type/JavaTypeToFireboltSQLString.java +++ b/src/main/java/com/firebolt/jdbc/type/JavaTypeToFireboltSQLString.java @@ -35,7 +35,7 @@ public enum JavaTypeToFireboltSQLString { UUID(java.util.UUID.class, Object::toString), BYTE(Byte.class, value -> Byte.toString(((Number) value).byteValue())), SHORT(Short.class, value -> Short.toString(((Number) value).shortValue())), - STRING(String.class, getSQLStringValueOfString()), + STRING(String.class, getSQLStringValueOfString(ParserVersion.CURRENT), getSQLStringValueOfStringVersioned()), LONG(Long.class, value -> Long.toString(((Number)value).longValue())), INTEGER(Integer.class, value -> Integer.toString(((Number)value).intValue())), BIG_INTEGER(BigInteger.class, value -> value instanceof BigInteger ? value.toString() : Long.toString(((Number)value).longValue())), @@ -47,8 +47,11 @@ public enum JavaTypeToFireboltSQLString { ARRAY(Array.class, SqlArrayUtil::arrayToString), BYTE_ARRAY(byte[].class, value -> ofNullable(byteArrayToHexString((byte[])value, true)).map(x -> format("E'%s'::BYTEA", x)).orElse(null)), ; - private static final List> characterToEscapedCharacterPairs = List.of( + + private static final List> legacyCharacterToEscapedCharacterPairs = List.of( Map.entry("\0", "\\0"), Map.entry("\\", "\\\\"), Map.entry("'", "''")); + private static final List> characterToEscapedCharacterPairs = List.of( + Map.entry("'", "''")); //https://docs.oracle.com/javase/1.5.0/docs/guide/jdbc/getstart/mapping.html private static final Map> jdbcTypeToClass = Map.ofEntries( Map.entry(JDBCType.CHAR, String.class), @@ -100,22 +103,33 @@ public enum JavaTypeToFireboltSQLString { } public static String transformAny(Object object) throws SQLException { - return transformAny(object, () -> getType(object)); + return transformAny(object, ParserVersion.CURRENT); + } + + public static String transformAny(Object object, ParserVersion version) throws SQLException { + return transformAny(object, () -> getType(object), version); } public static String transformAny(Object object, int sqlType) throws SQLException { - return transformAny(object, () -> getType(sqlType)); + return transformAny(object, sqlType, ParserVersion.CURRENT); } - private static String transformAny(Object object, CheckedSupplier> classSupplier) throws SQLException { - return object == null ? NULL_VALUE : transformAny(object, classSupplier.get()); + public static String transformAny(Object object, int sqlType, ParserVersion version) throws SQLException { + return transformAny(object, () -> getType(sqlType), version); } - private static String transformAny(Object object, Class objectType) throws SQLException { + private static String transformAny(Object object, CheckedSupplier> classSupplier, ParserVersion version) throws SQLException { + return object == null ? NULL_VALUE : transformAny(object, classSupplier.get(), version); + } + + private static String transformAny(Object object, Class objectType, ParserVersion version) throws SQLException { JavaTypeToFireboltSQLString converter = Optional.ofNullable(classToType.get(objectType)) .orElseThrow(() -> new FireboltException( format("Cannot convert type %s. The type is not supported.", objectType), TYPE_NOT_SUPPORTED)); + if (version == ParserVersion.LEGACY && object instanceof String) { + return converter.transform(object, version); + } return converter.transform(object); } @@ -133,10 +147,15 @@ private static Class getType(int sqlType) throws SQLException { } } - private static CheckedFunction getSQLStringValueOfString() { + private static CheckedBiFunction getSQLStringValueOfStringVersioned() { + return (value, version) -> getSQLStringValueOfString((ParserVersion) version).apply(value); + } + + private static CheckedFunction getSQLStringValueOfString(ParserVersion version) { return value -> { String escaped = (String) value; - for (Entry specialCharacter : characterToEscapedCharacterPairs) { + List> charactersToEscape = version == ParserVersion.LEGACY ? legacyCharacterToEscapedCharacterPairs : characterToEscapedCharacterPairs; + for (Entry specialCharacter : charactersToEscape) { escaped = escaped.replace(specialCharacter.getKey(), specialCharacter.getValue()); } return format("'%s'", escaped); diff --git a/src/main/java/com/firebolt/jdbc/type/ParserVersion.java b/src/main/java/com/firebolt/jdbc/type/ParserVersion.java new file mode 100644 index 000000000..31a061212 --- /dev/null +++ b/src/main/java/com/firebolt/jdbc/type/ParserVersion.java @@ -0,0 +1,5 @@ +package com.firebolt.jdbc.type; + +public enum ParserVersion { + LEGACY, CURRENT +} diff --git a/src/test/java/com/firebolt/jdbc/client/query/StatementClientImplTest.java b/src/test/java/com/firebolt/jdbc/client/query/StatementClientImplTest.java index eae589a79..e6c0e5b0a 100644 --- a/src/test/java/com/firebolt/jdbc/client/query/StatementClientImplTest.java +++ b/src/test/java/com/firebolt/jdbc/client/query/StatementClientImplTest.java @@ -10,6 +10,7 @@ import com.firebolt.jdbc.exception.FireboltException; import com.firebolt.jdbc.statement.StatementInfoWrapper; import com.firebolt.jdbc.statement.StatementUtil; +import com.firebolt.jdbc.type.ParserVersion; import lombok.NonNull; import okhttp3.Call; import okhttp3.Dispatcher; @@ -349,7 +350,7 @@ private FireboltConnection use(int mockedInfraVersion, Properties props, String Call useCall = getMockedCallWithResponse(200, "", responseHeaders); Call select1Call = getMockedCallWithResponse(200, ""); when(okHttpClient.newCall(any())).thenReturn(useCall, select1Call); - FireboltConnection connection = new FireboltConnection("url", props, "0") { + FireboltConnection connection = new FireboltConnection("url", props, "0", ParserVersion.CURRENT) { { this.infraVersion = mockedInfraVersion; try { diff --git a/src/test/java/com/firebolt/jdbc/connection/FireboltConnectionServiceSecretTest.java b/src/test/java/com/firebolt/jdbc/connection/FireboltConnectionServiceSecretTest.java index 4c02fc295..381d01f05 100644 --- a/src/test/java/com/firebolt/jdbc/connection/FireboltConnectionServiceSecretTest.java +++ b/src/test/java/com/firebolt/jdbc/connection/FireboltConnectionServiceSecretTest.java @@ -5,6 +5,7 @@ import com.firebolt.jdbc.connection.settings.FireboltProperties; import com.firebolt.jdbc.exception.FireboltException; import com.firebolt.jdbc.service.FireboltGatewayUrlService; +import com.firebolt.jdbc.type.ParserVersion; import org.junit.jupiter.api.Test; import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.CsvSource; @@ -100,7 +101,9 @@ void checkSystemEngineEndpoint(String gatewayUrl, String expectedHost, String ex @SuppressWarnings("unchecked") FireboltAccountRetriever fireboltGatewayUrlClient = mock(FireboltAccountRetriever.class); when(fireboltGatewayUrlClient.retrieve(any(), any())).thenReturn(new GatewayUrlResponse(gatewayUrl)); FireboltGatewayUrlService gatewayUrlService = new FireboltGatewayUrlService(fireboltGatewayUrlClient); - FireboltConnection connection = new FireboltConnectionServiceSecret(SYSTEM_ENGINE_URL, connectionProperties, fireboltAuthenticationService, gatewayUrlService, fireboltStatementService, fireboltEngineService, fireboltAccountIdService); + FireboltConnection connection = new FireboltConnectionServiceSecret(SYSTEM_ENGINE_URL, connectionProperties, + fireboltAuthenticationService, gatewayUrlService, fireboltStatementService, fireboltEngineService, + fireboltAccountIdService, ParserVersion.CURRENT); FireboltProperties sessionProperties = connection.getSessionProperties(); assertEquals(expectedHost, sessionProperties.getHost()); assertEquals(expectedProps == null ? Map.of() : Arrays.stream(expectedProps.split(";")).map(kv -> kv.split("=")).collect(toMap(kv -> kv[0], kv -> kv[1])), sessionProperties.getAdditionalProperties()); @@ -113,6 +116,7 @@ void shouldNotFetchTokenNorEngineHostForLocalFirebolt() throws SQLException { } protected FireboltConnection createConnection(String url, Properties props) throws SQLException { - return new FireboltConnectionServiceSecret(url, props, fireboltAuthenticationService, fireboltGatewayUrlService, fireboltStatementService, fireboltEngineService, fireboltAccountIdService); + return new FireboltConnectionServiceSecret(url, props, fireboltAuthenticationService, fireboltGatewayUrlService, + fireboltStatementService, fireboltEngineService, fireboltAccountIdService, ParserVersion.CURRENT); } } diff --git a/src/test/java/com/firebolt/jdbc/connection/FireboltConnectionUserPasswordTest.java b/src/test/java/com/firebolt/jdbc/connection/FireboltConnectionUserPasswordTest.java index 927fcdf34..473f72bc3 100644 --- a/src/test/java/com/firebolt/jdbc/connection/FireboltConnectionUserPasswordTest.java +++ b/src/test/java/com/firebolt/jdbc/connection/FireboltConnectionUserPasswordTest.java @@ -1,5 +1,6 @@ package com.firebolt.jdbc.connection; +import com.firebolt.jdbc.type.ParserVersion; import org.junit.jupiter.api.Test; import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.CsvSource; @@ -73,6 +74,7 @@ void getMetadata(String engine) throws SQLException { } protected FireboltConnection createConnection(String url, Properties props) throws SQLException { - return new FireboltConnectionUserPassword(url, props, fireboltAuthenticationService, fireboltStatementService, fireboltEngineService); + return new FireboltConnectionUserPassword(url, props, fireboltAuthenticationService, fireboltStatementService, + fireboltEngineService, ParserVersion.LEGACY); } } diff --git a/src/test/java/com/firebolt/jdbc/type/JavaTypeToFireboltSQLStringTest.java b/src/test/java/com/firebolt/jdbc/type/JavaTypeToFireboltSQLStringTest.java index 86c111c17..7e7b24ff1 100644 --- a/src/test/java/com/firebolt/jdbc/type/JavaTypeToFireboltSQLStringTest.java +++ b/src/test/java/com/firebolt/jdbc/type/JavaTypeToFireboltSQLStringTest.java @@ -69,9 +69,30 @@ void shouldTransformShortToString() throws SQLException { @Test void shouldEscapeCharactersWhenTransformingFromString() throws SQLException { + // quotes are escaped assertEquals("'105'' OR 1=1--'' '", JavaTypeToFireboltSQLString.STRING.transform("105' OR 1=1--' ")); - assertEquals("'105'' OR 1=1--'' '", JavaTypeToFireboltSQLString.transformAny("105' OR 1=1--' ")); + // \0 is not escaped + assertEquals("'105\0'", JavaTypeToFireboltSQLString.STRING.transform("105\0")); + assertEquals("'105\0'", JavaTypeToFireboltSQLString.transformAny("105\0")); + // backslashes are not escaped + assertEquals("'105\\'", JavaTypeToFireboltSQLString.STRING.transform("105\\")); + assertEquals("'105\\'", JavaTypeToFireboltSQLString.transformAny("105\\")); + } + + @Test + void shouldEscapeCharactersWhenTransformingFromStringLegacy() throws SQLException { + // quotes are escaped + assertEquals("'105'' OR 1=1--'' '", + JavaTypeToFireboltSQLString.STRING.transform("105' OR 1=1--' ", ParserVersion.LEGACY)); + assertEquals("'105'' OR 1=1--'' '", + JavaTypeToFireboltSQLString.transformAny("105' OR 1=1--' ", ParserVersion.LEGACY)); + // \0 is escaped + assertEquals("'105\\\\0'", JavaTypeToFireboltSQLString.STRING.transform("105\0", ParserVersion.LEGACY)); + assertEquals("'105\\\\0'", JavaTypeToFireboltSQLString.transformAny("105\0", ParserVersion.LEGACY)); + // backslashes are escaped + assertEquals("'105\\\\'", JavaTypeToFireboltSQLString.STRING.transform("105\\", ParserVersion.LEGACY)); + assertEquals("'105\\\\'", JavaTypeToFireboltSQLString.transformAny("105\\", ParserVersion.LEGACY)); } @Test