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 cc4351becb4 Simplify MySQL session character set handling (#39390)
cc4351becb4 is described below
commit cc4351becb458e981c4f300aa1c967b9301a7548
Author: Liang Zhang <[email protected]>
AuthorDate: Mon Aug 10 00:07:19 2026 +0800
Simplify MySQL session character set handling (#39390)
* Simplify MySQL session character set handling
- reuse the common invalid parameter exception
- normalize charset assignments in one pass
- remove the obsolete MySQL charset SPI
* Simplify MySQL session character set handling
- reuse the common invalid parameter exception
- normalize charset assignments in one pass
- remove the obsolete MySQL charset SPI
* Simplify MySQL session character set handling
- reuse the common invalid parameter exception
- normalize charset assignments in one pass
- remove the obsolete MySQL charset SPI
---
.../exception/WrongValueForVariableException.java | 36 -------
.../mysql/mapper/MySQLDialectExceptionMapper.java | 8 +-
.../mapper/MySQLDialectExceptionMapperTest.java | 22 ++++-
.../mysql/constant/MySQLCharacterSets.java | 3 +-
.../reachability-metadata.json | 6 --
.../connector/ProxyDatabaseConnectionManager.java | 16 ++--
.../ProxyDatabaseConnectionManagerTest.java | 7 --
.../executor/MySQLSetVariableAdminExecutor.java | 106 ++++++++++-----------
.../charset/MySQLCharsetVariableProvider.java | 71 --------------
.../charset/MySQLSessionCharsetContext.java | 38 +-------
...ecutor.variable.charset.CharsetVariableProvider | 18 ----
.../MySQLSetVariableAdminExecutorTest.java | 32 +++++--
.../charset/MySQLCharsetVariableProviderTest.java | 81 ----------------
.../charset/MySQLSessionCharsetContextTest.java | 26 ++---
.../MySQLAuthenticationEngineTest.java | 45 +++------
.../select_aliased_system_variables_with_scope.xml | 2 +-
.../cases/dal/dataset/select_system_variables.xml | 2 +-
17 files changed, 128 insertions(+), 391 deletions(-)
diff --git
a/database/exception/dialect/mysql/src/main/java/org/apache/shardingsphere/database/exception/mysql/exception/WrongValueForVariableException.java
b/database/exception/dialect/mysql/src/main/java/org/apache/shardingsphere/database/exception/mysql/exception/WrongValueForVariableException.java
deleted file mode 100644
index b07dbb917f4..00000000000
---
a/database/exception/dialect/mysql/src/main/java/org/apache/shardingsphere/database/exception/mysql/exception/WrongValueForVariableException.java
+++ /dev/null
@@ -1,36 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements. See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF licenses this file to You under the Apache License, Version 2.0
- * (the "License"); you may not use this file except in compliance with
- * the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package org.apache.shardingsphere.database.exception.mysql.exception;
-
-import lombok.Getter;
-import lombok.RequiredArgsConstructor;
-import
org.apache.shardingsphere.database.exception.core.exception.SQLDialectException;
-
-/**
- * Wrong value for variable exception.
- */
-@RequiredArgsConstructor
-@Getter
-public final class WrongValueForVariableException extends SQLDialectException {
-
- private static final long serialVersionUID = 7832630692242160301L;
-
- private final String variableName;
-
- private final String value;
-}
diff --git
a/database/exception/dialect/mysql/src/main/java/org/apache/shardingsphere/database/exception/mysql/mapper/MySQLDialectExceptionMapper.java
b/database/exception/dialect/mysql/src/main/java/org/apache/shardingsphere/database/exception/mysql/mapper/MySQLDialectExceptionMapper.java
index 9f7041c5ee7..821c969f02c 100644
---
a/database/exception/dialect/mysql/src/main/java/org/apache/shardingsphere/database/exception/mysql/mapper/MySQLDialectExceptionMapper.java
+++
b/database/exception/dialect/mysql/src/main/java/org/apache/shardingsphere/database/exception/mysql/mapper/MySQLDialectExceptionMapper.java
@@ -20,6 +20,7 @@ package
org.apache.shardingsphere.database.exception.mysql.mapper;
import
org.apache.shardingsphere.database.exception.core.exception.SQLDialectException;
import
org.apache.shardingsphere.database.exception.core.exception.connection.AccessDeniedException;
import
org.apache.shardingsphere.database.exception.core.exception.connection.TooManyConnectionsException;
+import
org.apache.shardingsphere.database.exception.core.exception.data.InvalidParameterValueException;
import
org.apache.shardingsphere.database.exception.core.exception.data.InsertColumnsAndValuesMismatchedException;
import
org.apache.shardingsphere.database.exception.core.exception.syntax.column.ColumnNotFoundException;
import
org.apache.shardingsphere.database.exception.core.exception.syntax.database.DatabaseCreateExistsException;
@@ -42,7 +43,6 @@ import
org.apache.shardingsphere.database.exception.mysql.exception.UnknownChars
import
org.apache.shardingsphere.database.exception.mysql.exception.UnknownCollationException;
import
org.apache.shardingsphere.database.exception.mysql.exception.UnknownSystemVariableException;
import
org.apache.shardingsphere.database.exception.mysql.exception.UnsupportedPreparedStatementException;
-import
org.apache.shardingsphere.database.exception.mysql.exception.WrongValueForVariableException;
import
org.apache.shardingsphere.database.exception.mysql.vendor.MySQLVendorError;
import
org.apache.shardingsphere.infra.exception.external.sql.vendor.VendorError;
import org.apache.shardingsphere.infra.exception.generic.UnknownSQLException;
@@ -101,9 +101,9 @@ public final class MySQLDialectExceptionMapper implements
SQLDialectExceptionMap
if (sqlDialectException instanceof UnknownCollationException) {
return toSQLException(MySQLVendorError.ER_UNKNOWN_COLLATION,
((UnknownCollationException) sqlDialectException).getCollation());
}
- if (sqlDialectException instanceof WrongValueForVariableException) {
- WrongValueForVariableException ex =
(WrongValueForVariableException) sqlDialectException;
- return toSQLException(MySQLVendorError.ER_WRONG_VALUE_FOR_VAR,
ex.getVariableName(), ex.getValue());
+ if (sqlDialectException instanceof InvalidParameterValueException) {
+ InvalidParameterValueException ex =
(InvalidParameterValueException) sqlDialectException;
+ return toSQLException(MySQLVendorError.ER_WRONG_VALUE_FOR_VAR,
ex.getParameterName(), ex.getParameterValue());
}
if (sqlDialectException instanceof CollationCharsetMismatchException) {
CollationCharsetMismatchException ex =
(CollationCharsetMismatchException) sqlDialectException;
diff --git
a/database/exception/dialect/mysql/src/test/java/org/apache/shardingsphere/database/exception/mysql/mapper/MySQLDialectExceptionMapperTest.java
b/database/exception/dialect/mysql/src/test/java/org/apache/shardingsphere/database/exception/mysql/mapper/MySQLDialectExceptionMapperTest.java
index b850a49c3d9..032d25ec8af 100644
---
a/database/exception/dialect/mysql/src/test/java/org/apache/shardingsphere/database/exception/mysql/mapper/MySQLDialectExceptionMapperTest.java
+++
b/database/exception/dialect/mysql/src/test/java/org/apache/shardingsphere/database/exception/mysql/mapper/MySQLDialectExceptionMapperTest.java
@@ -22,6 +22,7 @@ import
org.apache.shardingsphere.database.connector.core.type.DatabaseType;
import
org.apache.shardingsphere.database.exception.core.exception.SQLDialectException;
import
org.apache.shardingsphere.database.exception.core.exception.connection.AccessDeniedException;
import
org.apache.shardingsphere.database.exception.core.exception.connection.TooManyConnectionsException;
+import
org.apache.shardingsphere.database.exception.core.exception.data.InvalidParameterValueException;
import
org.apache.shardingsphere.database.exception.core.exception.data.InsertColumnsAndValuesMismatchedException;
import
org.apache.shardingsphere.database.exception.core.exception.syntax.column.ColumnNotFoundException;
import
org.apache.shardingsphere.database.exception.core.exception.syntax.database.DatabaseCreateExistsException;
@@ -44,7 +45,6 @@ import
org.apache.shardingsphere.database.exception.mysql.exception.UnknownChars
import
org.apache.shardingsphere.database.exception.mysql.exception.UnknownCollationException;
import
org.apache.shardingsphere.database.exception.mysql.exception.UnknownSystemVariableException;
import
org.apache.shardingsphere.database.exception.mysql.exception.UnsupportedPreparedStatementException;
-import
org.apache.shardingsphere.database.exception.mysql.exception.WrongValueForVariableException;
import
org.apache.shardingsphere.database.exception.mysql.vendor.MySQLVendorError;
import
org.apache.shardingsphere.infra.exception.external.sql.vendor.VendorError;
import org.apache.shardingsphere.infra.exception.generic.UnknownSQLException;
@@ -83,6 +83,23 @@ class MySQLDialectExceptionMapperTest {
assertSQLExceptionWithMessage(mapper.convert(new
UnknownDatabaseException("logic_db")), MySQLVendorError.ER_BAD_DB_ERROR,
"logic_db");
}
+ @Test
+ void assertConvertWithUnknownCollation() {
+ assertSQLExceptionWithMessage(mapper.convert(new
UnknownCollationException("foo_collation")),
MySQLVendorError.ER_UNKNOWN_COLLATION, "foo_collation");
+ }
+
+ @Test
+ void assertConvertWithInvalidParameterValue() {
+ assertSQLExceptionWithMessage(
+ mapper.convert(new
InvalidParameterValueException("character_set_client", "ucs2")),
MySQLVendorError.ER_WRONG_VALUE_FOR_VAR, "character_set_client", "ucs2");
+ }
+
+ @Test
+ void assertConvertWithCollationCharsetMismatch() {
+ assertSQLExceptionWithMessage(mapper.convert(new
CollationCharsetMismatchException("utf8mb4_bin", "latin1")),
+ MySQLVendorError.ER_COLLATION_CHARSET_MISMATCH, "utf8mb4_bin",
"latin1");
+ }
+
@Test
void assertConvertWithUsingPassword() {
assertSQLExceptionWithMessage(mapper.convert(new
AccessDeniedException("root", "127.0.0.1", true)),
MySQLVendorError.ER_ACCESS_DENIED_ERROR, "root", "127.0.0.1", "YES");
@@ -124,9 +141,6 @@ class MySQLDialectExceptionMapperTest {
Arguments.of("unsupported_prepared_statement",
UnsupportedPreparedStatementException.class,
MySQLVendorError.ER_UNSUPPORTED_PS),
Arguments.of("too_many_placeholders",
TooManyPlaceholdersException.class, MySQLVendorError.ER_PS_MANY_PARAM),
Arguments.of("unknown_charset", UnknownCharsetException.class,
MySQLVendorError.ER_UNKNOWN_CHARACTER_SET),
- Arguments.of("unknown_collation",
UnknownCollationException.class, MySQLVendorError.ER_UNKNOWN_COLLATION),
- Arguments.of("wrong_value_for_variable",
WrongValueForVariableException.class, MySQLVendorError.ER_WRONG_VALUE_FOR_VAR),
- Arguments.of("collation_charset_mismatch",
CollationCharsetMismatchException.class,
MySQLVendorError.ER_COLLATION_CHARSET_MISMATCH),
Arguments.of("handshake", HandshakeException.class,
MySQLVendorError.ER_HANDSHAKE_ERROR),
Arguments.of("access_denied_without_password",
AccessDeniedException.class, MySQLVendorError.ER_ACCESS_DENIED_ERROR),
Arguments.of("database_access_denied",
DatabaseAccessDeniedException.class, MySQLVendorError.ER_DBACCESS_DENIED_ERROR),
diff --git
a/database/protocol/dialect/mysql/src/main/java/org/apache/shardingsphere/database/protocol/mysql/constant/MySQLCharacterSets.java
b/database/protocol/dialect/mysql/src/main/java/org/apache/shardingsphere/database/protocol/mysql/constant/MySQLCharacterSets.java
index 4ca3284bc68..aa02bd81e6e 100644
---
a/database/protocol/dialect/mysql/src/main/java/org/apache/shardingsphere/database/protocol/mysql/constant/MySQLCharacterSets.java
+++
b/database/protocol/dialect/mysql/src/main/java/org/apache/shardingsphere/database/protocol/mysql/constant/MySQLCharacterSets.java
@@ -27,7 +27,6 @@ import java.nio.charset.StandardCharsets;
import java.nio.charset.UnsupportedCharsetException;
import java.util.Arrays;
import java.util.Collections;
-import java.util.LinkedHashMap;
import java.util.Locale;
import java.util.Map;
import java.util.function.Function;
@@ -319,7 +318,7 @@ public enum MySQLCharacterSets {
private static final Map<Integer, MySQLCharacterSets> CHARACTER_SET_MAP =
Collections.unmodifiableMap(Arrays.stream(values()).collect(Collectors.toMap(each
-> each.id, Function.identity())));
private static final Map<String, MySQLCharacterSets> DEFAULT_COLLATION_MAP
= Collections.unmodifiableMap(Arrays.stream(values()).filter(each -> null !=
each.charset)
- .collect(Collectors.toMap(MySQLCharacterSets::getCharacterSetName,
Function.identity(), MySQLCharacterSets::getDefaultCollation,
LinkedHashMap::new)));
+ .collect(Collectors.toMap(MySQLCharacterSets::getCharacterSetName,
Function.identity(), MySQLCharacterSets::getDefaultCollation)));
private static final Map<String, MySQLCharacterSets> COLLATION_MAP =
Collections.unmodifiableMap(Arrays.stream(values()).filter(each -> null !=
each.charset)
.collect(Collectors.toMap(MySQLCharacterSets::getCollationName,
Function.identity())));
diff --git
a/infra/reachability-metadata/src/main/resources/META-INF/native-image/org.apache.shardingsphere/generated-reachability-metadata/reachability-metadata.json
b/infra/reachability-metadata/src/main/resources/META-INF/native-image/org.apache.shardingsphere/generated-reachability-metadata/reachability-metadata.json
index 6625e0d8dc9..dbfef5fdd3d 100644
---
a/infra/reachability-metadata/src/main/resources/META-INF/native-image/org.apache.shardingsphere/generated-reachability-metadata/reachability-metadata.json
+++
b/infra/reachability-metadata/src/main/resources/META-INF/native-image/org.apache.shardingsphere/generated-reachability-metadata/reachability-metadata.json
@@ -3477,12 +3477,6 @@
},
"type":
"org.apache.shardingsphere.proxy.backend.mysql.handler.admin.MySQLAdminExecutorCreator"
},
- {
- "condition": {
- "typeReached":
"org.apache.shardingsphere.proxy.backend.handler.admin.executor.variable.charset.CharsetSetExecutor"
- },
- "type":
"org.apache.shardingsphere.proxy.backend.mysql.handler.admin.executor.variable.charset.MySQLCharsetVariableProvider"
- },
{
"condition": {
"typeReached":
"org.apache.shardingsphere.proxy.backend.handler.admin.executor.variable.session.SessionVariableRecordExecutor"
diff --git
a/proxy/backend/core/src/main/java/org/apache/shardingsphere/proxy/backend/connector/ProxyDatabaseConnectionManager.java
b/proxy/backend/core/src/main/java/org/apache/shardingsphere/proxy/backend/connector/ProxyDatabaseConnectionManager.java
index 839d95aa743..6406b794af2 100644
---
a/proxy/backend/core/src/main/java/org/apache/shardingsphere/proxy/backend/connector/ProxyDatabaseConnectionManager.java
+++
b/proxy/backend/core/src/main/java/org/apache/shardingsphere/proxy/backend/connector/ProxyDatabaseConnectionManager.java
@@ -74,7 +74,7 @@ public final class ProxyDatabaseConnectionManager implements
DatabaseConnectionM
private final Collection<ConnectionPostProcessor> connectionPostProcessors
= new LinkedList<>();
@Getter(AccessLevel.NONE)
- private final Set<Connection> connectionsRequiringSessionVariableReplay =
Collections.newSetFromMap(new ConcurrentHashMap<>(64));
+ private final Set<Connection> pendingReplayConnections =
Collections.newSetFromMap(new ConcurrentHashMap<>(64));
private final ConnectionResourceLock connectionResourceLock = new
ConnectionResourceLock();
@@ -125,12 +125,12 @@ public final class ProxyDatabaseConnectionManager
implements DatabaseConnectionM
}
private void replaySessionVariablesIfNecessary(final List<Connection>
connections) throws SQLException {
- if (connectionsRequiringSessionVariableReplay.isEmpty()) {
+ if (pendingReplayConnections.isEmpty()) {
return;
}
List<Connection> replayConnections = new LinkedList<>();
for (Connection each : connections) {
- if (null != each &&
connectionsRequiringSessionVariableReplay.contains(each)) {
+ if (null != each && pendingReplayConnections.contains(each)) {
replayConnections.add(each);
}
}
@@ -145,7 +145,7 @@ public final class ProxyDatabaseConnectionManager
implements DatabaseConnectionM
}
throw ex;
} finally {
-
connectionsRequiringSessionVariableReplay.removeAll(replayConnections);
+ pendingReplayConnections.removeAll(replayConnections);
}
}
@@ -272,11 +272,7 @@ public final class ProxyDatabaseConnectionManager
implements DatabaseConnectionM
*/
public void markSessionVariablesDirty() {
synchronized (cachedConnections) {
- for (Connection each : cachedConnections.values()) {
- if (null != each) {
- connectionsRequiringSessionVariableReplay.add(each);
- }
- }
+ pendingReplayConnections.addAll(cachedConnections.values());
}
}
@@ -417,7 +413,7 @@ public final class ProxyDatabaseConnectionManager
implements DatabaseConnectionM
}
}
cachedConnections.clear();
- connectionsRequiringSessionVariableReplay.clear();
+ pendingReplayConnections.clear();
}
connectionSession.getPreparedStatementCacheContext().closeAll();
if (!forceRollback) {
diff --git
a/proxy/backend/core/src/test/java/org/apache/shardingsphere/proxy/backend/connector/ProxyDatabaseConnectionManagerTest.java
b/proxy/backend/core/src/test/java/org/apache/shardingsphere/proxy/backend/connector/ProxyDatabaseConnectionManagerTest.java
index ade421d65f9..a7bb49babbe 100644
---
a/proxy/backend/core/src/test/java/org/apache/shardingsphere/proxy/backend/connector/ProxyDatabaseConnectionManagerTest.java
+++
b/proxy/backend/core/src/test/java/org/apache/shardingsphere/proxy/backend/connector/ProxyDatabaseConnectionManagerTest.java
@@ -89,7 +89,6 @@ import static org.hamcrest.Matchers.is;
import static org.hamcrest.Matchers.not;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.junit.jupiter.api.Assertions.assertArrayEquals;
-import static org.junit.jupiter.api.Assertions.assertDoesNotThrow;
import static org.junit.jupiter.api.Assertions.assertFalse;
import static org.junit.jupiter.api.Assertions.assertThrows;
import static org.junit.jupiter.api.Assertions.assertTrue;
@@ -394,12 +393,6 @@ class ProxyDatabaseConnectionManagerTest {
verify(connection.createStatement()).execute("SET key=value");
}
- @Test
- void assertMarkSessionVariablesDirtyWithNullConnection() {
- databaseConnectionManager.getCachedConnections().put("foo_db.ds1",
null);
-
assertDoesNotThrow(databaseConnectionManager::markSessionVariablesDirty);
- }
-
@Test
void assertGetCachedConnectionsAndFailedToReplayDirtySessionVariables()
throws SQLException {
connectionSession.getRequiredSessionVariableRecorder().setVariable("key",
"value");
diff --git
a/proxy/backend/dialect/mysql/src/main/java/org/apache/shardingsphere/proxy/backend/mysql/handler/admin/executor/MySQLSetVariableAdminExecutor.java
b/proxy/backend/dialect/mysql/src/main/java/org/apache/shardingsphere/proxy/backend/mysql/handler/admin/executor/MySQLSetVariableAdminExecutor.java
index c4a62f73d3e..3ef23dec45d 100644
---
a/proxy/backend/dialect/mysql/src/main/java/org/apache/shardingsphere/proxy/backend/mysql/handler/admin/executor/MySQLSetVariableAdminExecutor.java
+++
b/proxy/backend/dialect/mysql/src/main/java/org/apache/shardingsphere/proxy/backend/mysql/handler/admin/executor/MySQLSetVariableAdminExecutor.java
@@ -19,9 +19,9 @@ package
org.apache.shardingsphere.proxy.backend.mysql.handler.admin.executor;
import lombok.RequiredArgsConstructor;
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.database.exception.mysql.exception.CollationCharsetMismatchException;
import
org.apache.shardingsphere.database.exception.mysql.exception.UnknownSystemVariableException;
-import
org.apache.shardingsphere.database.exception.mysql.exception.WrongValueForVariableException;
import
org.apache.shardingsphere.database.protocol.mysql.constant.MySQLCharacterSets;
import
org.apache.shardingsphere.database.protocol.mysql.constant.MySQLConstants;
import
org.apache.shardingsphere.infra.binder.context.statement.SQLStatementContext;
@@ -52,7 +52,6 @@ import java.util.LinkedHashMap;
import java.util.List;
import java.util.Locale;
import java.util.Map;
-import java.util.Optional;
import java.util.stream.Collectors;
/**
@@ -77,11 +76,33 @@ public final class MySQLSetVariableAdminExecutor implements
DatabaseAdminUpdateE
public void execute(final ConnectionSession connectionSession, final
ShardingSphereMetaData metaData) throws SQLException {
List<VariableAssignSegment> sessionVariableAssigns =
extractSessionVariableAssigns();
validateSessionVariables(sessionVariableAssigns.stream().map(VariableAssignSegment::getVariable).collect(Collectors.toList()));
- validateSetNamesCollation(sessionVariableAssigns);
- updateCharsetContext(connectionSession, sessionVariableAssigns);
+ boolean setNamesWithCollation =
isSetNamesWithCollation(sessionVariableAssigns);
+ MySQLSessionCharsetContext charsetContext = setNamesWithCollation ?
MySQLSessionCharsetContext.create(MySQLConstants.DEFAULT_CHARSET) : null;
+ Map<String, String> replayedSessionVariables = new LinkedHashMap<>();
+ String connectionCollationReplayValue = null;
+ for (int i = 0; i < sessionVariableAssigns.size(); i++) {
+ VariableAssignSegment each = sessionVariableAssigns.get(i);
+ String variableName = each.getVariable().getVariable();
+ if (!isCharsetVariable(variableName)) {
+ replayedSessionVariables.put(variableName,
each.getAssignValue());
+ continue;
+ }
+ if (null == charsetContext) {
+ charsetContext =
MySQLSessionCharsetContext.get(connectionSession.getAttributeMap());
+ }
+ charsetContext = updateCharsetContext(charsetContext, each,
setNamesWithCollation && i < 4);
+ if (CHARACTER_SET_CONNECTION.equalsIgnoreCase(variableName) ||
COLLATION_CONNECTION.equalsIgnoreCase(variableName)) {
+ connectionCollationReplayValue = String.format("'%s'",
charsetContext.getConnectionCollationName());
+ }
+ }
+ if (null != charsetContext) {
+ charsetContext.apply(connectionSession.getAttributeMap());
+ }
SessionVariableRecordExecutor sessionVariableRecordExecutor = new
SessionVariableRecordExecutor(sqlStatement.getDatabaseType(),
connectionSession);
-
sessionVariableRecordExecutor.recordVariable(extractReplayedSessionVariables(sessionVariableAssigns));
-
getConnectionCollationReplayValue(sessionVariableAssigns).ifPresent(each ->
sessionVariableRecordExecutor.recordVariable(COLLATION_CONNECTION, each));
+ sessionVariableRecordExecutor.recordVariable(replayedSessionVariables);
+ if (null != connectionCollationReplayValue) {
+ sessionVariableRecordExecutor.recordVariable(COLLATION_CONNECTION,
connectionCollationReplayValue);
+ }
executeSetGlobalVariablesIfPresent(connectionSession, metaData);
}
@@ -99,17 +120,6 @@ public final class MySQLSetVariableAdminExecutor implements
DatabaseAdminUpdateE
}
}
- private void validateSetNamesCollation(final List<VariableAssignSegment>
variableAssigns) {
- if (!isSetNamesWithCollation(variableAssigns)) {
- return;
- }
- MySQLCharacterSets characterSet =
parseCharacterSet(variableAssigns.get(2).getAssignValue());
- MySQLCharacterSets collation =
parseCollation(variableAssigns.get(3).getAssignValue());
- if
(!characterSet.getCharacterSetName().equals(collation.getCharacterSetName())) {
- throw new
CollationCharsetMismatchException(collation.getCollationName(),
characterSet.getCharacterSetName());
- }
- }
-
private boolean isSetNamesWithCollation(final List<VariableAssignSegment>
variableAssigns) {
if (variableAssigns.size() < 4) {
return false;
@@ -122,28 +132,27 @@ public final class MySQLSetVariableAdminExecutor
implements DatabaseAdminUpdateE
&& variableAssigns.subList(1, 4).stream().allMatch(each ->
first.getStartIndex() == each.getStartIndex() && first.getStopIndex() ==
each.getStopIndex());
}
- private void updateCharsetContext(final ConnectionSession
connectionSession, final List<VariableAssignSegment> variableAssigns) {
- if (variableAssigns.stream().noneMatch(each ->
isCharsetVariable(each.getVariable().getVariable()))) {
- return;
- }
- MySQLSessionCharsetContext result =
MySQLSessionCharsetContext.get(connectionSession.getAttributeMap());
- for (VariableAssignSegment each : variableAssigns) {
- result = updateCharsetContext(result, each);
- }
- result.apply(connectionSession.getAttributeMap());
- }
-
- private MySQLSessionCharsetContext updateCharsetContext(final
MySQLSessionCharsetContext context, final VariableAssignSegment variableAssign)
{
+ private MySQLSessionCharsetContext updateCharsetContext(final
MySQLSessionCharsetContext context, final VariableAssignSegment variableAssign,
+ final boolean
setNamesWithCollationAssignment) {
String variableName =
variableAssign.getVariable().getVariable().toLowerCase(Locale.ROOT);
switch (variableName) {
case CHARACTER_SET_CLIENT:
- return
context.withClientCharacterSet(parseClientCharacterSet(variableAssign.getAssignValue()));
+ return
context.withClientCharacterSet(setNamesWithCollationAssignment
+ ? parseCharacterSet(variableAssign.getAssignValue())
+ :
parseClientCharacterSet(variableAssign.getAssignValue()));
case CHARACTER_SET_RESULTS:
return updateResultCharacterSet(context,
variableAssign.getAssignValue());
case CHARACTER_SET_CONNECTION:
- return
context.withConnectionCharacterSet(parseConnectionCharacterSet(variableAssign.getAssignValue()));
+ return
context.withConnectionCollation(parseConnectionCharacterSet(variableAssign.getAssignValue()));
case COLLATION_CONNECTION:
- return
context.withConnectionCollation(parseCollation(variableAssign.getAssignValue()));
+ MySQLCharacterSets collation =
parseCollation(variableAssign.getAssignValue());
+ if (setNamesWithCollationAssignment &&
!context.getConnectionCharacterSetName().equals(collation.getCharacterSetName()))
{
+ throw new
CollationCharsetMismatchException(collation.getCollationName(),
context.getConnectionCharacterSetName());
+ }
+ if (setNamesWithCollationAssignment) {
+
validateClientCharacterSet(context.getClientCharacterSetName());
+ }
+ return context.withConnectionCollation(collation);
default:
return context;
}
@@ -151,12 +160,16 @@ public final class MySQLSetVariableAdminExecutor
implements DatabaseAdminUpdateE
private MySQLCharacterSets parseClientCharacterSet(final String value) {
String normalizedValue =
formatVariableValue(value).toLowerCase(Locale.ROOT);
- if (IMPERMISSIBLE_CLIENT_CHARACTER_SETS.contains(normalizedValue)) {
- throw new WrongValueForVariableException(CHARACTER_SET_CLIENT,
normalizedValue);
- }
+ validateClientCharacterSet(normalizedValue);
return parseCharacterSet(value);
}
+ private void validateClientCharacterSet(final String characterSet) {
+ if (IMPERMISSIBLE_CLIENT_CHARACTER_SETS.contains(characterSet)) {
+ throw new InvalidParameterValueException(CHARACTER_SET_CLIENT,
characterSet);
+ }
+ }
+
private MySQLSessionCharsetContext updateResultCharacterSet(final
MySQLSessionCharsetContext context, final String value) {
String normalizedValue =
formatVariableValue(value).toLowerCase(Locale.ROOT);
if (isKeyword(value, "null")) {
@@ -198,34 +211,11 @@ public final class MySQLSetVariableAdminExecutor
implements DatabaseAdminUpdateE
return QuoteCharacter.SINGLE_QUOTE.isWrapped(value) ||
QuoteCharacter.QUOTE.isWrapped(value) ? value.substring(1, value.length() - 1)
: value.trim();
}
- private Map<String, String> extractReplayedSessionVariables(final
Collection<VariableAssignSegment> variableAssigns) {
- Map<String, String> result = new LinkedHashMap<>();
- for (VariableAssignSegment each : variableAssigns) {
- if (!isCharsetVariable(each.getVariable().getVariable())) {
- result.put(each.getVariable().getVariable(),
each.getAssignValue());
- }
- }
- return result;
- }
-
private boolean isCharsetVariable(final String variableName) {
return CHARACTER_SET_CLIENT.equalsIgnoreCase(variableName) ||
CHARACTER_SET_RESULTS.equalsIgnoreCase(variableName)
|| CHARACTER_SET_CONNECTION.equalsIgnoreCase(variableName) ||
COLLATION_CONNECTION.equalsIgnoreCase(variableName);
}
- private Optional<String> getConnectionCollationReplayValue(final
Collection<VariableAssignSegment> variableAssigns) {
- String result = null;
- for (VariableAssignSegment each : variableAssigns) {
- String variableName = each.getVariable().getVariable();
- if (CHARACTER_SET_CONNECTION.equalsIgnoreCase(variableName)) {
- result = String.format("'%s'",
parseConnectionCharacterSet(each.getAssignValue()).getCollationName());
- } else if (COLLATION_CONNECTION.equalsIgnoreCase(variableName)) {
- result = String.format("'%s'",
parseCollation(each.getAssignValue()).getCollationName());
- }
- }
- return Optional.ofNullable(result);
- }
-
private void executeSetGlobalVariablesIfPresent(final ConnectionSession
connectionSession, final ShardingSphereMetaData metaData) throws SQLException {
if (null == connectionSession.getUsedDatabaseName()) {
return;
diff --git
a/proxy/backend/dialect/mysql/src/main/java/org/apache/shardingsphere/proxy/backend/mysql/handler/admin/executor/variable/charset/MySQLCharsetVariableProvider.java
b/proxy/backend/dialect/mysql/src/main/java/org/apache/shardingsphere/proxy/backend/mysql/handler/admin/executor/variable/charset/MySQLCharsetVariableProvider.java
deleted file mode 100644
index 0b364373a98..00000000000
---
a/proxy/backend/dialect/mysql/src/main/java/org/apache/shardingsphere/proxy/backend/mysql/handler/admin/executor/variable/charset/MySQLCharsetVariableProvider.java
+++ /dev/null
@@ -1,71 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements. See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF licenses this file to You under the Apache License, Version 2.0
- * (the "License"); you may not use this file except in compliance with
- * the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package
org.apache.shardingsphere.proxy.backend.mysql.handler.admin.executor.variable.charset;
-
-import
org.apache.shardingsphere.database.connector.core.metadata.database.enums.QuoteCharacter;
-import
org.apache.shardingsphere.database.exception.mysql.exception.UnknownCharsetException;
-import
org.apache.shardingsphere.database.protocol.mysql.constant.MySQLConstants;
-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.Arrays;
-import java.util.Collection;
-import java.util.Locale;
-
-/**
- * Charset variable provider of MySQL.
- */
-public final class MySQLCharsetVariableProvider implements
CharsetVariableProvider {
-
- @Override
- public Collection<String> getCharsetVariables() {
- return Arrays.asList("charset", "character_set_client",
"character_set_results", "character_set_connection");
- }
-
- @Override
- public boolean shouldSet(final String variableName, final String
variableValue) {
- return !"character_set_results".equalsIgnoreCase(variableName) ||
!"null".equalsIgnoreCase(formatValue(variableValue));
- }
-
- @Override
- public Charset parseCharset(final String variableValue) {
- String formattedValue = formatValue(variableValue);
- switch (formattedValue.toLowerCase(Locale.ROOT)) {
- case "default":
- return MySQLConstants.DEFAULT_CHARSET.getCharset();
- case "utf8mb4":
- return StandardCharsets.UTF_8;
- default:
- try {
- return Charset.forName(formattedValue);
- } catch (final IllegalArgumentException ex) {
- throw new
UnknownCharsetException(formattedValue.toLowerCase(Locale.ROOT));
- }
- }
- }
-
- private String formatValue(final String variableValue) {
- return QuoteCharacter.SINGLE_QUOTE.isWrapped(variableValue) ||
QuoteCharacter.QUOTE.isWrapped(variableValue) ? variableValue.substring(1,
variableValue.length() - 1) : variableValue.trim();
- }
-
- @Override
- public String getDatabaseType() {
- return "MySQL";
- }
-}
diff --git
a/proxy/backend/dialect/mysql/src/main/java/org/apache/shardingsphere/proxy/backend/mysql/handler/admin/executor/variable/charset/MySQLSessionCharsetContext.java
b/proxy/backend/dialect/mysql/src/main/java/org/apache/shardingsphere/proxy/backend/mysql/handler/admin/executor/variable/charset/MySQLSessionCharsetContext.java
index 88965c0c44d..3995d0ca1b7 100644
---
a/proxy/backend/dialect/mysql/src/main/java/org/apache/shardingsphere/proxy/backend/mysql/handler/admin/executor/variable/charset/MySQLSessionCharsetContext.java
+++
b/proxy/backend/dialect/mysql/src/main/java/org/apache/shardingsphere/proxy/backend/mysql/handler/admin/executor/variable/charset/MySQLSessionCharsetContext.java
@@ -24,21 +24,20 @@ import
org.apache.shardingsphere.database.protocol.constant.CommonConstants;
import
org.apache.shardingsphere.database.protocol.mysql.constant.MySQLCharacterSets;
import
org.apache.shardingsphere.database.protocol.mysql.constant.MySQLConstants;
-import java.nio.charset.Charset;
import java.util.Optional;
/**
* MySQL session character set context.
*/
-@Getter
public final class MySQLSessionCharsetContext {
- public static final AttributeKey<MySQLSessionCharsetContext> ATTRIBUTE_KEY
= AttributeKey.valueOf(MySQLSessionCharsetContext.class.getName());
+ private static final AttributeKey<MySQLSessionCharsetContext>
ATTRIBUTE_KEY =
AttributeKey.valueOf(MySQLSessionCharsetContext.class.getName());
private final MySQLCharacterSets clientCharacterSet;
private final Optional<MySQLCharacterSets> resultCharacterSet;
+ @Getter
private final Optional<String> resultCharacterSetName;
private final MySQLCharacterSets connectionCollation;
@@ -110,16 +109,6 @@ public final class MySQLSessionCharsetContext {
return new MySQLSessionCharsetContext(clientCharacterSet,
Optional.empty(), Optional.of("binary"), connectionCollation);
}
- /**
- * Create a context with the default collation of the specified connection
character set.
- *
- * @param characterSet connection character set and its default collation
- * @return updated context
- */
- public MySQLSessionCharsetContext withConnectionCharacterSet(final
MySQLCharacterSets characterSet) {
- return new MySQLSessionCharsetContext(clientCharacterSet,
resultCharacterSet, resultCharacterSetName, characterSet);
- }
-
/**
* Create a context with the specified connection collation.
*
@@ -130,24 +119,6 @@ public final class MySQLSessionCharsetContext {
return new MySQLSessionCharsetContext(clientCharacterSet,
resultCharacterSet, resultCharacterSetName, collation);
}
- /**
- * Get result encoding character set.
- *
- * @return result encoding character set
- */
- public Charset getResultCharset() {
- return
resultCharacterSet.orElse(MySQLConstants.DEFAULT_CHARSET).getCharset();
- }
-
- /**
- * Get result metadata collation.
- *
- * @return result metadata collation
- */
- public MySQLCharacterSets getResultCollation() {
- return resultCharacterSet.orElse(MySQLConstants.DEFAULT_CHARSET);
- }
-
/**
* Get client character set name.
*
@@ -181,9 +152,10 @@ public final class MySQLSessionCharsetContext {
* @param attributeMap attribute map
*/
public void apply(final AttributeMap attributeMap) {
+ MySQLCharacterSets resultCollation =
resultCharacterSet.orElse(MySQLConstants.DEFAULT_CHARSET);
attributeMap.attr(ATTRIBUTE_KEY).set(this);
attributeMap.attr(CommonConstants.CHARSET_ATTRIBUTE_KEY).set(clientCharacterSet.getCharset());
-
attributeMap.attr(MySQLConstants.RESULT_CHARSET_ATTRIBUTE_KEY).set(getResultCharset());
-
attributeMap.attr(MySQLConstants.CHARACTER_SET_ATTRIBUTE_KEY).set(getResultCollation());
+
attributeMap.attr(MySQLConstants.RESULT_CHARSET_ATTRIBUTE_KEY).set(resultCollation.getCharset());
+
attributeMap.attr(MySQLConstants.CHARACTER_SET_ATTRIBUTE_KEY).set(resultCollation);
}
}
diff --git
a/proxy/backend/dialect/mysql/src/main/resources/META-INF/services/org.apache.shardingsphere.proxy.backend.handler.admin.executor.variable.charset.CharsetVariableProvider
b/proxy/backend/dialect/mysql/src/main/resources/META-INF/services/org.apache.shardingsphere.proxy.backend.handler.admin.executor.variable.charset.CharsetVariableProvider
deleted file mode 100644
index 66f1d579ed7..00000000000
---
a/proxy/backend/dialect/mysql/src/main/resources/META-INF/services/org.apache.shardingsphere.proxy.backend.handler.admin.executor.variable.charset.CharsetVariableProvider
+++ /dev/null
@@ -1,18 +0,0 @@
-#
-# Licensed to the Apache Software Foundation (ASF) under one or more
-# contributor license agreements. See the NOTICE file distributed with
-# this work for additional information regarding copyright ownership.
-# The ASF licenses this file to You under the Apache License, Version 2.0
-# (the "License"); you may not use this file except in compliance with
-# the License. You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-#
-
-org.apache.shardingsphere.proxy.backend.mysql.handler.admin.executor.variable.charset.MySQLCharsetVariableProvider
diff --git
a/proxy/backend/dialect/mysql/src/test/java/org/apache/shardingsphere/proxy/backend/mysql/handler/admin/executor/MySQLSetVariableAdminExecutorTest.java
b/proxy/backend/dialect/mysql/src/test/java/org/apache/shardingsphere/proxy/backend/mysql/handler/admin/executor/MySQLSetVariableAdminExecutorTest.java
index c75b517ac35..0add6dc0b94 100644
---
a/proxy/backend/dialect/mysql/src/test/java/org/apache/shardingsphere/proxy/backend/mysql/handler/admin/executor/MySQLSetVariableAdminExecutorTest.java
+++
b/proxy/backend/dialect/mysql/src/test/java/org/apache/shardingsphere/proxy/backend/mysql/handler/admin/executor/MySQLSetVariableAdminExecutorTest.java
@@ -19,12 +19,12 @@ package
org.apache.shardingsphere.proxy.backend.mysql.handler.admin.executor;
import io.netty.util.DefaultAttributeMap;
import org.apache.shardingsphere.database.connector.core.type.DatabaseType;
+import
org.apache.shardingsphere.database.exception.core.exception.data.InvalidParameterValueException;
import
org.apache.shardingsphere.database.exception.mysql.exception.CollationCharsetMismatchException;
import
org.apache.shardingsphere.database.exception.mysql.exception.ErrorGlobalVariableException;
import
org.apache.shardingsphere.database.exception.mysql.exception.UnknownCharsetException;
import
org.apache.shardingsphere.database.exception.mysql.exception.UnknownCollationException;
import
org.apache.shardingsphere.database.exception.mysql.exception.UnknownSystemVariableException;
-import
org.apache.shardingsphere.database.exception.mysql.exception.WrongValueForVariableException;
import org.apache.shardingsphere.database.protocol.constant.CommonConstants;
import
org.apache.shardingsphere.database.protocol.mysql.constant.MySQLCharacterSets;
import
org.apache.shardingsphere.database.protocol.mysql.constant.MySQLConstants;
@@ -187,7 +187,7 @@ class MySQLSetVariableAdminExecutorTest {
new VariableAssignSegment(0, 0, new VariableSegment(0, 0,
"character_set_connection"), "utf8mb4")));
ConnectionSession connectionSession =
mockReplayableConnectionSession();
new
MySQLSetVariableAdminExecutor(setStatement).execute(connectionSession, mock());
-
assertThat(MySQLSessionCharsetContext.get(connectionSession.getAttributeMap()).getConnectionCollation(),
is(MySQLCharacterSets.UTF8MB4_UNICODE_CI));
+
assertThat(MySQLSessionCharsetContext.get(connectionSession.getAttributeMap()).getConnectionCollationName(),
is("utf8mb4_unicode_ci"));
assertThat(connectionSession.getRequiredSessionVariableRecorder().toSetSQLs(databaseType.getType()),
is(Collections.singletonList("SET
collation_connection='utf8mb4_unicode_ci'")));
}
@@ -198,9 +198,9 @@ class MySQLSetVariableAdminExecutorTest {
ConnectionSession connectionSession =
mockReplayableConnectionSession();
new
MySQLSetVariableAdminExecutor(setStatement).execute(connectionSession, mock());
MySQLSessionCharsetContext actual =
MySQLSessionCharsetContext.get(connectionSession.getAttributeMap());
- assertThat(actual.getClientCharacterSet(),
is(MySQLConstants.DEFAULT_CHARSET));
- assertThat(actual.getResultCharacterSet(),
is(Optional.of(MySQLConstants.DEFAULT_CHARSET)));
- assertThat(actual.getConnectionCollation(),
is(MySQLCharacterSets.UTF8MB4_UNICODE_CI));
+ assertThat(actual.getClientCharacterSetName(),
is(MySQLConstants.DEFAULT_CHARSET.getCharacterSetName()));
+ assertThat(actual.getResultCharacterSetName(),
is(Optional.of(MySQLConstants.DEFAULT_CHARSET.getCharacterSetName())));
+ assertThat(actual.getConnectionCollationName(),
is("utf8mb4_unicode_ci"));
assertThat(connectionSession.getRequiredSessionVariableRecorder().toSetSQLs(databaseType.getType()),
is(Collections.singletonList("SET
collation_connection='utf8mb4_unicode_ci'")));
}
@@ -211,7 +211,7 @@ class MySQLSetVariableAdminExecutorTest {
new VariableAssignSegment(0, 0, new VariableSegment(0, 0,
"collation_connection"), "DEFAULT")));
ConnectionSession connectionSession =
mockReplayableConnectionSession();
new
MySQLSetVariableAdminExecutor(setStatement).execute(connectionSession, mock());
-
assertThat(MySQLSessionCharsetContext.get(connectionSession.getAttributeMap()).getConnectionCollation(),
is(MySQLCharacterSets.UTF8MB4_UNICODE_CI));
+
assertThat(MySQLSessionCharsetContext.get(connectionSession.getAttributeMap()).getConnectionCollationName(),
is("utf8mb4_unicode_ci"));
assertThat(connectionSession.getRequiredSessionVariableRecorder().toSetSQLs(databaseType.getType()),
is(Collections.singletonList("SET
collation_connection='utf8mb4_unicode_ci'")));
}
@@ -240,10 +240,28 @@ class MySQLSetVariableAdminExecutorTest {
ConnectionSession connectionSession = mockConnectionSession();
MySQLSessionCharsetContext expected =
MySQLSessionCharsetContext.create(MySQLCharacterSets.LATIN1_SWEDISH_CI);
expected.apply(connectionSession.getAttributeMap());
- assertThrows(WrongValueForVariableException.class, () -> new
MySQLSetVariableAdminExecutor(setStatement).execute(connectionSession, mock()));
+ InvalidParameterValueException actual = assertThrows(
+ InvalidParameterValueException.class, () -> new
MySQLSetVariableAdminExecutor(setStatement).execute(connectionSession, mock()));
+ assertThat(actual.getParameterName(), is("character_set_client"));
+ assertThat(actual.getParameterValue(), is("ucs2"));
assertThat(MySQLSessionCharsetContext.get(connectionSession.getAttributeMap()),
is(expected));
}
+ @Test
+ void assertExecuteSetNamesWithImpermissibleClientCharacterSet() {
+ SetStatement setStatement = parseSetStatement("SET NAMES utf16 COLLATE
utf16_general_ci");
+ InvalidParameterValueException actual = assertThrows(
+ InvalidParameterValueException.class, () -> new
MySQLSetVariableAdminExecutor(setStatement).execute(mock(ConnectionSession.class),
mock()));
+ assertThat(actual.getParameterName(), is("character_set_client"));
+ assertThat(actual.getParameterValue(), is("utf16"));
+ }
+
+ @Test
+ void
assertExecuteSetNamesWithImpermissibleClientCharacterSetAndMismatchedCollation()
{
+ SetStatement setStatement = parseSetStatement("SET NAMES utf16 COLLATE
utf8mb4_bin");
+ assertThrows(CollationCharsetMismatchException.class, () -> new
MySQLSetVariableAdminExecutor(setStatement).execute(mock(ConnectionSession.class),
mock()));
+ }
+
@Test
void assertExecuteWithMismatchedSetNamesCollation() {
SetStatement setStatement = parseSetStatement("SET NAMES latin1
COLLATE utf8mb4_bin");
diff --git
a/proxy/backend/dialect/mysql/src/test/java/org/apache/shardingsphere/proxy/backend/mysql/handler/admin/executor/variable/charset/MySQLCharsetVariableProviderTest.java
b/proxy/backend/dialect/mysql/src/test/java/org/apache/shardingsphere/proxy/backend/mysql/handler/admin/executor/variable/charset/MySQLCharsetVariableProviderTest.java
deleted file mode 100644
index 919f452ed0d..00000000000
---
a/proxy/backend/dialect/mysql/src/test/java/org/apache/shardingsphere/proxy/backend/mysql/handler/admin/executor/variable/charset/MySQLCharsetVariableProviderTest.java
+++ /dev/null
@@ -1,81 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements. See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF licenses this file to You under the Apache License, Version 2.0
- * (the "License"); you may not use this file except in compliance with
- * the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package
org.apache.shardingsphere.proxy.backend.mysql.handler.admin.executor.variable.charset;
-
-import
org.apache.shardingsphere.database.connector.core.spi.DatabaseTypedSPILoader;
-import org.apache.shardingsphere.database.connector.core.type.DatabaseType;
-import
org.apache.shardingsphere.database.exception.mysql.exception.UnknownCharsetException;
-import
org.apache.shardingsphere.database.protocol.mysql.constant.MySQLConstants;
-import org.apache.shardingsphere.infra.spi.type.typed.TypedSPILoader;
-import
org.apache.shardingsphere.proxy.backend.handler.admin.executor.variable.charset.CharsetVariableProvider;
-import org.junit.jupiter.api.Test;
-import org.junit.jupiter.params.ParameterizedTest;
-import org.junit.jupiter.params.provider.Arguments;
-import org.junit.jupiter.params.provider.MethodSource;
-
-import java.nio.charset.Charset;
-import java.nio.charset.StandardCharsets;
-import java.util.stream.Stream;
-
-import static org.hamcrest.MatcherAssert.assertThat;
-import static org.hamcrest.Matchers.contains;
-import static org.hamcrest.Matchers.is;
-import static org.junit.jupiter.api.Assertions.assertFalse;
-import static org.junit.jupiter.api.Assertions.assertTrue;
-import static org.junit.jupiter.api.Assertions.assertThrows;
-
-class MySQLCharsetVariableProviderTest {
-
- private final DatabaseType databaseType =
TypedSPILoader.getService(DatabaseType.class, "MySQL");
-
- private final CharsetVariableProvider provider =
DatabaseTypedSPILoader.getService(CharsetVariableProvider.class, databaseType);
-
- @Test
- void assertCharsetVariablesAndDatabaseType() {
- assertThat(provider.getCharsetVariables(), contains("charset",
"character_set_client", "character_set_results", "character_set_connection"));
- }
-
- @Test
- void assertParseCharsetWithUnknown() {
- assertThrows(UnknownCharsetException.class, () ->
provider.parseCharset("unknown_charset"));
- }
-
- @Test
- void assertShouldSetWhenCharacterSetResultsWithNull() {
- assertFalse(provider.shouldSet("character_set_results", "NULL"));
- }
-
- @Test
- void assertShouldSetWhenCharacterSetResultsWithCharset() {
- assertTrue(provider.shouldSet("character_set_results", "'utf8mb4'"));
- }
-
- @ParameterizedTest(name = "{0}")
- @MethodSource("successArguments")
- void assertParseCharset(final String input, final Charset expected) {
- assertThat(provider.parseCharset(input), is(expected));
- }
-
- private static Stream<Arguments> successArguments() {
- return Stream.of(
- Arguments.arguments(" default ",
MySQLConstants.DEFAULT_CHARSET.getCharset()),
- Arguments.arguments("latin1 ", Charset.forName("latin1")),
- Arguments.arguments("'utf8mb4'", StandardCharsets.UTF_8),
- Arguments.arguments("\"utf8mb4\"", StandardCharsets.UTF_8));
- }
-}
diff --git
a/proxy/backend/dialect/mysql/src/test/java/org/apache/shardingsphere/proxy/backend/mysql/handler/admin/executor/variable/charset/MySQLSessionCharsetContextTest.java
b/proxy/backend/dialect/mysql/src/test/java/org/apache/shardingsphere/proxy/backend/mysql/handler/admin/executor/variable/charset/MySQLSessionCharsetContextTest.java
index d4cb644785c..6ceea97431e 100644
---
a/proxy/backend/dialect/mysql/src/test/java/org/apache/shardingsphere/proxy/backend/mysql/handler/admin/executor/variable/charset/MySQLSessionCharsetContextTest.java
+++
b/proxy/backend/dialect/mysql/src/test/java/org/apache/shardingsphere/proxy/backend/mysql/handler/admin/executor/variable/charset/MySQLSessionCharsetContextTest.java
@@ -42,45 +42,40 @@ class MySQLSessionCharsetContextTest {
@Test
void assertGetDefaultContext() {
- assertThat(MySQLSessionCharsetContext.get(new
DefaultAttributeMap()).getClientCharacterSet(),
is(MySQLConstants.DEFAULT_CHARSET));
+ assertThat(MySQLSessionCharsetContext.get(new
DefaultAttributeMap()).getClientCharacterSetName(),
is(MySQLConstants.DEFAULT_CHARSET.getCharacterSetName()));
}
@Test
void assertGetStoredContext() {
DefaultAttributeMap attributeMap = new DefaultAttributeMap();
MySQLSessionCharsetContext expected =
MySQLSessionCharsetContext.create(MySQLCharacterSets.LATIN1_SWEDISH_CI);
-
attributeMap.attr(MySQLSessionCharsetContext.ATTRIBUTE_KEY).set(expected);
+ expected.apply(attributeMap);
assertThat(MySQLSessionCharsetContext.get(attributeMap), is(expected));
}
@Test
void assertWithClientCharacterSet() {
MySQLSessionCharsetContext actual =
MySQLSessionCharsetContext.create(MySQLConstants.DEFAULT_CHARSET).withClientCharacterSet(MySQLCharacterSets.LATIN1_SWEDISH_CI);
- assertThat(actual.getClientCharacterSet(),
is(MySQLCharacterSets.LATIN1_SWEDISH_CI));
- assertThat(actual.getResultCharacterSet(),
is(Optional.of(MySQLConstants.DEFAULT_CHARSET)));
+ assertThat(actual.getClientCharacterSetName(), is("latin1"));
+ assertThat(actual.getResultCharacterSetName(),
is(Optional.of(MySQLConstants.DEFAULT_CHARSET.getCharacterSetName())));
}
@Test
void assertWithResultCharacterSet() {
MySQLSessionCharsetContext actual =
MySQLSessionCharsetContext.create(MySQLConstants.DEFAULT_CHARSET).withResultCharacterSet(MySQLCharacterSets.LATIN1_SWEDISH_CI);
- assertThat(actual.getResultCharacterSet(),
is(Optional.of(MySQLCharacterSets.LATIN1_SWEDISH_CI)));
- assertThat(actual.getResultCharset(), is(StandardCharsets.ISO_8859_1));
+ assertThat(actual.getResultCharacterSetName(),
is(Optional.of("latin1")));
}
@Test
void assertWithoutResultConversion() {
MySQLSessionCharsetContext actual =
MySQLSessionCharsetContext.create(MySQLCharacterSets.LATIN1_SWEDISH_CI).withoutResultConversion();
assertThat(actual.getResultCharacterSetName(), is(Optional.empty()));
- assertThat(actual.getResultCharset(), is(StandardCharsets.UTF_8));
- assertThat(actual.getResultCollation(),
is(MySQLConstants.DEFAULT_CHARSET));
}
@Test
void assertWithBinaryResult() {
MySQLSessionCharsetContext actual =
MySQLSessionCharsetContext.create(MySQLCharacterSets.LATIN1_SWEDISH_CI).withBinaryResult();
assertThat(actual.getResultCharacterSetName(),
is(Optional.of("binary")));
- assertThat(actual.getResultCharset(), is(StandardCharsets.UTF_8));
- assertThat(actual.getResultCollation(),
is(MySQLConstants.DEFAULT_CHARSET));
}
@Test
@@ -92,18 +87,11 @@ class MySQLSessionCharsetContextTest {
assertThat(attributeMap.attr(MySQLConstants.CHARACTER_SET_ATTRIBUTE_KEY).get(),
is(MySQLConstants.DEFAULT_CHARSET));
}
- @Test
- void assertWithConnectionCharacterSet() {
- MySQLSessionCharsetContext actual =
MySQLSessionCharsetContext.create(MySQLConstants.DEFAULT_CHARSET).withConnectionCharacterSet(MySQLCharacterSets.LATIN1_SWEDISH_CI);
- assertThat(actual.getConnectionCharacterSetName(), is("latin1"));
- assertThat(actual.getConnectionCollationName(),
is("latin1_swedish_ci"));
- }
-
@Test
void assertWithConnectionCollation() {
MySQLSessionCharsetContext actual =
MySQLSessionCharsetContext.create(MySQLConstants.DEFAULT_CHARSET).withConnectionCollation(MySQLCharacterSets.UTF8MB4_BIN);
assertThat(actual.getConnectionCharacterSetName(), is("utf8mb4"));
- assertThat(actual.getConnectionCollation(),
is(MySQLCharacterSets.UTF8MB4_BIN));
+ assertThat(actual.getConnectionCollationName(), is("utf8mb4_bin"));
}
@Test
@@ -111,7 +99,7 @@ class MySQLSessionCharsetContextTest {
DefaultAttributeMap attributeMap = new DefaultAttributeMap();
MySQLSessionCharsetContext expected =
MySQLSessionCharsetContext.create(MySQLCharacterSets.LATIN1_SWEDISH_CI);
expected.apply(attributeMap);
-
assertThat(attributeMap.attr(MySQLSessionCharsetContext.ATTRIBUTE_KEY).get(),
is(expected));
+ assertThat(MySQLSessionCharsetContext.get(attributeMap), is(expected));
assertThat(attributeMap.attr(CommonConstants.CHARSET_ATTRIBUTE_KEY).get(),
is(StandardCharsets.ISO_8859_1));
assertThat(attributeMap.attr(MySQLConstants.RESULT_CHARSET_ATTRIBUTE_KEY).get(),
is(StandardCharsets.ISO_8859_1));
assertThat(attributeMap.attr(MySQLConstants.CHARACTER_SET_ATTRIBUTE_KEY).get(),
is(MySQLCharacterSets.LATIN1_SWEDISH_CI));
diff --git
a/proxy/frontend/dialect/mysql/src/test/java/org/apache/shardingsphere/proxy/frontend/mysql/authentication/MySQLAuthenticationEngineTest.java
b/proxy/frontend/dialect/mysql/src/test/java/org/apache/shardingsphere/proxy/frontend/mysql/authentication/MySQLAuthenticationEngineTest.java
index e0ba5ee9b74..6909a63f512 100644
---
a/proxy/frontend/dialect/mysql/src/test/java/org/apache/shardingsphere/proxy/frontend/mysql/authentication/MySQLAuthenticationEngineTest.java
+++
b/proxy/frontend/dialect/mysql/src/test/java/org/apache/shardingsphere/proxy/frontend/mysql/authentication/MySQLAuthenticationEngineTest.java
@@ -24,7 +24,7 @@ import io.netty.channel.Channel;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelPipeline;
import io.netty.channel.epoll.EpollDomainSocketChannel;
-import io.netty.util.Attribute;
+import io.netty.util.DefaultAttributeMap;
import lombok.SneakyThrows;
import org.apache.shardingsphere.authentication.Authenticator;
import org.apache.shardingsphere.authentication.AuthenticatorFactory;
@@ -39,10 +39,8 @@ import
org.apache.shardingsphere.database.exception.core.exception.syntax.databa
import
org.apache.shardingsphere.database.exception.mysql.exception.DatabaseAccessDeniedException;
import
org.apache.shardingsphere.database.exception.mysql.exception.HandshakeException;
import
org.apache.shardingsphere.database.exception.mysql.vendor.MySQLVendorError;
-import org.apache.shardingsphere.database.protocol.constant.CommonConstants;
import
org.apache.shardingsphere.database.protocol.mysql.constant.MySQLAuthenticationMethod;
import
org.apache.shardingsphere.database.protocol.mysql.constant.MySQLCapabilityFlag;
-import
org.apache.shardingsphere.database.protocol.mysql.constant.MySQLCharacterSets;
import
org.apache.shardingsphere.database.protocol.mysql.constant.MySQLConnectionPhase;
import
org.apache.shardingsphere.database.protocol.mysql.constant.MySQLConstants;
import
org.apache.shardingsphere.database.protocol.mysql.packet.generic.MySQLErrPacket;
@@ -67,7 +65,6 @@ import
org.apache.shardingsphere.test.infra.framework.extension.mock.AutoMockExt
import
org.apache.shardingsphere.test.infra.framework.extension.mock.StaticMockSettings;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
-import org.mockito.ArgumentCaptor;
import org.mockito.MockedConstruction;
import org.mockito.MockedConstruction.Context;
import org.mockito.internal.configuration.plugins.Plugins;
@@ -142,23 +139,17 @@ class MySQLAuthenticationEngineTest {
assertThat(arguments.get(1), is(new Object[0]));
}
- @SuppressWarnings("unchecked")
@Test
void assertAuthenticationMethodMismatch() {
AuthorityRule rule = mock(AuthorityRule.class);
when(rule.getAuthenticatorType(any())).thenReturn("");
setConnectionPhase(MySQLConnectionPhase.AUTH_PHASE_FAST_PATH);
MySQLPacketPayload payload = mock(MySQLPacketPayload.class);
- ChannelHandlerContext channelHandlerContext =
mock(ChannelHandlerContext.class);
Channel channel = mock(Channel.class);
when(payload.readStringNulByBytes()).thenReturn("root".getBytes());
when(channel.remoteAddress()).thenReturn(new
InetSocketAddress("localhost", 3307));
-
when(channel.attr(CommonConstants.CHARSET_ATTRIBUTE_KEY)).thenReturn(mock(Attribute.class));
-
when(channel.attr(MySQLConstants.CHARACTER_SET_ATTRIBUTE_KEY)).thenReturn(mock(Attribute.class));
-
when(channel.attr(MySQLConstants.RESULT_CHARSET_ATTRIBUTE_KEY)).thenReturn(mock(Attribute.class));
-
when(channel.attr(MySQLSessionCharsetContext.ATTRIBUTE_KEY)).thenReturn(mock(Attribute.class));
-
when(channel.attr(MySQLConstants.OPTION_MULTI_STATEMENTS_ATTRIBUTE_KEY)).thenReturn(mock(Attribute.class));
-
when(channel.attr(MySQLConstants.CONNECTION_ATTRIBUTES_ATTRIBUTE_KEY)).thenReturn(mock(Attribute.class));
+ mockAttributes(channel);
+ ChannelHandlerContext channelHandlerContext =
mock(ChannelHandlerContext.class);
when(channelHandlerContext.channel()).thenReturn(channel);
when(payload.readInt1()).thenReturn(1);
when(payload.readInt4()).thenReturn(MySQLCapabilityFlag.CLIENT_PLUGIN_AUTH.getValue());
@@ -321,12 +312,9 @@ class MySQLAuthenticationEngineTest {
ChannelHandlerContext context = mockChannelHandlerContext();
ContextManager contextManager = mockContextManager(rule);
when(ProxyContext.getInstance().getContextManager()).thenReturn(contextManager);
- Attribute<MySQLSessionCharsetContext> charsetContextAttribute =
context.channel().attr(MySQLSessionCharsetContext.ATTRIBUTE_KEY);
authenticationEngine.authenticate(context, getPayload("root", null,
authResponse));
verify(context).writeAndFlush(any(MySQLOKPacket.class));
- ArgumentCaptor<MySQLSessionCharsetContext> charsetContextCaptor =
ArgumentCaptor.forClass(MySQLSessionCharsetContext.class);
- verify(charsetContextAttribute).set(charsetContextCaptor.capture());
- assertThat(charsetContextCaptor.getValue().getClientCharacterSet(),
is(MySQLCharacterSets.BIG5_CHINESE_CI));
+
assertThat(MySQLSessionCharsetContext.get(context.channel()).getClientCharacterSetName(),
is("big5"));
}
@Test
@@ -381,40 +369,31 @@ class MySQLAuthenticationEngineTest {
return result;
}
- @SuppressWarnings("unchecked")
private ChannelHandlerContext mockDomainSocketChannelHandlerContext() {
- ChannelHandlerContext result = mock(ChannelHandlerContext.class);
EpollDomainSocketChannel channel =
mock(EpollDomainSocketChannel.class, RETURNS_DEEP_STUBS);
Channel parentChannel = mock(Channel.class);
SocketAddress socketAddress = mock(SocketAddress.class);
when(socketAddress.toString()).thenReturn("local_host");
when(parentChannel.localAddress()).thenReturn(socketAddress);
when(channel.parent()).thenReturn(parentChannel);
-
when(channel.attr(CommonConstants.CHARSET_ATTRIBUTE_KEY)).thenReturn(mock(Attribute.class));
-
when(channel.attr(MySQLConstants.CHARACTER_SET_ATTRIBUTE_KEY)).thenReturn(mock(Attribute.class));
-
when(channel.attr(MySQLConstants.RESULT_CHARSET_ATTRIBUTE_KEY)).thenReturn(mock(Attribute.class));
-
when(channel.attr(MySQLSessionCharsetContext.ATTRIBUTE_KEY)).thenReturn(mock(Attribute.class));
-
when(channel.attr(MySQLConstants.SEQUENCE_ID_ATTRIBUTE_KEY)).thenReturn(mock(Attribute.class));
-
when(channel.attr(MySQLConstants.OPTION_MULTI_STATEMENTS_ATTRIBUTE_KEY)).thenReturn(mock(Attribute.class));
-
when(channel.attr(MySQLConstants.CONNECTION_ATTRIBUTES_ATTRIBUTE_KEY)).thenReturn(mock(Attribute.class));
+ mockAttributes(channel);
+ ChannelHandlerContext result = mock(ChannelHandlerContext.class);
doReturn(channel).when(result).channel();
return result;
}
- @SuppressWarnings("unchecked")
private Channel getChannel() {
Channel result = mock(Channel.class);
doReturn(getRemoteAddress()).when(result).remoteAddress();
-
when(result.attr(CommonConstants.CHARSET_ATTRIBUTE_KEY)).thenReturn(mock(Attribute.class));
-
when(result.attr(MySQLConstants.CHARACTER_SET_ATTRIBUTE_KEY)).thenReturn(mock(Attribute.class));
-
when(result.attr(MySQLConstants.RESULT_CHARSET_ATTRIBUTE_KEY)).thenReturn(mock(Attribute.class));
-
when(result.attr(MySQLSessionCharsetContext.ATTRIBUTE_KEY)).thenReturn(mock(Attribute.class));
-
when(result.attr(MySQLConstants.SEQUENCE_ID_ATTRIBUTE_KEY)).thenReturn(mock(Attribute.class));
-
when(result.attr(MySQLConstants.OPTION_MULTI_STATEMENTS_ATTRIBUTE_KEY)).thenReturn(mock(Attribute.class));
-
when(result.attr(MySQLConstants.CONNECTION_ATTRIBUTES_ATTRIBUTE_KEY)).thenReturn(mock(Attribute.class));
+ mockAttributes(result);
return result;
}
+ private void mockAttributes(final Channel channel) {
+ DefaultAttributeMap attributes = new DefaultAttributeMap();
+ when(channel.attr(any())).thenAnswer(invocation ->
attributes.attr(invocation.getArgument(0)));
+ }
+
private SocketAddress getRemoteAddress() {
SocketAddress result = mock(SocketAddress.class);
when(result.toString()).thenReturn("127.0.0.1");
diff --git
a/test/e2e/sql/src/test/resources/cases/dal/dataset/select_aliased_system_variables_with_scope.xml
b/test/e2e/sql/src/test/resources/cases/dal/dataset/select_aliased_system_variables_with_scope.xml
index 4986b9f038f..2d3df7306d1 100644
---
a/test/e2e/sql/src/test/resources/cases/dal/dataset/select_aliased_system_variables_with_scope.xml
+++
b/test/e2e/sql/src/test/resources/cases/dal/dataset/select_aliased_system_variables_with_scope.xml
@@ -460,5 +460,5 @@
<column name="thread_concurrency" />
<column name="timed_mutexes" />
</metadata>
- <row values="0, , 33062, , , , , , , , , TLSv1.2,TLSv1.3, *,,, 1, 1, 1, 1,
1, 0, 0, , 0, *, 32768, 0, 0, 1, 2592000, 0, 0, 1, 0, 1, 0, 8192, 0, 32768, 0,
3, 25000, , 8388608, utf8mb4, utf8mb4, utf8mb4, binary, utf8mb4, utf8mb4,
utf8mb4, , 0, utf8mb4_unicode_ci, utf8mb4_unicode_ci, utf8mb4_unicode_ci, 10,
8912, 18446744073709551615, 0, 0, 1000, , , caching_sha2_password,
utf8mb4_unicode_ci, 0, , 0, , 0, 100, 300, 1000, , 1, 4, 0, FALSE, 200, 0, 0,
1, , 0, 0, 1, + -><()~*:"& [...]
+ <row values="0, , 33062, , , , , , , , , TLSv1.2,TLSv1.3, *,,, 1, 1, 1, 1,
1, 0, 0, , 0, *, 32768, 0, 0, 1, 2592000, 0, 0, 1, 0, 1, 0, 8192, 0, 32768, 0,
3, 25000, , 8388608, utf8mb4, utf8mb4, utf8mb4, binary, null, utf8mb4, utf8mb4,
, 0, utf8mb4_unicode_ci, utf8mb4_unicode_ci, utf8mb4_unicode_ci, 10, 8912,
18446744073709551615, 0, 0, 1000, , , caching_sha2_password,
utf8mb4_unicode_ci, 0, , 0, , 0, 100, 300, 1000, , 1, 4, 0, FALSE, 200, 0, 0,
1, , 0, 0, 1, + -><()~*:"&quo [...]
</dataset>
diff --git
a/test/e2e/sql/src/test/resources/cases/dal/dataset/select_system_variables.xml
b/test/e2e/sql/src/test/resources/cases/dal/dataset/select_system_variables.xml
index ee2142df3e7..0fc80c46a2c 100644
---
a/test/e2e/sql/src/test/resources/cases/dal/dataset/select_system_variables.xml
+++
b/test/e2e/sql/src/test/resources/cases/dal/dataset/select_system_variables.xml
@@ -460,5 +460,5 @@
<column name="@@thread_concurrency" />
<column name="@@timed_mutexes" />
</metadata>
- <row values="0, , 33062, , , , , , , , , TLSv1.2,TLSv1.3, *,,, 1, 1, 1, 1,
1, 0, 0, , 0, *, 32768, 0, 0, 1, 2592000, 0, 0, 1, 0, 1, 0, 8192, 0, 32768, 0,
3, 25000, , 8388608, utf8mb4, utf8mb4, utf8mb4, binary, utf8mb4, utf8mb4,
utf8mb4, , 0, utf8mb4_unicode_ci, utf8mb4_unicode_ci, utf8mb4_unicode_ci, 10,
8912, 18446744073709551615, 0, 0, 1000, , , caching_sha2_password,
utf8mb4_unicode_ci, 0, , 0, , 0, 100, 300, 1000, , 1, 4, 0, FALSE, 200, 0, 0,
1, , 0, 0, 1, + -><()~*:"& [...]
+ <row values="0, , 33062, , , , , , , , , TLSv1.2,TLSv1.3, *,,, 1, 1, 1, 1,
1, 0, 0, , 0, *, 32768, 0, 0, 1, 2592000, 0, 0, 1, 0, 1, 0, 8192, 0, 32768, 0,
3, 25000, , 8388608, utf8mb4, utf8mb4, utf8mb4, binary, null, utf8mb4, utf8mb4,
, 0, utf8mb4_unicode_ci, utf8mb4_unicode_ci, utf8mb4_unicode_ci, 10, 8912,
18446744073709551615, 0, 0, 1000, , , caching_sha2_password,
utf8mb4_unicode_ci, 0, , 0, , 0, 100, 300, 1000, , 1, 4, 0, FALSE, 200, 0, 0,
1, , 0, 0, 1, + -><()~*:"&quo [...]
</dataset>