This is an automated email from the ASF dual-hosted git repository.

terrymanu pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/shardingsphere.git


The following commit(s) were added to refs/heads/master by this push:
     new 176a24adc50 Fix: force utf-8 encoding for PostgreSQL backend 
connections in proxy (#38645)
176a24adc50 is described below

commit 176a24adc50c1094196253c4c5b6eb49c5716f92
Author: KazenkE <[email protected]>
AuthorDate: Thu Jun 4 14:35:34 2026 +0800

    Fix: force utf-8 encoding for PostgreSQL backend connections in proxy 
(#38645)
    
    * force utf-8 encoding for PostgreSQL backend connections in proxy
    
    * remove redundant blank lines.
    
    * enforce UTF8-only client_encoding at startup and remove trailing 
whitespace.
    
    * fix the problem with check spotless.
    
    * normalize PostgreSQL client_encoding to UTF8 and prevent DEFAULT replay 
on RESET
    
    * Fix PostgreSQL startup client_encoding normalization to be 
locale-independent by using Locale.ROOT
    
    * fix the problem with check spotless.
---
 .../PostgreSQLResetVariableAdminExecutor.java      |  6 +-
 .../executor/PostgreSQLShowVariableExecutor.java   |  2 +-
 .../charset/PostgreSQLCharsetVariableProvider.java | 15 +++-
 .../PostgreSQLResetVariableAdminExecutorTest.java  | 29 +++++++
 .../PostgreSQLSetVariableAdminExecutorTest.java    | 29 +++++++
 .../PostgreSQLCharsetVariableProviderTest.java     |  4 +-
 .../PostgreSQLAuthenticationEngine.java            | 25 +++++-
 .../postgresql/command/query/extended/Portal.java  | 26 +++++-
 .../query/simple/PostgreSQLComQueryExecutor.java   | 25 +++++-
 .../PostgreSQLAuthenticationEngineTest.java        | 99 +++++++++++++++++++++-
 .../command/query/extended/PortalTest.java         | 52 +++++++++++-
 .../simple/PostgreSQLComQueryExecutorTest.java     | 58 +++++++++++++
 12 files changed, 353 insertions(+), 17 deletions(-)

diff --git 
a/proxy/backend/dialect/postgresql/src/main/java/org/apache/shardingsphere/proxy/backend/postgresql/handler/admin/executor/PostgreSQLResetVariableAdminExecutor.java
 
b/proxy/backend/dialect/postgresql/src/main/java/org/apache/shardingsphere/proxy/backend/postgresql/handler/admin/executor/PostgreSQLResetVariableAdminExecutor.java
index 0a03b80dd36..32c4ec0f87a 100644
--- 
a/proxy/backend/dialect/postgresql/src/main/java/org/apache/shardingsphere/proxy/backend/postgresql/handler/admin/executor/PostgreSQLResetVariableAdminExecutor.java
+++ 
b/proxy/backend/dialect/postgresql/src/main/java/org/apache/shardingsphere/proxy/backend/postgresql/handler/admin/executor/PostgreSQLResetVariableAdminExecutor.java
@@ -33,6 +33,8 @@ import 
org.apache.shardingsphere.sql.parser.statement.postgresql.dal.PostgreSQLR
 @RequiredArgsConstructor
 public final class PostgreSQLResetVariableAdminExecutor implements 
DatabaseAdminUpdateExecutor {
     
+    private static final String CLIENT_ENCODING = "client_encoding";
+    
     private static final String DEFAULT = "DEFAULT";
     
     private final DatabaseType databaseType = 
TypedSPILoader.getService(DatabaseType.class, "PostgreSQL");
@@ -43,6 +45,8 @@ public final class PostgreSQLResetVariableAdminExecutor 
implements DatabaseAdmin
     public void execute(final ConnectionSession connectionSession, final 
ShardingSphereMetaData metaData) {
         String variableName = 
resetParameterStatement.getConfigurationParameter();
         new CharsetSetExecutor(databaseType, 
connectionSession).set(variableName, DEFAULT);
-        new SessionVariableRecordExecutor(databaseType, 
connectionSession).recordVariable(variableName, DEFAULT);
+        if (!CLIENT_ENCODING.equalsIgnoreCase(variableName)) {
+            new SessionVariableRecordExecutor(databaseType, 
connectionSession).recordVariable(variableName, DEFAULT);
+        }
     }
 }
diff --git 
a/proxy/backend/dialect/postgresql/src/main/java/org/apache/shardingsphere/proxy/backend/postgresql/handler/admin/executor/PostgreSQLShowVariableExecutor.java
 
b/proxy/backend/dialect/postgresql/src/main/java/org/apache/shardingsphere/proxy/backend/postgresql/handler/admin/executor/PostgreSQLShowVariableExecutor.java
index 2c8f9ed9a5d..992d566dea1 100644
--- 
a/proxy/backend/dialect/postgresql/src/main/java/org/apache/shardingsphere/proxy/backend/postgresql/handler/admin/executor/PostgreSQLShowVariableExecutor.java
+++ 
b/proxy/backend/dialect/postgresql/src/main/java/org/apache/shardingsphere/proxy/backend/postgresql/handler/admin/executor/PostgreSQLShowVariableExecutor.java
@@ -50,7 +50,7 @@ public final class PostgreSQLShowVariableExecutor implements 
DatabaseAdminQueryE
     
     static {
         VARIABLE_ROW_DATA_GENERATORS.put("application_name", connectionSession 
-> new String[]{"application_name", "PostgreSQL", "Sets the application name to 
be reported in statistics and logs."});
-        VARIABLE_ROW_DATA_GENERATORS.put("client_encoding", connectionSession 
-> new String[]{"client_encoding", "UTF8", "Sets the client's character set 
encoding."});
+        VARIABLE_ROW_DATA_GENERATORS.put("client_encoding", connectionSession 
-> new String[]{"client_encoding", "UTF8", "Proxy policy fixes the backend 
client encoding to UTF8."});
         VARIABLE_ROW_DATA_GENERATORS.put("integer_datetimes", 
connectionSession -> new String[]{"integer_datetimes", "on", "Shows whether 
datetimes are integer based."});
         VARIABLE_ROW_DATA_GENERATORS.put("timezone", connectionSession -> new 
String[]{"TimeZone", "Etc/UTC", "Sets the time zone for displaying and 
interpreting time stamps."});
         VARIABLE_ROW_DATA_GENERATORS.put("transaction_isolation", 
connectionSession -> {
diff --git 
a/proxy/backend/dialect/postgresql/src/main/java/org/apache/shardingsphere/proxy/backend/postgresql/handler/admin/executor/variable/charset/PostgreSQLCharsetVariableProvider.java
 
b/proxy/backend/dialect/postgresql/src/main/java/org/apache/shardingsphere/proxy/backend/postgresql/handler/admin/executor/variable/charset/PostgreSQLCharsetVariableProvider.java
index 263904a65cc..a817f527ee6 100644
--- 
a/proxy/backend/dialect/postgresql/src/main/java/org/apache/shardingsphere/proxy/backend/postgresql/handler/admin/executor/variable/charset/PostgreSQLCharsetVariableProvider.java
+++ 
b/proxy/backend/dialect/postgresql/src/main/java/org/apache/shardingsphere/proxy/backend/postgresql/handler/admin/executor/variable/charset/PostgreSQLCharsetVariableProvider.java
@@ -17,10 +17,12 @@
 
 package 
org.apache.shardingsphere.proxy.backend.postgresql.handler.admin.executor.variable.charset;
 
+import 
org.apache.shardingsphere.database.connector.core.metadata.database.enums.QuoteCharacter;
 import 
org.apache.shardingsphere.database.exception.core.exception.data.InvalidParameterValueException;
 import 
org.apache.shardingsphere.proxy.backend.handler.admin.executor.variable.charset.CharsetVariableProvider;
 
 import java.nio.charset.Charset;
+import java.nio.charset.StandardCharsets;
 import java.util.Collection;
 import java.util.Collections;
 import java.util.Locale;
@@ -37,12 +39,17 @@ public final class PostgreSQLCharsetVariableProvider 
implements CharsetVariableP
     
     @Override
     public Charset parseCharset(final String variableValue) {
-        String formattedValue = variableValue.trim().toLowerCase(Locale.ROOT);
-        try {
-            return "default".equals(formattedValue) ? Charset.defaultCharset() 
: PostgreSQLCharacterSets.findCharacterSet(formattedValue);
-        } catch (final IllegalArgumentException ignored) {
+        String formattedValue = 
formatValue(variableValue).toLowerCase(Locale.ROOT);
+        boolean isDefault = "default".equals(formattedValue);
+        boolean isUtf8 = "utf8".equals(formattedValue) || 
"utf-8".equals(formattedValue) || "utf_8".equals(formattedValue) || 
"unicode".equals(formattedValue);
+        if (!isDefault && !isUtf8) {
             throw new InvalidParameterValueException("client_encoding", 
formattedValue);
         }
+        return StandardCharsets.UTF_8;
+    }
+    
+    private String formatValue(final String value) {
+        return QuoteCharacter.SINGLE_QUOTE.isWrapped(value) || 
QuoteCharacter.QUOTE.isWrapped(value) ? value.substring(1, value.length() - 
1).trim() : value.trim();
     }
     
     @Override
diff --git 
a/proxy/backend/dialect/postgresql/src/test/java/org/apache/shardingsphere/proxy/backend/postgresql/handler/admin/executor/PostgreSQLResetVariableAdminExecutorTest.java
 
b/proxy/backend/dialect/postgresql/src/test/java/org/apache/shardingsphere/proxy/backend/postgresql/handler/admin/executor/PostgreSQLResetVariableAdminExecutorTest.java
index fafaec9a6aa..8de105dbf11 100644
--- 
a/proxy/backend/dialect/postgresql/src/test/java/org/apache/shardingsphere/proxy/backend/postgresql/handler/admin/executor/PostgreSQLResetVariableAdminExecutorTest.java
+++ 
b/proxy/backend/dialect/postgresql/src/test/java/org/apache/shardingsphere/proxy/backend/postgresql/handler/admin/executor/PostgreSQLResetVariableAdminExecutorTest.java
@@ -17,22 +17,29 @@
 
 package 
org.apache.shardingsphere.proxy.backend.postgresql.handler.admin.executor;
 
+import io.netty.util.Attribute;
+import io.netty.util.AttributeMap;
 import 
org.apache.shardingsphere.database.connector.core.spi.DatabaseTypedSPILoader;
+import org.apache.shardingsphere.database.protocol.constant.CommonConstants;
 import org.apache.shardingsphere.database.connector.core.type.DatabaseType;
 import org.apache.shardingsphere.infra.spi.type.typed.TypedSPILoader;
 import 
org.apache.shardingsphere.proxy.backend.handler.admin.executor.variable.charset.CharsetVariableProvider;
 import 
org.apache.shardingsphere.proxy.backend.handler.admin.executor.variable.session.ReplayedSessionVariableProvider;
+import 
org.apache.shardingsphere.proxy.backend.postgresql.handler.admin.executor.variable.charset.PostgreSQLCharsetVariableProvider;
 import org.apache.shardingsphere.proxy.backend.session.ConnectionSession;
 import 
org.apache.shardingsphere.proxy.backend.session.RequiredSessionVariableRecorder;
 import 
org.apache.shardingsphere.sql.parser.statement.postgresql.dal.PostgreSQLResetParameterStatement;
 import org.junit.jupiter.api.Test;
 import org.mockito.MockedStatic;
 
+import java.nio.charset.Charset;
+import java.nio.charset.StandardCharsets;
 import java.util.Optional;
 
 import static org.mockito.Mockito.RETURNS_DEEP_STUBS;
 import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.mockStatic;
+import static org.mockito.Mockito.never;
 import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.when;
 
@@ -55,4 +62,26 @@ class PostgreSQLResetVariableAdminExecutorTest {
             verify(requiredSessionVariableRecorder).setVariable("key", 
"DEFAULT");
         }
     }
+    
+    @Test
+    void assertExecuteWithClientEncoding() {
+        PostgreSQLResetVariableAdminExecutor executor = new 
PostgreSQLResetVariableAdminExecutor(new 
PostgreSQLResetParameterStatement(databaseType, "client_encoding"));
+        ConnectionSession connectionSession = mock(ConnectionSession.class);
+        RequiredSessionVariableRecorder requiredSessionVariableRecorder = 
mock(RequiredSessionVariableRecorder.class);
+        AttributeMap attributeMap = mock(AttributeMap.class);
+        @SuppressWarnings("unchecked")
+        Attribute<Charset> attribute = mock(Attribute.class);
+        
when(connectionSession.getRequiredSessionVariableRecorder()).thenReturn(requiredSessionVariableRecorder);
+        when(connectionSession.getAttributeMap()).thenReturn(attributeMap);
+        
when(attributeMap.attr(CommonConstants.CHARSET_ATTRIBUTE_KEY)).thenReturn(attribute);
+        try (MockedStatic<DatabaseTypedSPILoader> databaseTypedSPILoader = 
mockStatic(DatabaseTypedSPILoader.class)) {
+            databaseTypedSPILoader.when(() -> 
DatabaseTypedSPILoader.findService(CharsetVariableProvider.class, 
databaseType)).thenReturn(Optional.of(new PostgreSQLCharsetVariableProvider()));
+            ReplayedSessionVariableProvider replayedSessionVariableProvider = 
mock(ReplayedSessionVariableProvider.class);
+            
when(replayedSessionVariableProvider.isNeedToReplay("client_encoding")).thenReturn(true);
+            databaseTypedSPILoader.when(() -> 
DatabaseTypedSPILoader.findService(ReplayedSessionVariableProvider.class, 
databaseType)).thenReturn(Optional.of(replayedSessionVariableProvider));
+            executor.execute(connectionSession, mock());
+            verify(attribute).set(StandardCharsets.UTF_8);
+            verify(requiredSessionVariableRecorder, 
never()).setVariable("client_encoding", "DEFAULT");
+        }
+    }
 }
diff --git 
a/proxy/backend/dialect/postgresql/src/test/java/org/apache/shardingsphere/proxy/backend/postgresql/handler/admin/executor/PostgreSQLSetVariableAdminExecutorTest.java
 
b/proxy/backend/dialect/postgresql/src/test/java/org/apache/shardingsphere/proxy/backend/postgresql/handler/admin/executor/PostgreSQLSetVariableAdminExecutorTest.java
index 342fd420058..968a38bcc3b 100644
--- 
a/proxy/backend/dialect/postgresql/src/test/java/org/apache/shardingsphere/proxy/backend/postgresql/handler/admin/executor/PostgreSQLSetVariableAdminExecutorTest.java
+++ 
b/proxy/backend/dialect/postgresql/src/test/java/org/apache/shardingsphere/proxy/backend/postgresql/handler/admin/executor/PostgreSQLSetVariableAdminExecutorTest.java
@@ -17,23 +17,33 @@
 
 package 
org.apache.shardingsphere.proxy.backend.postgresql.handler.admin.executor;
 
+import io.netty.util.Attribute;
+import io.netty.util.AttributeMap;
 import 
org.apache.shardingsphere.database.connector.core.spi.DatabaseTypedSPILoader;
+import org.apache.shardingsphere.database.protocol.constant.CommonConstants;
 import org.apache.shardingsphere.database.connector.core.type.DatabaseType;
+import 
org.apache.shardingsphere.database.exception.core.exception.data.InvalidParameterValueException;
 import org.apache.shardingsphere.infra.spi.type.typed.TypedSPILoader;
+import 
org.apache.shardingsphere.proxy.backend.handler.admin.executor.variable.charset.CharsetVariableProvider;
 import 
org.apache.shardingsphere.proxy.backend.handler.admin.executor.variable.session.ReplayedSessionVariableProvider;
 import org.apache.shardingsphere.proxy.backend.session.ConnectionSession;
 import 
org.apache.shardingsphere.proxy.backend.session.RequiredSessionVariableRecorder;
+import 
org.apache.shardingsphere.proxy.backend.postgresql.handler.admin.executor.variable.charset.PostgreSQLCharsetVariableProvider;
 import 
org.apache.shardingsphere.sql.parser.statement.core.segment.dal.VariableAssignSegment;
 import 
org.apache.shardingsphere.sql.parser.statement.core.segment.dal.VariableSegment;
 import 
org.apache.shardingsphere.sql.parser.statement.core.statement.type.dal.SetStatement;
 import org.junit.jupiter.api.Test;
 import org.mockito.MockedStatic;
 
+import java.nio.charset.Charset;
 import java.util.Collections;
 import java.util.Optional;
 
+import static org.junit.jupiter.api.Assertions.assertThrows;
+import static org.mockito.ArgumentMatchers.anyString;
 import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.mockStatic;
+import static org.mockito.Mockito.never;
 import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.when;
 
@@ -56,4 +66,23 @@ class PostgreSQLSetVariableAdminExecutorTest {
             verify(requiredSessionVariableRecorder).setVariable("key", 
"value");
         }
     }
+    
+    @Test
+    void assertExecuteWithInvalidClientEncoding() {
+        SetStatement setStatement = new SetStatement(databaseType, 
Collections.singletonList(new VariableAssignSegment(0, 0, new 
VariableSegment(0, 0, "client_encoding"), "'LATIN1'")));
+        PostgreSQLSetVariableAdminExecutor executor = new 
PostgreSQLSetVariableAdminExecutor(setStatement);
+        ConnectionSession connectionSession = mock(ConnectionSession.class);
+        RequiredSessionVariableRecorder requiredSessionVariableRecorder = 
mock(RequiredSessionVariableRecorder.class);
+        AttributeMap attributeMap = mock(AttributeMap.class);
+        @SuppressWarnings("unchecked")
+        Attribute<Charset> attribute = mock(Attribute.class);
+        
when(connectionSession.getRequiredSessionVariableRecorder()).thenReturn(requiredSessionVariableRecorder);
+        when(connectionSession.getAttributeMap()).thenReturn(attributeMap);
+        
when(attributeMap.attr(CommonConstants.CHARSET_ATTRIBUTE_KEY)).thenReturn(attribute);
+        try (MockedStatic<DatabaseTypedSPILoader> databaseTypedSPILoader = 
mockStatic(DatabaseTypedSPILoader.class)) {
+            databaseTypedSPILoader.when(() -> 
DatabaseTypedSPILoader.findService(CharsetVariableProvider.class, 
databaseType)).thenReturn(Optional.of(new PostgreSQLCharsetVariableProvider()));
+            assertThrows(InvalidParameterValueException.class, () -> 
executor.execute(connectionSession, mock()));
+            verify(requiredSessionVariableRecorder, 
never()).setVariable(anyString(), anyString());
+        }
+    }
 }
diff --git 
a/proxy/backend/dialect/postgresql/src/test/java/org/apache/shardingsphere/proxy/backend/postgresql/handler/admin/executor/variable/charset/PostgreSQLCharsetVariableProviderTest.java
 
b/proxy/backend/dialect/postgresql/src/test/java/org/apache/shardingsphere/proxy/backend/postgresql/handler/admin/executor/variable/charset/PostgreSQLCharsetVariableProviderTest.java
index 05d03a499fb..be024876416 100644
--- 
a/proxy/backend/dialect/postgresql/src/test/java/org/apache/shardingsphere/proxy/backend/postgresql/handler/admin/executor/variable/charset/PostgreSQLCharsetVariableProviderTest.java
+++ 
b/proxy/backend/dialect/postgresql/src/test/java/org/apache/shardingsphere/proxy/backend/postgresql/handler/admin/executor/variable/charset/PostgreSQLCharsetVariableProviderTest.java
@@ -46,7 +46,7 @@ class PostgreSQLCharsetVariableProviderTest {
     @Test
     void assertParseDefaultCharset() {
         Charset actual = provider.parseCharset("default");
-        assertThat(actual, is(Charset.defaultCharset()));
+        assertThat(actual, is(StandardCharsets.UTF_8));
     }
     
     @Test
@@ -57,6 +57,6 @@ class PostgreSQLCharsetVariableProviderTest {
     
     @Test
     void assertParseInvalidCharset() {
-        assertThrows(InvalidParameterValueException.class, () -> 
provider.parseCharset("invalid_charset"));
+        assertThrows(InvalidParameterValueException.class, () -> 
provider.parseCharset("latin1"));
     }
 }
diff --git 
a/proxy/frontend/dialect/postgresql/src/main/java/org/apache/shardingsphere/proxy/frontend/postgresql/authentication/PostgreSQLAuthenticationEngine.java
 
b/proxy/frontend/dialect/postgresql/src/main/java/org/apache/shardingsphere/proxy/frontend/postgresql/authentication/PostgreSQLAuthenticationEngine.java
index 49bff692119..4cc200b501e 100644
--- 
a/proxy/frontend/dialect/postgresql/src/main/java/org/apache/shardingsphere/proxy/frontend/postgresql/authentication/PostgreSQLAuthenticationEngine.java
+++ 
b/proxy/frontend/dialect/postgresql/src/main/java/org/apache/shardingsphere/proxy/frontend/postgresql/authentication/PostgreSQLAuthenticationEngine.java
@@ -28,6 +28,7 @@ import 
org.apache.shardingsphere.authority.checker.AuthorityChecker;
 import org.apache.shardingsphere.authority.rule.AuthorityRule;
 import org.apache.shardingsphere.database.connector.core.type.DatabaseType;
 import 
org.apache.shardingsphere.database.exception.core.exception.syntax.database.UnknownDatabaseException;
+import 
org.apache.shardingsphere.database.exception.core.exception.data.InvalidParameterValueException;
 import 
org.apache.shardingsphere.database.exception.postgresql.exception.authority.EmptyUsernameException;
 import 
org.apache.shardingsphere.database.exception.postgresql.exception.authority.InvalidPasswordException;
 import 
org.apache.shardingsphere.database.exception.postgresql.exception.authority.PrivilegeNotGrantedException;
@@ -61,6 +62,8 @@ import 
org.apache.shardingsphere.proxy.frontend.connection.ConnectionIdGenerator
 import 
org.apache.shardingsphere.proxy.frontend.postgresql.authentication.authenticator.PostgreSQLAuthenticatorType;
 import org.apache.shardingsphere.proxy.frontend.ssl.ProxySSLContext;
 
+import java.nio.charset.StandardCharsets;
+import java.util.Locale;
 import java.util.Optional;
 
 /**
@@ -132,17 +135,33 @@ public final class PostgreSQLAuthenticationEngine 
implements AuthenticationEngin
     }
     
     private AuthenticationResult processStartupMessage(final 
ChannelHandlerContext context, final PostgreSQLPacketPayload payload, final 
AuthorityRule rule) {
-        startupMessageReceived = true;
         PostgreSQLComStartupPacket startupPacket = new 
PostgreSQLComStartupPacket(payload);
-        clientEncoding = startupPacket.getClientEncoding();
-        
context.channel().attr(CommonConstants.CHARSET_ATTRIBUTE_KEY).set(PostgreSQLCharacterSets.findCharacterSet(clientEncoding));
+        clientEncoding = 
getNormalizedClientEncoding(startupPacket.getClientEncoding());
+        
context.channel().attr(CommonConstants.CHARSET_ATTRIBUTE_KEY).set(StandardCharsets.UTF_8);
         String username = startupPacket.getUsername();
         ShardingSpherePreconditions.checkNotEmpty(username, 
EmptyUsernameException::new);
+        startupMessageReceived = true;
         context.writeAndFlush(getIdentifierPacket(username, rule));
         currentAuthResult = AuthenticationResultBuilder.continued(username, 
"", startupPacket.getDatabase());
         return currentAuthResult;
     }
     
+    private String getNormalizedClientEncoding(final String clientEncoding) {
+        String formattedClientEncoding = formatClientEncoding(clientEncoding);
+        String lowerCaseClientEncoding = 
formattedClientEncoding.toLowerCase(Locale.ROOT);
+        boolean isDefault = "default".equals(lowerCaseClientEncoding);
+        boolean isUtf8 = "utf8".equals(lowerCaseClientEncoding) || 
"utf-8".equals(lowerCaseClientEncoding) || 
"utf_8".equals(lowerCaseClientEncoding)
+                || "unicode".equals(lowerCaseClientEncoding);
+        if (!isDefault && !isUtf8) {
+            throw new InvalidParameterValueException("client_encoding", 
lowerCaseClientEncoding);
+        }
+        return PostgreSQLCharacterSets.UTF8.name();
+    }
+    
+    private String formatClientEncoding(final String value) {
+        return value.startsWith("'") && value.endsWith("'") || 
value.startsWith("\"") && value.endsWith("\"") ? value.substring(1, 
value.length() - 1).trim() : value.trim();
+    }
+    
     private PostgreSQLIdentifierPacket getIdentifierPacket(final String 
username, final AuthorityRule rule) {
         Optional<Authenticator> authenticator = rule.findUser(new 
Grantee(username)).map(optional -> new 
AuthenticatorFactory<>(PostgreSQLAuthenticatorType.class, 
rule).newInstance(optional));
         if (authenticator.isPresent() && 
PostgreSQLAuthenticationMethod.PASSWORD.getMethodName().equals(authenticator.get().getAuthenticationMethodName()))
 {
diff --git 
a/proxy/frontend/dialect/postgresql/src/main/java/org/apache/shardingsphere/proxy/frontend/postgresql/command/query/extended/Portal.java
 
b/proxy/frontend/dialect/postgresql/src/main/java/org/apache/shardingsphere/proxy/frontend/postgresql/command/query/extended/Portal.java
index 954a292c53d..504c97bb980 100644
--- 
a/proxy/frontend/dialect/postgresql/src/main/java/org/apache/shardingsphere/proxy/frontend/postgresql/command/query/extended/Portal.java
+++ 
b/proxy/frontend/dialect/postgresql/src/main/java/org/apache/shardingsphere/proxy/frontend/postgresql/command/query/extended/Portal.java
@@ -54,6 +54,7 @@ import 
org.apache.shardingsphere.sql.parser.statement.core.segment.dal.VariableA
 import 
org.apache.shardingsphere.sql.parser.statement.core.statement.SQLStatement;
 import 
org.apache.shardingsphere.sql.parser.statement.core.statement.type.dal.EmptyStatement;
 import 
org.apache.shardingsphere.sql.parser.statement.core.statement.type.dal.SetStatement;
+import 
org.apache.shardingsphere.sql.parser.statement.postgresql.dal.PostgreSQLResetParameterStatement;
 
 import java.sql.SQLException;
 import java.util.ArrayList;
@@ -66,6 +67,10 @@ import java.util.List;
  */
 public final class Portal {
     
+    private static final String CLIENT_ENCODING = "client_encoding";
+    
+    private static final String UTF8 = "UTF8";
+    
     @Getter
     private final String name;
     
@@ -153,6 +158,10 @@ public final class Portal {
             result.addAll(createParameterStatusResponse((SetStatement) 
sqlStatement));
             return result;
         }
+        if (responseHeader instanceof UpdateResponseHeader && sqlStatement 
instanceof PostgreSQLResetParameterStatement) {
+            
result.addAll(createParameterStatusResponse((PostgreSQLResetParameterStatement) 
sqlStatement));
+            return result;
+        }
         result.add(createExecutionCompletedPacket(maxRows > 0 && maxRows == 
result.size(), result.size()));
         return result;
     }
@@ -161,11 +170,26 @@ public final class Portal {
         List<PostgreSQLPacket> result = new ArrayList<>(2);
         result.add(new PostgreSQLCommandCompletePacket("SET", 0L));
         for (VariableAssignSegment each : sqlStatement.getVariableAssigns()) {
-            result.add(new 
PostgreSQLParameterStatusPacket(each.getVariable().getVariable(), null == 
each.getAssignValue() ? null : 
QuoteCharacter.unwrapText(each.getAssignValue())));
+            String variableName = each.getVariable().getVariable();
+            String variableValue = isClientEncodingVariable(variableName) ? 
UTF8 : null == each.getAssignValue() ? null : 
QuoteCharacter.unwrapText(each.getAssignValue());
+            result.add(new PostgreSQLParameterStatusPacket(variableName, 
variableValue));
         }
         return result;
     }
     
+    private List<PostgreSQLPacket> createParameterStatusResponse(final 
PostgreSQLResetParameterStatement sqlStatement) {
+        List<PostgreSQLPacket> result = new ArrayList<>(2);
+        result.add(new PostgreSQLCommandCompletePacket("RESET", 0L));
+        if 
(isClientEncodingVariable(sqlStatement.getConfigurationParameter())) {
+            result.add(new PostgreSQLParameterStatusPacket(CLIENT_ENCODING, 
UTF8));
+        }
+        return result;
+    }
+    
+    private boolean isClientEncodingVariable(final String variableName) {
+        return CLIENT_ENCODING.equalsIgnoreCase(variableName);
+    }
+    
     private boolean hasNext() throws SQLException {
         return proxyBackendHandler.next();
     }
diff --git 
a/proxy/frontend/dialect/postgresql/src/main/java/org/apache/shardingsphere/proxy/frontend/postgresql/command/query/simple/PostgreSQLComQueryExecutor.java
 
b/proxy/frontend/dialect/postgresql/src/main/java/org/apache/shardingsphere/proxy/frontend/postgresql/command/query/simple/PostgreSQLComQueryExecutor.java
index 06167baf41e..90eb68e68f1 100644
--- 
a/proxy/frontend/dialect/postgresql/src/main/java/org/apache/shardingsphere/proxy/frontend/postgresql/command/query/simple/PostgreSQLComQueryExecutor.java
+++ 
b/proxy/frontend/dialect/postgresql/src/main/java/org/apache/shardingsphere/proxy/frontend/postgresql/command/query/simple/PostgreSQLComQueryExecutor.java
@@ -48,6 +48,7 @@ import 
org.apache.shardingsphere.sql.parser.statement.core.statement.type.dal.Em
 import 
org.apache.shardingsphere.sql.parser.statement.core.statement.type.dal.SetStatement;
 import 
org.apache.shardingsphere.sql.parser.statement.core.statement.type.tcl.CommitStatement;
 import 
org.apache.shardingsphere.sql.parser.statement.core.statement.type.tcl.RollbackStatement;
+import 
org.apache.shardingsphere.sql.parser.statement.postgresql.dal.PostgreSQLResetParameterStatement;
 
 import java.sql.SQLException;
 import java.util.ArrayList;
@@ -60,6 +61,10 @@ import java.util.LinkedList;
  */
 public final class PostgreSQLComQueryExecutor implements QueryCommandExecutor {
     
+    private static final String CLIENT_ENCODING = "client_encoding";
+    
+    private static final String UTF8 = "UTF8";
+    
     private final PortalContext portalContext;
     
     private final ProxyBackendHandler proxyBackendHandler;
@@ -106,6 +111,9 @@ public final class PostgreSQLComQueryExecutor implements 
QueryCommandExecutor {
         if (sqlStatement instanceof SetStatement) {
             return createParameterStatusResponse((SetStatement) sqlStatement);
         }
+        if (sqlStatement instanceof PostgreSQLResetParameterStatement) {
+            return 
createParameterStatusResponse((PostgreSQLResetParameterStatement) sqlStatement);
+        }
         return Collections.singletonList(sqlStatement instanceof EmptyStatement
                 ? new PostgreSQLEmptyQueryResponsePacket()
                 : new 
PostgreSQLCommandCompletePacket(PostgreSQLCommand.valueOf(sqlStatement.getClass()).map(PostgreSQLCommand::getTag).orElse(""),
 updateResponseHeader.getUpdateCount()));
@@ -115,11 +123,26 @@ public final class PostgreSQLComQueryExecutor implements 
QueryCommandExecutor {
         Collection<DatabasePacket> result = new ArrayList<>(2);
         result.add(new PostgreSQLCommandCompletePacket("SET", 0L));
         for (VariableAssignSegment each : sqlStatement.getVariableAssigns()) {
-            result.add(new 
PostgreSQLParameterStatusPacket(each.getVariable().getVariable(), null == 
each.getAssignValue() ? null : 
QuoteCharacter.unwrapText(each.getAssignValue())));
+            String variableName = each.getVariable().getVariable();
+            String variableValue = isClientEncodingVariable(variableName) ? 
UTF8 : null == each.getAssignValue() ? null : 
QuoteCharacter.unwrapText(each.getAssignValue());
+            result.add(new PostgreSQLParameterStatusPacket(variableName, 
variableValue));
+        }
+        return result;
+    }
+    
+    private Collection<DatabasePacket> createParameterStatusResponse(final 
PostgreSQLResetParameterStatement sqlStatement) {
+        Collection<DatabasePacket> result = new ArrayList<>(2);
+        result.add(new PostgreSQLCommandCompletePacket("RESET", 0L));
+        if 
(isClientEncodingVariable(sqlStatement.getConfigurationParameter())) {
+            result.add(new PostgreSQLParameterStatusPacket(CLIENT_ENCODING, 
UTF8));
         }
         return result;
     }
     
+    private boolean isClientEncodingVariable(final String variableName) {
+        return CLIENT_ENCODING.equalsIgnoreCase(variableName);
+    }
+    
     @Override
     public boolean next() throws SQLException {
         return proxyBackendHandler.next();
diff --git 
a/proxy/frontend/dialect/postgresql/src/test/java/org/apache/shardingsphere/proxy/frontend/postgresql/authentication/PostgreSQLAuthenticationEngineTest.java
 
b/proxy/frontend/dialect/postgresql/src/test/java/org/apache/shardingsphere/proxy/frontend/postgresql/authentication/PostgreSQLAuthenticationEngineTest.java
index 58329757ff8..33e8d323c88 100644
--- 
a/proxy/frontend/dialect/postgresql/src/test/java/org/apache/shardingsphere/proxy/frontend/postgresql/authentication/PostgreSQLAuthenticationEngineTest.java
+++ 
b/proxy/frontend/dialect/postgresql/src/test/java/org/apache/shardingsphere/proxy/frontend/postgresql/authentication/PostgreSQLAuthenticationEngineTest.java
@@ -34,6 +34,7 @@ import org.apache.shardingsphere.authority.rule.AuthorityRule;
 import org.apache.shardingsphere.authority.rule.builder.AuthorityRuleBuilder;
 import org.apache.shardingsphere.database.connector.core.type.DatabaseType;
 import 
org.apache.shardingsphere.database.exception.core.exception.syntax.database.UnknownDatabaseException;
+import 
org.apache.shardingsphere.database.exception.core.exception.data.InvalidParameterValueException;
 import 
org.apache.shardingsphere.database.exception.postgresql.exception.authority.EmptyUsernameException;
 import 
org.apache.shardingsphere.database.exception.postgresql.exception.authority.InvalidPasswordException;
 import 
org.apache.shardingsphere.database.exception.postgresql.exception.authority.PrivilegeNotGrantedException;
@@ -43,6 +44,7 @@ import 
org.apache.shardingsphere.database.protocol.constant.CommonConstants;
 import 
org.apache.shardingsphere.database.protocol.postgresql.constant.PostgreSQLAuthenticationMethod;
 import 
org.apache.shardingsphere.database.protocol.postgresql.packet.generic.PostgreSQLReadyForQueryPacket;
 import 
org.apache.shardingsphere.database.protocol.postgresql.packet.handshake.PostgreSQLAuthenticationOKPacket;
+import 
org.apache.shardingsphere.database.protocol.postgresql.packet.handshake.PostgreSQLParameterStatusPacket;
 import 
org.apache.shardingsphere.database.protocol.postgresql.packet.handshake.PostgreSQLSSLUnwillingPacket;
 import 
org.apache.shardingsphere.database.protocol.postgresql.packet.handshake.PostgreSQLSSLWillingPacket;
 import 
org.apache.shardingsphere.database.protocol.postgresql.packet.handshake.authentication.PostgreSQLPasswordAuthenticationPacket;
@@ -72,9 +74,14 @@ import org.mockito.ArgumentCaptor;
 import org.mockito.Mock;
 import org.mockito.internal.configuration.plugins.Plugins;
 
+import java.nio.charset.Charset;
 import java.nio.charset.StandardCharsets;
 import java.security.MessageDigest;
+import java.util.Collection;
 import java.util.Collections;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Locale;
 import java.util.Map;
 import java.util.Optional;
 import java.util.Properties;
@@ -87,7 +94,9 @@ import static org.junit.jupiter.api.Assertions.assertTrue;
 import static org.mockito.ArgumentMatchers.any;
 import static org.mockito.ArgumentMatchers.eq;
 import static org.mockito.Mockito.RETURNS_DEEP_STUBS;
+import static org.mockito.Mockito.atLeastOnce;
 import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.never;
 import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.when;
 
@@ -106,10 +115,13 @@ class PostgreSQLAuthenticationEngineTest {
     @Mock(answer = Answers.RETURNS_DEEP_STUBS)
     private ChannelHandlerContext channelHandlerContext;
     
+    @Mock
+    private Attribute<Charset> charsetAttribute;
+    
     @SuppressWarnings("unchecked")
     @BeforeEach
     void setup() {
-        
when(channelHandlerContext.channel().attr(CommonConstants.CHARSET_ATTRIBUTE_KEY)).thenReturn(mock(Attribute.class));
+        
when(channelHandlerContext.channel().attr(CommonConstants.CHARSET_ATTRIBUTE_KEY)).thenReturn(charsetAttribute);
     }
     
     @SneakyThrows(ReflectiveOperationException.class)
@@ -184,6 +196,17 @@ class PostgreSQLAuthenticationEngineTest {
         assertFalse(actual.isFinished());
     }
     
+    @Test
+    void assertStartupRejectsInvalidClientEncoding() {
+        AuthorityRule authorityRule = createAuthorityRule(new 
UserConfiguration(USERNAME, PASSWORD, "", null, false), Collections.emptyMap(), 
null);
+        MetaDataContexts metaDataContexts = 
createMetaDataContexts(authorityRule, false, null);
+        ContextManager contextManager = mockContextManager(metaDataContexts);
+        
when(ProxyContext.getInstance().getContextManager()).thenReturn(contextManager);
+        PostgreSQLPacketPayload payload = createStartupPayload(USERNAME, 
DATABASE_NAME, "LATIN1");
+        assertThrows(InvalidParameterValueException.class, () -> 
authenticationEngine.authenticate(channelHandlerContext, payload));
+        verify(channelHandlerContext, 
never()).writeAndFlush(any(PostgreSQLPasswordAuthenticationPacket.class));
+    }
+    
     @Test
     void assertAuthenticateWithNonPasswordMessage() {
         setAlreadyReceivedStartupMessage(authenticationEngine);
@@ -289,11 +312,45 @@ class PostgreSQLAuthenticationEngineTest {
         assertTrue(actual.isFinished());
     }
     
+    @Test
+    void assertStartupUsesDefaultClientEncoding() throws 
ReflectiveOperationException {
+        assertAuthenticateAndClientEncodingParameterStatus(USERNAME, 
DATABASE_NAME, null);
+        verify(charsetAttribute).set(StandardCharsets.UTF_8);
+    }
+    
+    @Test
+    void assertStartupNormalizesUtf8DashAlias() throws 
ReflectiveOperationException {
+        assertAuthenticateAndClientEncodingParameterStatus(USERNAME, 
DATABASE_NAME, "UTF-8");
+        verify(charsetAttribute).set(StandardCharsets.UTF_8);
+    }
+    
+    @Test
+    void assertStartupNormalizesUnicodeAlias() throws 
ReflectiveOperationException {
+        assertAuthenticateAndClientEncodingParameterStatus(USERNAME, 
DATABASE_NAME, "UNICODE");
+        verify(charsetAttribute).set(StandardCharsets.UTF_8);
+    }
+    
+    @Test
+    void assertStartupNormalizesUnicodeAliasUnderTurkishLocale() throws 
ReflectiveOperationException {
+        Locale originalLocale = Locale.getDefault();
+        try {
+            Locale.setDefault(new Locale("tr", "TR"));
+            assertAuthenticateAndClientEncodingParameterStatus(USERNAME, 
DATABASE_NAME, "UNICODE");
+            verify(charsetAttribute).set(StandardCharsets.UTF_8);
+        } finally {
+            Locale.setDefault(originalLocale);
+        }
+    }
+    
     private ByteBuf createByteBuf(final int initialCapacity, final int 
maxCapacity) {
         return new UnpooledHeapByteBuf(UnpooledByteBufAllocator.DEFAULT, 
initialCapacity, maxCapacity);
     }
     
     private PostgreSQLPacketPayload createStartupPayload(final String 
username, final String databaseName) {
+        return createStartupPayload(username, databaseName, "UTF8");
+    }
+    
+    private PostgreSQLPacketPayload createStartupPayload(final String 
username, final String databaseName, final String clientEncoding) {
         PostgreSQLPacketPayload payload = new 
PostgreSQLPacketPayload(createByteBuf(32, 256), StandardCharsets.UTF_8);
         payload.writeInt4(64);
         payload.writeInt4(196608);
@@ -303,8 +360,10 @@ class PostgreSQLAuthenticationEngineTest {
             payload.writeStringNul("database");
             payload.writeStringNul(databaseName);
         }
-        payload.writeStringNul("client_encoding");
-        payload.writeStringNul("UTF8");
+        if (null != clientEncoding) {
+            payload.writeStringNul("client_encoding");
+            payload.writeStringNul(clientEncoding);
+        }
         return payload;
     }
     
@@ -355,4 +414,38 @@ class PostgreSQLAuthenticationEngineTest {
     private byte[] getMd5Salt(final PostgreSQLAuthenticationEngine target) {
         return (byte[]) 
Plugins.getMemberAccessor().get(PostgreSQLAuthenticationEngine.class.getDeclaredField("md5Salt"),
 target);
     }
+    
+    private void assertAuthenticateAndClientEncodingParameterStatus(final 
String username, final String databaseName, final String startupClientEncoding) 
throws ReflectiveOperationException {
+        AuthorityRule authorityRule = createAuthorityRule(new 
UserConfiguration(username, PASSWORD, "", null, false), Collections.emptyMap(), 
null);
+        MetaDataContexts metaDataContexts = 
createMetaDataContexts(authorityRule, true, databaseName);
+        ContextManager contextManager = mockContextManager(metaDataContexts);
+        
when(ProxyContext.getInstance().getContextManager()).thenReturn(contextManager);
+        authenticationEngine.authenticate(channelHandlerContext, 
createStartupPayload(username, databaseName, startupClientEncoding));
+        byte[] md5Salt = getMd5Salt(authenticationEngine);
+        authenticationEngine.authenticate(channelHandlerContext, 
createPasswordMessage(createMd5Digest(username, PASSWORD, md5Salt)));
+        ArgumentCaptor<Object> writeArgumentCaptor = 
ArgumentCaptor.forClass(Object.class);
+        verify(channelHandlerContext, 
atLeastOnce()).write(writeArgumentCaptor.capture());
+        String actualClientEncoding = 
getClientEncodingValue(extractParameterStatusPackets(writeArgumentCaptor.getAllValues()));
+        assertThat(actualClientEncoding, is("UTF8"));
+    }
+    
+    private Collection<PostgreSQLParameterStatusPacket> 
extractParameterStatusPackets(final List<Object> packets) {
+        Collection<PostgreSQLParameterStatusPacket> result = new 
LinkedList<>();
+        for (Object each : packets) {
+            if (each instanceof PostgreSQLParameterStatusPacket) {
+                result.add((PostgreSQLParameterStatusPacket) each);
+            }
+        }
+        return result;
+    }
+    
+    private String getClientEncodingValue(final 
Collection<PostgreSQLParameterStatusPacket> packets) throws 
ReflectiveOperationException {
+        for (PostgreSQLParameterStatusPacket each : packets) {
+            String actualKey = (String) 
Plugins.getMemberAccessor().get(PostgreSQLParameterStatusPacket.class.getDeclaredField("key"),
 each);
+            if ("client_encoding".equals(actualKey)) {
+                return (String) 
Plugins.getMemberAccessor().get(PostgreSQLParameterStatusPacket.class.getDeclaredField("value"),
 each);
+            }
+        }
+        return "";
+    }
 }
diff --git 
a/proxy/frontend/dialect/postgresql/src/test/java/org/apache/shardingsphere/proxy/frontend/postgresql/command/query/extended/PortalTest.java
 
b/proxy/frontend/dialect/postgresql/src/test/java/org/apache/shardingsphere/proxy/frontend/postgresql/command/query/extended/PortalTest.java
index f5b82b99d32..1aa91f84c6a 100644
--- 
a/proxy/frontend/dialect/postgresql/src/test/java/org/apache/shardingsphere/proxy/frontend/postgresql/command/query/extended/PortalTest.java
+++ 
b/proxy/frontend/dialect/postgresql/src/test/java/org/apache/shardingsphere/proxy/frontend/postgresql/command/query/extended/PortalTest.java
@@ -31,6 +31,7 @@ import 
org.apache.shardingsphere.database.protocol.postgresql.packet.command.que
 import 
org.apache.shardingsphere.database.protocol.postgresql.packet.command.query.extended.execute.PostgreSQLPortalSuspendedPacket;
 import 
org.apache.shardingsphere.database.protocol.postgresql.packet.generic.PostgreSQLCommandCompletePacket;
 import 
org.apache.shardingsphere.database.protocol.postgresql.packet.handshake.PostgreSQLParameterStatusPacket;
+import 
org.apache.shardingsphere.database.exception.core.exception.data.InvalidParameterValueException;
 import 
org.apache.shardingsphere.infra.binder.context.statement.SQLStatementContext;
 import 
org.apache.shardingsphere.infra.binder.context.statement.type.CommonSQLStatementContext;
 import 
org.apache.shardingsphere.infra.binder.context.statement.type.dml.InsertStatementContext;
@@ -58,6 +59,7 @@ import 
org.apache.shardingsphere.sql.parser.statement.core.statement.type.dal.Em
 import 
org.apache.shardingsphere.sql.parser.statement.core.statement.type.dal.SetStatement;
 import 
org.apache.shardingsphere.sql.parser.statement.core.statement.type.dml.InsertStatement;
 import 
org.apache.shardingsphere.sql.parser.statement.core.statement.type.dml.SelectStatement;
+import 
org.apache.shardingsphere.sql.parser.statement.postgresql.dal.PostgreSQLResetParameterStatement;
 import 
org.apache.shardingsphere.test.infra.framework.extension.mock.AutoMockExtension;
 import 
org.apache.shardingsphere.test.infra.framework.extension.mock.StaticMockSettings;
 import org.junit.jupiter.api.BeforeEach;
@@ -321,7 +323,55 @@ class PortalTest {
         PostgreSQLParameterStatusPacket parameterStatusPacket = 
(PostgreSQLParameterStatusPacket) actualPackets.get(1);
         String actualValue = (String) 
Plugins.getMemberAccessor().get(PostgreSQLParameterStatusPacket.class.getDeclaredField("value"),
 parameterStatusPacket);
         assertThat(actualPackets.get(0), 
isA(PostgreSQLCommandCompletePacket.class));
-        assertThat(actualValue, is("utf8"));
+        assertThat(actualValue, is("UTF8"));
+    }
+    
+    @Test
+    void assertExecuteSetStatementWithDefaultClientEncoding() throws 
SQLException, ReflectiveOperationException {
+        UpdateResponseHeader responseHeader = mock(UpdateResponseHeader.class);
+        when(proxyBackendHandler.execute()).thenReturn(responseHeader);
+        when(proxyBackendHandler.next()).thenReturn(false);
+        VariableAssignSegment assignSegment = new VariableAssignSegment(0, 0, 
new VariableSegment(0, 0, "client_encoding"), "DEFAULT");
+        SetStatement setStatement = new SetStatement(databaseType, 
Collections.singletonList(assignSegment));
+        PostgreSQLServerPreparedStatement preparedStatement = new 
PostgreSQLServerPreparedStatement(
+                "", new CommonSQLStatementContext(setStatement), new 
HintValueContext(), Collections.emptyList(), Collections.emptyList());
+        Portal portal = new Portal("", preparedStatement, 
Collections.emptyList(), Collections.emptyList(), databaseConnectionManager);
+        portal.bind();
+        List<DatabasePacket> actualPackets = portal.execute(0);
+        PostgreSQLParameterStatusPacket parameterStatusPacket = 
(PostgreSQLParameterStatusPacket) actualPackets.get(1);
+        String actualValue = (String) 
Plugins.getMemberAccessor().get(PostgreSQLParameterStatusPacket.class.getDeclaredField("value"),
 parameterStatusPacket);
+        assertThat(actualValue, is("UTF8"));
+    }
+    
+    @Test
+    void assertExecuteResetClientEncoding() throws SQLException, 
ReflectiveOperationException {
+        UpdateResponseHeader responseHeader = mock(UpdateResponseHeader.class);
+        when(proxyBackendHandler.execute()).thenReturn(responseHeader);
+        when(proxyBackendHandler.next()).thenReturn(false);
+        PostgreSQLResetParameterStatement resetStatement = new 
PostgreSQLResetParameterStatement(databaseType, "client_encoding");
+        PostgreSQLServerPreparedStatement preparedStatement = new 
PostgreSQLServerPreparedStatement(
+                "", new CommonSQLStatementContext(resetStatement), new 
HintValueContext(), Collections.emptyList(), Collections.emptyList());
+        Portal portal = new Portal("", preparedStatement, 
Collections.emptyList(), Collections.emptyList(), databaseConnectionManager);
+        portal.bind();
+        List<DatabasePacket> actualPackets = portal.execute(0);
+        PostgreSQLCommandCompletePacket commandCompletePacket = 
(PostgreSQLCommandCompletePacket) actualPackets.get(0);
+        PostgreSQLParameterStatusPacket parameterStatusPacket = 
(PostgreSQLParameterStatusPacket) actualPackets.get(1);
+        String command = (String) 
Plugins.getMemberAccessor().get(PostgreSQLCommandCompletePacket.class.getDeclaredField("sqlCommand"),
 commandCompletePacket);
+        String actualValue = (String) 
Plugins.getMemberAccessor().get(PostgreSQLParameterStatusPacket.class.getDeclaredField("value"),
 parameterStatusPacket);
+        assertThat(actualPackets.size(), is(2));
+        assertThat(command, is("RESET"));
+        assertThat(actualValue, is("UTF8"));
+    }
+    
+    @Test
+    void assertBindWithInvalidClientEncoding() throws SQLException {
+        when(proxyBackendHandler.execute()).thenThrow(new 
InvalidParameterValueException("client_encoding", "latin1"));
+        VariableAssignSegment assignSegment = new VariableAssignSegment(0, 0, 
new VariableSegment(0, 0, "client_encoding"), "'LATIN1'");
+        SetStatement setStatement = new SetStatement(databaseType, 
Collections.singletonList(assignSegment));
+        PostgreSQLServerPreparedStatement preparedStatement = new 
PostgreSQLServerPreparedStatement(
+                "", new CommonSQLStatementContext(setStatement), new 
HintValueContext(), Collections.emptyList(), Collections.emptyList());
+        Portal portal = new Portal("", preparedStatement, 
Collections.emptyList(), Collections.emptyList(), databaseConnectionManager);
+        assertThrows(InvalidParameterValueException.class, portal::bind);
     }
     
     @Test
diff --git 
a/proxy/frontend/dialect/postgresql/src/test/java/org/apache/shardingsphere/proxy/frontend/postgresql/command/query/simple/PostgreSQLComQueryExecutorTest.java
 
b/proxy/frontend/dialect/postgresql/src/test/java/org/apache/shardingsphere/proxy/frontend/postgresql/command/query/simple/PostgreSQLComQueryExecutorTest.java
index 698240f598e..00cf2bb5984 100644
--- 
a/proxy/frontend/dialect/postgresql/src/test/java/org/apache/shardingsphere/proxy/frontend/postgresql/command/query/simple/PostgreSQLComQueryExecutorTest.java
+++ 
b/proxy/frontend/dialect/postgresql/src/test/java/org/apache/shardingsphere/proxy/frontend/postgresql/command/query/simple/PostgreSQLComQueryExecutorTest.java
@@ -27,6 +27,7 @@ import 
org.apache.shardingsphere.database.protocol.postgresql.packet.command.que
 import 
org.apache.shardingsphere.database.protocol.postgresql.packet.command.query.simple.PostgreSQLComQueryPacket;
 import 
org.apache.shardingsphere.database.protocol.postgresql.packet.generic.PostgreSQLCommandCompletePacket;
 import 
org.apache.shardingsphere.database.protocol.postgresql.packet.handshake.PostgreSQLParameterStatusPacket;
+import 
org.apache.shardingsphere.database.exception.core.exception.data.InvalidParameterValueException;
 import org.apache.shardingsphere.infra.hint.HintValueContext;
 import org.apache.shardingsphere.infra.spi.type.typed.TypedSPILoader;
 import org.apache.shardingsphere.proxy.backend.handler.ProxyBackendHandler;
@@ -47,6 +48,7 @@ import 
org.apache.shardingsphere.sql.parser.statement.core.statement.type.dal.Se
 import 
org.apache.shardingsphere.sql.parser.statement.core.statement.type.dml.InsertStatement;
 import 
org.apache.shardingsphere.sql.parser.statement.core.statement.type.tcl.CommitStatement;
 import 
org.apache.shardingsphere.sql.parser.statement.core.statement.type.tcl.RollbackStatement;
+import 
org.apache.shardingsphere.sql.parser.statement.postgresql.dal.PostgreSQLResetParameterStatement;
 import 
org.apache.shardingsphere.test.infra.framework.extension.mock.AutoMockExtension;
 import 
org.apache.shardingsphere.test.infra.framework.extension.mock.StaticMockSettings;
 import org.junit.jupiter.api.BeforeEach;
@@ -71,6 +73,7 @@ import java.util.stream.Stream;
 import static org.hamcrest.Matchers.is;
 import static org.hamcrest.MatcherAssert.assertThat;
 import static org.hamcrest.Matchers.isA;
+import static org.junit.jupiter.api.Assertions.assertThrows;
 import static org.junit.jupiter.api.Assertions.assertNull;
 import static org.junit.jupiter.api.Assertions.assertTrue;
 import static org.mockito.Mockito.mock;
@@ -183,6 +186,61 @@ class PostgreSQLComQueryExecutorTest {
         verify(portalContext, never()).closeAll();
     }
     
+    @Test
+    void assertExecuteUpdateWithClientEncoding() throws SQLException, 
ReflectiveOperationException {
+        VariableAssignSegment assignSegment = new VariableAssignSegment(0, 0, 
new VariableSegment(0, 0, "client_encoding"), "'UTF8'");
+        UpdateResponseHeader updateResponseHeader = new 
UpdateResponseHeader(new SetStatement(DATABASE_TYPE, 
Collections.singletonList(assignSegment)));
+        when(proxyBackendHandler.execute()).thenReturn(updateResponseHeader);
+        Collection<DatabasePacket> actual = queryExecutor.execute();
+        Iterator<DatabasePacket> iterator = actual.iterator();
+        PostgreSQLCommandCompletePacket commandCompletePacket = 
(PostgreSQLCommandCompletePacket) iterator.next();
+        PostgreSQLParameterStatusPacket parameterStatusPacket = 
(PostgreSQLParameterStatusPacket) iterator.next();
+        assertThat(actual.size(), is(2));
+        assertThat(getSqlCommand(commandCompletePacket), is("SET"));
+        assertThat(getParameterStatusKey(parameterStatusPacket), 
is("client_encoding"));
+        assertThat(getParameterStatusValue(parameterStatusPacket), is("UTF8"));
+    }
+    
+    @Test
+    void assertExecuteUpdateWithClientEncodingDefault() throws SQLException, 
ReflectiveOperationException {
+        VariableAssignSegment assignSegment = new VariableAssignSegment(0, 0, 
new VariableSegment(0, 0, "client_encoding"), "DEFAULT");
+        UpdateResponseHeader updateResponseHeader = new 
UpdateResponseHeader(new SetStatement(DATABASE_TYPE, 
Collections.singletonList(assignSegment)));
+        when(proxyBackendHandler.execute()).thenReturn(updateResponseHeader);
+        Collection<DatabasePacket> actual = queryExecutor.execute();
+        PostgreSQLParameterStatusPacket parameterStatusPacket = 
(PostgreSQLParameterStatusPacket) new ArrayList<>(actual).get(1);
+        assertThat(getParameterStatusValue(parameterStatusPacket), is("UTF8"));
+    }
+    
+    @Test
+    void assertExecuteUpdateWithClientEncodingAlias() throws SQLException, 
ReflectiveOperationException {
+        VariableAssignSegment assignSegment = new VariableAssignSegment(0, 0, 
new VariableSegment(0, 0, "client_encoding"), "'unicode'");
+        UpdateResponseHeader updateResponseHeader = new 
UpdateResponseHeader(new SetStatement(DATABASE_TYPE, 
Collections.singletonList(assignSegment)));
+        when(proxyBackendHandler.execute()).thenReturn(updateResponseHeader);
+        Collection<DatabasePacket> actual = queryExecutor.execute();
+        PostgreSQLParameterStatusPacket parameterStatusPacket = 
(PostgreSQLParameterStatusPacket) new ArrayList<>(actual).get(1);
+        assertThat(getParameterStatusValue(parameterStatusPacket), is("UTF8"));
+    }
+    
+    @Test
+    void assertExecuteUpdateWithResetClientEncoding() throws SQLException, 
ReflectiveOperationException {
+        UpdateResponseHeader updateResponseHeader = new 
UpdateResponseHeader(new PostgreSQLResetParameterStatement(DATABASE_TYPE, 
"client_encoding"));
+        when(proxyBackendHandler.execute()).thenReturn(updateResponseHeader);
+        Collection<DatabasePacket> actual = queryExecutor.execute();
+        Iterator<DatabasePacket> iterator = actual.iterator();
+        PostgreSQLCommandCompletePacket commandCompletePacket = 
(PostgreSQLCommandCompletePacket) iterator.next();
+        PostgreSQLParameterStatusPacket parameterStatusPacket = 
(PostgreSQLParameterStatusPacket) iterator.next();
+        assertThat(actual.size(), is(2));
+        assertThat(getSqlCommand(commandCompletePacket), is("RESET"));
+        assertThat(getParameterStatusKey(parameterStatusPacket), 
is("client_encoding"));
+        assertThat(getParameterStatusValue(parameterStatusPacket), is("UTF8"));
+    }
+    
+    @Test
+    void assertExecuteWithInvalidClientEncoding() throws SQLException {
+        when(proxyBackendHandler.execute()).thenThrow(new 
InvalidParameterValueException("client_encoding", "latin1"));
+        assertThrows(InvalidParameterValueException.class, () -> 
queryExecutor.execute());
+    }
+    
     @Test
     void assertNext() throws SQLException {
         when(proxyBackendHandler.next()).thenReturn(true);

Reply via email to