This is an automated email from the ASF dual-hosted git repository.
panjuan 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 ab2c5361f5a Supports replaying session variables on database
connections (#19826)
ab2c5361f5a is described below
commit ab2c5361f5aae091e8c637e3951eefbedab0e398
Author: 吴伟杰 <[email protected]>
AuthorDate: Wed Aug 3 14:09:40 2022 +0800
Supports replaying session variables on database connections (#19826)
* Add logic of replaying set session on connections
* Add ReplayRequiredSessionVariables SPI
* Handling PostgreSQL RESET statement
* Add ReplayRequiredSessionVariablesLoaderTest
* Add PostgreSQLResetVariableAdminExecutorTest
* Add RequiredSessionVariableRecorderTest
* Add TODO in RequiredSessionVariableRecorder
* Complete JDBCBackendConnectionTest
* Revise variables and conditions
* Complete PostgreSQLAdminExecutorCreatorTest
* Complete tests for DefaultSessionVariableHandler
---
.../jdbc/connection/JDBCBackendConnection.java | 57 +++++++++
.../ReplayRequiredSessionVariables.java} | 24 ++--
.../ReplayRequiredSessionVariablesLoader.java | 44 +++++++
.../mysql/DefaultMySQLSessionVariableHandler.java | 11 +-
.../DefaultPostgreSQLSessionVariableHandler.java | 11 +-
.../postgresql/PostgreSQLAdminExecutorCreator.java | 9 +-
...a => PostgreSQLResetVariableAdminExecutor.java} | 21 +++-
.../proxy/backend/session/ConnectionSession.java | 2 +
.../session/RequiredSessionVariableRecorder.java | 129 +++++++++++++++++++++
.../jdbc/connection/JDBCBackendConnectionTest.java | 77 ++++++++++++
.../ReplayRequiredSessionVariablesLoaderTest.java} | 23 ++--
.../FixtureReplayRequiredSessionVariables.java} | 23 ++--
.../DefaultMySQLSessionVariableHandlerTest.java | 53 +++++++++
...efaultPostgreSQLSessionVariableHandlerTest.java | 21 +++-
.../PostgreSQLAdminExecutorCreatorTest.java | 9 ++
.../PostgreSQLResetVariableAdminExecutorTest.java | 42 +++++++
.../RequiredSessionVariableRecorderTest.java | 75 ++++++++++++
...r.admin.executor.ReplayRequiredSessionVariables | 18 +++
18 files changed, 610 insertions(+), 39 deletions(-)
diff --git
a/shardingsphere-proxy/shardingsphere-proxy-backend/src/main/java/org/apache/shardingsphere/proxy/backend/communication/jdbc/connection/JDBCBackendConnection.java
b/shardingsphere-proxy/shardingsphere-proxy-backend/src/main/java/org/apache/shardingsphere/proxy/backend/communication/jdbc/connection/JDBCBackendConnection.java
index 1d24788bd59..19cb274b5ae 100644
---
a/shardingsphere-proxy/shardingsphere-proxy-backend/src/main/java/org/apache/shardingsphere/proxy/backend/communication/jdbc/connection/JDBCBackendConnection.java
+++
b/shardingsphere-proxy/shardingsphere-proxy-backend/src/main/java/org/apache/shardingsphere/proxy/backend/communication/jdbc/connection/JDBCBackendConnection.java
@@ -35,6 +35,7 @@ import
org.apache.shardingsphere.transaction.core.TransactionType;
import java.sql.Connection;
import java.sql.SQLException;
+import java.sql.Statement;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
@@ -93,6 +94,7 @@ public final class JDBCBackendConnection implements
BackendConnection<Void>, Exe
private List<Connection> createNewConnections(final String dataSourceName,
final int connectionSize, final ConnectionMode connectionMode) throws
SQLException {
Preconditions.checkNotNull(connectionSession.getDatabaseName(),
"Current schema is null.");
List<Connection> result =
ProxyContext.getInstance().getBackendDataSource().getConnections(connectionSession.getDatabaseName(),
dataSourceName, connectionSize, connectionMode);
+ setSessionVariablesIfNecessary(result);
for (Connection each : result) {
replayTransactionOption(each);
}
@@ -104,6 +106,36 @@ public final class JDBCBackendConnection implements
BackendConnection<Void>, Exe
return result;
}
+ private void setSessionVariablesIfNecessary(final List<Connection>
connections) throws SQLException {
+ if (connectionSession.getRequiredSessionVariableRecorder().isEmpty()
|| connections.isEmpty()) {
+ return;
+ }
+ String databaseType =
connections.iterator().next().getMetaData().getDatabaseProductName();
+ List<String> setSQLs =
connectionSession.getRequiredSessionVariableRecorder().toSetSQLs(databaseType);
+ SQLException sqlException = null;
+ for (Connection each : connections) {
+ try (Statement statement = each.createStatement()) {
+ for (String eachSetSQL : setSQLs) {
+ statement.execute(eachSetSQL);
+ }
+ } catch (final SQLException ex) {
+ sqlException = ex;
+ break;
+ }
+ }
+ if (null == sqlException) {
+ return;
+ }
+ for (Connection each : connections) {
+ try {
+ each.close();
+ } catch (final SQLException ex) {
+ sqlException.setNextException(ex);
+ }
+ }
+ throw sqlException;
+ }
+
private void replayMethodsInvocation(final Connection target) {
for (ConnectionPostProcessor<Connection> each :
connectionPostProcessors) {
each.process(target);
@@ -245,6 +277,7 @@ public final class JDBCBackendConnection implements
BackendConnection<Void>, Exe
public Collection<SQLException> closeConnections(final boolean
forceRollback) {
Collection<SQLException> result = new LinkedList<>();
synchronized (cachedConnections) {
+ resetSessionVariablesIfNecessary(cachedConnections.values(),
result);
for (Connection each : cachedConnections.values()) {
try {
if (forceRollback &&
connectionSession.getTransactionStatus().isInTransaction()) {
@@ -262,4 +295,28 @@ public final class JDBCBackendConnection implements
BackendConnection<Void>, Exe
}
return result;
}
+
+ private void resetSessionVariablesIfNecessary(final Collection<Connection>
values, final Collection<SQLException> exceptions) {
+ if (connectionSession.getRequiredSessionVariableRecorder().isEmpty()
|| values.isEmpty()) {
+ return;
+ }
+ String databaseType;
+ try {
+ databaseType =
values.iterator().next().getMetaData().getDatabaseProductName();
+ } catch (final SQLException ex) {
+ exceptions.add(ex);
+ return;
+ }
+ List<String> resetSQLs =
connectionSession.getRequiredSessionVariableRecorder().toResetSQLs(databaseType);
+ for (Connection each : values) {
+ try (Statement statement = each.createStatement()) {
+ for (String eachResetSQL : resetSQLs) {
+ statement.execute(eachResetSQL);
+ }
+ } catch (final SQLException ex) {
+ exceptions.add(ex);
+ }
+ }
+
connectionSession.getRequiredSessionVariableRecorder().removeVariablesWithDefaultValue();
+ }
}
diff --git
a/shardingsphere-proxy/shardingsphere-proxy-backend/src/main/java/org/apache/shardingsphere/proxy/backend/handler/admin/mysql/DefaultMySQLSessionVariableHandler.java
b/shardingsphere-proxy/shardingsphere-proxy-backend/src/main/java/org/apache/shardingsphere/proxy/backend/handler/admin/executor/ReplayRequiredSessionVariables.java
similarity index 59%
copy from
shardingsphere-proxy/shardingsphere-proxy-backend/src/main/java/org/apache/shardingsphere/proxy/backend/handler/admin/mysql/DefaultMySQLSessionVariableHandler.java
copy to
shardingsphere-proxy/shardingsphere-proxy-backend/src/main/java/org/apache/shardingsphere/proxy/backend/handler/admin/executor/ReplayRequiredSessionVariables.java
index 21bd448ac35..9a138565511 100644
---
a/shardingsphere-proxy/shardingsphere-proxy-backend/src/main/java/org/apache/shardingsphere/proxy/backend/handler/admin/mysql/DefaultMySQLSessionVariableHandler.java
+++
b/shardingsphere-proxy/shardingsphere-proxy-backend/src/main/java/org/apache/shardingsphere/proxy/backend/handler/admin/executor/ReplayRequiredSessionVariables.java
@@ -15,19 +15,23 @@
* limitations under the License.
*/
-package org.apache.shardingsphere.proxy.backend.handler.admin.mysql;
+package org.apache.shardingsphere.proxy.backend.handler.admin.executor;
-import lombok.extern.slf4j.Slf4j;
-import org.apache.shardingsphere.proxy.backend.session.ConnectionSession;
+import org.apache.shardingsphere.spi.annotation.SingletonSPI;
+import org.apache.shardingsphere.spi.type.typed.TypedSPI;
+
+import java.util.Collection;
/**
- * Default session variable handler for MySQL.
+ * Declaring variables need to be replayed on connections.
*/
-@Slf4j
-public final class DefaultMySQLSessionVariableHandler implements
MySQLSessionVariableHandler {
+@SingletonSPI
+public interface ReplayRequiredSessionVariables extends TypedSPI {
- @Override
- public void handle(final ConnectionSession connectionSession, final String
variableName, final String assignValue) {
- log.debug("Set statement {} = {} was discarded.", variableName,
assignValue);
- }
+ /**
+ * Get variables need to be replayed on connections.
+ *
+ * @return variables need to be replayed on connections
+ */
+ Collection<String> getReplayRequiredVariables();
}
diff --git
a/shardingsphere-proxy/shardingsphere-proxy-backend/src/main/java/org/apache/shardingsphere/proxy/backend/handler/admin/executor/ReplayRequiredSessionVariablesLoader.java
b/shardingsphere-proxy/shardingsphere-proxy-backend/src/main/java/org/apache/shardingsphere/proxy/backend/handler/admin/executor/ReplayRequiredSessionVariablesLoader.java
new file mode 100644
index 00000000000..dbbde7ebfd3
--- /dev/null
+++
b/shardingsphere-proxy/shardingsphere-proxy-backend/src/main/java/org/apache/shardingsphere/proxy/backend/handler/admin/executor/ReplayRequiredSessionVariablesLoader.java
@@ -0,0 +1,44 @@
+/*
+ * 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.handler.admin.executor;
+
+import org.apache.shardingsphere.spi.ShardingSphereServiceLoader;
+import org.apache.shardingsphere.spi.type.typed.TypedSPIRegistry;
+
+import java.util.Collection;
+import java.util.Collections;
+
+/**
+ * Loader for session variables need to be replayed.
+ */
+public final class ReplayRequiredSessionVariablesLoader {
+
+ static {
+
ShardingSphereServiceLoader.register(ReplayRequiredSessionVariables.class);
+ }
+
+ /**
+ * Get session variables need to be replayed on connections.
+ *
+ * @param databaseType database type
+ * @return session variables need to be replayed on connections
+ */
+ public static Collection<String> getVariables(final String databaseType) {
+ return
TypedSPIRegistry.findRegisteredService(ReplayRequiredSessionVariables.class,
databaseType).orElseGet(() ->
Collections::emptySet).getReplayRequiredVariables();
+ }
+}
diff --git
a/shardingsphere-proxy/shardingsphere-proxy-backend/src/main/java/org/apache/shardingsphere/proxy/backend/handler/admin/mysql/DefaultMySQLSessionVariableHandler.java
b/shardingsphere-proxy/shardingsphere-proxy-backend/src/main/java/org/apache/shardingsphere/proxy/backend/handler/admin/mysql/DefaultMySQLSessionVariableHandler.java
index 21bd448ac35..399549b9189 100644
---
a/shardingsphere-proxy/shardingsphere-proxy-backend/src/main/java/org/apache/shardingsphere/proxy/backend/handler/admin/mysql/DefaultMySQLSessionVariableHandler.java
+++
b/shardingsphere-proxy/shardingsphere-proxy-backend/src/main/java/org/apache/shardingsphere/proxy/backend/handler/admin/mysql/DefaultMySQLSessionVariableHandler.java
@@ -18,16 +18,25 @@
package org.apache.shardingsphere.proxy.backend.handler.admin.mysql;
import lombok.extern.slf4j.Slf4j;
+import
org.apache.shardingsphere.proxy.backend.handler.admin.executor.ReplayRequiredSessionVariablesLoader;
import org.apache.shardingsphere.proxy.backend.session.ConnectionSession;
+import java.util.Collection;
+
/**
* Default session variable handler for MySQL.
*/
@Slf4j
public final class DefaultMySQLSessionVariableHandler implements
MySQLSessionVariableHandler {
+ private final Collection<String> replayRequiredSessionVariables =
ReplayRequiredSessionVariablesLoader.getVariables("MySQL");
+
@Override
public void handle(final ConnectionSession connectionSession, final String
variableName, final String assignValue) {
- log.debug("Set statement {} = {} was discarded.", variableName,
assignValue);
+ if (!replayRequiredSessionVariables.contains(variableName)) {
+ log.debug("Set statement {} = {} was discarded.", variableName,
assignValue);
+ } else {
+
connectionSession.getRequiredSessionVariableRecorder().setVariable(variableName,
assignValue);
+ }
}
}
diff --git
a/shardingsphere-proxy/shardingsphere-proxy-backend/src/main/java/org/apache/shardingsphere/proxy/backend/handler/admin/postgresql/DefaultPostgreSQLSessionVariableHandler.java
b/shardingsphere-proxy/shardingsphere-proxy-backend/src/main/java/org/apache/shardingsphere/proxy/backend/handler/admin/postgresql/DefaultPostgreSQLSessionVariableHandler.java
index 8a7fda73950..c3bf4b062d3 100644
---
a/shardingsphere-proxy/shardingsphere-proxy-backend/src/main/java/org/apache/shardingsphere/proxy/backend/handler/admin/postgresql/DefaultPostgreSQLSessionVariableHandler.java
+++
b/shardingsphere-proxy/shardingsphere-proxy-backend/src/main/java/org/apache/shardingsphere/proxy/backend/handler/admin/postgresql/DefaultPostgreSQLSessionVariableHandler.java
@@ -18,16 +18,25 @@
package org.apache.shardingsphere.proxy.backend.handler.admin.postgresql;
import lombok.extern.slf4j.Slf4j;
+import
org.apache.shardingsphere.proxy.backend.handler.admin.executor.ReplayRequiredSessionVariablesLoader;
import org.apache.shardingsphere.proxy.backend.session.ConnectionSession;
+import java.util.Collection;
+
/**
* Default session variable handler for PostgreSQL.
*/
@Slf4j
public final class DefaultPostgreSQLSessionVariableHandler implements
PostgreSQLSessionVariableHandler {
+ private final Collection<String> replayRequiredSessionVariables =
ReplayRequiredSessionVariablesLoader.getVariables("PostgreSQL");
+
@Override
public void handle(final ConnectionSession connectionSession, final String
variableName, final String assignValue) {
- log.debug("Set statement {} = {} was discarded.", variableName,
assignValue);
+ if (!replayRequiredSessionVariables.contains(variableName)) {
+ log.debug("Set statement {} = {} was discarded.", variableName,
assignValue);
+ } else {
+
connectionSession.getRequiredSessionVariableRecorder().setVariable(variableName,
assignValue);
+ }
}
}
diff --git
a/shardingsphere-proxy/shardingsphere-proxy-backend/src/main/java/org/apache/shardingsphere/proxy/backend/handler/admin/postgresql/PostgreSQLAdminExecutorCreator.java
b/shardingsphere-proxy/shardingsphere-proxy-backend/src/main/java/org/apache/shardingsphere/proxy/backend/handler/admin/postgresql/PostgreSQLAdminExecutorCreator.java
index 5344acab355..77df8fc5cb9 100644
---
a/shardingsphere-proxy/shardingsphere-proxy-backend/src/main/java/org/apache/shardingsphere/proxy/backend/handler/admin/postgresql/PostgreSQLAdminExecutorCreator.java
+++
b/shardingsphere-proxy/shardingsphere-proxy-backend/src/main/java/org/apache/shardingsphere/proxy/backend/handler/admin/postgresql/PostgreSQLAdminExecutorCreator.java
@@ -28,6 +28,7 @@ import
org.apache.shardingsphere.sql.parser.sql.common.segment.generic.table.Sim
import
org.apache.shardingsphere.sql.parser.sql.common.segment.generic.table.SubqueryTableSegment;
import
org.apache.shardingsphere.sql.parser.sql.common.segment.generic.table.TableSegment;
import org.apache.shardingsphere.sql.parser.sql.common.statement.SQLStatement;
+import
org.apache.shardingsphere.sql.parser.sql.common.statement.dal.ResetParameterStatement;
import
org.apache.shardingsphere.sql.parser.sql.common.statement.dal.SetStatement;
import
org.apache.shardingsphere.sql.parser.sql.common.statement.dml.SelectStatement;
@@ -76,7 +77,13 @@ public final class PostgreSQLAdminExecutorCreator implements
DatabaseAdminExecut
}
}
}
- return sqlStatement instanceof SetStatement ? Optional.of(new
PostgreSQLSetVariableAdminExecutor((SetStatement) sqlStatement)) :
Optional.empty();
+ if (sqlStatement instanceof SetStatement) {
+ return Optional.of(new
PostgreSQLSetVariableAdminExecutor((SetStatement) sqlStatement));
+ }
+ if (sqlStatement instanceof ResetParameterStatement) {
+ return Optional.of(new
PostgreSQLResetVariableAdminExecutor((ResetParameterStatement) sqlStatement));
+ }
+ return Optional.empty();
}
private boolean isQueryPgTable(final Collection<String>
selectedTableNames) {
diff --git
a/shardingsphere-proxy/shardingsphere-proxy-backend/src/main/java/org/apache/shardingsphere/proxy/backend/handler/admin/postgresql/DefaultPostgreSQLSessionVariableHandler.java
b/shardingsphere-proxy/shardingsphere-proxy-backend/src/main/java/org/apache/shardingsphere/proxy/backend/handler/admin/postgresql/PostgreSQLResetVariableAdminExecutor.java
similarity index 53%
copy from
shardingsphere-proxy/shardingsphere-proxy-backend/src/main/java/org/apache/shardingsphere/proxy/backend/handler/admin/postgresql/DefaultPostgreSQLSessionVariableHandler.java
copy to
shardingsphere-proxy/shardingsphere-proxy-backend/src/main/java/org/apache/shardingsphere/proxy/backend/handler/admin/postgresql/PostgreSQLResetVariableAdminExecutor.java
index 8a7fda73950..b10d93e7cd8 100644
---
a/shardingsphere-proxy/shardingsphere-proxy-backend/src/main/java/org/apache/shardingsphere/proxy/backend/handler/admin/postgresql/DefaultPostgreSQLSessionVariableHandler.java
+++
b/shardingsphere-proxy/shardingsphere-proxy-backend/src/main/java/org/apache/shardingsphere/proxy/backend/handler/admin/postgresql/PostgreSQLResetVariableAdminExecutor.java
@@ -17,17 +17,26 @@
package org.apache.shardingsphere.proxy.backend.handler.admin.postgresql;
-import lombok.extern.slf4j.Slf4j;
+import lombok.RequiredArgsConstructor;
+import
org.apache.shardingsphere.proxy.backend.handler.admin.executor.DatabaseAdminExecutor;
import org.apache.shardingsphere.proxy.backend.session.ConnectionSession;
+import
org.apache.shardingsphere.sql.parser.sql.common.statement.dal.ResetParameterStatement;
+
+import java.sql.SQLException;
/**
- * Default session variable handler for PostgreSQL.
+ * Reset variable admin executor for PostgreSQL.
*/
-@Slf4j
-public final class DefaultPostgreSQLSessionVariableHandler implements
PostgreSQLSessionVariableHandler {
+@RequiredArgsConstructor
+public final class PostgreSQLResetVariableAdminExecutor implements
DatabaseAdminExecutor {
+
+ private static final String DEFAULT = "DEFAULT";
+
+ private final ResetParameterStatement resetParameterStatement;
@Override
- public void handle(final ConnectionSession connectionSession, final String
variableName, final String assignValue) {
- log.debug("Set statement {} = {} was discarded.", variableName,
assignValue);
+ public void execute(final ConnectionSession connectionSession) throws
SQLException {
+ String variableName =
resetParameterStatement.getConfigurationParameter();
+
PostgreSQLSessionVariableHandlerFactory.getHandler(variableName).handle(connectionSession,
variableName, DEFAULT);
}
}
diff --git
a/shardingsphere-proxy/shardingsphere-proxy-backend/src/main/java/org/apache/shardingsphere/proxy/backend/session/ConnectionSession.java
b/shardingsphere-proxy/shardingsphere-proxy-backend/src/main/java/org/apache/shardingsphere/proxy/backend/session/ConnectionSession.java
index 11ee8452468..7fe7ec5efff 100644
---
a/shardingsphere-proxy/shardingsphere-proxy-backend/src/main/java/org/apache/shardingsphere/proxy/backend/session/ConnectionSession.java
+++
b/shardingsphere-proxy/shardingsphere-proxy-backend/src/main/java/org/apache/shardingsphere/proxy/backend/session/ConnectionSession.java
@@ -77,6 +77,8 @@ public final class ConnectionSession {
private final PreparedStatementRegistry preparedStatementRegistry = new
PreparedStatementRegistry();
+ private final RequiredSessionVariableRecorder
requiredSessionVariableRecorder = new RequiredSessionVariableRecorder();
+
public ConnectionSession(final DatabaseType databaseType, final
TransactionType initialTransactionType, final AttributeMap attributeMap) {
this.databaseType = databaseType;
transactionStatus = new TransactionStatus(initialTransactionType);
diff --git
a/shardingsphere-proxy/shardingsphere-proxy-backend/src/main/java/org/apache/shardingsphere/proxy/backend/session/RequiredSessionVariableRecorder.java
b/shardingsphere-proxy/shardingsphere-proxy-backend/src/main/java/org/apache/shardingsphere/proxy/backend/session/RequiredSessionVariableRecorder.java
new file mode 100644
index 00000000000..8259f73fc93
--- /dev/null
+++
b/shardingsphere-proxy/shardingsphere-proxy-backend/src/main/java/org/apache/shardingsphere/proxy/backend/session/RequiredSessionVariableRecorder.java
@@ -0,0 +1,129 @@
+/*
+ * 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.session;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+import java.util.Map;
+import java.util.Map.Entry;
+import java.util.StringJoiner;
+import java.util.concurrent.ConcurrentHashMap;
+
+/**
+ * This class only records variables need to be replayed on connections.
+ */
+public final class RequiredSessionVariableRecorder {
+
+ private static final String DEFAULT = "DEFAULT";
+
+ private final Map<String, String> sessionVariables = new
ConcurrentHashMap<>();
+
+ /**
+ * Set variable.
+ *
+ * @param variableName variable name
+ * @param variableValue variable value
+ */
+ public void setVariable(final String variableName, final String
variableValue) {
+ sessionVariables.put(variableName, variableValue);
+ }
+
+ /**
+ * Return true if no session variable was set.
+ *
+ * @return true if no session variable was set
+ */
+ public boolean isEmpty() {
+ return sessionVariables.isEmpty();
+ }
+
+ /**
+ * Get set SQLs for database.
+ *
+ * @param databaseType database type
+ * @return set SQLs
+ */
+ public List<String> toSetSQLs(final String databaseType) {
+ if (sessionVariables.isEmpty()) {
+ return Collections.emptyList();
+ }
+ // TODO Refactor the following switch by SPI if we support more
database in future
+ switch (databaseType) {
+ case "MySQL":
+ return Collections.singletonList(aggregateToMySQLSetSQL());
+ case "PostgreSQL":
+ return convertToPostgreSQLSetSQLs();
+ default:
+ return Collections.emptyList();
+ }
+ }
+
+ private String aggregateToMySQLSetSQL() {
+ StringJoiner result = new StringJoiner(",", "SET ", "");
+ for (Entry<String, String> stringStringEntry :
sessionVariables.entrySet()) {
+ String s = stringStringEntry.getKey() + "=" +
stringStringEntry.getValue();
+ result.add(s);
+ }
+ return result.toString();
+ }
+
+ private List<String> convertToPostgreSQLSetSQLs() {
+ List<String> result = new ArrayList<>(sessionVariables.size());
+ for (Entry<String, String> entry : sessionVariables.entrySet()) {
+ result.add("SET " + entry.getKey() + "=" + entry.getValue());
+ }
+ return result;
+ }
+
+ /**
+ * Get reset SQLs for database.
+ *
+ * @param databaseType database type
+ * @return reset SQLs
+ */
+ public List<String> toResetSQLs(final String databaseType) {
+ if (sessionVariables.isEmpty()) {
+ return Collections.emptyList();
+ }
+ // TODO Refactor the following switch by SPI if we support more
database in future
+ switch (databaseType) {
+ case "MySQL":
+ return
Collections.singletonList(aggregateToMySQLSetDefaultSQLs());
+ case "PostgreSQL":
+ return Collections.singletonList("RESET ALL");
+ default:
+ return Collections.emptyList();
+ }
+ }
+
+ private String aggregateToMySQLSetDefaultSQLs() {
+ StringJoiner result = new StringJoiner(",", "SET ", "");
+ for (String each : sessionVariables.keySet()) {
+ result.add(each + "=" + DEFAULT);
+ }
+ return result.toString();
+ }
+
+ /**
+ * Remove variables with default value.
+ */
+ public void removeVariablesWithDefaultValue() {
+ sessionVariables.entrySet().removeIf(entry ->
DEFAULT.equalsIgnoreCase(entry.getValue()));
+ }
+}
diff --git
a/shardingsphere-proxy/shardingsphere-proxy-backend/src/test/java/org/apache/shardingsphere/proxy/backend/communication/jdbc/connection/JDBCBackendConnectionTest.java
b/shardingsphere-proxy/shardingsphere-proxy-backend/src/test/java/org/apache/shardingsphere/proxy/backend/communication/jdbc/connection/JDBCBackendConnectionTest.java
index 05b84612991..9c1b9bcd5c0 100644
---
a/shardingsphere-proxy/shardingsphere-proxy-backend/src/test/java/org/apache/shardingsphere/proxy/backend/communication/jdbc/connection/JDBCBackendConnectionTest.java
+++
b/shardingsphere-proxy/shardingsphere-proxy-backend/src/test/java/org/apache/shardingsphere/proxy/backend/communication/jdbc/connection/JDBCBackendConnectionTest.java
@@ -35,6 +35,7 @@ import
org.apache.shardingsphere.proxy.backend.context.ProxyContext;
import
org.apache.shardingsphere.proxy.backend.exception.BackendConnectionException;
import org.apache.shardingsphere.proxy.backend.handler.ProxyBackendHandler;
import org.apache.shardingsphere.proxy.backend.session.ConnectionSession;
+import
org.apache.shardingsphere.proxy.backend.session.RequiredSessionVariableRecorder;
import
org.apache.shardingsphere.proxy.backend.session.transaction.TransactionStatus;
import org.apache.shardingsphere.proxy.backend.util.ProxyContextRestorer;
import org.apache.shardingsphere.transaction.core.TransactionType;
@@ -44,6 +45,7 @@ import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Answers;
import org.mockito.Mock;
+import org.mockito.MockedStatic;
import org.mockito.junit.MockitoJUnitRunner;
import java.lang.reflect.Field;
@@ -51,6 +53,7 @@ import java.sql.Connection;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.Collection;
+import java.util.Collections;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
@@ -63,11 +66,13 @@ import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertThat;
import static org.junit.Assert.assertTrue;
import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.ArgumentMatchers.anyString;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.RETURNS_DEEP_STUBS;
import static org.mockito.Mockito.doThrow;
import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.mockStatic;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.times;
@@ -98,6 +103,7 @@ public final class JDBCBackendConnectionTest extends
ProxyContextRestorer {
JDBCBackendStatement backendStatement = new JDBCBackendStatement();
backendStatement.setDatabaseName(connectionSession.getDatabaseName());
when(connectionSession.getStatementManager()).thenReturn(backendStatement);
+
when(connectionSession.getRequiredSessionVariableRecorder()).thenReturn(new
RequiredSessionVariableRecorder());
}
private void setContextManager() {
@@ -265,6 +271,43 @@ public final class JDBCBackendConnectionTest extends
ProxyContextRestorer {
verify(connection, times(1)).createStatement();
}
+ @Test
+ public void assertGetConnectionsAndReplaySessionVariables() throws
SQLException {
+
connectionSession.getRequiredSessionVariableRecorder().setVariable("key",
"value");
+ List<Connection> actualConnections;
+ try (MockedStatic<ProxyContext> mockedStatic =
mockStatic(ProxyContext.class)) {
+ ProxyContext proxyContext = mock(ProxyContext.class,
RETURNS_DEEP_STUBS);
+
mockedStatic.when(ProxyContext::getInstance).thenReturn(proxyContext);
+ Connection connection = mock(Connection.class, RETURNS_DEEP_STUBS);
+
when(connection.getMetaData().getDatabaseProductName()).thenReturn("PostgreSQL");
+
when(proxyContext.getBackendDataSource().getConnections(anyString(),
anyString(), anyInt(), any(ConnectionMode.class)))
+ .thenReturn(Collections.singletonList(connection));
+ actualConnections = backendConnection.getConnections("", 1,
ConnectionMode.CONNECTION_STRICTLY);
+ }
+ Connection actualConnection = actualConnections.get(0);
+ verify(actualConnection.createStatement()).execute("SET key=value");
+ }
+
+ @Test
+ public void assertGetConnectionsAndFailedToReplaySessionVariables() throws
SQLException {
+
connectionSession.getRequiredSessionVariableRecorder().setVariable("key",
"value");
+ Connection connection = null;
+ SQLException expectedException = new SQLException();
+ try (MockedStatic<ProxyContext> mockedStatic =
mockStatic(ProxyContext.class)) {
+ ProxyContext proxyContext = mock(ProxyContext.class,
RETURNS_DEEP_STUBS);
+
mockedStatic.when(ProxyContext::getInstance).thenReturn(proxyContext);
+ connection = mock(Connection.class, RETURNS_DEEP_STUBS);
+
when(connection.getMetaData().getDatabaseProductName()).thenReturn("PostgreSQL");
+ when(connection.createStatement().execute("SET
key=value")).thenThrow(expectedException);
+
when(proxyContext.getBackendDataSource().getConnections(anyString(),
anyString(), anyInt(), any(ConnectionMode.class)))
+ .thenReturn(Collections.singletonList(connection));
+ backendConnection.getConnections("", 1,
ConnectionMode.CONNECTION_STRICTLY);
+ } catch (SQLException ex) {
+ assertThat(ex, is(expectedException));
+ verify(connection).close();
+ }
+ }
+
@Test
public void assertGetConnectionsWithoutTransactions() throws SQLException {
connectionSession.getTransactionStatus().setInTransaction(false);
@@ -392,4 +435,38 @@ public final class JDBCBackendConnectionTest extends
ProxyContextRestorer {
verify(backendConnection).closeHandlers(true);
verify(backendConnection).closeConnections(true);
}
+
+ @Test
+ public void assertCloseConnectionsAndResetVariables() throws SQLException {
+
connectionSession.getRequiredSessionVariableRecorder().setVariable("key",
"default");
+ Connection connection = mock(Connection.class, RETURNS_DEEP_STUBS);
+
when(connection.getMetaData().getDatabaseProductName()).thenReturn("PostgreSQL");
+ backendConnection.getCachedConnections().put("", connection);
+ backendConnection.closeConnections(false);
+ verify(connection.createStatement()).execute("RESET ALL");
+
assertTrue(connectionSession.getRequiredSessionVariableRecorder().isEmpty());
+ }
+
+ @Test
+ public void assertCloseConnectionsAndFailedToGetDatabaseType() throws
SQLException {
+
connectionSession.getRequiredSessionVariableRecorder().setVariable("key",
"default");
+ Connection connection = mock(Connection.class, RETURNS_DEEP_STUBS);
+ SQLException expectedException = new SQLException();
+
when(connection.getMetaData().getDatabaseProductName()).thenThrow(expectedException);
+ backendConnection.getCachedConnections().put("", connection);
+ Collection<SQLException> actualExceptions =
backendConnection.closeConnections(false);
+ assertThat(actualExceptions,
is(Collections.singletonList(expectedException)));
+ }
+
+ @Test
+ public void assertCloseConnectionsAndFailedToResetVariables() throws
SQLException {
+
connectionSession.getRequiredSessionVariableRecorder().setVariable("key",
"default");
+ Connection connection = mock(Connection.class, RETURNS_DEEP_STUBS);
+
when(connection.getMetaData().getDatabaseProductName()).thenReturn("PostgreSQL");
+ SQLException expectedException = new SQLException();
+ when(connection.createStatement()).thenThrow(expectedException);
+ backendConnection.getCachedConnections().put("", connection);
+ Collection<SQLException> actualExceptions =
backendConnection.closeConnections(false);
+ assertThat(actualExceptions,
is(Collections.singletonList(expectedException)));
+ }
}
diff --git
a/shardingsphere-proxy/shardingsphere-proxy-backend/src/test/java/org/apache/shardingsphere/proxy/backend/handler/admin/postgresql/DefaultPostgreSQLSessionVariableHandlerTest.java
b/shardingsphere-proxy/shardingsphere-proxy-backend/src/test/java/org/apache/shardingsphere/proxy/backend/handler/admin/executor/ReplayRequiredSessionVariablesLoaderTest.java
similarity index 56%
copy from
shardingsphere-proxy/shardingsphere-proxy-backend/src/test/java/org/apache/shardingsphere/proxy/backend/handler/admin/postgresql/DefaultPostgreSQLSessionVariableHandlerTest.java
copy to
shardingsphere-proxy/shardingsphere-proxy-backend/src/test/java/org/apache/shardingsphere/proxy/backend/handler/admin/executor/ReplayRequiredSessionVariablesLoaderTest.java
index a1e78e45359..39cd9448c09 100644
---
a/shardingsphere-proxy/shardingsphere-proxy-backend/src/test/java/org/apache/shardingsphere/proxy/backend/handler/admin/postgresql/DefaultPostgreSQLSessionVariableHandlerTest.java
+++
b/shardingsphere-proxy/shardingsphere-proxy-backend/src/test/java/org/apache/shardingsphere/proxy/backend/handler/admin/executor/ReplayRequiredSessionVariablesLoaderTest.java
@@ -15,20 +15,25 @@
* limitations under the License.
*/
-package org.apache.shardingsphere.proxy.backend.handler.admin.postgresql;
+package org.apache.shardingsphere.proxy.backend.handler.admin.executor;
-import org.apache.shardingsphere.proxy.backend.session.ConnectionSession;
import org.junit.Test;
-import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.verifyNoInteractions;
+import java.util.Collections;
-public final class DefaultPostgreSQLSessionVariableHandlerTest {
+import static org.hamcrest.CoreMatchers.is;
+import static org.junit.Assert.assertThat;
+import static org.junit.Assert.assertTrue;
+
+public final class ReplayRequiredSessionVariablesLoaderTest {
+
+ @Test
+ public void assertGetVariablesForUnknownDatabaseType() {
+
assertTrue(ReplayRequiredSessionVariablesLoader.getVariables("unknown").isEmpty());
+ }
@Test
- public void assertHandle() {
- ConnectionSession connectionSession = mock(ConnectionSession.class);
- new
DefaultPostgreSQLSessionVariableHandler().handle(connectionSession, "", "");
- verifyNoInteractions(connectionSession);
+ public void assertGetVariablesForExistType() {
+
assertThat(ReplayRequiredSessionVariablesLoader.getVariables("fixture"),
is(Collections.singleton("fixture_variable")));
}
}
diff --git
a/shardingsphere-proxy/shardingsphere-proxy-backend/src/main/java/org/apache/shardingsphere/proxy/backend/handler/admin/mysql/DefaultMySQLSessionVariableHandler.java
b/shardingsphere-proxy/shardingsphere-proxy-backend/src/test/java/org/apache/shardingsphere/proxy/backend/handler/admin/fixture/FixtureReplayRequiredSessionVariables.java
similarity index 60%
copy from
shardingsphere-proxy/shardingsphere-proxy-backend/src/main/java/org/apache/shardingsphere/proxy/backend/handler/admin/mysql/DefaultMySQLSessionVariableHandler.java
copy to
shardingsphere-proxy/shardingsphere-proxy-backend/src/test/java/org/apache/shardingsphere/proxy/backend/handler/admin/fixture/FixtureReplayRequiredSessionVariables.java
index 21bd448ac35..ad6b684f4b3 100644
---
a/shardingsphere-proxy/shardingsphere-proxy-backend/src/main/java/org/apache/shardingsphere/proxy/backend/handler/admin/mysql/DefaultMySQLSessionVariableHandler.java
+++
b/shardingsphere-proxy/shardingsphere-proxy-backend/src/test/java/org/apache/shardingsphere/proxy/backend/handler/admin/fixture/FixtureReplayRequiredSessionVariables.java
@@ -15,19 +15,22 @@
* limitations under the License.
*/
-package org.apache.shardingsphere.proxy.backend.handler.admin.mysql;
+package org.apache.shardingsphere.proxy.backend.handler.admin.fixture;
-import lombok.extern.slf4j.Slf4j;
-import org.apache.shardingsphere.proxy.backend.session.ConnectionSession;
+import
org.apache.shardingsphere.proxy.backend.handler.admin.executor.ReplayRequiredSessionVariables;
-/**
- * Default session variable handler for MySQL.
- */
-@Slf4j
-public final class DefaultMySQLSessionVariableHandler implements
MySQLSessionVariableHandler {
+import java.util.Collection;
+import java.util.Collections;
+
+public final class FixtureReplayRequiredSessionVariables implements
ReplayRequiredSessionVariables {
+
+ @Override
+ public Collection<String> getReplayRequiredVariables() {
+ return Collections.singleton("fixture_variable");
+ }
@Override
- public void handle(final ConnectionSession connectionSession, final String
variableName, final String assignValue) {
- log.debug("Set statement {} = {} was discarded.", variableName,
assignValue);
+ public String getType() {
+ return "fixture";
}
}
diff --git
a/shardingsphere-proxy/shardingsphere-proxy-backend/src/test/java/org/apache/shardingsphere/proxy/backend/handler/admin/mysql/DefaultMySQLSessionVariableHandlerTest.java
b/shardingsphere-proxy/shardingsphere-proxy-backend/src/test/java/org/apache/shardingsphere/proxy/backend/handler/admin/mysql/DefaultMySQLSessionVariableHandlerTest.java
new file mode 100644
index 00000000000..95a2e4b6b06
--- /dev/null
+++
b/shardingsphere-proxy/shardingsphere-proxy-backend/src/test/java/org/apache/shardingsphere/proxy/backend/handler/admin/mysql/DefaultMySQLSessionVariableHandlerTest.java
@@ -0,0 +1,53 @@
+/*
+ * 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.handler.admin.mysql;
+
+import
org.apache.shardingsphere.proxy.backend.handler.admin.executor.ReplayRequiredSessionVariablesLoader;
+import org.apache.shardingsphere.proxy.backend.session.ConnectionSession;
+import
org.apache.shardingsphere.proxy.backend.session.RequiredSessionVariableRecorder;
+import org.junit.Test;
+import org.mockito.MockedStatic;
+
+import java.util.Collections;
+
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.mockStatic;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.verifyNoInteractions;
+import static org.mockito.Mockito.when;
+
+public final class DefaultMySQLSessionVariableHandlerTest {
+
+ @Test
+ public void assertHandleDiscard() {
+ ConnectionSession connectionSession = mock(ConnectionSession.class);
+ new DefaultMySQLSessionVariableHandler().handle(connectionSession, "",
"");
+ verifyNoInteractions(connectionSession);
+ }
+
+ @Test
+ public void assertHandleRecord() {
+ ConnectionSession connectionSession = mock(ConnectionSession.class);
+
when(connectionSession.getRequiredSessionVariableRecorder()).thenReturn(mock(RequiredSessionVariableRecorder.class));
+ try (MockedStatic<ReplayRequiredSessionVariablesLoader> mockedStatic =
mockStatic(ReplayRequiredSessionVariablesLoader.class)) {
+ mockedStatic.when(() ->
ReplayRequiredSessionVariablesLoader.getVariables("MySQL")).thenReturn(Collections.singleton("sql_mode"));
+ new DefaultMySQLSessionVariableHandler().handle(connectionSession,
"sql_mode", "''");
+
verify(connectionSession.getRequiredSessionVariableRecorder()).setVariable("sql_mode",
"''");
+ }
+ }
+}
diff --git
a/shardingsphere-proxy/shardingsphere-proxy-backend/src/test/java/org/apache/shardingsphere/proxy/backend/handler/admin/postgresql/DefaultPostgreSQLSessionVariableHandlerTest.java
b/shardingsphere-proxy/shardingsphere-proxy-backend/src/test/java/org/apache/shardingsphere/proxy/backend/handler/admin/postgresql/DefaultPostgreSQLSessionVariableHandlerTest.java
index a1e78e45359..a1302748961 100644
---
a/shardingsphere-proxy/shardingsphere-proxy-backend/src/test/java/org/apache/shardingsphere/proxy/backend/handler/admin/postgresql/DefaultPostgreSQLSessionVariableHandlerTest.java
+++
b/shardingsphere-proxy/shardingsphere-proxy-backend/src/test/java/org/apache/shardingsphere/proxy/backend/handler/admin/postgresql/DefaultPostgreSQLSessionVariableHandlerTest.java
@@ -17,18 +17,37 @@
package org.apache.shardingsphere.proxy.backend.handler.admin.postgresql;
+import
org.apache.shardingsphere.proxy.backend.handler.admin.executor.ReplayRequiredSessionVariablesLoader;
import org.apache.shardingsphere.proxy.backend.session.ConnectionSession;
+import
org.apache.shardingsphere.proxy.backend.session.RequiredSessionVariableRecorder;
import org.junit.Test;
+import org.mockito.MockedStatic;
+
+import java.util.Collections;
import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.mockStatic;
+import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.verifyNoInteractions;
+import static org.mockito.Mockito.when;
public final class DefaultPostgreSQLSessionVariableHandlerTest {
@Test
- public void assertHandle() {
+ public void assertHandleDiscard() {
ConnectionSession connectionSession = mock(ConnectionSession.class);
new
DefaultPostgreSQLSessionVariableHandler().handle(connectionSession, "", "");
verifyNoInteractions(connectionSession);
}
+
+ @Test
+ public void assertHandleRecord() {
+ ConnectionSession connectionSession = mock(ConnectionSession.class);
+
when(connectionSession.getRequiredSessionVariableRecorder()).thenReturn(mock(RequiredSessionVariableRecorder.class));
+ try (MockedStatic<ReplayRequiredSessionVariablesLoader> mockedStatic =
mockStatic(ReplayRequiredSessionVariablesLoader.class)) {
+ mockedStatic.when(() ->
ReplayRequiredSessionVariablesLoader.getVariables("PostgreSQL")).thenReturn(Collections.singleton("datestyle"));
+ new
DefaultPostgreSQLSessionVariableHandler().handle(connectionSession,
"datestyle", "postgres");
+
verify(connectionSession.getRequiredSessionVariableRecorder()).setVariable("datestyle",
"postgres");
+ }
+ }
}
diff --git
a/shardingsphere-proxy/shardingsphere-proxy-backend/src/test/java/org/apache/shardingsphere/proxy/backend/handler/admin/postgresql/PostgreSQLAdminExecutorCreatorTest.java
b/shardingsphere-proxy/shardingsphere-proxy-backend/src/test/java/org/apache/shardingsphere/proxy/backend/handler/admin/postgresql/PostgreSQLAdminExecutorCreatorTest.java
index 8d4b49df73b..8c8143b5f9e 100644
---
a/shardingsphere-proxy/shardingsphere-proxy-backend/src/test/java/org/apache/shardingsphere/proxy/backend/handler/admin/postgresql/PostgreSQLAdminExecutorCreatorTest.java
+++
b/shardingsphere-proxy/shardingsphere-proxy-backend/src/test/java/org/apache/shardingsphere/proxy/backend/handler/admin/postgresql/PostgreSQLAdminExecutorCreatorTest.java
@@ -29,6 +29,7 @@ import
org.apache.shardingsphere.proxy.backend.handler.admin.postgresql.executor
import org.apache.shardingsphere.sql.parser.api.CacheOption;
import org.apache.shardingsphere.sql.parser.sql.common.statement.SQLStatement;
import
org.apache.shardingsphere.sql.parser.sql.common.statement.dml.SelectStatement;
+import
org.apache.shardingsphere.sql.parser.sql.dialect.statement.postgresql.dal.PostgreSQLResetParameterStatement;
import
org.apache.shardingsphere.sql.parser.sql.dialect.statement.postgresql.dal.PostgreSQLSetStatement;
import
org.apache.shardingsphere.sql.parser.sql.dialect.statement.postgresql.dml.PostgreSQLDeleteStatement;
import
org.apache.shardingsphere.sql.parser.sql.dialect.statement.postgresql.dml.PostgreSQLSelectStatement;
@@ -119,6 +120,14 @@ public final class PostgreSQLAdminExecutorCreatorTest {
assertThat(actual.get(),
instanceOf(PostgreSQLSetVariableAdminExecutor.class));
}
+ @Test
+ public void assertCreateWithResetStatement() {
+ Optional<DatabaseAdminExecutor> actual = new
PostgreSQLAdminExecutorCreator()
+ .create(new CommonSQLStatementContext<>(new
PostgreSQLResetParameterStatement("client_encoding")), "RESET client_encoding",
"");
+ assertTrue(actual.isPresent());
+ assertThat(actual.get(),
instanceOf(PostgreSQLResetVariableAdminExecutor.class));
+ }
+
@Test
public void assertCreateWithDMLStatement() {
DeleteStatementContext sqlStatementContext = new
DeleteStatementContext(new PostgreSQLDeleteStatement());
diff --git
a/shardingsphere-proxy/shardingsphere-proxy-backend/src/test/java/org/apache/shardingsphere/proxy/backend/handler/admin/postgresql/PostgreSQLResetVariableAdminExecutorTest.java
b/shardingsphere-proxy/shardingsphere-proxy-backend/src/test/java/org/apache/shardingsphere/proxy/backend/handler/admin/postgresql/PostgreSQLResetVariableAdminExecutorTest.java
new file mode 100644
index 00000000000..f80afddab13
--- /dev/null
+++
b/shardingsphere-proxy/shardingsphere-proxy-backend/src/test/java/org/apache/shardingsphere/proxy/backend/handler/admin/postgresql/PostgreSQLResetVariableAdminExecutorTest.java
@@ -0,0 +1,42 @@
+/*
+ * 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.handler.admin.postgresql;
+
+import
org.apache.shardingsphere.sql.parser.sql.dialect.statement.postgresql.dal.PostgreSQLResetParameterStatement;
+import org.junit.Test;
+import org.mockito.MockedStatic;
+
+import java.sql.SQLException;
+
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.mockStatic;
+import static org.mockito.Mockito.verify;
+
+public final class PostgreSQLResetVariableAdminExecutorTest {
+
+ @Test
+ public void assertExecute() throws SQLException {
+ PostgreSQLResetVariableAdminExecutor executor = new
PostgreSQLResetVariableAdminExecutor(new
PostgreSQLResetParameterStatement("key"));
+ try (MockedStatic<PostgreSQLSessionVariableHandlerFactory> mockStatic
= mockStatic(PostgreSQLSessionVariableHandlerFactory.class)) {
+ PostgreSQLSessionVariableHandler mockHandler =
mock(PostgreSQLSessionVariableHandler.class);
+ mockStatic.when(() ->
PostgreSQLSessionVariableHandlerFactory.getHandler("key")).thenReturn(mockHandler);
+ executor.execute(null);
+ verify(mockHandler).handle(null, "key", "DEFAULT");
+ }
+ }
+}
diff --git
a/shardingsphere-proxy/shardingsphere-proxy-backend/src/test/java/org/apache/shardingsphere/proxy/backend/session/RequiredSessionVariableRecorderTest.java
b/shardingsphere-proxy/shardingsphere-proxy-backend/src/test/java/org/apache/shardingsphere/proxy/backend/session/RequiredSessionVariableRecorderTest.java
new file mode 100644
index 00000000000..326e9c9836d
--- /dev/null
+++
b/shardingsphere-proxy/shardingsphere-proxy-backend/src/test/java/org/apache/shardingsphere/proxy/backend/session/RequiredSessionVariableRecorderTest.java
@@ -0,0 +1,75 @@
+/*
+ * 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.session;
+
+import org.junit.Test;
+
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.HashSet;
+
+import static org.hamcrest.CoreMatchers.is;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertThat;
+import static org.junit.Assert.assertTrue;
+
+public final class RequiredSessionVariableRecorderTest {
+
+ @Test
+ public void assertRecordMySQLVariables() {
+ RequiredSessionVariableRecorder recorder = new
RequiredSessionVariableRecorder();
+ assertTrue(recorder.isEmpty());
+ String databaseType = "MySQL";
+ assertTrue(recorder.toSetSQLs(databaseType).isEmpty());
+ assertTrue(recorder.toResetSQLs(databaseType).isEmpty());
+ recorder.setVariable("sql_mode", "default");
+ recorder.setVariable("max_sort_length", "1024");
+ assertFalse(recorder.isEmpty());
+ assertThat(recorder.toSetSQLs(databaseType),
is(Collections.singletonList("SET sql_mode=default,max_sort_length=1024")));
+ assertThat(recorder.toResetSQLs(databaseType),
is(Collections.singletonList("SET sql_mode=DEFAULT,max_sort_length=DEFAULT")));
+ recorder.removeVariablesWithDefaultValue();
+ assertThat(recorder.toSetSQLs(databaseType),
is(Collections.singletonList("SET max_sort_length=1024")));
+ assertThat(recorder.toResetSQLs(databaseType),
is(Collections.singletonList("SET max_sort_length=DEFAULT")));
+ }
+
+ @Test
+ public void assertRecordPostgreSQLVariables() {
+ RequiredSessionVariableRecorder recorder = new
RequiredSessionVariableRecorder();
+ assertTrue(recorder.isEmpty());
+ String databaseType = "PostgreSQL";
+ assertTrue(recorder.toSetSQLs(databaseType).isEmpty());
+ assertTrue(recorder.toResetSQLs(databaseType).isEmpty());
+ recorder.setVariable("client_encoding", "utf8");
+ recorder.setVariable("datestyle", "default");
+ assertFalse(recorder.isEmpty());
+ assertThat(new HashSet<>(recorder.toSetSQLs(databaseType)), is(new
HashSet<>(Arrays.asList("SET client_encoding=utf8", "SET datestyle=default"))));
+ assertThat(recorder.toResetSQLs(databaseType),
is(Collections.singletonList("RESET ALL")));
+ recorder.removeVariablesWithDefaultValue();
+ assertThat(recorder.toSetSQLs(databaseType),
is(Collections.singletonList("SET client_encoding=utf8")));
+ assertThat(recorder.toResetSQLs(databaseType),
is(Collections.singletonList("RESET ALL")));
+ }
+
+ @Test
+ public void assertRecordUnsupportedDatabaseType() {
+ RequiredSessionVariableRecorder recorder = new
RequiredSessionVariableRecorder();
+ assertTrue(recorder.isEmpty());
+ recorder.setVariable("key", "value");
+ assertTrue(recorder.toSetSQLs("unsupported").isEmpty());
+ assertTrue(recorder.toResetSQLs("unsupported").isEmpty());
+ }
+}
diff --git
a/shardingsphere-proxy/shardingsphere-proxy-backend/src/test/resources/META-INF/services/org.apache.shardingsphere.proxy.backend.handler.admin.executor.ReplayRequiredSessionVariables
b/shardingsphere-proxy/shardingsphere-proxy-backend/src/test/resources/META-INF/services/org.apache.shardingsphere.proxy.backend.handler.admin.executor.ReplayRequiredSessionVariables
new file mode 100644
index 00000000000..fb0d9f3b112
--- /dev/null
+++
b/shardingsphere-proxy/shardingsphere-proxy-backend/src/test/resources/META-INF/services/org.apache.shardingsphere.proxy.backend.handler.admin.executor.ReplayRequiredSessionVariables
@@ -0,0 +1,18 @@
+#
+# 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.handler.admin.fixture.FixtureReplayRequiredSessionVariables