diff --git a/pgjdbc/src/main/java/org/postgresql/core/QueryExecutor.java b/pgjdbc/src/main/java/org/postgresql/core/QueryExecutor.java index f6878f23607a0b4cd2ffeda057d9ebbe01f6f763..42e9de2bbd08155b6e6ddb439e3c31d3b8903241 100644 --- a/pgjdbc/src/main/java/org/postgresql/core/QueryExecutor.java +++ b/pgjdbc/src/main/java/org/postgresql/core/QueryExecutor.java @@ -514,4 +514,17 @@ public interface QueryExecutor extends TypeTransferModeRegistry { * @param enableOutparamOveride true or false */ void setEnableOutparamOveride(boolean enableOutparamOveride); + + /** + * set encoding for this client + * @param clientEncoding encoding str + */ + void setClientEncoding(String clientEncoding); + + /** + * Get encoding for this client + * + * @return encoding str + */ + String getClientEncoding(); } diff --git a/pgjdbc/src/main/java/org/postgresql/core/Utils.java b/pgjdbc/src/main/java/org/postgresql/core/Utils.java index 7b1c9d7fec1039e8896f280497b10f3998a574ea..cad36235eca2f4f58f929e9d269692046a2fce43 100644 --- a/pgjdbc/src/main/java/org/postgresql/core/Utils.java +++ b/pgjdbc/src/main/java/org/postgresql/core/Utils.java @@ -9,11 +9,11 @@ package org.postgresql.core; import org.postgresql.util.GT; import org.postgresql.util.PSQLException; import org.postgresql.util.PSQLState; -import org.postgresql.core.v3.ConnectionFactoryImpl; + import java.io.IOException; import java.nio.charset.Charset; +import java.nio.charset.StandardCharsets; import java.sql.SQLException; -import java.util.Locale; /** * Collection of utilities used by the protocol-level code. @@ -45,7 +45,22 @@ public class Utils { // for performance measurements. // In OracleJDK 6u65, 7u55, and 8u40 String.getBytes(Charset) is // 3 times faster than other JDK approaches. - return str.getBytes(Charset.forName(ConnectionFactoryImpl.CLIENT_ENCODING)); + return encodeUTF8(str, StandardCharsets.UTF_8.name()); + } + + /** + * Encode a String with encoding + * + * @param str the string to encode + * @param encoding encoding type default utf-8 + * @return the encoding representation of {@code str} + */ + public static byte[] encodeUTF8(String str, String encoding) { + // See org.postgresql.benchmark.encoding.UTF8Encoding#string_getBytes + // for performance measurements. + // In OracleJDK 6u65, 7u55, and 8u40 String.getBytes(Charset) is + // 3 times faster than other JDK approaches. + return str.getBytes(Charset.forName(encoding)); } /** @@ -60,8 +75,7 @@ public class Utils { * @return the sbuf argument; or a new string builder for sbuf == null * @throws SQLException if the string contains a \0 character */ - public static StringBuilder escapeLiteral(StringBuilder sbuf, String value, - boolean standardConformingStrings) throws SQLException { + public static StringBuilder escapeLiteral(StringBuilder sbuf, String value, boolean standardConformingStrings) throws SQLException { if (sbuf == null) { sbuf = new StringBuilder((value.length() + 10) / 10 * 11); // Add 10% for escaping. } diff --git a/pgjdbc/src/main/java/org/postgresql/core/v3/ConnectionFactoryImpl.java b/pgjdbc/src/main/java/org/postgresql/core/v3/ConnectionFactoryImpl.java index 03858651bfd6b4be12b8c5f9c18253367b064c1b..fdf0facf8842807eec3edf40b028bbb026ed4d31 100644 --- a/pgjdbc/src/main/java/org/postgresql/core/v3/ConnectionFactoryImpl.java +++ b/pgjdbc/src/main/java/org/postgresql/core/v3/ConnectionFactoryImpl.java @@ -60,16 +60,16 @@ public class ConnectionFactoryImpl extends ConnectionFactory { private static final int AUTH_REQ_GSS_CONTINUE = 8; private static final int AUTH_REQ_SSPI = 9; - public static String CLIENT_ENCODING = "UTF8"; + public String CLIENT_ENCODING = "UTF8"; public static String USE_BOOLEAN = "false"; private static final int AUTH_REQ_SHA256 = 10; private static final int AUTH_REQ_MD5_SHA256 = 11; - private static final int AUTH_REQ_SM3 = 13; + private static final int AUTH_REQ_SM3 = 13; private static final int PLAIN_PASSWORD = 0; - private static final int MD5_PASSWORD = 1; - private static final int SHA256_PASSWORD = 2; - private static final int SM3_PASSWORD = 3; - private static final int ERROR_PASSWORD = 4; + private static final int MD5_PASSWORD = 1; + private static final int SHA256_PASSWORD = 2; + private static final int SM3_PASSWORD = 3; + private static final int ERROR_PASSWORD = 4; private static final int PROTOCOL_VERSION_351 = 351; private static final int PROTOCOL_VERSION_350 = 350; private int protocolVerion = PROTOCOL_VERSION_351; @@ -86,11 +86,11 @@ public class ConnectionFactoryImpl extends ConnectionFactory { CLIENT_ENCODING_WHITELIST.put("GBK", "GBK"); CLIENT_ENCODING_WHITELIST.put("LATIN1", "LATIN1"); } - public static void setStaticClientEncoding(String client) { - ConnectionFactoryImpl.CLIENT_ENCODING = client; - } +// public static void setStaticClientEncoding(String client) { +// this.CLIENT_ENCODING = client; +// } public void setClientEncoding(String client) { - setStaticClientEncoding(client); + this.CLIENT_ENCODING = client; } public static void setStaticUseBoolean(String useBoolean) { ConnectionFactoryImpl.USE_BOOLEAN = useBoolean; @@ -308,7 +308,8 @@ public class ConnectionFactoryImpl extends ConnectionFactory { QueryExecutor queryExecutor = new QueryExecutorImpl(newStream, user, database, cancelSignalTimeout, info); queryExecutor.setProtocolVersion(this.protocolVerion); - + // set encoding for queryExecutor + queryExecutor.setClientEncoding(this.CLIENT_ENCODING); //Check MasterCluster or SecondaryCluster if (PGProperty.PRIORITY_SERVERS.get(info) != null) { ClusterStatus currentClusterStatus = queryClusterStatus(queryExecutor); @@ -591,8 +592,8 @@ public class ConnectionFactoryImpl extends ConnectionFactory { pgStream.sendInteger2(0); // protocol minor else if(this.protocolVerion == PROTOCOL_VERSION_350) pgStream.sendInteger2(50); // protocol minor - else if(this.protocolVerion == PROTOCOL_VERSION_351) - pgStream.sendInteger2(51); // protocol minor + else if(this.protocolVerion == PROTOCOL_VERSION_351) + pgStream.sendInteger2(51); // protocol minor for (byte[] encodedParam : encodedParams) { pgStream.send(encodedParam); pgStream.sendChar(0); diff --git a/pgjdbc/src/main/java/org/postgresql/core/v3/Portal.java b/pgjdbc/src/main/java/org/postgresql/core/v3/Portal.java index 8a93abc4827f3cd8caba3a78de7ac654cffb8b12..587a67b3708df42576e699aabc39f8591b523ccb 100644 --- a/pgjdbc/src/main/java/org/postgresql/core/v3/Portal.java +++ b/pgjdbc/src/main/java/org/postgresql/core/v3/Portal.java @@ -10,6 +10,7 @@ import org.postgresql.core.ResultCursor; import org.postgresql.core.Utils; import java.lang.ref.PhantomReference; +import java.nio.charset.StandardCharsets; /** * V3 ResultCursor implementation in terms of backend Portals. This holds the state of a single @@ -18,10 +19,15 @@ import java.lang.ref.PhantomReference; * @author Oliver Jowett (oliver@opencloud.com) */ class Portal implements ResultCursor { + Portal(SimpleQuery query, String portalName) { + this(query, portalName, StandardCharsets.UTF_8.name()); + } + + Portal(SimpleQuery query, String portalName, String clientEncoding) { this.query = query; this.portalName = portalName; - this.encodedName = Utils.encodeUTF8(portalName); + this.encodedName = Utils.encodeUTF8(portalName, clientEncoding); } public void close() { diff --git a/pgjdbc/src/main/java/org/postgresql/core/v3/QueryExecutorImpl.java b/pgjdbc/src/main/java/org/postgresql/core/v3/QueryExecutorImpl.java index 455dd54cc53bed588de848af345af6cbdda1320a..baa71982723b2f9d09216d11c359a044d0ae4573 100644 --- a/pgjdbc/src/main/java/org/postgresql/core/v3/QueryExecutorImpl.java +++ b/pgjdbc/src/main/java/org/postgresql/core/v3/QueryExecutorImpl.java @@ -5,7 +5,7 @@ // Copyright (c) 2004, Open Cloud Limited. package org.postgresql.core.v3; -import org.postgresql.Driver; + import org.postgresql.PGProperty; import org.postgresql.copy.CopyIn; import org.postgresql.copy.CopyOperation; @@ -36,13 +36,14 @@ import org.postgresql.core.v3.replication.V3ReplicationProtocol; import org.postgresql.jdbc.AutoSave; import org.postgresql.jdbc.BatchResultHandler; import org.postgresql.jdbc.TimestampUtils; +import org.postgresql.log.Log; +import org.postgresql.log.Logger; import org.postgresql.util.GT; import org.postgresql.util.PSQLException; import org.postgresql.util.PSQLState; import org.postgresql.util.PSQLWarning; import org.postgresql.util.ServerErrorMessage; -import org.postgresql.log.Logger; -import org.postgresql.log.Log; + import java.io.IOException; import java.lang.ref.PhantomReference; import java.lang.ref.Reference; @@ -128,6 +129,8 @@ public class QueryExecutorImpl extends QueryExecutorBase { private boolean enableOutparamOveride; + private String clientEncoding; + /** * {@code CommandComplete(B)} messages are quite common, so we reuse instance to parse those */ @@ -183,6 +186,16 @@ public class QueryExecutorImpl extends QueryExecutorBase { this.enableOutparamOveride = enableOutparamOveride; } + @Override + public void setClientEncoding(String clientEncoding) { + this.clientEncoding = clientEncoding; + } + + @Override + public String getClientEncoding() { + return this.clientEncoding; + } + /** * When database compatibility mode is A database and the parameter overload function * is turned on, add out parameter description message. @@ -745,7 +758,7 @@ public class QueryExecutorImpl extends QueryExecutorBase { if (params.isNull(i)) { encodedSize += 4; } else { - encodedSize += 4 + params.getV3Length(i); + encodedSize += 4 + params.getV3Length(i, getClientEncoding()); } } @@ -762,8 +775,8 @@ public class QueryExecutorImpl extends QueryExecutorBase { if (params.isNull(i)) { pgStream.sendInteger4(-1); } else { - pgStream.sendInteger4(params.getV3Length(i)); // Parameter size - params.writeV3Value(i, pgStream); + pgStream.sendInteger4(params.getV3Length(i, getClientEncoding())); // Parameter size + params.writeV3Value(i, pgStream, getClientEncoding()); } } pgStream.sendInteger2(1); // Binary result format @@ -943,7 +956,7 @@ public class QueryExecutorImpl extends QueryExecutorBase { if (!suppressBegin) { doSubprotocolBegin(); } - byte[] buf = Utils.encodeUTF8(sql); + byte[] buf = Utils.encodeUTF8(sql, getClientEncoding()); try { LOGGER.trace(" FE=> Query(CopyStart)"); @@ -1566,7 +1579,7 @@ public class QueryExecutorImpl extends QueryExecutorBase { // NB: Must clone the OID array, as it's a direct reference to // the SimpleParameterList's internal array that might be modified // under us. - query.setStatementName(statementName, deallocateEpoch); + query.setStatementName(statementName, deallocateEpoch, getClientEncoding()); query.setPrepareTypes(typeOIDs); registerParsedQuery(query, statementName); } @@ -1622,7 +1635,7 @@ public class QueryExecutorImpl extends QueryExecutorBase { // Send Parse. // - byte[] queryUtf8 = Utils.encodeUTF8(nativeSql); + byte[] queryUtf8 = Utils.encodeUTF8(nativeSql, getClientEncoding()); // Total size = 4 (size field) // + N + 1 (statement name, zero-terminated) @@ -1694,7 +1707,7 @@ public class QueryExecutorImpl extends QueryExecutorBase { if (params.isNull(i)) { encodedSize += 4; } else { - encodedSize += (long) 4 + params.getV3Length(i); + encodedSize += (long) 4 + params.getV3Length(i, getClientEncoding()); } } @@ -1774,9 +1787,9 @@ public class QueryExecutorImpl extends QueryExecutorBase { if (params.isNull(i)) { pgStream.sendInteger4(-1); // Magic size of -1 means NULL } else { - pgStream.sendInteger4(params.getV3Length(i)); // Parameter size + pgStream.sendInteger4(params.getV3Length(i, getClientEncoding())); // Parameter size try { - params.writeV3Value(i, pgStream); // Parameter value + params.writeV3Value(i, pgStream, getClientEncoding()); // Parameter value } catch (PGBindException be) { bindException = be; } @@ -1823,7 +1836,7 @@ public class QueryExecutorImpl extends QueryExecutorBase { if (params.isNull(i)) { encodedSize += 4; } else { - encodedSize += (long) 4 + params.getV3Length(i); + encodedSize += (long) 4 + params.getV3Length(i, getClientEncoding()); } } } @@ -1925,9 +1938,9 @@ public class QueryExecutorImpl extends QueryExecutorBase { if (params.isNull(i)) { pgStream.sendInteger4(-1); // Magic size of -1 means NULL } else { - pgStream.sendInteger4(params.getV3Length(i)); // Parameter size + pgStream.sendInteger4(params.getV3Length(i, getClientEncoding())); // Parameter size try { - params.writeV3Value(i, pgStream); // Parameter value + params.writeV3Value(i, pgStream, getClientEncoding()); // Parameter value } catch (PGBindException be) { bindException = be; } @@ -2045,7 +2058,7 @@ public class QueryExecutorImpl extends QueryExecutorBase { LOGGER.trace("[" + socketAddress + "] " + " FE=> ClosePortal(" + portalName + ")"); - byte[] encodedPortalName = (portalName == null ? null : Utils.encodeUTF8(portalName)); + byte[] encodedPortalName = (portalName == null ? null : Utils.encodeUTF8(portalName, getClientEncoding())); int encodedSize = (encodedPortalName == null ? 0 : encodedPortalName.length); // Total size = 4 (size field) + 1 (close type, 'P') + 1 + N (portal name) @@ -2065,7 +2078,7 @@ public class QueryExecutorImpl extends QueryExecutorBase { LOGGER.trace("[" + socketAddress + "] " + " FE=> CloseStatement(" + statementName + ")"); - byte[] encodedStatementName = Utils.encodeUTF8(statementName); + byte[] encodedStatementName = Utils.encodeUTF8(statementName, getClientEncoding()); // Total size = 4 (size field) + 1 (close type, 'S') + N + 1 (statement name) pgStream.sendChar('C'); // Close @@ -2171,7 +2184,7 @@ public class QueryExecutorImpl extends QueryExecutorBase { Portal portal = null; if (usePortal) { String portalName = "C_" + (nextUniqueID++); - portal = new Portal(query, portalName); + portal = new Portal(query, portalName, getClientEncoding()); } sendBind(query, params, portal, noBinaryTransfer); @@ -2272,7 +2285,7 @@ public class QueryExecutorImpl extends QueryExecutorBase { Portal portal = null; if (usePortal) { String portalName = "C_" + (nextUniqueID++); - portal = new Portal(query, portalName); + portal = new Portal(query, portalName, getClientEncoding()); } sendBatchBind(query, parameterLists, portal, noBinaryTransfer, batchnum, rows); diff --git a/pgjdbc/src/main/java/org/postgresql/core/v3/SimpleParameterList.java b/pgjdbc/src/main/java/org/postgresql/core/v3/SimpleParameterList.java index 4ecceb6f2b536621809a373c7354d5008df323c1..f15142743f5acd1bf41a5838c1ac1f15dce674c3 100644 --- a/pgjdbc/src/main/java/org/postgresql/core/v3/SimpleParameterList.java +++ b/pgjdbc/src/main/java/org/postgresql/core/v3/SimpleParameterList.java @@ -400,7 +400,7 @@ class SimpleParameterList implements V3ParameterList { return (byte) (flags[index] & INOUT); } - int getV3Length(int index) { + int getV3Length(int index, String clientEncoding) { --index; // Null? @@ -421,13 +421,13 @@ class SimpleParameterList implements V3ParameterList { // Already encoded? if (encoded[index] == null) { // Encode value and compute actual length using UTF-8. - encoded[index] = Utils.encodeUTF8(paramValues[index].toString()); + encoded[index] = Utils.encodeUTF8(paramValues[index].toString(), clientEncoding); } return encoded[index].length; } - void writeV3Value(int index, PGStream pgStream) throws IOException { + void writeV3Value(int index, PGStream pgStream, String clientEncoding) throws IOException { --index; // Null? @@ -449,7 +449,7 @@ class SimpleParameterList implements V3ParameterList { // Encoded string. if (encoded[index] == null) { - encoded[index] = Utils.encodeUTF8((String) paramValues[index]); + encoded[index] = Utils.encodeUTF8((String) paramValues[index], clientEncoding); } pgStream.send(encoded[index]); } diff --git a/pgjdbc/src/main/java/org/postgresql/core/v3/SimpleQuery.java b/pgjdbc/src/main/java/org/postgresql/core/v3/SimpleQuery.java index 1f5d3d63709ca9f6abe9bf9b250e9f7f87b26a8a..1fed3732ea3620a2b13c87f27544e46cdd1b02f4 100644 --- a/pgjdbc/src/main/java/org/postgresql/core/v3/SimpleQuery.java +++ b/pgjdbc/src/main/java/org/postgresql/core/v3/SimpleQuery.java @@ -127,10 +127,10 @@ class SimpleQuery implements Query { nativeQuery.nativeSql = sql; } - void setStatementName(String statementName, short deallocateEpoch) { + void setStatementName(String statementName, short deallocateEpoch, String clientEncoding) { assert statementName != null : "statement name should not be null"; this.statementName = statementName; - this.encodedStatementName = Utils.encodeUTF8(statementName); + this.encodedStatementName = Utils.encodeUTF8(statementName, clientEncoding); this.deallocateEpoch = deallocateEpoch; }