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 e3ab362512e Fix JDBC database null handling and preserve original 
exceptions (#40239)
e3ab362512e is described below

commit e3ab362512ee71f6235f69db7868a92f2ed8e544
Author: Liang Zhang <[email protected]>
AuthorDate: Mon Oct 19 20:27:02 2026 +0800

    Fix JDBC database null handling and preserve original exceptions (#40239)
---
 .../jdbc/adapter/AbstractStatementAdapter.java     |  10 +-
 .../core/connection/ShardingSphereConnection.java  |   2 +-
 .../metadata/ShardingSphereDatabaseMetaData.java   |   8 +-
 .../core/statement/DistSQLStatementExecutor.java   |   3 +-
 .../core/statement/ShardingSphereStatement.java    |  36 ++--
 .../connection/ShardingSphereConnectionTest.java   | 184 ++++++++++++++++
 .../ShardingSphereDatabaseMetaDataTest.java        |  94 ++++++++-
 .../statement/DistSQLStatementExecutorTest.java    |  71 ++++++-
 .../ShardingSpherePreparedStatementTest.java       | 172 +++++++++++++++
 .../statement/ShardingSphereStatementTest.java     | 235 ++++++++++++++++++++-
 10 files changed, 783 insertions(+), 32 deletions(-)

diff --git 
a/jdbc/src/main/java/org/apache/shardingsphere/driver/jdbc/adapter/AbstractStatementAdapter.java
 
b/jdbc/src/main/java/org/apache/shardingsphere/driver/jdbc/adapter/AbstractStatementAdapter.java
index 1146a540f51..a6ab8aedf45 100644
--- 
a/jdbc/src/main/java/org/apache/shardingsphere/driver/jdbc/adapter/AbstractStatementAdapter.java
+++ 
b/jdbc/src/main/java/org/apache/shardingsphere/driver/jdbc/adapter/AbstractStatementAdapter.java
@@ -28,6 +28,7 @@ import 
org.apache.shardingsphere.driver.jdbc.core.connection.ShardingSphereConne
 import org.apache.shardingsphere.driver.jdbc.core.statement.StatementManager;
 import org.apache.shardingsphere.infra.exception.ShardingSpherePreconditions;
 import org.apache.shardingsphere.infra.metadata.ShardingSphereMetaData;
+import 
org.apache.shardingsphere.infra.metadata.database.ShardingSphereDatabase;
 import 
org.apache.shardingsphere.infra.session.connection.transaction.TransactionConnectionContext;
 import 
org.apache.shardingsphere.sql.parser.statement.core.statement.SQLStatement;
 import 
org.apache.shardingsphere.sql.parser.statement.core.statement.type.tcl.CommitStatement;
@@ -81,7 +82,7 @@ public abstract class AbstractStatementAdapter extends 
WrapperAdapter implements
         if (!transactionContext.isExceptionOccur()) {
             return;
         }
-        DatabaseType databaseType = 
connection.getContextManager().getMetaDataContexts().getMetaData().getDatabase(connection.getCurrentDatabaseName()).getProtocolType();
+        DatabaseType databaseType = 
connection.getContextManager().getDatabase(connection.getCurrentDatabaseName()).getProtocolType();
         if (new 
DatabaseTypeRegistry(databaseType).getDialectDatabaseMetaData().getTransactionOption().isAllowCommitAndRollbackOnlyWhenTransactionFailed())
 {
             ShardingSpherePreconditions.checkState(sqlStatement instanceof 
CommitStatement || sqlStatement instanceof RollbackStatement,
                     () -> new SQLFeatureNotSupportedException("Current 
transaction is aborted, commands ignored until end of transaction block."));
@@ -96,8 +97,11 @@ public abstract class AbstractStatementAdapter extends 
WrapperAdapter implements
     
     protected final void handleExceptionInTransaction(final 
ShardingSphereConnection connection, final ShardingSphereMetaData metaData) {
         if 
(connection.getDatabaseConnectionManager().getConnectionContext().getTransactionContext().isInTransaction())
 {
-            DatabaseType databaseType = 
metaData.getDatabase(connection.getCurrentDatabaseName()).getProtocolType();
-            DialectDatabaseMetaData dialectDatabaseMetaData = new 
DatabaseTypeRegistry(databaseType).getDialectDatabaseMetaData();
+            ShardingSphereDatabase database = 
metaData.getDatabase(connection.getCurrentDatabaseName());
+            if (null == database) {
+                return;
+            }
+            DialectDatabaseMetaData dialectDatabaseMetaData = new 
DatabaseTypeRegistry(database.getProtocolType()).getDialectDatabaseMetaData();
             if 
(dialectDatabaseMetaData.getSchemaOption().getDefaultSchema().isPresent()) {
                 
connection.getDatabaseConnectionManager().getConnectionContext().getTransactionContext().setExceptionOccur(true);
             }
diff --git 
a/jdbc/src/main/java/org/apache/shardingsphere/driver/jdbc/core/connection/ShardingSphereConnection.java
 
b/jdbc/src/main/java/org/apache/shardingsphere/driver/jdbc/core/connection/ShardingSphereConnection.java
index 5b7c2fe5f80..76bafd178b0 100644
--- 
a/jdbc/src/main/java/org/apache/shardingsphere/driver/jdbc/core/connection/ShardingSphereConnection.java
+++ 
b/jdbc/src/main/java/org/apache/shardingsphere/driver/jdbc/core/connection/ShardingSphereConnection.java
@@ -238,7 +238,7 @@ public final class ShardingSphereConnection extends 
AbstractConnectionAdapter {
     }
     
     private boolean isSchemaSupportedDatabaseType() {
-        DatabaseType databaseType = 
contextManager.getMetaDataContexts().getMetaData().getDatabase(currentDatabaseName).getProtocolType();
+        DatabaseType databaseType = 
contextManager.getDatabase(currentDatabaseName).getProtocolType();
         return new 
DatabaseTypeRegistry(databaseType).getDialectDatabaseMetaData().getSchemaOption().getDefaultSchema().isPresent();
     }
     
diff --git 
a/jdbc/src/main/java/org/apache/shardingsphere/driver/jdbc/core/datasource/metadata/ShardingSphereDatabaseMetaData.java
 
b/jdbc/src/main/java/org/apache/shardingsphere/driver/jdbc/core/datasource/metadata/ShardingSphereDatabaseMetaData.java
index 98fca70d984..62220572bf3 100644
--- 
a/jdbc/src/main/java/org/apache/shardingsphere/driver/jdbc/core/datasource/metadata/ShardingSphereDatabaseMetaData.java
+++ 
b/jdbc/src/main/java/org/apache/shardingsphere/driver/jdbc/core/datasource/metadata/ShardingSphereDatabaseMetaData.java
@@ -50,7 +50,7 @@ public final class ShardingSphereDatabaseMetaData extends 
AdaptedDatabaseMetaDat
     public ShardingSphereDatabaseMetaData(final ShardingSphereConnection 
connection) throws SQLException {
         
super(connection.getDatabaseConnectionManager().getRandomConnection().getMetaData());
         this.connection = connection;
-        rules = 
connection.getContextManager().getMetaDataContexts().getMetaData().getDatabase(connection.getCurrentDatabaseName()).getRuleMetaData().getRules();
+        rules = 
connection.getContextManager().getDatabase(connection.getCurrentDatabaseName()).getRuleMetaData().getRules();
     }
     
     @Override
@@ -231,8 +231,7 @@ public final class ShardingSphereDatabaseMetaData extends 
AdaptedDatabaseMetaDat
             return null;
         }
         // TODO consider get actual catalog by logic catalog rather than 
random physical datasource's catalog.
-        ConnectionProperties connectionProps = connection.getContextManager()
-                
.getMetaDataContexts().getMetaData().getDatabase(connection.getCurrentDatabaseName()).getResourceMetaData().getStorageUnits().get(getDataSourceName()).getConnectionProperties();
+        ConnectionProperties connectionProps = 
connection.getContextManager().getStorageUnits(connection.getCurrentDatabaseName()).get(getDataSourceName()).getConnectionProperties();
         return connectionProps.getCatalog();
     }
     
@@ -241,8 +240,7 @@ public final class ShardingSphereDatabaseMetaData extends 
AdaptedDatabaseMetaDat
             return null;
         }
         // TODO consider get actual schema by logic catalog rather than random 
physical datasource's schema.
-        ConnectionProperties connectionProps = connection.getContextManager()
-                
.getMetaDataContexts().getMetaData().getDatabase(connection.getCurrentDatabaseName()).getResourceMetaData().getStorageUnits().get(getDataSourceName()).getConnectionProperties();
+        ConnectionProperties connectionProps = 
connection.getContextManager().getStorageUnits(connection.getCurrentDatabaseName()).get(getDataSourceName()).getConnectionProperties();
         return 
Optional.ofNullable(connectionProps.getSchema()).map(String::toUpperCase).orElse(null);
     }
     
diff --git 
a/jdbc/src/main/java/org/apache/shardingsphere/driver/jdbc/core/statement/DistSQLStatementExecutor.java
 
b/jdbc/src/main/java/org/apache/shardingsphere/driver/jdbc/core/statement/DistSQLStatementExecutor.java
index 4548a20c34b..29fb349f970 100644
--- 
a/jdbc/src/main/java/org/apache/shardingsphere/driver/jdbc/core/statement/DistSQLStatementExecutor.java
+++ 
b/jdbc/src/main/java/org/apache/shardingsphere/driver/jdbc/core/statement/DistSQLStatementExecutor.java
@@ -87,8 +87,7 @@ final class DistSQLStatementExecutor {
     
     private DistSQLConnectionContext createConnectionContext(final 
QueryContext queryContext) {
         DistSQLConnectionContext result = new 
DistSQLConnectionContext(queryContext, 
connection.getDatabaseConnectionManager().getConnectionSize(),
-                
connection.getContextManager().getMetaDataContexts().getMetaData().getDatabase(connection.getCurrentDatabaseName()).getProtocolType(),
-                connection.getDatabaseConnectionManager(), statementManager);
+                
connection.getContextManager().getDatabase(connection.getCurrentDatabaseName()).getProtocolType(),
 connection.getDatabaseConnectionManager(), statementManager);
         result.setProcessId(connection.getProcessId());
         return result;
     }
diff --git 
a/jdbc/src/main/java/org/apache/shardingsphere/driver/jdbc/core/statement/ShardingSphereStatement.java
 
b/jdbc/src/main/java/org/apache/shardingsphere/driver/jdbc/core/statement/ShardingSphereStatement.java
index 06e9221a960..c90c8cb7c0a 100644
--- 
a/jdbc/src/main/java/org/apache/shardingsphere/driver/jdbc/core/statement/ShardingSphereStatement.java
+++ 
b/jdbc/src/main/java/org/apache/shardingsphere/driver/jdbc/core/statement/ShardingSphereStatement.java
@@ -23,6 +23,8 @@ import 
org.apache.shardingsphere.database.connector.core.metadata.database.metad
 import org.apache.shardingsphere.database.connector.core.type.DatabaseType;
 import 
org.apache.shardingsphere.database.connector.core.type.DatabaseTypeRegistry;
 import 
org.apache.shardingsphere.database.exception.core.SQLExceptionTransformEngine;
+import 
org.apache.shardingsphere.database.exception.core.exception.SQLDialectException;
+import 
org.apache.shardingsphere.database.exception.core.exception.protocol.DatabaseProtocolException;
 import org.apache.shardingsphere.distsql.statement.DistSQLStatement;
 import 
org.apache.shardingsphere.driver.executor.callback.add.StatementAddCallback;
 import 
org.apache.shardingsphere.driver.executor.callback.execute.StatementExecuteCallback;
@@ -40,6 +42,7 @@ import 
org.apache.shardingsphere.infra.binder.context.statement.type.CommonSQLSt
 import 
org.apache.shardingsphere.infra.binder.context.statement.type.dml.InsertStatementContext;
 import org.apache.shardingsphere.infra.binder.engine.SQLBindEngine;
 import org.apache.shardingsphere.infra.exception.ShardingSpherePreconditions;
+import org.apache.shardingsphere.infra.exception.generic.UnknownSQLException;
 import 
org.apache.shardingsphere.infra.exception.kernel.syntax.EmptySQLException;
 import 
org.apache.shardingsphere.infra.executor.sql.prepare.driver.jdbc.JDBCDriverType;
 import 
org.apache.shardingsphere.infra.executor.sql.prepare.driver.jdbc.StatementOption;
@@ -106,9 +109,10 @@ public final class ShardingSphereStatement extends 
AbstractStatementAdapter {
         this.connection = connection;
         metaData = 
connection.getContextManager().getMetaDataContexts().getMetaData();
         statementOption = new StatementOption(resultSetType, 
resultSetConcurrency, resultSetHoldability);
+        ShardingSphereDatabase currentDatabase = 
connection.getContextManager().getDatabase(connection.getCurrentDatabaseName());
         statementManager = new StatementManager();
         connection.registerStatementManager(statementManager);
-        driverExecutorFacade = new DriverExecutorFacade(connection, 
statementOption, statementManager, JDBCDriverType.STATEMENT, 
metaData.getDatabase(connection.getCurrentDatabaseName()));
+        driverExecutorFacade = new DriverExecutorFacade(connection, 
statementOption, statementManager, JDBCDriverType.STATEMENT, currentDatabase);
         distSQLStatementExecutor = new DistSQLStatementExecutor(connection, 
statementManager, this);
         batchStatementExecutor = new BatchStatementExecutor(this);
         statements = new LinkedList<>();
@@ -133,10 +137,18 @@ public final class ShardingSphereStatement extends 
AbstractStatementAdapter {
             // CHECKSTYLE:ON
             handleExceptionInTransaction(connection, metaData);
             currentResultSet = null;
-            throw SQLExceptionTransformEngine.toSQLException(ex, 
metaData.getDatabase(usedDatabaseName).getProtocolType());
+            throw toSQLException(ex);
         }
     }
     
+    private SQLException toSQLException(final Exception cause) {
+        if (cause instanceof SQLDialectException && !(cause instanceof 
DatabaseProtocolException)) {
+            ShardingSphereDatabase usedDatabase = 
metaData.getDatabase(usedDatabaseName);
+            return null == usedDatabase ? new 
UnknownSQLException(cause).toSQLException() : 
SQLExceptionTransformEngine.toSQLException(cause, 
usedDatabase.getProtocolType());
+        }
+        return SQLExceptionTransformEngine.toSQLException(cause, 
metaData.getProtocolType());
+    }
+    
     @Override
     public int executeUpdate(final String sql) throws SQLException {
         try {
@@ -145,7 +157,7 @@ public final class ShardingSphereStatement extends 
AbstractStatementAdapter {
         } catch (final RuntimeException | SQLException ex) {
             // CHECKSTYLE:ON
             handleExceptionInTransaction(connection, metaData);
-            throw SQLExceptionTransformEngine.toSQLException(ex, 
metaData.getDatabase(usedDatabaseName).getProtocolType());
+            throw toSQLException(ex);
         }
     }
     
@@ -160,7 +172,7 @@ public final class ShardingSphereStatement extends 
AbstractStatementAdapter {
         } catch (final RuntimeException | SQLException ex) {
             // CHECKSTYLE:ON
             handleExceptionInTransaction(connection, metaData);
-            throw SQLExceptionTransformEngine.toSQLException(ex, 
metaData.getDatabase(usedDatabaseName).getProtocolType());
+            throw toSQLException(ex);
         }
     }
     
@@ -173,7 +185,7 @@ public final class ShardingSphereStatement extends 
AbstractStatementAdapter {
         } catch (final RuntimeException | SQLException ex) {
             // CHECKSTYLE:ON
             handleExceptionInTransaction(connection, metaData);
-            throw SQLExceptionTransformEngine.toSQLException(ex, 
metaData.getDatabase(usedDatabaseName).getProtocolType());
+            throw toSQLException(ex);
         }
     }
     
@@ -186,7 +198,7 @@ public final class ShardingSphereStatement extends 
AbstractStatementAdapter {
         } catch (final RuntimeException | SQLException ex) {
             // CHECKSTYLE:ON
             handleExceptionInTransaction(connection, metaData);
-            throw SQLExceptionTransformEngine.toSQLException(ex, 
metaData.getDatabase(usedDatabaseName).getProtocolType());
+            throw toSQLException(ex);
         }
     }
     
@@ -211,7 +223,7 @@ public final class ShardingSphereStatement extends 
AbstractStatementAdapter {
         } catch (final SQLException ex) {
             // CHECKSTYLE:ON
             handleExceptionInTransaction(connection, metaData);
-            throw SQLExceptionTransformEngine.toSQLException(ex, 
metaData.getDatabase(usedDatabaseName).getProtocolType());
+            throw ex;
         }
     }
     
@@ -226,7 +238,7 @@ public final class ShardingSphereStatement extends 
AbstractStatementAdapter {
         } catch (final SQLException ex) {
             // CHECKSTYLE:ON
             handleExceptionInTransaction(connection, metaData);
-            throw SQLExceptionTransformEngine.toSQLException(ex, 
metaData.getDatabase(usedDatabaseName).getProtocolType());
+            throw ex;
         }
     }
     
@@ -239,7 +251,7 @@ public final class ShardingSphereStatement extends 
AbstractStatementAdapter {
         } catch (final SQLException ex) {
             // CHECKSTYLE:ON
             handleExceptionInTransaction(connection, metaData);
-            throw SQLExceptionTransformEngine.toSQLException(ex, 
metaData.getDatabase(usedDatabaseName).getProtocolType());
+            throw ex;
         }
     }
     
@@ -252,7 +264,7 @@ public final class ShardingSphereStatement extends 
AbstractStatementAdapter {
         } catch (final SQLException ex) {
             // CHECKSTYLE:ON
             handleExceptionInTransaction(connection, metaData);
-            throw SQLExceptionTransformEngine.toSQLException(ex, 
metaData.getDatabase(usedDatabaseName).getProtocolType());
+            throw ex;
         }
     }
     
@@ -279,7 +291,7 @@ public final class ShardingSphereStatement extends 
AbstractStatementAdapter {
             // CHECKSTYLE:OFF
         } catch (final RuntimeException ex) {
             // CHECKSTYLE:ON
-            throw SQLExceptionTransformEngine.toSQLException(ex, 
metaData.getDatabase(usedDatabaseName).getProtocolType());
+            throw toSQLException(ex);
         }
     }
     
@@ -416,7 +428,7 @@ public final class ShardingSphereStatement extends 
AbstractStatementAdapter {
     
     private String getGeneratedKeysColumnName(final String columnName) {
         Optional<DialectGeneratedKeyOption> generatedKeyOption =
-                new 
DatabaseTypeRegistry(metaData.getDatabase(usedDatabaseName).getProtocolType()).getDialectDatabaseMetaData().getGeneratedKeyOption();
+                new 
DatabaseTypeRegistry(connection.getContextManager().getDatabase(usedDatabaseName).getProtocolType()).getDialectDatabaseMetaData().getGeneratedKeyOption();
         return generatedKeyOption.isPresent() ? 
generatedKeyOption.get().getColumnName() : columnName;
     }
     
diff --git 
a/jdbc/src/test/java/org/apache/shardingsphere/driver/jdbc/core/connection/ShardingSphereConnectionTest.java
 
b/jdbc/src/test/java/org/apache/shardingsphere/driver/jdbc/core/connection/ShardingSphereConnectionTest.java
index 47847e8cbaf..0db6fb19a86 100644
--- 
a/jdbc/src/test/java/org/apache/shardingsphere/driver/jdbc/core/connection/ShardingSphereConnectionTest.java
+++ 
b/jdbc/src/test/java/org/apache/shardingsphere/driver/jdbc/core/connection/ShardingSphereConnectionTest.java
@@ -18,10 +18,15 @@
 package org.apache.shardingsphere.driver.jdbc.core.connection;
 
 import lombok.SneakyThrows;
+import 
org.apache.shardingsphere.database.connector.core.metadata.database.metadata.DialectDatabaseMetaData;
+import org.apache.shardingsphere.database.connector.core.type.DatabaseType;
+import 
org.apache.shardingsphere.database.connector.core.type.DatabaseTypeRegistry;
+import 
org.apache.shardingsphere.database.exception.core.exception.syntax.database.UnknownDatabaseException;
 import 
org.apache.shardingsphere.infra.executor.sql.execute.engine.ConnectionMode;
 import 
org.apache.shardingsphere.infra.metadata.database.resource.unit.StorageUnit;
 import org.apache.shardingsphere.infra.metadata.database.rule.RuleMetaData;
 import org.apache.shardingsphere.infra.session.connection.ConnectionContext;
+import org.apache.shardingsphere.infra.spi.type.typed.TypedSPILoader;
 import org.apache.shardingsphere.mode.manager.ContextManager;
 import org.apache.shardingsphere.test.infra.fixture.jdbc.MockedDataSource;
 import org.apache.shardingsphere.transaction.ConnectionTransaction;
@@ -30,21 +35,29 @@ import 
org.apache.shardingsphere.transaction.api.TransactionType;
 import 
org.apache.shardingsphere.transaction.config.TransactionRuleConfiguration;
 import org.apache.shardingsphere.transaction.rule.TransactionRule;
 import org.junit.jupiter.api.Test;
+import org.mockito.MockedConstruction;
 import org.mockito.internal.configuration.plugins.Plugins;
 
 import javax.sql.DataSource;
 import java.sql.Connection;
 import java.sql.SQLException;
+import java.sql.SQLFeatureNotSupportedException;
+import java.sql.Savepoint;
 import java.util.Collections;
 import java.util.Optional;
 import java.util.Properties;
 
 import static org.hamcrest.MatcherAssert.assertThat;
 import static org.hamcrest.Matchers.is;
+import static org.hamcrest.Matchers.sameInstance;
 import static org.junit.jupiter.api.Assertions.assertFalse;
+import static org.junit.jupiter.api.Assertions.assertThrows;
 import static org.junit.jupiter.api.Assertions.assertTrue;
 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.mockConstruction;
+import static org.mockito.Mockito.never;
 import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.when;
 
@@ -140,6 +153,177 @@ class ShardingSphereConnectionTest {
         return result;
     }
     
+    @Test
+    void assertRollbackSavepointWithDroppedDatabase() throws SQLException {
+        ContextManager contextManager = mockContextManager();
+        try (ShardingSphereConnection connection = new 
ShardingSphereConnection("foo_db", contextManager)) {
+            UnknownDatabaseException expected = new 
UnknownDatabaseException("foo_db");
+            mockDroppedDatabase(contextManager, expected);
+            assertThat(assertThrows(UnknownDatabaseException.class, () -> 
connection.rollback(mock(Savepoint.class))), sameInstance(expected));
+        }
+    }
+    
+    @Test
+    void assertSetNamedSavepointWithDroppedDatabase() throws SQLException {
+        ContextManager contextManager = mockContextManager();
+        try (ShardingSphereConnection connection = new 
ShardingSphereConnection("foo_db", contextManager)) {
+            UnknownDatabaseException expected = new 
UnknownDatabaseException("foo_db");
+            mockDroppedDatabase(contextManager, expected);
+            assertThat(assertThrows(UnknownDatabaseException.class, () -> 
connection.setSavepoint("foo_savepoint")), sameInstance(expected));
+        }
+    }
+    
+    @Test
+    void assertSetSavepointWithDroppedDatabase() throws SQLException {
+        ContextManager contextManager = mockContextManager();
+        try (ShardingSphereConnection connection = new 
ShardingSphereConnection("foo_db", contextManager)) {
+            UnknownDatabaseException expected = new 
UnknownDatabaseException("foo_db");
+            mockDroppedDatabase(contextManager, expected);
+            assertThat(assertThrows(UnknownDatabaseException.class, 
connection::setSavepoint), sameInstance(expected));
+        }
+    }
+    
+    @Test
+    void assertReleaseSavepointWithDroppedDatabase() throws SQLException {
+        ContextManager contextManager = mockContextManager();
+        try (ShardingSphereConnection connection = new 
ShardingSphereConnection("foo_db", contextManager)) {
+            UnknownDatabaseException expected = new 
UnknownDatabaseException("foo_db");
+            mockDroppedDatabase(contextManager, expected);
+            assertThat(assertThrows(UnknownDatabaseException.class, () -> 
connection.releaseSavepoint(mock(Savepoint.class))), sameInstance(expected));
+        }
+    }
+    
+    private void mockDroppedDatabase(final ContextManager contextManager, 
final UnknownDatabaseException ex) {
+        
when(contextManager.getMetaDataContexts().getMetaData().getDatabase("foo_db")).thenReturn(null);
+        when(contextManager.getDatabase("foo_db")).thenThrow(ex);
+    }
+    
+    @Test
+    void assertSetSavepointOutsidePostgreSQLTransaction() throws SQLException {
+        ContextManager contextManager = 
mockContextManagerWithDatabaseType("PostgreSQL");
+        DialectDatabaseMetaData dialectDatabaseMetaData = 
mock(DialectDatabaseMetaData.class, RETURNS_DEEP_STUBS);
+        
when(dialectDatabaseMetaData.getSchemaOption().getDefaultSchema()).thenReturn(Optional.of("public"));
+        try (
+                ShardingSphereConnection connection = new 
ShardingSphereConnection("foo_db", contextManager);
+                MockedConstruction<DatabaseTypeRegistry> ignored = 
mockConstruction(DatabaseTypeRegistry.class,
+                        (registry, context) -> 
when(registry.getDialectDatabaseMetaData()).thenReturn(dialectDatabaseMetaData)))
 {
+            SQLFeatureNotSupportedException actual = 
assertThrows(SQLFeatureNotSupportedException.class, connection::setSavepoint);
+            assertThat(actual.getMessage(), is("Savepoint can only be used in 
transaction blocks"));
+        }
+    }
+    
+    @Test
+    void assertSetNamedSavepointOutsideMySQLTransaction() throws SQLException {
+        try (ShardingSphereConnection connection = new 
ShardingSphereConnection("foo_db", 
mockContextManagerWithDatabaseType("MySQL"))) {
+            DriverDatabaseConnectionManager databaseConnectionManager = 
mockConnectionManager(connection, mock(ConnectionTransaction.class));
+            Savepoint expected = mock(Savepoint.class);
+            
when(databaseConnectionManager.setSavepoint("foo_savepoint")).thenReturn(expected);
+            assertThat(connection.setSavepoint("foo_savepoint"), 
sameInstance(expected));
+        }
+    }
+    
+    @Test
+    void assertSetSavepointOutsideMySQLTransaction() throws SQLException {
+        try (ShardingSphereConnection connection = new 
ShardingSphereConnection("foo_db", 
mockContextManagerWithDatabaseType("MySQL"))) {
+            DriverDatabaseConnectionManager databaseConnectionManager = 
mockConnectionManager(connection, mock(ConnectionTransaction.class));
+            Savepoint expected = mock(Savepoint.class);
+            
when(databaseConnectionManager.setSavepoint()).thenReturn(expected);
+            assertThat(connection.setSavepoint(), sameInstance(expected));
+        }
+    }
+    
+    @Test
+    void assertRollbackSavepointWithSQLException() throws SQLException {
+        try (ShardingSphereConnection connection = new 
ShardingSphereConnection("foo_db", 
mockContextManagerWithDatabaseType("MySQL"))) {
+            DriverDatabaseConnectionManager databaseConnectionManager = 
mockConnectionManager(connection, mock(ConnectionTransaction.class));
+            Savepoint savepoint = mock(Savepoint.class);
+            SQLException expected = new SQLException("foo_savepoint failure");
+            
doThrow(expected).when(databaseConnectionManager).rollback(savepoint);
+            assertThat(assertThrows(SQLException.class, () -> 
connection.rollback(savepoint)), sameInstance(expected));
+        }
+    }
+    
+    @Test
+    void assertReleaseSavepointOutsideMySQLTransaction() throws SQLException {
+        try (ShardingSphereConnection connection = new 
ShardingSphereConnection("foo_db", 
mockContextManagerWithDatabaseType("MySQL"))) {
+            DriverDatabaseConnectionManager databaseConnectionManager = 
mockConnectionManager(connection, mock(ConnectionTransaction.class));
+            Savepoint savepoint = mock(Savepoint.class);
+            connection.releaseSavepoint(savepoint);
+            verify(databaseConnectionManager, 
never()).releaseSavepoint(savepoint);
+        }
+    }
+    
+    private ContextManager mockContextManagerWithDatabaseType(final String 
databaseType) {
+        ContextManager result = mockContextManager();
+        
when(result.getDatabase("foo_db").getProtocolType()).thenReturn(TypedSPILoader.getService(DatabaseType.class,
 databaseType));
+        return result;
+    }
+    
+    @Test
+    void assertRollbackSavepointInTransaction() throws SQLException {
+        ContextManager contextManager = mockContextManager();
+        try (ShardingSphereConnection connection = new 
ShardingSphereConnection("foo_db", contextManager)) {
+            DriverDatabaseConnectionManager databaseConnectionManager = 
mockConnectionManagerInTransaction(connection);
+            Savepoint savepoint = mock(Savepoint.class);
+            connection.rollback(savepoint);
+            verify(databaseConnectionManager).rollback(savepoint);
+            verify(contextManager, never()).getDatabase("foo_db");
+        }
+    }
+    
+    @Test
+    void assertSetNamedSavepointInTransaction() throws SQLException {
+        ContextManager contextManager = mockContextManager();
+        try (ShardingSphereConnection connection = new 
ShardingSphereConnection("foo_db", contextManager)) {
+            DriverDatabaseConnectionManager databaseConnectionManager = 
mockConnectionManagerInTransaction(connection);
+            Savepoint expected = mock(Savepoint.class);
+            
when(databaseConnectionManager.setSavepoint("foo_savepoint")).thenReturn(expected);
+            assertThat(connection.setSavepoint("foo_savepoint"), 
sameInstance(expected));
+            verify(contextManager, never()).getDatabase("foo_db");
+        }
+    }
+    
+    @Test
+    void assertSetSavepointInTransaction() throws SQLException {
+        ContextManager contextManager = mockContextManager();
+        try (ShardingSphereConnection connection = new 
ShardingSphereConnection("foo_db", contextManager)) {
+            DriverDatabaseConnectionManager databaseConnectionManager = 
mockConnectionManagerInTransaction(connection);
+            Savepoint expected = mock(Savepoint.class);
+            
when(databaseConnectionManager.setSavepoint()).thenReturn(expected);
+            assertThat(connection.setSavepoint(), sameInstance(expected));
+            verify(contextManager, never()).getDatabase("foo_db");
+        }
+    }
+    
+    @Test
+    void assertReleaseSavepointInTransaction() throws SQLException {
+        ContextManager contextManager = mockContextManager();
+        try (ShardingSphereConnection connection = new 
ShardingSphereConnection("foo_db", contextManager)) {
+            DriverDatabaseConnectionManager databaseConnectionManager = 
mockConnectionManagerInTransaction(connection);
+            Savepoint savepoint = mock(Savepoint.class);
+            connection.releaseSavepoint(savepoint);
+            verify(databaseConnectionManager).releaseSavepoint(savepoint);
+            verify(contextManager, never()).getDatabase("foo_db");
+        }
+    }
+    
+    private DriverDatabaseConnectionManager 
mockConnectionManagerInTransaction(final ShardingSphereConnection connection) {
+        ConnectionTransaction connectionTransaction = 
mock(ConnectionTransaction.class);
+        when(connectionTransaction.isHoldTransaction(true)).thenReturn(true);
+        return mockConnectionManager(connection, connectionTransaction);
+    }
+    
+    @Test
+    void assertSetSavepointOnClosedConnection() throws SQLException {
+        ContextManager contextManager = mockContextManager();
+        try (ShardingSphereConnection connection = new 
ShardingSphereConnection("foo_db", contextManager)) {
+            connection.close();
+            SQLException actual = assertThrows(SQLException.class, 
connection::setSavepoint);
+            assertThat(actual.getMessage(), is("Connection has been closed."));
+            verify(contextManager, never()).getDatabase("foo_db");
+        }
+    }
+    
     @Test
     void assertIsValidWhenEmptyConnection() throws SQLException {
         try (ShardingSphereConnection connection = new 
ShardingSphereConnection("foo_db", mockContextManager())) {
diff --git 
a/jdbc/src/test/java/org/apache/shardingsphere/driver/jdbc/core/datasource/metadata/ShardingSphereDatabaseMetaDataTest.java
 
b/jdbc/src/test/java/org/apache/shardingsphere/driver/jdbc/core/datasource/metadata/ShardingSphereDatabaseMetaDataTest.java
index 24ed42a7b67..3cdd5fc2cf0 100644
--- 
a/jdbc/src/test/java/org/apache/shardingsphere/driver/jdbc/core/datasource/metadata/ShardingSphereDatabaseMetaDataTest.java
+++ 
b/jdbc/src/test/java/org/apache/shardingsphere/driver/jdbc/core/datasource/metadata/ShardingSphereDatabaseMetaDataTest.java
@@ -18,10 +18,12 @@
 package org.apache.shardingsphere.driver.jdbc.core.datasource.metadata;
 
 import 
org.apache.shardingsphere.database.connector.core.jdbcurl.parser.ConnectionProperties;
+import 
org.apache.shardingsphere.database.exception.core.exception.syntax.database.UnknownDatabaseException;
 import 
org.apache.shardingsphere.driver.jdbc.core.connection.ShardingSphereConnection;
 import 
org.apache.shardingsphere.driver.jdbc.core.resultset.DatabaseMetaDataResultSet;
 import org.apache.shardingsphere.infra.instance.ComputeNodeInstanceContext;
 import 
org.apache.shardingsphere.infra.metadata.database.ShardingSphereDatabase;
+import 
org.apache.shardingsphere.infra.metadata.database.resource.unit.StorageUnit;
 import org.apache.shardingsphere.mode.metadata.MetaDataContexts;
 import org.apache.shardingsphere.sharding.api.config.ShardingRuleConfiguration;
 import 
org.apache.shardingsphere.sharding.api.config.rule.ShardingTableRuleConfiguration;
@@ -30,6 +32,9 @@ import 
org.apache.shardingsphere.test.infra.fixture.jdbc.MockedDataSource;
 import org.junit.jupiter.api.BeforeEach;
 import org.junit.jupiter.api.Test;
 import org.junit.jupiter.api.extension.ExtendWith;
+import org.junit.jupiter.params.ParameterizedTest;
+import org.junit.jupiter.params.provider.Arguments;
+import org.junit.jupiter.params.provider.MethodSource;
 import org.mockito.Answers;
 import org.mockito.Mock;
 import org.mockito.junit.jupiter.MockitoExtension;
@@ -44,10 +49,14 @@ import java.sql.ResultSetMetaData;
 import java.sql.SQLException;
 import java.sql.Types;
 import java.util.Collections;
+import java.util.Map;
+import java.util.stream.Stream;
 
 import static org.hamcrest.MatcherAssert.assertThat;
 import static org.hamcrest.Matchers.is;
 import static org.hamcrest.Matchers.isA;
+import static org.hamcrest.Matchers.sameInstance;
+import static org.junit.jupiter.api.Assertions.assertThrows;
 import static org.mockito.Mockito.RETURNS_DEEP_STUBS;
 import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.when;
@@ -69,6 +78,9 @@ class ShardingSphereDatabaseMetaDataTest {
     @Mock
     private DatabaseMetaData databaseMetaData;
     
+    @Mock
+    private ConnectionProperties connectionProps;
+    
     @Mock
     private ResultSet resultSet;
     
@@ -91,9 +103,11 @@ class ShardingSphereDatabaseMetaDataTest {
         
when(shardingSphereConnection.getCurrentDatabaseName()).thenReturn("foo_db");
         ShardingSphereDatabase database = mock(ShardingSphereDatabase.class, 
RETURNS_DEEP_STUBS);
         
when(metaDataContexts.getMetaData().getDatabase(shardingSphereConnection.getCurrentDatabaseName())).thenReturn(database);
+        
when(shardingSphereConnection.getContextManager().getDatabase("foo_db")).thenReturn(database);
+        Map<String, StorageUnit> storageUnits = 
database.getResourceMetaData().getStorageUnits();
+        
when(shardingSphereConnection.getContextManager().getStorageUnits("foo_db")).thenReturn(storageUnits);
         ShardingRule shardingRule = mockShardingRule();
         
when(database.getRuleMetaData().getRules()).thenReturn(Collections.singleton(shardingRule));
-        ConnectionProperties connectionProps = 
mock(ConnectionProperties.class);
         when(connectionProps.getCatalog()).thenReturn("test");
         when(connectionProps.getSchema()).thenReturn("test");
         
when(database.getResourceMetaData().getStorageUnits().get(DATA_SOURCE_NAME).getConnectionProperties()).thenReturn(connectionProps);
@@ -107,6 +121,23 @@ class ShardingSphereDatabaseMetaDataTest {
         return new ShardingRule(ruleConfig, 
Collections.singletonMap(DATA_SOURCE_NAME, new MockedDataSource()), 
mock(ComputeNodeInstanceContext.class), Collections.emptyList());
     }
     
+    @Test
+    void assertConstructAfterDatabaseRemoval() {
+        UnknownDatabaseException expectedException = new 
UnknownDatabaseException("foo_db");
+        
when(metaDataContexts.getMetaData().getDatabase("foo_db")).thenReturn(null);
+        
when(shardingSphereConnection.getContextManager().getDatabase("foo_db")).thenThrow(expectedException);
+        assertThat(assertThrows(UnknownDatabaseException.class, () -> new 
ShardingSphereDatabaseMetaData(shardingSphereConnection)), 
sameInstance(expectedException));
+    }
+    
+    @Test
+    void assertConstructWithPhysicalMetadataExceptionAfterDatabaseRemoval() 
throws SQLException {
+        SQLException expectedException = new SQLException("physical metadata 
failed");
+        when(connection.getMetaData()).thenThrow(expectedException);
+        
when(metaDataContexts.getMetaData().getDatabase("foo_db")).thenReturn(null);
+        
when(shardingSphereConnection.getContextManager().getDatabase("foo_db")).thenThrow(new
 UnknownDatabaseException("foo_db"));
+        assertThat(assertThrows(SQLException.class, () -> new 
ShardingSphereDatabaseMetaData(shardingSphereConnection)), 
sameInstance(expectedException));
+    }
+    
     @Test
     void assertGetURL() throws SQLException {
         assertThat(shardingSphereDatabaseMetaData.getURL(), 
is(databaseMetaData.getURL()));
@@ -889,10 +920,67 @@ class ShardingSphereDatabaseMetaDataTest {
         assertThat(shardingSphereDatabaseMetaData.getSchemas(), 
isA(DatabaseMetaDataResultSet.class));
     }
     
+    @ParameterizedTest(name = "{0}")
+    @MethodSource("getSchemasArguments")
+    void assertGetSchemasForCatalogAndSchemaPattern(final String name, final 
String catalog, final String schemaPattern, final String expectedCatalog,
+                                                    final String 
expectedSchema) throws SQLException {
+        when(databaseMetaData.getSchemas(expectedCatalog, 
expectedSchema)).thenReturn(resultSet);
+        assertThat(shardingSphereDatabaseMetaData.getSchemas(catalog, 
schemaPattern), isA(DatabaseMetaDataResultSet.class));
+    }
+    
+    private static Stream<Arguments> getSchemasArguments() {
+        return Stream.of(
+                Arguments.of("null catalog and schema", null, null, null, 
null),
+                Arguments.of("catalog only", "foo_catalog", null, "test", 
null),
+                Arguments.of("schema only", null, "foo_schema", null, "TEST"),
+                Arguments.of("catalog and schema", "foo_catalog", 
"foo_schema", "test", "TEST"));
+    }
+    
     @Test
-    void assertGetSchemasForCatalogAndSchemaPattern() throws SQLException {
+    void assertGetSchemasForCatalogAfterDatabaseRemoval() {
+        UnknownDatabaseException expectedException = new 
UnknownDatabaseException("foo_db");
+        
when(metaDataContexts.getMetaData().getDatabase("foo_db")).thenReturn(null);
+        
when(shardingSphereConnection.getContextManager().getStorageUnits("foo_db")).thenThrow(expectedException);
+        assertThat(assertThrows(UnknownDatabaseException.class, () -> 
shardingSphereDatabaseMetaData.getSchemas("foo_catalog", null)), 
sameInstance(expectedException));
+    }
+    
+    @Test
+    void assertGetSchemasForSchemaAfterDatabaseRemoval() {
+        UnknownDatabaseException expectedException = new 
UnknownDatabaseException("foo_db");
+        
when(metaDataContexts.getMetaData().getDatabase("foo_db")).thenReturn(null);
+        
when(shardingSphereConnection.getContextManager().getStorageUnits("foo_db")).thenThrow(expectedException);
+        assertThat(assertThrows(UnknownDatabaseException.class, () -> 
shardingSphereDatabaseMetaData.getSchemas(null, "foo_schema")), 
sameInstance(expectedException));
+    }
+    
+    @Test
+    void assertGetSchemasForNullCatalogAndSchemaAfterDatabaseRemoval() throws 
SQLException {
+        
when(metaDataContexts.getMetaData().getDatabase("foo_db")).thenReturn(null);
+        
when(shardingSphereConnection.getContextManager().getStorageUnits("foo_db")).thenThrow(new
 UnknownDatabaseException("foo_db"));
+        when(databaseMetaData.getSchemas(null, null)).thenReturn(resultSet);
+        assertThat(shardingSphereDatabaseMetaData.getSchemas(null, null), 
isA(DatabaseMetaDataResultSet.class));
+    }
+    
+    @Test
+    void assertGetSchemasWithNullPhysicalCatalog() throws SQLException {
+        when(connectionProps.getCatalog()).thenReturn(null);
+        when(databaseMetaData.getSchemas(null, "TEST")).thenReturn(resultSet);
+        assertThat(shardingSphereDatabaseMetaData.getSchemas("foo_catalog", 
"foo_schema"), isA(DatabaseMetaDataResultSet.class));
+    }
+    
+    @Test
+    void assertGetSchemasWithNullPhysicalSchema() throws SQLException {
+        when(connectionProps.getSchema()).thenReturn(null);
         when(databaseMetaData.getSchemas("test", null)).thenReturn(resultSet);
-        assertThat(shardingSphereDatabaseMetaData.getSchemas("test", null), 
isA(DatabaseMetaDataResultSet.class));
+        assertThat(shardingSphereDatabaseMetaData.getSchemas("foo_catalog", 
"foo_schema"), isA(DatabaseMetaDataResultSet.class));
+    }
+    
+    @Test
+    void assertGetSchemasWithPhysicalMetadataExceptionAfterDatabaseRemoval() 
throws SQLException {
+        SQLException expectedException = new SQLException("physical metadata 
failed");
+        when(connection.getMetaData()).thenThrow(expectedException);
+        
when(metaDataContexts.getMetaData().getDatabase("foo_db")).thenReturn(null);
+        
when(shardingSphereConnection.getContextManager().getStorageUnits("foo_db")).thenThrow(new
 UnknownDatabaseException("foo_db"));
+        assertThat(assertThrows(SQLException.class, () -> 
shardingSphereDatabaseMetaData.getSchemas("foo_catalog", "foo_schema")), 
sameInstance(expectedException));
     }
     
     @Test
diff --git 
a/jdbc/src/test/java/org/apache/shardingsphere/driver/jdbc/core/statement/DistSQLStatementExecutorTest.java
 
b/jdbc/src/test/java/org/apache/shardingsphere/driver/jdbc/core/statement/DistSQLStatementExecutorTest.java
index 3c0bc503798..f364e71561b 100644
--- 
a/jdbc/src/test/java/org/apache/shardingsphere/driver/jdbc/core/statement/DistSQLStatementExecutorTest.java
+++ 
b/jdbc/src/test/java/org/apache/shardingsphere/driver/jdbc/core/statement/DistSQLStatementExecutorTest.java
@@ -17,18 +17,23 @@
 
 package org.apache.shardingsphere.driver.jdbc.core.statement;
 
+import org.apache.shardingsphere.database.connector.core.type.DatabaseType;
+import 
org.apache.shardingsphere.database.exception.core.exception.syntax.database.UnknownDatabaseException;
 import 
org.apache.shardingsphere.distsql.handler.context.DistSQLConnectionContext;
 import 
org.apache.shardingsphere.distsql.handler.engine.query.DistSQLQueryExecuteEngine;
 import 
org.apache.shardingsphere.distsql.handler.engine.update.DistSQLUpdateExecuteEngine;
 import org.apache.shardingsphere.distsql.segment.AlgorithmSegment;
 import org.apache.shardingsphere.distsql.statement.DistSQLStatement;
+import 
org.apache.shardingsphere.distsql.statement.type.ral.queryable.QueryableRALStatement;
 import 
org.apache.shardingsphere.distsql.statement.type.ral.updatable.LockClusterStatement;
 import 
org.apache.shardingsphere.distsql.statement.type.rdl.resource.unit.type.UnregisterStorageUnitStatement;
+import org.apache.shardingsphere.distsql.statement.type.rql.RQLStatement;
 import org.apache.shardingsphere.distsql.statement.type.rul.sql.ParseStatement;
 import 
org.apache.shardingsphere.driver.jdbc.core.connection.ShardingSphereConnection;
 import 
org.apache.shardingsphere.infra.exception.generic.UnsupportedSQLOperationException;
 import 
org.apache.shardingsphere.infra.merge.result.impl.local.LocalDataQueryResultRow;
 import org.apache.shardingsphere.infra.session.query.QueryContext;
+import org.apache.shardingsphere.infra.spi.type.typed.TypedSPILoader;
 import org.apache.shardingsphere.mode.exclusive.ExclusiveOperatorEngine;
 import 
org.apache.shardingsphere.mode.exclusive.callback.ExclusiveOperationVoidCallback;
 import org.apache.shardingsphere.mode.manager.ContextManager;
@@ -50,6 +55,7 @@ import java.util.stream.Stream;
 import static org.hamcrest.MatcherAssert.assertThat;
 import static org.hamcrest.Matchers.is;
 import static org.hamcrest.Matchers.sameInstance;
+import static org.junit.jupiter.api.Assertions.assertNull;
 import static org.junit.jupiter.api.Assertions.assertThrows;
 import static org.junit.jupiter.api.Assertions.assertTrue;
 import static org.mockito.ArgumentMatchers.any;
@@ -68,6 +74,10 @@ class DistSQLStatementExecutorTest {
     void assertExecuteQuery() throws SQLException {
         QueryContext queryContext = createQueryContext(new 
ParseStatement("SELECT 1"));
         ShardingSphereConnection connection = 
mock(ShardingSphereConnection.class, RETURNS_DEEP_STUBS);
+        DatabaseType protocolType = 
TypedSPILoader.getService(DatabaseType.class, "PostgreSQL");
+        when(connection.getCurrentDatabaseName()).thenReturn("foo_db");
+        
when(connection.getContextManager().getDatabase("foo_db").getProtocolType()).thenReturn(protocolType);
+        
when(connection.getContextManager().getMetaDataContexts().getMetaData().getProtocolType()).thenReturn(TypedSPILoader.getService(DatabaseType.class,
 "MySQL"));
         when(connection.getProcessId()).thenReturn("foo_process_id");
         Statement statement = mock(Statement.class);
         
when(statement.getResultSetType()).thenReturn(ResultSet.TYPE_FORWARD_ONLY);
@@ -82,6 +92,8 @@ class DistSQLStatementExecutorTest {
                 verify(mockedEngines.constructed().get(0)).executeQuery();
                 assertThat(actualConnectionContext.get().getQueryContext(), 
sameInstance(queryContext));
                 assertThat(actualConnectionContext.get().getProcessId(), 
is("foo_process_id"));
+                assertThat(actualConnectionContext.get().getProtocolType(), 
sameInstance(protocolType));
+                
assertNull(queryContext.getSqlStatementContext().getSqlStatement().getDatabaseType());
                 assertTrue(actual.next());
                 assertThat(actual.getString(1), is("foo_value"));
             }
@@ -92,12 +104,53 @@ class DistSQLStatementExecutorTest {
     void assertExecuteUpdate() throws SQLException {
         QueryContext queryContext = createQueryContext(new 
UnregisterStorageUnitStatement(Collections.singleton("foo_ds"), false, false));
         ShardingSphereConnection connection = 
mock(ShardingSphereConnection.class, RETURNS_DEEP_STUBS);
-        try (MockedConstruction<DistSQLUpdateExecuteEngine> mockedEngines = 
mockConstruction(DistSQLUpdateExecuteEngine.class)) {
+        DatabaseType protocolType = 
TypedSPILoader.getService(DatabaseType.class, "PostgreSQL");
+        when(connection.getCurrentDatabaseName()).thenReturn("foo_db");
+        
when(connection.getContextManager().getDatabase("foo_db").getProtocolType()).thenReturn(protocolType);
+        AtomicReference<DistSQLConnectionContext> actualConnectionContext = 
new AtomicReference<>();
+        try (
+                MockedConstruction<DistSQLUpdateExecuteEngine> mockedEngines = 
mockConstruction(DistSQLUpdateExecuteEngine.class,
+                        (mock, context) -> 
actualConnectionContext.set((DistSQLConnectionContext) 
context.arguments().get(3)))) {
             new DistSQLStatementExecutor(connection, 
mock(StatementManager.class), 
mock(Statement.class)).executeUpdate(queryContext);
             verify(mockedEngines.constructed().get(0)).executeUpdate();
+            assertThat(actualConnectionContext.get().getProtocolType(), 
sameInstance(protocolType));
+            
assertNull(queryContext.getSqlStatementContext().getSqlStatement().getDatabaseType());
         }
     }
     
+    @Test
+    void assertExecuteQueryWithoutDatabase() {
+        ShardingSphereConnection connection = 
mock(ShardingSphereConnection.class, RETURNS_DEEP_STUBS);
+        when(connection.getCurrentDatabaseName()).thenReturn("foo_db");
+        
when(connection.getContextManager().getMetaDataContexts().getMetaData().getDatabase("foo_db")).thenReturn(null);
+        UnknownDatabaseException expected = new 
UnknownDatabaseException("foo_db");
+        
when(connection.getContextManager().getDatabase("foo_db")).thenThrow(expected);
+        DistSQLStatementExecutor executor = new 
DistSQLStatementExecutor(connection, mock(StatementManager.class), 
mock(Statement.class));
+        assertThat(assertThrows(UnknownDatabaseException.class, () -> 
executor.executeQuery(createQueryContext(new ParseStatement("SELECT 1")))), 
sameInstance(expected));
+    }
+    
+    @Test
+    void assertExecuteUpdateWithoutDatabase() {
+        ShardingSphereConnection connection = 
mock(ShardingSphereConnection.class, RETURNS_DEEP_STUBS);
+        when(connection.getCurrentDatabaseName()).thenReturn("foo_db");
+        
when(connection.getContextManager().getMetaDataContexts().getMetaData().getDatabase("foo_db")).thenReturn(null);
+        UnknownDatabaseException expected = new 
UnknownDatabaseException("foo_db");
+        
when(connection.getContextManager().getDatabase("foo_db")).thenThrow(expected);
+        DistSQLStatementExecutor executor = new 
DistSQLStatementExecutor(connection, mock(StatementManager.class), 
mock(Statement.class));
+        QueryContext queryContext = createQueryContext(new 
UnregisterStorageUnitStatement(Collections.singleton("foo_ds"), false, false));
+        assertThat(assertThrows(UnknownDatabaseException.class, () -> 
executor.executeUpdate(queryContext)), sameInstance(expected));
+    }
+    
+    @Test
+    void assertExecuteUpdateInTransaction() {
+        ShardingSphereConnection connection = 
mock(ShardingSphereConnection.class, RETURNS_DEEP_STUBS);
+        
when(connection.getDatabaseConnectionManager().getConnectionContext().getTransactionContext().isInTransaction()).thenReturn(true);
+        DistSQLStatementExecutor executor = new 
DistSQLStatementExecutor(connection, mock(StatementManager.class), 
mock(Statement.class));
+        QueryContext queryContext = createQueryContext(new 
UnregisterStorageUnitStatement(Collections.singleton("foo_ds"), false, false));
+        UnsupportedSQLOperationException actual = 
assertThrows(UnsupportedSQLOperationException.class, () -> 
executor.executeUpdate(queryContext));
+        assertThat(actual.getMessage(), is("Unsupported SQL operation: 
Non-query DistSQL is not supported within a transaction."));
+    }
+    
     @ParameterizedTest(name = "{0}")
     @MethodSource("provideLockClusterStatements")
     void assertExecuteUpdateWithLockCluster(final String name, final 
LockClusterStatement sqlStatement,
@@ -132,6 +185,22 @@ class DistSQLStatementExecutorTest {
         assertThrows(UnsupportedSQLOperationException.class, () -> 
executor.isQuery(queryContext));
     }
     
+    @ParameterizedTest(name = "{0}")
+    @MethodSource("provideQueriesInTransaction")
+    void assertIsQueryInTransaction(final String name, final DistSQLStatement 
sqlStatement) throws SQLException {
+        ShardingSphereConnection connection = 
mock(ShardingSphereConnection.class, RETURNS_DEEP_STUBS);
+        
when(connection.getDatabaseConnectionManager().getConnectionContext().getTransactionContext().isInTransaction()).thenReturn(true);
+        DistSQLStatementExecutor executor = new 
DistSQLStatementExecutor(connection, mock(StatementManager.class), 
mock(Statement.class));
+        assertTrue(executor.isQuery(createQueryContext(sqlStatement)));
+    }
+    
+    private static Stream<Arguments> provideQueriesInTransaction() {
+        return Stream.of(
+                Arguments.of("RQL query", mock(RQLStatement.class)),
+                Arguments.of("RUL query", new ParseStatement("SELECT 1")),
+                Arguments.of("queryable RAL", 
mock(QueryableRALStatement.class)));
+    }
+    
     private QueryContext createQueryContext(final DistSQLStatement 
sqlStatement) {
         QueryContext result = mock(QueryContext.class, RETURNS_DEEP_STUBS);
         
when(result.getSqlStatementContext().getSqlStatement()).thenReturn(sqlStatement);
diff --git 
a/jdbc/src/test/java/org/apache/shardingsphere/driver/jdbc/core/statement/ShardingSpherePreparedStatementTest.java
 
b/jdbc/src/test/java/org/apache/shardingsphere/driver/jdbc/core/statement/ShardingSpherePreparedStatementTest.java
index edb4d1e7970..91d667207ae 100644
--- 
a/jdbc/src/test/java/org/apache/shardingsphere/driver/jdbc/core/statement/ShardingSpherePreparedStatementTest.java
+++ 
b/jdbc/src/test/java/org/apache/shardingsphere/driver/jdbc/core/statement/ShardingSpherePreparedStatementTest.java
@@ -18,7 +18,11 @@
 package org.apache.shardingsphere.driver.jdbc.core.statement;
 
 import lombok.SneakyThrows;
+import 
org.apache.shardingsphere.database.connector.core.metadata.database.metadata.DialectDatabaseMetaData;
 import org.apache.shardingsphere.database.connector.core.type.DatabaseType;
+import 
org.apache.shardingsphere.database.connector.core.type.DatabaseTypeRegistry;
+import 
org.apache.shardingsphere.database.exception.core.exception.syntax.database.UnknownDatabaseException;
+import 
org.apache.shardingsphere.driver.executor.engine.facade.DriverExecutorFacade;
 import 
org.apache.shardingsphere.driver.jdbc.core.connection.ShardingSphereConnection;
 import 
org.apache.shardingsphere.driver.jdbc.core.resultset.GeneratedKeysResultSet;
 import 
org.apache.shardingsphere.infra.binder.context.segment.insert.keygen.GeneratedKeyContext;
@@ -27,32 +31,48 @@ import 
org.apache.shardingsphere.infra.config.props.ConfigurationProperties;
 import org.apache.shardingsphere.infra.metadata.ShardingSphereMetaData;
 import 
org.apache.shardingsphere.infra.metadata.database.ShardingSphereDatabase;
 import org.apache.shardingsphere.infra.metadata.database.rule.RuleMetaData;
+import 
org.apache.shardingsphere.infra.session.connection.transaction.TransactionConnectionContext;
 import org.apache.shardingsphere.infra.spi.type.typed.TypedSPILoader;
 import org.apache.shardingsphere.parser.rule.SQLParserRule;
 import 
org.apache.shardingsphere.parser.rule.builder.DefaultSQLParserRuleConfigurationBuilder;
+import 
org.apache.shardingsphere.sql.parser.statement.core.statement.SQLStatement;
+import 
org.apache.shardingsphere.sql.parser.statement.core.statement.type.tcl.CommitStatement;
+import 
org.apache.shardingsphere.sql.parser.statement.core.statement.type.tcl.RollbackStatement;
 import org.apache.shardingsphere.sqlfederation.rule.SQLFederationRule;
 import 
org.apache.shardingsphere.sqlfederation.rule.builder.DefaultSQLFederationRuleConfigurationBuilder;
 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 org.mockito.MockedConstruction;
 import org.mockito.internal.configuration.plugins.Plugins;
 
 import java.lang.reflect.Field;
 import java.sql.PreparedStatement;
 import java.sql.ResultSet;
 import java.sql.SQLException;
+import java.sql.SQLFeatureNotSupportedException;
 import java.sql.Statement;
 import java.util.Arrays;
 import java.util.Collection;
 import java.util.Collections;
 import java.util.Optional;
 import java.util.Properties;
+import java.util.concurrent.atomic.AtomicReference;
+import java.util.stream.Stream;
 
 import static org.hamcrest.MatcherAssert.assertThat;
 import static org.hamcrest.Matchers.is;
 import static org.hamcrest.Matchers.not;
+import static org.hamcrest.Matchers.sameInstance;
 import static org.junit.jupiter.api.Assertions.assertFalse;
+import static org.junit.jupiter.api.Assertions.assertThrows;
 import static org.junit.jupiter.api.Assertions.assertTrue;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyBoolean;
 import static org.mockito.Mockito.RETURNS_DEEP_STUBS;
 import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.mockConstruction;
 import static org.mockito.Mockito.never;
 import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.when;
@@ -98,6 +118,158 @@ class ShardingSpherePreparedStatementTest {
         verify(generatedKeys, never()).getObject(1);
     }
     
+    @ParameterizedTest(name = "{0}")
+    @MethodSource("provideTransactionStates")
+    void assertExecuteUpdateAfterDatabaseRemoval(final String name, final 
boolean inTransaction, final boolean exceptionOccur) throws SQLException, 
ReflectiveOperationException {
+        ShardingSpherePreparedStatement preparedStatement = 
createPreparedStatementWithSQLStatement(new 
SQLStatement(TypedSPILoader.getService(DatabaseType.class, "SQL92")));
+        ShardingSphereConnection connection = 
preparedStatement.getConnection();
+        TransactionConnectionContext transactionContext = 
connection.getDatabaseConnectionManager().getConnectionContext().getTransactionContext();
+        when(transactionContext.isInTransaction()).thenReturn(inTransaction);
+        when(transactionContext.isExceptionOccur()).thenReturn(exceptionOccur);
+        SQLException expected = new SQLException("foo_failure", "foo_state", 
123, new IllegalStateException("foo_cause"));
+        DriverExecutorFacade executor = mockExecutor(preparedStatement);
+        when(executor.executeUpdate(any(), any(), any(), any(), any(), 
any())).thenAnswer(invocation -> {
+            
when(connection.getContextManager().getMetaDataContexts().getMetaData().getDatabase("foo_db")).thenReturn(null);
+            throw expected;
+        });
+        assertThat(assertThrows(SQLException.class, 
preparedStatement::executeUpdate), sameInstance(expected));
+        verify(transactionContext, never()).setExceptionOccur(anyBoolean());
+    }
+    
+    private ShardingSpherePreparedStatement 
createPreparedStatementWithSQLStatement(final SQLStatement sqlStatement) throws 
SQLException {
+        DatabaseType databaseType = sqlStatement.getDatabaseType();
+        ShardingSphereMetaData metaData = createMetaData(databaseType);
+        SQLParserRule parserRule = mock(SQLParserRule.class, 
RETURNS_DEEP_STUBS);
+        when(parserRule.getSQLParserEngine(databaseType).parse("foo_sql", 
true)).thenReturn(sqlStatement);
+        SQLFederationRule federationRule = 
metaData.getGlobalRuleMetaData().getSingleRule(SQLFederationRule.class);
+        when(metaData.getGlobalRuleMetaData()).thenReturn(new 
RuleMetaData(Arrays.asList(parserRule, federationRule)));
+        ShardingSphereConnection connection = 
mock(ShardingSphereConnection.class, RETURNS_DEEP_STUBS);
+        
when(connection.getContextManager().getMetaDataContexts().getMetaData()).thenReturn(metaData);
+        when(connection.getCurrentDatabaseName()).thenReturn("foo_db");
+        ShardingSphereDatabase database = metaData.getDatabase("foo_db");
+        
when(connection.getContextManager().getDatabase("foo_db")).thenReturn(database);
+        return new ShardingSpherePreparedStatement(connection, "foo_sql");
+    }
+    
+    private DriverExecutorFacade mockExecutor(final 
ShardingSpherePreparedStatement preparedStatement) throws 
ReflectiveOperationException {
+        DriverExecutorFacade result = mock(DriverExecutorFacade.class);
+        
Plugins.getMemberAccessor().set(ShardingSpherePreparedStatement.class.getDeclaredField("driverExecutorFacade"),
 preparedStatement, result);
+        return result;
+    }
+    
+    private static Stream<Arguments> provideTransactionStates() {
+        return Stream.of(Arguments.of("outside transaction", false, false), 
Arguments.of("active transaction", true, false), Arguments.of("failed 
transaction", true, true));
+    }
+    
+    @Test
+    void assertExecuteUpdateWithRuntimeExceptionAfterDatabaseRemoval() throws 
SQLException, ReflectiveOperationException {
+        ShardingSpherePreparedStatement preparedStatement = 
createPreparedStatementWithSQLStatement(new 
SQLStatement(TypedSPILoader.getService(DatabaseType.class, "SQL92")));
+        ShardingSphereConnection connection = 
preparedStatement.getConnection();
+        
when(connection.getDatabaseConnectionManager().getConnectionContext().getTransactionContext().isInTransaction()).thenReturn(true);
+        IllegalStateException expected = new 
IllegalStateException("foo_failure");
+        DriverExecutorFacade executor = mockExecutor(preparedStatement);
+        when(executor.executeUpdate(any(), any(), any(), any(), any(), 
any())).thenAnswer(invocation -> {
+            
when(connection.getContextManager().getMetaDataContexts().getMetaData().getDatabase("foo_db")).thenReturn(null);
+            throw expected;
+        });
+        assertThat(assertThrows(SQLException.class, 
preparedStatement::executeUpdate).getCause(), sameInstance(expected));
+    }
+    
+    @Test
+    void assertExecuteUpdateWhenFailedTransactionDatabaseIsMissing() throws 
SQLException {
+        ShardingSpherePreparedStatement preparedStatement = 
createPreparedStatementWithSQLStatement(new 
SQLStatement(TypedSPILoader.getService(DatabaseType.class, "SQL92")));
+        ShardingSphereConnection connection = 
preparedStatement.getConnection();
+        
when(connection.getDatabaseConnectionManager().getConnectionContext().getTransactionContext().isExceptionOccur()).thenReturn(true);
+        
when(connection.getContextManager().getMetaDataContexts().getMetaData().getDatabase("foo_db")).thenReturn(null);
+        UnknownDatabaseException expected = new 
UnknownDatabaseException("foo_db");
+        
when(connection.getContextManager().getDatabase("foo_db")).thenThrow(expected);
+        assertThat(assertThrows(SQLException.class, 
preparedStatement::executeUpdate).getCause(), sameInstance(expected));
+    }
+    
+    @Test
+    void assertExecuteUpdateWhenPostgreSQLTransactionFailed() throws 
SQLException {
+        DatabaseType databaseType = 
TypedSPILoader.getService(DatabaseType.class, "PostgreSQL");
+        ShardingSpherePreparedStatement preparedStatement = 
createPreparedStatementWithSQLStatement(new SQLStatement(databaseType));
+        ShardingSphereConnection connection = 
preparedStatement.getConnection();
+        
when(connection.getDatabaseConnectionManager().getConnectionContext().getTransactionContext().isExceptionOccur()).thenReturn(true);
+        
when(connection.getContextManager().getMetaDataContexts().getMetaData().getProtocolType()).thenReturn(TypedSPILoader.getService(DatabaseType.class,
 "MySQL"));
+        DialectDatabaseMetaData dialectDatabaseMetaData = 
mock(DialectDatabaseMetaData.class, RETURNS_DEEP_STUBS);
+        
when(dialectDatabaseMetaData.getTransactionOption().isAllowCommitAndRollbackOnlyWhenTransactionFailed()).thenReturn(true);
+        AtomicReference<DatabaseType> actualProtocolType = new 
AtomicReference<>();
+        try (MockedConstruction<DatabaseTypeRegistry> ignored = 
mockConstruction(DatabaseTypeRegistry.class, (mock, context) -> {
+            actualProtocolType.set((DatabaseType) context.arguments().get(0));
+            
when(mock.getDialectDatabaseMetaData()).thenReturn(dialectDatabaseMetaData);
+        })) {
+            SQLException actual = 
assertThrows(SQLFeatureNotSupportedException.class, 
preparedStatement::executeUpdate);
+            assertThat(actual.getMessage(), is("Current transaction is 
aborted, commands ignored until end of transaction block."));
+            assertThat(actualProtocolType.get(), sameInstance(databaseType));
+        }
+    }
+    
+    @Test
+    void assertExecuteUpdateCommitWhenPostgreSQLTransactionFailed() throws 
SQLException, ReflectiveOperationException {
+        ShardingSpherePreparedStatement preparedStatement = 
createPreparedStatementWithSQLStatement(new 
CommitStatement(TypedSPILoader.getService(DatabaseType.class, "PostgreSQL")));
+        
when(preparedStatement.getConnection().getDatabaseConnectionManager().getConnectionContext().getTransactionContext().isExceptionOccur()).thenReturn(true);
+        mockExecutor(preparedStatement);
+        try (MockedConstruction<DatabaseTypeRegistry> ignored = 
mockFailedTransactionOption()) {
+            assertThat(preparedStatement.executeUpdate(), is(0));
+        }
+    }
+    
+    private MockedConstruction<DatabaseTypeRegistry> 
mockFailedTransactionOption() {
+        DialectDatabaseMetaData dialectDatabaseMetaData = 
mock(DialectDatabaseMetaData.class, RETURNS_DEEP_STUBS);
+        
when(dialectDatabaseMetaData.getTransactionOption().isAllowCommitAndRollbackOnlyWhenTransactionFailed()).thenReturn(true);
+        return mockConstruction(DatabaseTypeRegistry.class, (mock, context) -> 
when(mock.getDialectDatabaseMetaData()).thenReturn(dialectDatabaseMetaData));
+    }
+    
+    @Test
+    void assertExecuteUpdateRollbackWhenPostgreSQLTransactionFailed() throws 
SQLException, ReflectiveOperationException {
+        ShardingSpherePreparedStatement preparedStatement = 
createPreparedStatementWithSQLStatement(new 
RollbackStatement(TypedSPILoader.getService(DatabaseType.class, "PostgreSQL")));
+        
when(preparedStatement.getConnection().getDatabaseConnectionManager().getConnectionContext().getTransactionContext().isExceptionOccur()).thenReturn(true);
+        mockExecutor(preparedStatement);
+        try (MockedConstruction<DatabaseTypeRegistry> ignored = 
mockFailedTransactionOption()) {
+            assertThat(preparedStatement.executeUpdate(), is(0));
+        }
+    }
+    
+    @Test
+    void assertExecuteUpdateWhenMySQLTransactionFailed() throws SQLException, 
ReflectiveOperationException {
+        ShardingSpherePreparedStatement preparedStatement = 
createPreparedStatementWithSQLStatement(new 
SQLStatement(TypedSPILoader.getService(DatabaseType.class, "MySQL")));
+        
when(preparedStatement.getConnection().getDatabaseConnectionManager().getConnectionContext().getTransactionContext().isExceptionOccur()).thenReturn(true);
+        mockExecutor(preparedStatement);
+        assertThat(preparedStatement.executeUpdate(), is(0));
+    }
+    
+    @Test
+    void assertExecuteUpdateMarksPostgreSQLTransactionFailed() throws 
SQLException, ReflectiveOperationException {
+        ShardingSpherePreparedStatement preparedStatement = 
createPreparedStatementWithSQLStatement(new 
SQLStatement(TypedSPILoader.getService(DatabaseType.class, "PostgreSQL")));
+        TransactionConnectionContext transactionContext = 
preparedStatement.getConnection().getDatabaseConnectionManager().getConnectionContext().getTransactionContext();
+        when(transactionContext.isInTransaction()).thenReturn(true);
+        DriverExecutorFacade executor = mockExecutor(preparedStatement);
+        SQLException expected = new SQLException("foo_failure");
+        when(executor.executeUpdate(any(), any(), any(), any(), any(), 
any())).thenThrow(expected);
+        DialectDatabaseMetaData dialectDatabaseMetaData = 
mock(DialectDatabaseMetaData.class, RETURNS_DEEP_STUBS);
+        
when(dialectDatabaseMetaData.getSchemaOption().getDefaultSchema()).thenReturn(Optional.of("public"));
+        try (
+                MockedConstruction<DatabaseTypeRegistry> ignored = 
mockConstruction(DatabaseTypeRegistry.class,
+                        (mock, context) -> 
when(mock.getDialectDatabaseMetaData()).thenReturn(dialectDatabaseMetaData))) {
+            assertThat(assertThrows(SQLException.class, 
preparedStatement::executeUpdate), sameInstance(expected));
+            verify(transactionContext).setExceptionOccur(true);
+        }
+    }
+    
+    @Test
+    void assertExecuteUpdateKeepsMySQLTransactionState() throws SQLException, 
ReflectiveOperationException {
+        ShardingSpherePreparedStatement preparedStatement = 
createPreparedStatementWithSQLStatement(new 
SQLStatement(TypedSPILoader.getService(DatabaseType.class, "MySQL")));
+        TransactionConnectionContext transactionContext = 
preparedStatement.getConnection().getDatabaseConnectionManager().getConnectionContext().getTransactionContext();
+        when(transactionContext.isInTransaction()).thenReturn(true);
+        DriverExecutorFacade executor = mockExecutor(preparedStatement);
+        SQLException expected = new SQLException("foo_failure");
+        when(executor.executeUpdate(any(), any(), any(), any(), any(), 
any())).thenThrow(expected);
+        assertThat(assertThrows(SQLException.class, 
preparedStatement::executeUpdate), sameInstance(expected));
+        verify(transactionContext, never()).setExceptionOccur(anyBoolean());
+    }
+    
     private ShardingSpherePreparedStatement createPreparedStatement() throws 
SQLException {
         return 
createPreparedStatement(TypedSPILoader.getService(DatabaseType.class, "SQL92"));
     }
diff --git 
a/jdbc/src/test/java/org/apache/shardingsphere/driver/jdbc/core/statement/ShardingSphereStatementTest.java
 
b/jdbc/src/test/java/org/apache/shardingsphere/driver/jdbc/core/statement/ShardingSphereStatementTest.java
index 65269aba096..f86742f03a3 100644
--- 
a/jdbc/src/test/java/org/apache/shardingsphere/driver/jdbc/core/statement/ShardingSphereStatementTest.java
+++ 
b/jdbc/src/test/java/org/apache/shardingsphere/driver/jdbc/core/statement/ShardingSphereStatementTest.java
@@ -19,10 +19,17 @@ package 
org.apache.shardingsphere.driver.jdbc.core.statement;
 
 import lombok.SneakyThrows;
 import org.apache.shardingsphere.database.connector.core.type.DatabaseType;
+import 
org.apache.shardingsphere.database.exception.core.SQLExceptionTransformEngine;
+import 
org.apache.shardingsphere.database.exception.core.exception.protocol.DatabaseProtocolException;
+import 
org.apache.shardingsphere.database.exception.core.exception.syntax.database.NoDatabaseSelectedException;
+import 
org.apache.shardingsphere.database.exception.core.exception.syntax.database.UnknownDatabaseException;
+import org.apache.shardingsphere.distsql.statement.type.rul.sql.ParseStatement;
 import 
org.apache.shardingsphere.driver.jdbc.core.connection.ShardingSphereConnection;
 import 
org.apache.shardingsphere.infra.binder.context.segment.insert.keygen.GeneratedKeyContext;
+import 
org.apache.shardingsphere.infra.binder.context.statement.SQLStatementContext;
 import 
org.apache.shardingsphere.infra.binder.context.statement.type.dml.InsertStatementContext;
 import org.apache.shardingsphere.infra.config.props.ConfigurationProperties;
+import 
org.apache.shardingsphere.infra.exception.external.sql.ShardingSphereSQLException;
 import org.apache.shardingsphere.infra.metadata.ShardingSphereMetaData;
 import 
org.apache.shardingsphere.infra.metadata.database.ShardingSphereDatabase;
 import org.apache.shardingsphere.infra.metadata.database.rule.RuleMetaData;
@@ -33,6 +40,10 @@ import 
org.apache.shardingsphere.parser.rule.builder.DefaultSQLParserRuleConfigu
 import org.apache.shardingsphere.sqlfederation.rule.SQLFederationRule;
 import 
org.apache.shardingsphere.sqlfederation.rule.builder.DefaultSQLFederationRuleConfigurationBuilder;
 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 org.mockito.MockedStatic;
 import org.mockito.internal.configuration.plugins.Plugins;
 
 import java.lang.reflect.Field;
@@ -44,12 +55,21 @@ import java.util.Collection;
 import java.util.Collections;
 import java.util.Optional;
 import java.util.Properties;
+import java.util.stream.Stream;
 
 import static org.hamcrest.MatcherAssert.assertThat;
 import static org.hamcrest.Matchers.is;
+import static org.hamcrest.Matchers.sameInstance;
+import static org.junit.jupiter.api.Assertions.assertFalse;
+import static org.junit.jupiter.api.Assertions.assertNull;
+import static org.junit.jupiter.api.Assertions.assertThrows;
 import static org.junit.jupiter.api.Assertions.assertTrue;
+import static org.mockito.ArgumentMatchers.any;
 import static org.mockito.Mockito.RETURNS_DEEP_STUBS;
+import static org.mockito.Mockito.doAnswer;
+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.verify;
 import static org.mockito.Mockito.when;
@@ -90,12 +110,216 @@ class ShardingSphereStatementTest {
         verify(generatedKeys, never()).getObject(1);
     }
     
+    @Test
+    void assertCreateStatementAfterDatabaseRemoved() {
+        ShardingSphereConnection connection = 
createConnection(createMetaData(TypedSPILoader.getService(DatabaseType.class, 
"MySQL")));
+        UnknownDatabaseException expected = removeDatabase(connection);
+        assertThat(assertThrows(UnknownDatabaseException.class, () -> new 
ShardingSphereStatement(connection)), sameInstance(expected));
+        verify(connection, never()).registerStatementManager(any());
+    }
+    
+    private UnknownDatabaseException removeDatabase(final 
ShardingSphereConnection connection) {
+        UnknownDatabaseException result = new 
UnknownDatabaseException("foo_db");
+        
when(connection.getContextManager().getMetaDataContexts().getMetaData().getDatabase("foo_db")).thenReturn(null);
+        
when(connection.getContextManager().getDatabase("foo_db")).thenThrow(result);
+        return result;
+    }
+    
+    @Test
+    void assertGetGeneratedKeysAfterDatabaseRemoved() {
+        ShardingSphereStatement statement = 
createStatement(TypedSPILoader.getService(DatabaseType.class, "MySQL"));
+        setInsertStatementContext(statement);
+        UnknownDatabaseException expected = 
removeDatabase(statement.getConnection());
+        assertThat(assertThrows(UnknownDatabaseException.class, 
statement::getGeneratedKeys), sameInstance(expected));
+    }
+    
+    @Test
+    void assertGetGeneratedKeysWithoutColumnName() throws SQLException, 
ReflectiveOperationException {
+        ShardingSphereStatement statement = 
createStatement(TypedSPILoader.getService(DatabaseType.class, "SQL92"));
+        QueryContext queryContext = mock(QueryContext.class);
+        
when(queryContext.getSqlStatementContext()).thenReturn(mock(SQLStatementContext.class));
+        
Plugins.getMemberAccessor().set(ShardingSphereStatement.class.getDeclaredField("queryContext"),
 statement, queryContext);
+        try (ResultSet actual = statement.getGeneratedKeys()) {
+            assertNull(actual.getMetaData().getColumnName(1));
+            assertFalse(actual.next());
+        }
+    }
+    
+    @Test
+    void assertGetGeneratedKeysWithDifferentGlobalProtocol() throws 
SQLException {
+        ShardingSphereStatement statement = 
createStatement(TypedSPILoader.getService(DatabaseType.class, "MySQL"));
+        
when(statement.getConnection().getContextManager().getMetaDataContexts().getMetaData().getProtocolType()).thenReturn(TypedSPILoader.getService(DatabaseType.class,
 "PostgreSQL"));
+        setInsertStatementContext(statement);
+        try (ResultSet actual = statement.getGeneratedKeys()) {
+            assertThat(actual.getMetaData().getColumnName(1), 
is("GENERATED_KEY"));
+            assertFalse(actual.next());
+        }
+    }
+    
+    @Test
+    void assertGetGeneratedKeysWithBackendFailure() throws SQLException {
+        ShardingSphereStatement statement = 
createStatement(TypedSPILoader.getService(DatabaseType.class, "MySQL"));
+        setInsertStatementContext(statement);
+        Statement routedStatement = mock(Statement.class);
+        SQLException expected = new SQLException("foo_failure", "42000", 123);
+        when(routedStatement.getGeneratedKeys()).thenThrow(expected);
+        statement.getRoutedStatements().add(routedStatement);
+        assertThat(assertThrows(SQLException.class, 
statement::getGeneratedKeys), sameInstance(expected));
+    }
+    
+    @Test
+    void assertExecuteQueryWithDatabaseRemovedDuringFailure() throws 
SQLException {
+        SQLException expected = new SQLException("foo_failure", "42000", 123);
+        ShardingSphereStatement statement = createFailingStatement(expected);
+        assertThat(assertThrows(SQLException.class, () -> 
statement.executeQuery("SELECT 1")), sameInstance(expected));
+    }
+    
+    private ShardingSphereStatement createFailingStatement(final Exception 
cause) throws SQLException {
+        ShardingSphereStatement result = 
createStatement(TypedSPILoader.getService(DatabaseType.class, "MySQL"));
+        Statement routedStatement = mock(Statement.class);
+        doAnswer(invocation -> {
+            removeDatabase(result.getConnection());
+            throw cause;
+        }).when(routedStatement).close();
+        getStatements(result).add(routedStatement);
+        return result;
+    }
+    
+    @Test
+    void assertExecuteUpdateWithDatabaseRemovedDuringFailure() throws 
SQLException {
+        SQLException expected = new SQLException("foo_failure", "42000", 123);
+        ShardingSphereStatement statement = createFailingStatement(expected);
+        assertThat(assertThrows(SQLException.class, () -> 
statement.executeUpdate("SELECT 1")), sameInstance(expected));
+    }
+    
+    @Test
+    void assertExecuteUpdateWithGeneratedKeysDuringDatabaseRemoval() throws 
SQLException {
+        SQLException expected = new SQLException("foo_failure");
+        ShardingSphereStatement statement = createFailingStatement(expected);
+        assertThat(assertThrows(SQLException.class, () -> 
statement.executeUpdate("SELECT 1", Statement.RETURN_GENERATED_KEYS)), 
sameInstance(expected));
+    }
+    
+    @Test
+    void assertExecuteUpdateWithColumnIndexesDuringDatabaseRemoval() throws 
SQLException {
+        SQLException expected = new SQLException("foo_failure");
+        ShardingSphereStatement statement = createFailingStatement(expected);
+        assertThat(assertThrows(SQLException.class, () -> 
statement.executeUpdate("SELECT 1", new int[]{1})), sameInstance(expected));
+    }
+    
+    @Test
+    void assertExecuteUpdateWithColumnNamesDuringDatabaseRemoval() throws 
SQLException {
+        SQLException expected = new SQLException("foo_failure");
+        ShardingSphereStatement statement = createFailingStatement(expected);
+        assertThat(assertThrows(SQLException.class, () -> 
statement.executeUpdate("SELECT 1", new String[]{"id"})), 
sameInstance(expected));
+    }
+    
+    @Test
+    void assertExecuteWithDatabaseRemovedDuringFailure() throws SQLException {
+        SQLException expected = new SQLException("foo_failure", "42000", 123);
+        ShardingSphereStatement statement = createFailingStatement(expected);
+        assertThat(assertThrows(SQLException.class, () -> 
statement.execute("SELECT 1")), sameInstance(expected));
+    }
+    
+    @Test
+    void assertExecuteWithGeneratedKeysDuringDatabaseRemoval() throws 
SQLException {
+        SQLException expected = new SQLException("foo_failure");
+        ShardingSphereStatement statement = createFailingStatement(expected);
+        assertThat(assertThrows(SQLException.class, () -> 
statement.execute("SELECT 1", Statement.RETURN_GENERATED_KEYS)), 
sameInstance(expected));
+    }
+    
+    @Test
+    void assertExecuteWithColumnIndexesDuringDatabaseRemoval() throws 
SQLException {
+        SQLException expected = new SQLException("foo_failure");
+        ShardingSphereStatement statement = createFailingStatement(expected);
+        assertThat(assertThrows(SQLException.class, () -> 
statement.execute("SELECT 1", new int[]{1})), sameInstance(expected));
+    }
+    
+    @Test
+    void assertExecuteWithColumnNamesDuringDatabaseRemoval() throws 
SQLException {
+        SQLException expected = new SQLException("foo_failure");
+        ShardingSphereStatement statement = createFailingStatement(expected);
+        assertThat(assertThrows(SQLException.class, () -> 
statement.execute("SELECT 1", new String[]{"id"})), sameInstance(expected));
+    }
+    
+    @Test
+    void assertExecuteDistSQLDuringDatabaseRemoval() {
+        DatabaseType databaseType = 
TypedSPILoader.getService(DatabaseType.class, "MySQL");
+        ShardingSphereStatement statement = createStatement(databaseType);
+        ShardingSphereConnection connection = statement.getConnection();
+        ShardingSphereMetaData metaData = 
connection.getContextManager().getMetaDataContexts().getMetaData();
+        SQLParserRule parserRule = mock(SQLParserRule.class, 
RETURNS_DEEP_STUBS);
+        RuleMetaData globalRuleMetaData = new 
RuleMetaData(Collections.singletonList(parserRule));
+        when(metaData.getGlobalRuleMetaData()).thenReturn(globalRuleMetaData);
+        ParseStatement sqlStatement = new ParseStatement("SELECT 1");
+        UnknownDatabaseException expected = new 
UnknownDatabaseException("foo_db");
+        when(parserRule.getSQLParserEngine(databaseType).parse("foo_distsql", 
false)).thenAnswer(invocation -> {
+            when(metaData.getDatabase("foo_db")).thenReturn(null);
+            
when(connection.getContextManager().getDatabase("foo_db")).thenThrow(expected);
+            return sqlStatement;
+        });
+        SQLException actual = assertThrows(SQLException.class, () -> 
statement.execute("foo_distsql"));
+        assertThat(actual.getCause(), sameInstance(expected));
+    }
+    
+    @ParameterizedTest(name = "{0}")
+    @MethodSource("provideFailuresDuringDatabaseRemoval")
+    void assertExecuteUpdateWithRuntimeFailureDuringDatabaseRemoval(final 
String name, final RuntimeException expected) throws SQLException {
+        ShardingSphereStatement statement = createFailingStatement(expected);
+        SQLException actual = assertThrows(SQLException.class, () -> 
statement.executeUpdate("SELECT 1"));
+        assertThat(actual.getCause(), sameInstance(expected));
+    }
+    
+    private static Stream<Arguments> provideFailuresDuringDatabaseRemoval() {
+        return Stream.of(
+                Arguments.of("runtime failure", new 
IllegalStateException("foo_failure")),
+                Arguments.of("database missing", new 
UnknownDatabaseException("foo_db")),
+                Arguments.of("database not selected", new 
NoDatabaseSelectedException()));
+    }
+    
+    @Test
+    void assertExecuteUpdateWithProtocolFailureDuringDatabaseRemoval() throws 
SQLException {
+        ShardingSphereStatement statement = createFailingStatement(new 
DatabaseProtocolException("foo_failure"));
+        SQLException actual = assertThrows(SQLException.class, () -> 
statement.executeUpdate("SELECT 1"));
+        assertThat(actual.getErrorCode(), is(30002));
+        assertThat(actual.getMessage(), is("Database protocol exception: 
foo_failure"));
+    }
+    
+    @Test
+    void assertExecuteUpdateWithSqlFailureDuringDatabaseRemoval() throws 
SQLException {
+        ShardingSphereSQLException cause = 
mock(ShardingSphereSQLException.class);
+        SQLException expected = new SQLException("foo_failure", "42000", 123);
+        when(cause.toSQLException()).thenReturn(expected);
+        ShardingSphereStatement statement = createFailingStatement(cause);
+        assertThat(assertThrows(SQLException.class, () -> 
statement.executeUpdate("SELECT 1")), sameInstance(expected));
+    }
+    
+    @Test
+    void assertExecuteUpdateWithDifferentGlobalProtocol() throws SQLException {
+        DatabaseType databaseType = 
TypedSPILoader.getService(DatabaseType.class, "MySQL");
+        ShardingSphereStatement statement = createStatement(databaseType);
+        
when(statement.getConnection().getContextManager().getMetaDataContexts().getMetaData().getProtocolType()).thenReturn(TypedSPILoader.getService(DatabaseType.class,
 "PostgreSQL"));
+        Statement routedStatement = mock(Statement.class);
+        UnknownDatabaseException cause = new 
UnknownDatabaseException("foo_db");
+        doThrow(cause).when(routedStatement).close();
+        statement.getRoutedStatements().add(routedStatement);
+        SQLException expected = new SQLException("foo_failure");
+        try (MockedStatic<SQLExceptionTransformEngine> mockedEngine = 
mockStatic(SQLExceptionTransformEngine.class)) {
+            mockedEngine.when(() -> 
SQLExceptionTransformEngine.toSQLException(cause, 
databaseType)).thenReturn(expected);
+            assertThat(assertThrows(SQLException.class, () -> 
statement.executeUpdate("SELECT 1")), sameInstance(expected));
+        }
+    }
+    
     private ShardingSphereStatement createStatement(final DatabaseType 
databaseType) {
-        ShardingSphereMetaData metaData = createMetaData(databaseType);
-        ShardingSphereConnection connection = 
mock(ShardingSphereConnection.class, RETURNS_DEEP_STUBS);
-        
when(connection.getContextManager().getMetaDataContexts().getMetaData()).thenReturn(metaData);
-        when(connection.getCurrentDatabaseName()).thenReturn("foo_db");
-        return new ShardingSphereStatement(connection);
+        return new 
ShardingSphereStatement(createConnection(createMetaData(databaseType)));
+    }
+    
+    private ShardingSphereConnection createConnection(final 
ShardingSphereMetaData metaData) {
+        ShardingSphereConnection result = mock(ShardingSphereConnection.class, 
RETURNS_DEEP_STUBS);
+        
when(result.getContextManager().getMetaDataContexts().getMetaData()).thenReturn(metaData);
+        when(result.getCurrentDatabaseName()).thenReturn("foo_db");
+        ShardingSphereDatabase database = metaData.getDatabase("foo_db");
+        
when(result.getContextManager().getDatabase("foo_db")).thenReturn(database);
+        return result;
     }
     
     private ShardingSphereMetaData createMetaData(final DatabaseType 
databaseType) {
@@ -104,6 +328,7 @@ class ShardingSphereStatementTest {
         when(database.getRuleMetaData()).thenReturn(new 
RuleMetaData(Collections.emptyList()));
         ShardingSphereMetaData result = mock(ShardingSphereMetaData.class);
         when(result.getDatabase("foo_db")).thenReturn(database);
+        when(result.getProtocolType()).thenReturn(databaseType);
         when(result.getGlobalRuleMetaData()).thenReturn(new 
RuleMetaData(Arrays.asList(
                 new SQLParserRule(new 
DefaultSQLParserRuleConfigurationBuilder().build()),
                 new SQLFederationRule(new 
DefaultSQLFederationRuleConfigurationBuilder().build(), 
Collections.emptyList()))));

Reply via email to