This is an automated email from the ASF dual-hosted git repository.
zhangliang 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 af8401d1ef4 Add more test cases on ProxyJDBCExecutorCallbackTest
(#37497)
af8401d1ef4 is described below
commit af8401d1ef40c03a4ba7461c1057523d6e959a35
Author: Liang Zhang <[email protected]>
AuthorDate: Wed Dec 24 23:31:53 2025 +0800
Add more test cases on ProxyJDBCExecutorCallbackTest (#37497)
---
.../callback/ProxyJDBCExecutorCallbackTest.java | 250 +++++++++++++++++++++
1 file changed, 250 insertions(+)
diff --git
a/proxy/backend/core/src/test/java/org/apache/shardingsphere/proxy/backend/connector/jdbc/executor/callback/ProxyJDBCExecutorCallbackTest.java
b/proxy/backend/core/src/test/java/org/apache/shardingsphere/proxy/backend/connector/jdbc/executor/callback/ProxyJDBCExecutorCallbackTest.java
new file mode 100644
index 00000000000..ba3dce655b1
--- /dev/null
+++
b/proxy/backend/core/src/test/java/org/apache/shardingsphere/proxy/backend/connector/jdbc/executor/callback/ProxyJDBCExecutorCallbackTest.java
@@ -0,0 +1,250 @@
+/*
+ * 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.connector.jdbc.executor.callback;
+
+import
org.apache.shardingsphere.database.connector.core.spi.DatabaseTypedSPILoader;
+import org.apache.shardingsphere.database.connector.core.type.DatabaseType;
+import org.apache.shardingsphere.infra.config.props.ConfigurationProperties;
+import org.apache.shardingsphere.infra.config.props.ConfigurationPropertyKey;
+import
org.apache.shardingsphere.infra.executor.sql.execute.engine.ConnectionMode;
+import
org.apache.shardingsphere.infra.executor.sql.execute.result.ExecuteResult;
+import
org.apache.shardingsphere.infra.executor.sql.execute.result.query.impl.driver.jdbc.type.memory.JDBCMemoryQueryResult;
+import
org.apache.shardingsphere.infra.executor.sql.execute.result.query.impl.driver.jdbc.type.stream.JDBCStreamQueryResult;
+import
org.apache.shardingsphere.infra.executor.sql.execute.result.update.UpdateResult;
+import org.apache.shardingsphere.infra.metadata.ShardingSphereMetaData;
+import
org.apache.shardingsphere.infra.metadata.database.ShardingSphereDatabase;
+import
org.apache.shardingsphere.infra.metadata.database.resource.ResourceMetaData;
+import org.apache.shardingsphere.infra.spi.type.typed.TypedSPILoader;
+import org.apache.shardingsphere.infra.util.props.PropertiesBuilder;
+import org.apache.shardingsphere.infra.util.props.PropertiesBuilder.Property;
+import org.apache.shardingsphere.mode.manager.ContextManager;
+import
org.apache.shardingsphere.proxy.backend.connector.DatabaseProxyConnector;
+import
org.apache.shardingsphere.proxy.backend.connector.sane.DialectSaneQueryResultEngine;
+import org.apache.shardingsphere.proxy.backend.context.ProxyContext;
+import
org.apache.shardingsphere.sql.parser.statement.core.statement.SQLStatement;
+import org.junit.jupiter.api.AfterEach;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.extension.ExtendWith;
+import org.mockito.Mock;
+import org.mockito.MockedStatic;
+import org.mockito.internal.configuration.plugins.Plugins;
+import org.mockito.junit.jupiter.MockitoExtension;
+
+import java.lang.reflect.Field;
+import java.sql.ResultSet;
+import java.sql.ResultSetMetaData;
+import java.sql.SQLException;
+import java.sql.SQLFeatureNotSupportedException;
+import java.sql.Statement;
+import java.sql.Types;
+import java.util.ArrayDeque;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.Optional;
+import java.util.Properties;
+import java.util.Queue;
+
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.hamcrest.Matchers.is;
+import static org.hamcrest.Matchers.isA;
+import static org.junit.jupiter.api.Assertions.assertFalse;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyBoolean;
+import static org.mockito.ArgumentMatchers.anyString;
+import static org.mockito.Mockito.CALLS_REAL_METHODS;
+import static org.mockito.Mockito.RETURNS_DEEP_STUBS;
+import static org.mockito.Mockito.lenient;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.mockStatic;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+import static org.mockito.Mockito.withSettings;
+
+@ExtendWith(MockitoExtension.class)
+class ProxyJDBCExecutorCallbackTest {
+
+ private final DatabaseType databaseType =
TypedSPILoader.getService(DatabaseType.class, "FIXTURE");
+
+ private Object originalContextManager;
+
+ @Mock
+ private DatabaseProxyConnector databaseProxyConnector;
+
+ @Mock
+ private ResourceMetaData resourceMetaData;
+
+ @Mock
+ private SQLStatement sqlStatement;
+
+ @Mock
+ private SQLException sqlException;
+
+ @AfterEach
+ void resetContextManager() throws ReflectiveOperationException {
+
Plugins.getMemberAccessor().set(ProxyContext.class.getDeclaredField("contextManager"),
ProxyContext.getInstance(), originalContextManager);
+ }
+
+ @Test
+ void assertExecuteSQLCoversQueryModesAndFetchMetaDataToggle() throws
ReflectiveOperationException, SQLException {
+ setContextManager(mock(ContextManager.class));
+ Statement streamStatement = mock(Statement.class);
+ ResultSet streamResultSet = mock(ResultSet.class);
+ ResultSetMetaData streamMetaData = mock(ResultSetMetaData.class);
+ when(streamStatement.getResultSet()).thenReturn(streamResultSet);
+ when(streamResultSet.getMetaData()).thenReturn(streamMetaData);
+ Statement memoryStatement = mock(Statement.class);
+ ResultSet memoryResultSet = mock(ResultSet.class);
+ ResultSetMetaData memoryMetaData = mock(ResultSetMetaData.class);
+ when(memoryStatement.getResultSet()).thenReturn(memoryResultSet);
+ when(memoryResultSet.getMetaData()).thenReturn(memoryMetaData);
+ when(memoryMetaData.getColumnCount()).thenReturn(0);
+ ProxyJDBCExecutorCallback callback = mockCallback(true, true, true,
true);
+ ExecuteResult streamResult = callback.executeSQL("foo_sql",
streamStatement, ConnectionMode.MEMORY_STRICTLY, databaseType);
+ ExecuteResult memoryResult = callback.executeSQL("bar_sql",
memoryStatement, ConnectionMode.CONNECTION_STRICTLY, databaseType);
+ assertThat(streamResult, isA(JDBCStreamQueryResult.class));
+ assertThat(memoryResult, isA(JDBCMemoryQueryResult.class));
+ verify(databaseProxyConnector).add(streamStatement);
+ verify(databaseProxyConnector).add(streamResultSet);
+ verify(databaseProxyConnector).add(memoryStatement);
+ verify(databaseProxyConnector).add(memoryResultSet);
+ }
+
+ @Test
+ void assertExecuteSQLUpdateCoversGeneratedKeyBranches() throws
ReflectiveOperationException, SQLException {
+ setContextManager(mock(ContextManager.class));
+ Statement integerStatement = mock(Statement.class);
+ ResultSet integerResultSet = mock(ResultSet.class);
+ ResultSetMetaData integerMetaData = mock(ResultSetMetaData.class);
+ when(integerStatement.getUpdateCount()).thenReturn(3);
+ when(integerStatement.getGeneratedKeys()).thenReturn(integerResultSet);
+ when(integerResultSet.next()).thenReturn(true);
+ when(integerResultSet.getMetaData()).thenReturn(integerMetaData);
+ when(integerMetaData.getColumnType(1)).thenReturn(Types.INTEGER);
+ when(integerResultSet.getLong(1)).thenReturn(99L);
+ Statement nonIntegerStatement = mock(Statement.class);
+ ResultSet nonIntegerResultSet = mock(ResultSet.class);
+ ResultSetMetaData nonIntegerMetaData = mock(ResultSetMetaData.class);
+ when(nonIntegerStatement.getUpdateCount()).thenReturn(4);
+
when(nonIntegerStatement.getGeneratedKeys()).thenReturn(nonIntegerResultSet);
+ when(nonIntegerResultSet.next()).thenReturn(true);
+ when(nonIntegerResultSet.getMetaData()).thenReturn(nonIntegerMetaData);
+ when(nonIntegerMetaData.getColumnType(1)).thenReturn(Types.VARCHAR);
+ Statement noRowStatement = mock(Statement.class);
+ ResultSet emptyResultSet = mock(ResultSet.class);
+ when(noRowStatement.getUpdateCount()).thenReturn(5);
+ when(noRowStatement.getGeneratedKeys()).thenReturn(emptyResultSet);
+ Statement unsupportedStatement = mock(Statement.class);
+ when(unsupportedStatement.getUpdateCount()).thenReturn(6);
+ when(unsupportedStatement.getGeneratedKeys()).thenThrow(new
SQLFeatureNotSupportedException());
+ ProxyJDBCExecutorCallback callback = mockCallback(true, false, false,
false, false, false);
+ UpdateResult integerUpdate = (UpdateResult)
callback.executeSQL("int_sql", integerStatement,
ConnectionMode.MEMORY_STRICTLY, databaseType);
+ assertThat(integerUpdate.getUpdateCount(), is(3));
+ assertThat(integerUpdate.getLastInsertId(), is(99L));
+ UpdateResult nonIntegerUpdate = (UpdateResult)
callback.executeSQL("varchar_sql", nonIntegerStatement,
ConnectionMode.MEMORY_STRICTLY, databaseType);
+ assertThat(nonIntegerUpdate.getUpdateCount(), is(4));
+ assertThat(nonIntegerUpdate.getLastInsertId(), is(0L));
+ UpdateResult emptyUpdate = (UpdateResult)
callback.executeSQL("empty_sql", noRowStatement,
ConnectionMode.MEMORY_STRICTLY, databaseType);
+ assertThat(emptyUpdate.getUpdateCount(), is(5));
+ assertThat(emptyUpdate.getLastInsertId(), is(0L));
+ UpdateResult unsupportedUpdate = (UpdateResult)
callback.executeSQL("unsupported_sql", unsupportedStatement,
ConnectionMode.MEMORY_STRICTLY, databaseType);
+ assertThat(unsupportedUpdate.getUpdateCount(), is(6));
+ assertThat(unsupportedUpdate.getLastInsertId(), is(0L));
+ verify(databaseProxyConnector, times(4)).add(any(Statement.class));
+ }
+
+ @Test
+ void assertExecuteSQLUpdateWithoutGeneratedKeys() throws
ReflectiveOperationException, SQLException {
+ setContextManager(mock(ContextManager.class));
+ Statement statement = mock(Statement.class);
+ when(statement.getUpdateCount()).thenReturn(7);
+ ProxyJDBCExecutorCallback callback = mockCallback(false, false, false);
+ UpdateResult updateResult = (UpdateResult)
callback.executeSQL("no_key_sql", statement,
ConnectionMode.CONNECTION_STRICTLY, databaseType);
+ assertThat(updateResult.getUpdateCount(), is(7));
+ assertThat(updateResult.getLastInsertId(), is(0L));
+ verify(databaseProxyConnector).add(statement);
+ }
+
+ @Test
+ void assertGetSaneResultReturnsEngineResult() throws
ReflectiveOperationException, SQLException {
+ ShardingSphereMetaData metaData = mock(ShardingSphereMetaData.class);
+ when(metaData.getProps()).thenReturn(
+ new ConfigurationProperties(PropertiesBuilder.build(new
Property(ConfigurationPropertyKey.PROXY_FRONTEND_DATABASE_PROTOCOL_TYPE.getKey(),
databaseType.getType()))));
+ ContextManager contextManager = mock(ContextManager.class,
RETURNS_DEEP_STUBS);
+
when(contextManager.getMetaDataContexts().getMetaData()).thenReturn(metaData);
+ setContextManager(contextManager);
+ ProxyJDBCExecutorCallback callback = mockCallback(false, false);
+ try (MockedStatic<DatabaseTypedSPILoader> spiLoader =
mockStatic(DatabaseTypedSPILoader.class)) {
+ DialectSaneQueryResultEngine saneQueryResultEngine =
mock(DialectSaneQueryResultEngine.class);
+ ExecuteResult executeResult = mock(ExecuteResult.class);
+ spiLoader.when(() ->
DatabaseTypedSPILoader.findService(DialectSaneQueryResultEngine.class,
databaseType)).thenReturn(Optional.of(saneQueryResultEngine));
+ when(saneQueryResultEngine.getSaneQueryResult(sqlStatement,
sqlException)).thenReturn(Optional.of(executeResult));
+ Optional<ExecuteResult> actual =
callback.getSaneResult(sqlStatement, sqlException);
+ assertTrue(actual.isPresent());
+ assertThat(actual.get(), is(executeResult));
+ }
+ }
+
+ @Test
+ void assertGetSaneResultReturnsEmptyWhenNoDatabaseConfigured() throws
ReflectiveOperationException, SQLException {
+ ShardingSphereMetaData metaData = mock(ShardingSphereMetaData.class);
+ when(metaData.getProps()).thenReturn(new ConfigurationProperties(new
Properties()));
+ ContextManager contextManager = mock(ContextManager.class,
RETURNS_DEEP_STUBS);
+
when(contextManager.getMetaDataContexts().getMetaData()).thenReturn(metaData);
+ setContextManager(contextManager);
+ ProxyJDBCExecutorCallback callback = mockCallback(false, false);
+ try (MockedStatic<DatabaseTypedSPILoader> databaseTypedSPILoader =
mockStatic(DatabaseTypedSPILoader.class)) {
+ databaseTypedSPILoader.when(() ->
DatabaseTypedSPILoader.findService(DialectSaneQueryResultEngine.class,
databaseType)).thenReturn(Optional.empty());
+ assertFalse(callback.getSaneResult(sqlStatement,
sqlException).isPresent());
+ }
+ }
+
+ @Test
+ void assertGetSaneResultUsesExistingDatabaseProtocolType() throws
ReflectiveOperationException, SQLException {
+ ShardingSphereDatabase database = mock(ShardingSphereDatabase.class);
+ ShardingSphereMetaData metaData = mock(ShardingSphereMetaData.class);
+ when(metaData.getProps()).thenReturn(new ConfigurationProperties(new
Properties()));
+
when(metaData.getAllDatabases()).thenReturn(Collections.singletonList(database));
+ ContextManager contextManager = mock(ContextManager.class,
RETURNS_DEEP_STUBS);
+
when(contextManager.getMetaDataContexts().getMetaData()).thenReturn(metaData);
+ setContextManager(contextManager);
+ ProxyJDBCExecutorCallback callback = mockCallback(false, false);
+ try (MockedStatic<DatabaseTypedSPILoader> spiLoader =
mockStatic(DatabaseTypedSPILoader.class)) {
+ spiLoader.when(() ->
DatabaseTypedSPILoader.findService(DialectSaneQueryResultEngine.class,
databaseType)).thenReturn(Optional.empty());
+ Optional<ExecuteResult> actual =
callback.getSaneResult(sqlStatement, sqlException);
+ assertFalse(actual.isPresent());
+ }
+ }
+
+ private void setContextManager(final ContextManager contextManager) throws
ReflectiveOperationException {
+ Field contextManagerField =
ProxyContext.class.getDeclaredField("contextManager");
+ if (null == originalContextManager) {
+ originalContextManager =
Plugins.getMemberAccessor().get(contextManagerField,
ProxyContext.getInstance());
+ }
+ Plugins.getMemberAccessor().set(contextManagerField,
ProxyContext.getInstance(), contextManager);
+ }
+
+ private ProxyJDBCExecutorCallback mockCallback(final boolean
isReturnGeneratedKeys, final boolean fetchMetaData, final Boolean...
executeResults) throws SQLException {
+ ProxyJDBCExecutorCallback result =
mock(ProxyJDBCExecutorCallback.class, withSettings().useConstructor(
+ databaseType, resourceMetaData, sqlStatement,
databaseProxyConnector, isReturnGeneratedKeys, false,
fetchMetaData).defaultAnswer(CALLS_REAL_METHODS));
+ Queue<Boolean> executeResultQueue = new
ArrayDeque<>(Arrays.asList(executeResults));
+ lenient().when(result.execute(anyString(), any(Statement.class),
anyBoolean())).thenAnswer(invocation -> !executeResultQueue.isEmpty() &&
executeResultQueue.remove());
+ return result;
+ }
+}