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);