This is an automated email from the ASF dual-hosted git repository.

iamhucong 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 534e7f8eab7 Optimize dml reset data logic and add cache for data init 
to reduce e2e execute time (#38867)
534e7f8eab7 is described below

commit 534e7f8eab7e6bfcd3159928cca4386440948c55
Author: Zhengqiang Duan <[email protected]>
AuthorDate: Mon Jun 22 10:58:15 2026 +0800

    Optimize dml reset data logic and add cache for data init to reduce e2e 
execute time (#38867)
    
    * Optimize dml reset data logic and add cache for data init to reduce e2e 
execute time
    
    * Optimize SQL E2E execution performance
    
    * Fix SQL E2E DDL index wait
    
    * Fix SQL E2E DDL table wait
    
    * Refine SQL E2E collection declarations
    
    * Refactor DataSetResetScopeCalculator and DQLExclusiveExecutionDetector
---
 test/e2e/sql/pom.xml                               |  10 +
 .../e2e/sql/cases/SQLE2ETestCasesXMLLoader.java    |   4 +-
 .../test/e2e/sql/cases/dataset/DataSetLoader.java  |  31 +-
 .../e2e/sql/env/DataSetEnvironmentManager.java     | 272 +++++++++---
 .../e2e/sql/env/DataSetResetScopeCalculator.java   | 479 +++++++++++++++++++++
 .../test/e2e/sql/env/SQLE2EEnvironmentEngine.java  |   6 +-
 .../compose/ContainerComposerRegistry.java         |  26 +-
 .../test/e2e/sql/it/sql/ddl/DDLE2EIT.java          | 150 ++++++-
 .../test/e2e/sql/it/sql/dml/BaseDMLE2EIT.java      |  14 +-
 .../test/e2e/sql/it/sql/dml/BatchDMLE2EIT.java     |   3 -
 .../e2e/sql/it/sql/dql/AdditionalDQLE2EIT.java     |  62 ++-
 .../test/e2e/sql/it/sql/dql/BaseDQLE2EIT.java      |  54 ++-
 .../it/sql/dql/DQLExclusiveExecutionDetector.java  | 143 ++++++
 .../sql/dql/DQLExclusiveExecutionDetectorTest.java |  75 ++++
 .../test/e2e/sql/it/sql/dql/GeneralDQLE2EIT.java   |  16 +-
 .../src/test/resources/junit-platform.properties   |  22 +
 16 files changed, 1233 insertions(+), 134 deletions(-)

diff --git a/test/e2e/sql/pom.xml b/test/e2e/sql/pom.xml
index a01808fa94c..6735ad984a4 100644
--- a/test/e2e/sql/pom.xml
+++ b/test/e2e/sql/pom.xml
@@ -33,6 +33,16 @@
             <version>${project.version}</version>
             <type>test-jar</type>
         </dependency>
+        <dependency>
+            <groupId>org.apache.shardingsphere</groupId>
+            
<artifactId>shardingsphere-database-connector-postgresql</artifactId>
+            <version>${project.version}</version>
+        </dependency>
+        <dependency>
+            <groupId>org.apache.shardingsphere</groupId>
+            
<artifactId>shardingsphere-database-connector-opengauss</artifactId>
+            <version>${project.version}</version>
+        </dependency>
         
         <dependency>
             <groupId>org.postgresql</groupId>
diff --git 
a/test/e2e/sql/src/test/java/org/apache/shardingsphere/test/e2e/sql/cases/SQLE2ETestCasesXMLLoader.java
 
b/test/e2e/sql/src/test/java/org/apache/shardingsphere/test/e2e/sql/cases/SQLE2ETestCasesXMLLoader.java
index 1e4f5c8296f..17119f8b1bf 100644
--- 
a/test/e2e/sql/src/test/java/org/apache/shardingsphere/test/e2e/sql/cases/SQLE2ETestCasesXMLLoader.java
+++ 
b/test/e2e/sql/src/test/java/org/apache/shardingsphere/test/e2e/sql/cases/SQLE2ETestCasesXMLLoader.java
@@ -37,9 +37,9 @@ import java.nio.file.SimpleFileVisitor;
 import java.nio.file.attribute.BasicFileAttributes;
 import java.util.Collection;
 import java.util.Collections;
-import java.util.LinkedHashMap;
 import java.util.LinkedList;
 import java.util.Map;
+import java.util.concurrent.ConcurrentHashMap;
 import java.util.stream.Collectors;
 
 /**
@@ -51,7 +51,7 @@ public final class SQLE2ETestCasesXMLLoader {
     
     private static final SQLE2ETestCasesXMLLoader INSTANCE = new 
SQLE2ETestCasesXMLLoader();
     
-    private final Map<SQLCommandType, Collection<SQLE2ETestCaseContext>> 
testCaseContexts = new LinkedHashMap<>();
+    private final Map<SQLCommandType, Collection<SQLE2ETestCaseContext>> 
testCaseContexts = new ConcurrentHashMap<>();
     
     /**
      * Get singleton instance.
diff --git 
a/test/e2e/sql/src/test/java/org/apache/shardingsphere/test/e2e/sql/cases/dataset/DataSetLoader.java
 
b/test/e2e/sql/src/test/java/org/apache/shardingsphere/test/e2e/sql/cases/dataset/DataSetLoader.java
index 5e9e7e33f17..80f8dcba29f 100644
--- 
a/test/e2e/sql/src/test/java/org/apache/shardingsphere/test/e2e/sql/cases/dataset/DataSetLoader.java
+++ 
b/test/e2e/sql/src/test/java/org/apache/shardingsphere/test/e2e/sql/cases/dataset/DataSetLoader.java
@@ -29,6 +29,8 @@ import javax.xml.bind.JAXBException;
 import java.io.File;
 import java.io.FileReader;
 import java.io.IOException;
+import java.util.Map;
+import java.util.concurrent.ConcurrentHashMap;
 
 /**
  * Data set loader.
@@ -38,6 +40,10 @@ public final class DataSetLoader {
     
     private static final String DATA_SET_FOLDER_NAME = "dataset";
     
+    private static final JAXBContext JAXB_CONTEXT = createJAXBContext();
+    
+    private static final Map<String, DataSet> DATA_SET_CACHE = new 
ConcurrentHashMap<>();
+    
     /**
      * Load data set.
      *
@@ -48,10 +54,29 @@ public final class DataSetLoader {
      * @param dataSetFile name of data set file
      * @return data set
      */
-    @SneakyThrows({JAXBException.class, IOException.class})
     public static DataSet load(final String parentPath, final String scenario, 
final DatabaseType databaseType, final Mode mode, final String dataSetFile) {
-        try (FileReader reader = new FileReader(getFile(parentPath, scenario, 
databaseType, mode, dataSetFile))) {
-            return (DataSet) 
JAXBContext.newInstance(DataSet.class).createUnmarshaller().unmarshal(reader);
+        return load(getFile(parentPath, scenario, databaseType, mode, 
dataSetFile));
+    }
+    
+    /**
+     * Load data set.
+     *
+     * @param dataSetFile data set file
+     * @return data set
+     */
+    public static DataSet load(final String dataSetFile) {
+        return DATA_SET_CACHE.computeIfAbsent(dataSetFile, 
DataSetLoader::loadDataSet);
+    }
+    
+    @SneakyThrows(JAXBException.class)
+    private static JAXBContext createJAXBContext() {
+        return JAXBContext.newInstance(DataSet.class);
+    }
+    
+    @SneakyThrows({JAXBException.class, IOException.class})
+    private static DataSet loadDataSet(final String dataSetFile) {
+        try (FileReader reader = new FileReader(dataSetFile)) {
+            return (DataSet) 
JAXB_CONTEXT.createUnmarshaller().unmarshal(reader);
         }
     }
     
diff --git 
a/test/e2e/sql/src/test/java/org/apache/shardingsphere/test/e2e/sql/env/DataSetEnvironmentManager.java
 
b/test/e2e/sql/src/test/java/org/apache/shardingsphere/test/e2e/sql/env/DataSetEnvironmentManager.java
index bfeab25433b..e95667e3593 100644
--- 
a/test/e2e/sql/src/test/java/org/apache/shardingsphere/test/e2e/sql/env/DataSetEnvironmentManager.java
+++ 
b/test/e2e/sql/src/test/java/org/apache/shardingsphere/test/e2e/sql/env/DataSetEnvironmentManager.java
@@ -30,6 +30,7 @@ import 
org.apache.shardingsphere.infra.executor.kernel.ExecutorEngine;
 import 
org.apache.shardingsphere.infra.executor.kernel.thread.ExecutorServiceManager;
 import 
org.apache.shardingsphere.infra.expr.entry.InlineExpressionParserFactory;
 import org.apache.shardingsphere.test.e2e.sql.cases.dataset.DataSet;
+import org.apache.shardingsphere.test.e2e.sql.cases.dataset.DataSetLoader;
 import 
org.apache.shardingsphere.test.e2e.sql.cases.dataset.metadata.DataSetColumn;
 import 
org.apache.shardingsphere.test.e2e.sql.cases.dataset.metadata.DataSetMetaData;
 import org.apache.shardingsphere.test.e2e.sql.cases.dataset.row.DataSetRow;
@@ -37,23 +38,27 @@ import 
org.apache.shardingsphere.test.e2e.sql.cases.value.SQLValue;
 import org.apache.shardingsphere.test.e2e.sql.cases.value.SQLValueGroup;
 
 import javax.sql.DataSource;
-import javax.xml.bind.JAXBContext;
 import javax.xml.bind.JAXBException;
-import java.io.FileReader;
 import java.io.IOException;
 import java.sql.Connection;
 import java.sql.Date;
 import java.sql.PreparedStatement;
 import java.sql.SQLException;
 import java.util.Collection;
+import java.util.Collections;
+import java.util.HashSet;
 import java.util.LinkedHashMap;
 import java.util.LinkedList;
 import java.util.List;
+import java.util.Locale;
 import java.util.Map;
 import java.util.Map.Entry;
+import java.util.TreeSet;
 import java.util.concurrent.Callable;
+import java.util.concurrent.ConcurrentHashMap;
 import java.util.concurrent.ExecutionException;
 import java.util.concurrent.Future;
+import java.util.stream.Collectors;
 
 /**
  * Data set environment manager.
@@ -65,6 +70,16 @@ public final class DataSetEnvironmentManager {
     
     private static final String DATA_COLUMN_DELIMITER = ", ";
     
+    private static final Map<String, ResetPlan> RESET_PLAN_CACHE = new 
ConcurrentHashMap<>();
+    
+    private static final Map<String, DatabaseType> 
DATA_SOURCE_DATABASE_TYPE_CACHE = new ConcurrentHashMap<>();
+    
+    private static final Map<String, String> INSERT_SQL_CACHE = new 
ConcurrentHashMap<>();
+    
+    private static final Map<String, String> TRUNCATE_SQL_CACHE = new 
ConcurrentHashMap<>();
+    
+    private final String dataSetFile;
+    
     private final DataSet dataSet;
     
     private final Map<String, DataSource> dataSourceMap;
@@ -72,54 +87,69 @@ public final class DataSetEnvironmentManager {
     private final DatabaseType databaseType;
     
     public DataSetEnvironmentManager(final String dataSetFile, final 
Map<String, DataSource> dataSourceMap, final DatabaseType databaseType) throws 
IOException, JAXBException {
-        try (FileReader reader = new FileReader(dataSetFile)) {
-            dataSet = (DataSet) 
JAXBContext.newInstance(DataSet.class).createUnmarshaller().unmarshal(reader);
-        }
+        this.dataSetFile = dataSetFile;
+        dataSet = DataSetLoader.load(dataSetFile);
         this.dataSourceMap = dataSourceMap;
         this.databaseType = databaseType;
     }
     
     /**
-     * Fill data.
+     * Fill data for specified tables, or all tables when table names are 
empty.
+     *
+     * @param tableNames table names
      */
     @SneakyThrows({SQLException.class, InterruptedException.class, 
ExecutionException.class})
-    public void fillData() {
-        Map<DataNode, List<DataSetRow>> dataNodeListMap = getDataSetRowMap();
-        List<Callable<Void>> fillDataTasks = new LinkedList<>();
-        for (Entry<DataNode, List<DataSetRow>> entry : 
dataNodeListMap.entrySet()) {
-            DataNode dataNode = entry.getKey();
-            List<DataSetRow> dataSetRows = entry.getValue();
-            DataSetMetaData dataSetMetaData = dataSet.findMetaData(dataNode);
-            List<SQLValueGroup> sqlValueGroups = new LinkedList<>();
-            for (DataSetRow row : dataSetRows) {
-                sqlValueGroups.add(new SQLValueGroup(dataSetMetaData, 
row.splitValues(DATA_COLUMN_DELIMITER)));
-            }
-            String insertSQL;
-            DatabaseType databaseType;
-            try (Connection connection = 
dataSourceMap.get(dataNode.getDataSourceName()).getConnection()) {
-                databaseType = 
DatabaseTypeFactory.get(connection.getMetaData());
-                insertSQL = generateInsertSQL(dataNode.getTableName(), 
dataSetMetaData.getColumns(), databaseType);
-            }
-            fillDataTasks.add(new 
InsertTask(dataSourceMap.get(dataNode.getDataSourceName()), insertSQL, 
sqlValueGroups, databaseType));
-        }
+    public void fillData(final Collection<String> tableNames) {
+        List<Callable<Void>> fillDataTasks = 
createFillDataTasks(getResetPlan(tableNames));
         List<Future<Void>> futures = 
EXECUTOR_SERVICE_MANAGER.getExecutorService().invokeAll(fillDataTasks);
         for (Future<Void> future : futures) {
             future.get();
         }
     }
     
-    private Map<DataNode, List<DataSetRow>> getDataSetRowMap() {
-        Map<DataNode, List<DataSetRow>> result = new 
LinkedHashMap<>(dataSet.getRows().size(), 1F);
+    private List<Callable<Void>> createFillDataTasks(final ResetPlan 
resetPlan) throws SQLException {
+        List<Callable<Void>> result = new LinkedList<>();
+        for (Entry<DataNode, InsertDataNodePlan> entry : 
resetPlan.getInsertDataNodePlans().entrySet()) {
+            DataNode dataNode = entry.getKey();
+            DataSource dataSource = 
dataSourceMap.get(dataNode.getDataSourceName());
+            DatabaseType databaseType = 
getDatabaseType(dataNode.getDataSourceName(), dataSource);
+            InsertDataNodePlan insertDataNodePlan = entry.getValue();
+            result.add(new InsertTask(dataSource, 
getInsertSQL(dataNode.getTableName(), insertDataNodePlan.getColumnMetaData(), 
databaseType),
+                    insertDataNodePlan.getSqlValueGroups(), databaseType));
+        }
+        return result;
+    }
+    
+    private String getInsertSQL(final String tableName, final 
Collection<DataSetColumn> columnMetaData, final DatabaseType databaseType) {
+        return 
INSERT_SQL_CACHE.computeIfAbsent(getInsertSQLCacheKey(tableName, 
columnMetaData, databaseType), unused -> generateInsertSQL(tableName, 
columnMetaData, databaseType));
+    }
+    
+    private String getInsertSQLCacheKey(final String tableName, final 
Collection<DataSetColumn> columnMetaData, final DatabaseType databaseType) {
+        StringBuilder result = new 
StringBuilder(databaseType.getType()).append(':').append(tableName);
+        for (DataSetColumn each : columnMetaData) {
+            
result.append(':').append(each.getName()).append('#').append(each.getType());
+        }
+        return result.toString();
+    }
+    
+    private Map<DataNode, InsertDataNodePlan> createInsertDataNodePlans(final 
Collection<String> tableNames, final Map<DataNode, DataSetMetaData> 
metaDataMap) {
+        Map<DataNode, InsertDataNodePlan> result = new 
LinkedHashMap<>(dataSet.getRows().size(), 1F);
         for (DataSetRow each : dataSet.getRows()) {
-            // The data type of the current table is currently only used by 
mysql.
             if (each.getDataNode().contains("t_product_extend") && 
!"MySQL".equals(databaseType.getType())) {
                 continue;
             }
             DataNode dataNode = new DataNode(each.getDataNode());
+            if (!isMatchedDataNode(dataNode, tableNames)) {
+                continue;
+            }
+            DataSetMetaData dataSetMetaData = metaDataMap.get(dataNode);
+            if (null == dataSetMetaData) {
+                throw new IllegalArgumentException(String.format("Cannot find 
data node: %s", dataNode));
+            }
             if (!result.containsKey(dataNode)) {
-                result.put(dataNode, new LinkedList<>());
+                result.put(dataNode, new 
InsertDataNodePlan(dataSetMetaData.getColumns()));
             }
-            result.get(dataNode).add(each);
+            result.get(dataNode).addSQLValueGroup(new 
SQLValueGroup(dataSetMetaData, each.splitValues(DATA_COLUMN_DELIMITER)));
         }
         return result;
     }
@@ -147,45 +177,174 @@ public final class DataSetEnvironmentManager {
     }
     
     /**
-     * Clean data.
+     * Clean data for specified tables, or all tables when table names are 
empty.
+     *
+     * @param tableNames table names
      */
-    @SneakyThrows({InterruptedException.class, ExecutionException.class})
-    public void cleanData() {
-        List<Callable<Void>> deleteTasks = new LinkedList<>();
-        for (Entry<String, Collection<String>> entry : 
getDataNodeMap().entrySet()) {
-            deleteTasks.add(new DeleteTask(dataSourceMap.get(entry.getKey()), 
entry.getValue()));
-        }
+    @SneakyThrows({SQLException.class, InterruptedException.class, 
ExecutionException.class})
+    public void cleanData(final Collection<String> tableNames) {
+        List<Callable<Void>> deleteTasks = 
createDeleteTasks(getResetPlan(tableNames));
         List<Future<Void>> futures = 
EXECUTOR_SERVICE_MANAGER.getExecutorService().invokeAll(deleteTasks);
         for (Future<Void> future : futures) {
             future.get();
         }
     }
     
-    private Map<String, Collection<String>> getDataNodeMap() {
-        Map<String, Collection<String>> result = new LinkedHashMap<>();
+    private List<Callable<Void>> createDeleteTasks(final ResetPlan resetPlan) 
throws SQLException {
+        List<Callable<Void>> result = new LinkedList<>();
+        for (Entry<String, Collection<String>> entry : 
resetPlan.getTableNamesByDataSourceName().entrySet()) {
+            DatabaseType databaseType = getDatabaseType(entry.getKey(), 
dataSourceMap.get(entry.getKey()));
+            Collection<String> truncateSQLs = new LinkedList<>();
+            for (String each : entry.getValue()) {
+                truncateSQLs.add(getTruncateSQL(each, databaseType));
+            }
+            result.add(new DeleteTask(dataSourceMap.get(entry.getKey()), 
truncateSQLs));
+        }
+        return result;
+    }
+    
+    private String getTruncateSQL(final String tableName, final DatabaseType 
databaseType) {
+        return TRUNCATE_SQL_CACHE.computeIfAbsent(databaseType.getType() + ':' 
+ tableName, unused -> String.format("TRUNCATE TABLE %s", 
getQuotedTableName(tableName, databaseType)));
+    }
+    
+    private static String getQuotedTableName(final String tableName, final 
DatabaseType databaseType) {
+        DatabaseTypeRegistry databaseTypeRegistry = new 
DatabaseTypeRegistry(databaseType);
+        return 
databaseTypeRegistry.getDialectDatabaseMetaData().getQuoteCharacter().wrap(databaseTypeRegistry.formatIdentifierPattern(tableName));
+    }
+    
+    private ResetPlan getResetPlan(final Collection<String> tableNames) {
+        return 
RESET_PLAN_CACHE.computeIfAbsent(getResetPlanCacheKey(tableNames), unused -> 
createResetPlan(tableNames));
+    }
+    
+    private String getResetPlanCacheKey(final Collection<String> tableNames) {
+        return dataSetFile + ':' + databaseType.getType() + ':' + 
String.join(",", new TreeSet<>(getEffectiveTableNames(tableNames)));
+    }
+    
+    private ResetPlan createResetPlan(final Collection<String> tableNames) {
+        Collection<String> effectiveTableNames = 
getEffectiveTableNames(tableNames);
+        if (!effectiveTableNames.isEmpty() && 
!containsMatchedMetaData(effectiveTableNames)) {
+            effectiveTableNames = Collections.emptySet();
+        }
+        Map<String, Collection<String>> tableNamesByDataSourceName = new 
LinkedHashMap<>();
+        Map<DataNode, DataSetMetaData> metaDataMap = 
createMetaDataMap(effectiveTableNames, tableNamesByDataSourceName);
+        return new ResetPlan(createInsertDataNodePlans(effectiveTableNames, 
metaDataMap), tableNamesByDataSourceName);
+    }
+    
+    private Map<DataNode, DataSetMetaData> createMetaDataMap(final 
Collection<String> tableNames, final Map<String, Collection<String>> 
tableNamesByDataSourceName) {
+        Map<DataNode, DataSetMetaData> result = new LinkedHashMap<>();
         for (DataSetMetaData each : dataSet.getMetaDataList()) {
-            for (Entry<String, Collection<String>> entry : 
getDataNodeMap(each).entrySet()) {
-                if (!result.containsKey(entry.getKey())) {
-                    result.put(entry.getKey(), new LinkedList<>());
-                }
-                result.get(entry.getKey()).addAll(entry.getValue());
+            if (!isMatchedMetaData(each, tableNames)) {
+                continue;
+            }
+            for (String dataNodeText : 
InlineExpressionParserFactory.newInstance(each.getDataNodes()).splitAndEvaluate())
 {
+                DataNode dataNode = new DataNode(dataNodeText);
+                result.put(dataNode, each);
+                addTableName(tableNamesByDataSourceName, dataNode);
             }
         }
         return result;
     }
     
-    private Map<String, Collection<String>> getDataNodeMap(final 
DataSetMetaData dataSetMetaData) {
-        Map<String, Collection<String>> result = new LinkedHashMap<>();
+    private void addTableName(final Map<String, Collection<String>> 
tableNamesByDataSourceName, final DataNode dataNode) {
+        if 
(!tableNamesByDataSourceName.containsKey(dataNode.getDataSourceName())) {
+            tableNamesByDataSourceName.put(dataNode.getDataSourceName(), new 
LinkedList<>());
+        }
+        
tableNamesByDataSourceName.get(dataNode.getDataSourceName()).add(dataNode.getTableName());
+    }
+    
+    private Collection<String> getEffectiveTableNames(final Collection<String> 
tableNames) {
+        return tableNames.stream().filter(each -> null != each && 
!each.isEmpty()).map(each -> 
each.toLowerCase(Locale.ENGLISH)).collect(Collectors.toSet());
+    }
+    
+    private boolean isMatchedMetaData(final DataSetMetaData dataSetMetaData, 
final Collection<String> tableNames) {
+        if (tableNames.isEmpty()) {
+            return true;
+        }
+        if (null != dataSetMetaData.getTableName() && 
tableNames.contains(dataSetMetaData.getTableName().toLowerCase(Locale.ENGLISH)))
 {
+            return true;
+        }
         for (String each : 
InlineExpressionParserFactory.newInstance(dataSetMetaData.getDataNodes()).splitAndEvaluate())
 {
-            DataNode dataNode = new DataNode(each);
-            if (!result.containsKey(dataNode.getDataSourceName())) {
-                result.put(dataNode.getDataSourceName(), new LinkedList<>());
+            if (isMatchedDataNode(new DataNode(each), tableNames)) {
+                return true;
+            }
+        }
+        return false;
+    }
+    
+    private boolean containsMatchedMetaData(final Collection<String> 
tableNames) {
+        for (DataSetMetaData each : dataSet.getMetaDataList()) {
+            if (isMatchedMetaData(each, tableNames)) {
+                return true;
             }
-            
result.get(dataNode.getDataSourceName()).add(dataNode.getTableName());
         }
+        return false;
+    }
+    
+    private boolean isMatchedDataNode(final DataNode dataNode, final 
Collection<String> tableNames) {
+        return tableNames.isEmpty() || 
getPossibleTableNames(dataNode.getTableName()).stream().anyMatch(tableNames::contains);
+    }
+    
+    private Collection<String> getPossibleTableNames(final String tableName) {
+        Collection<String> result = new HashSet<>();
+        result.add(tableName.toLowerCase(Locale.ENGLISH));
+        result.add(tableName.replaceFirst("_[0-9]+$", 
"").toLowerCase(Locale.ENGLISH));
+        result.add(tableName.replaceFirst("[0-9]+$", 
"").toLowerCase(Locale.ENGLISH));
         return result;
     }
     
+    private DatabaseType getDatabaseType(final String dataSourceName, final 
DataSource dataSource) throws SQLException {
+        String cacheKey = getDataSourceDatabaseTypeCacheKey(dataSourceName, 
dataSource);
+        DatabaseType result = DATA_SOURCE_DATABASE_TYPE_CACHE.get(cacheKey);
+        if (null != result) {
+            return result;
+        }
+        try (Connection connection = dataSource.getConnection()) {
+            result = DatabaseTypeFactory.get(connection.getMetaData());
+            DATA_SOURCE_DATABASE_TYPE_CACHE.put(cacheKey, result);
+            return result;
+        }
+    }
+    
+    private String getDataSourceDatabaseTypeCacheKey(final String 
dataSourceName, final DataSource dataSource) {
+        return databaseType.getType() + ':' + dataSourceName + ':' + 
System.identityHashCode(dataSource);
+    }
+    
+    @RequiredArgsConstructor
+    private static final class ResetPlan {
+        
+        private final Map<DataNode, InsertDataNodePlan> insertDataNodePlans;
+        
+        private final Map<String, Collection<String>> 
tableNamesByDataSourceName;
+        
+        private Map<DataNode, InsertDataNodePlan> getInsertDataNodePlans() {
+            return insertDataNodePlans;
+        }
+        
+        private Map<String, Collection<String>> 
getTableNamesByDataSourceName() {
+            return tableNamesByDataSourceName;
+        }
+    }
+    
+    @RequiredArgsConstructor
+    private static final class InsertDataNodePlan {
+        
+        private final Collection<DataSetColumn> columnMetaData;
+        
+        private final Collection<SQLValueGroup> sqlValueGroups = new 
LinkedList<>();
+        
+        private Collection<DataSetColumn> getColumnMetaData() {
+            return columnMetaData;
+        }
+        
+        private Collection<SQLValueGroup> getSqlValueGroups() {
+            return sqlValueGroups;
+        }
+        
+        private void addSQLValueGroup(final SQLValueGroup sqlValueGroup) {
+            sqlValueGroups.add(sqlValueGroup);
+        }
+    }
+    
     @RequiredArgsConstructor
     private static final class InsertTask implements Callable<Void> {
         
@@ -243,25 +402,18 @@ public final class DataSetEnvironmentManager {
         
         private final DataSource dataSource;
         
-        private final Collection<String> tableNames;
+        private final Collection<String> truncateSQLs;
         
         @Override
         public Void call() throws SQLException {
             try (Connection connection = dataSource.getConnection()) {
-                DatabaseType databaseType = 
DatabaseTypeFactory.get(connection.getMetaData());
-                for (String each : tableNames) {
-                    String quotedTableName = getQuotedTableName(each, 
databaseType);
-                    try (PreparedStatement preparedStatement = 
connection.prepareStatement(String.format("TRUNCATE TABLE %s", 
quotedTableName))) {
+                for (String each : truncateSQLs) {
+                    try (PreparedStatement preparedStatement = 
connection.prepareStatement(each)) {
                         preparedStatement.execute();
                     }
                 }
             }
             return null;
         }
-        
-        private String getQuotedTableName(final String tableName, final 
DatabaseType databaseType) {
-            DatabaseTypeRegistry databaseTypeRegistry = new 
DatabaseTypeRegistry(databaseType);
-            return 
databaseTypeRegistry.getDialectDatabaseMetaData().getQuoteCharacter().wrap(databaseTypeRegistry.formatIdentifierPattern(tableName));
-        }
     }
 }
diff --git 
a/test/e2e/sql/src/test/java/org/apache/shardingsphere/test/e2e/sql/env/DataSetResetScopeCalculator.java
 
b/test/e2e/sql/src/test/java/org/apache/shardingsphere/test/e2e/sql/env/DataSetResetScopeCalculator.java
new file mode 100644
index 00000000000..158ef96a961
--- /dev/null
+++ 
b/test/e2e/sql/src/test/java/org/apache/shardingsphere/test/e2e/sql/env/DataSetResetScopeCalculator.java
@@ -0,0 +1,479 @@
+/*
+ * 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.test.e2e.sql.env;
+
+import org.antlr.v4.runtime.misc.ParseCancellationException;
+import 
org.apache.shardingsphere.database.exception.core.exception.syntax.sql.DialectSQLParsingException;
+import org.apache.shardingsphere.database.connector.core.type.DatabaseType;
+import org.apache.shardingsphere.infra.datanode.DataNode;
+import 
org.apache.shardingsphere.infra.expr.entry.InlineExpressionParserFactory;
+import org.apache.shardingsphere.infra.parser.SQLParserEngine;
+import org.apache.shardingsphere.infra.parser.ShardingSphereSQLParserEngine;
+import org.apache.shardingsphere.sql.parser.engine.api.CacheOption;
+import 
org.apache.shardingsphere.sql.parser.engine.exception.SQLParsingException;
+import 
org.apache.shardingsphere.sql.parser.statement.core.extractor.TableExtractor;
+import 
org.apache.shardingsphere.sql.parser.statement.core.segment.dml.assignment.ColumnAssignmentSegment;
+import 
org.apache.shardingsphere.sql.parser.statement.core.segment.dml.assignment.InsertValuesSegment;
+import 
org.apache.shardingsphere.sql.parser.statement.core.segment.dml.assignment.SetAssignmentSegment;
+import 
org.apache.shardingsphere.sql.parser.statement.core.segment.dml.column.OnDuplicateKeyColumnsSegment;
+import 
org.apache.shardingsphere.sql.parser.statement.core.segment.dml.expr.BetweenExpression;
+import 
org.apache.shardingsphere.sql.parser.statement.core.segment.dml.expr.BinaryOperationExpression;
+import 
org.apache.shardingsphere.sql.parser.statement.core.segment.dml.expr.CaseWhenExpression;
+import 
org.apache.shardingsphere.sql.parser.statement.core.segment.dml.expr.CollateExpression;
+import 
org.apache.shardingsphere.sql.parser.statement.core.segment.dml.expr.ExistsSubqueryExpression;
+import 
org.apache.shardingsphere.sql.parser.statement.core.segment.dml.expr.ExpressionSegment;
+import 
org.apache.shardingsphere.sql.parser.statement.core.segment.dml.expr.FunctionSegment;
+import 
org.apache.shardingsphere.sql.parser.statement.core.segment.dml.expr.InExpression;
+import 
org.apache.shardingsphere.sql.parser.statement.core.segment.dml.expr.KeyValueSegment;
+import 
org.apache.shardingsphere.sql.parser.statement.core.segment.dml.expr.ListExpression;
+import 
org.apache.shardingsphere.sql.parser.statement.core.segment.dml.expr.NotExpression;
+import 
org.apache.shardingsphere.sql.parser.statement.core.segment.dml.expr.QuantifySubqueryExpression;
+import 
org.apache.shardingsphere.sql.parser.statement.core.segment.dml.expr.RowExpression;
+import 
org.apache.shardingsphere.sql.parser.statement.core.segment.dml.expr.TypeCastExpression;
+import 
org.apache.shardingsphere.sql.parser.statement.core.segment.dml.expr.UnaryOperationExpression;
+import 
org.apache.shardingsphere.sql.parser.statement.core.segment.dml.expr.ValuesExpression;
+import 
org.apache.shardingsphere.sql.parser.statement.core.segment.dml.expr.complex.CommonTableExpressionSegment;
+import 
org.apache.shardingsphere.sql.parser.statement.core.segment.dml.expr.subquery.SubqueryExpressionSegment;
+import 
org.apache.shardingsphere.sql.parser.statement.core.segment.dml.expr.subquery.SubquerySegment;
+import 
org.apache.shardingsphere.sql.parser.statement.core.segment.dml.table.MultiTableConditionalIntoSegment;
+import 
org.apache.shardingsphere.sql.parser.statement.core.segment.dml.table.MultiTableInsertIntoSegment;
+import 
org.apache.shardingsphere.sql.parser.statement.core.segment.generic.WithSegment;
+import 
org.apache.shardingsphere.sql.parser.statement.core.segment.generic.table.DeleteMultiTableSegment;
+import 
org.apache.shardingsphere.sql.parser.statement.core.segment.generic.table.JoinTableSegment;
+import 
org.apache.shardingsphere.sql.parser.statement.core.segment.generic.table.SimpleTableSegment;
+import 
org.apache.shardingsphere.sql.parser.statement.core.segment.generic.table.SubqueryTableSegment;
+import 
org.apache.shardingsphere.sql.parser.statement.core.segment.generic.table.TableSegment;
+import 
org.apache.shardingsphere.sql.parser.statement.core.statement.SQLStatement;
+import 
org.apache.shardingsphere.sql.parser.statement.core.statement.type.dml.DeleteStatement;
+import 
org.apache.shardingsphere.sql.parser.statement.core.statement.type.dml.InsertStatement;
+import 
org.apache.shardingsphere.sql.parser.statement.core.statement.type.dml.MergeStatement;
+import 
org.apache.shardingsphere.sql.parser.statement.core.statement.type.dml.SelectStatement;
+import 
org.apache.shardingsphere.sql.parser.statement.core.statement.type.dml.UpdateStatement;
+import 
org.apache.shardingsphere.sql.parser.statement.core.util.MultiSQLSplitter;
+import 
org.apache.shardingsphere.test.e2e.sql.cases.casse.assertion.SQLE2ETestCaseAssertion;
+import 
org.apache.shardingsphere.test.e2e.sql.cases.casse.assertion.SQLE2ETestCaseAssertionSQL;
+import org.apache.shardingsphere.test.e2e.sql.cases.dataset.DataSetLoader;
+import 
org.apache.shardingsphere.test.e2e.sql.cases.dataset.metadata.DataSetMetaData;
+import 
org.apache.shardingsphere.test.e2e.sql.framework.param.model.AssertionTestParameter;
+import 
org.apache.shardingsphere.test.e2e.sql.framework.param.model.CaseTestParameter;
+import 
org.apache.shardingsphere.test.e2e.sql.framework.param.model.E2ETestParameter;
+
+import java.util.Collection;
+import java.util.Collections;
+import java.util.LinkedHashSet;
+import java.util.Locale;
+import java.util.Map;
+import java.util.concurrent.ConcurrentHashMap;
+
+/**
+ * Calculator for data set reset table scope.
+ */
+public final class DataSetResetScopeCalculator {
+    
+    private static final CacheOption CACHE_OPTION = new CacheOption(128, 
1024L);
+    
+    private static final Map<String, SQLParserEngine> SQL_PARSER_ENGINE_MAP = 
new ConcurrentHashMap<>();
+    
+    private static final Map<String, Collection<String>> 
RESET_TABLE_NAMES_CACHE = new ConcurrentHashMap<>();
+    
+    /**
+     * Get reset table names.
+     *
+     * @param testParam test parameter
+     * @return reset table names, or empty collection for full reset fallback
+     */
+    public Collection<String> getResetTableNames(final E2ETestParameter 
testParam) {
+        return 
RESET_TABLE_NAMES_CACHE.computeIfAbsent(getResetTableNamesCacheKey(testParam), 
unused -> calculateResetTableNames(testParam));
+    }
+    
+    private Collection<String> calculateResetTableNames(final E2ETestParameter 
testParam) {
+        Collection<String> result = new LinkedHashSet<>();
+        if (!addParsedTableNames(result, 
testParam.getTestCaseContext().getTestCase().getSql(), 
testParam.getDatabaseType())) {
+            return Collections.emptyList();
+        }
+        for (SQLE2ETestCaseAssertion each : getAssertions(testParam)) {
+            if (!addAssertionScope(result, testParam, each)) {
+                return Collections.emptyList();
+            }
+        }
+        return result;
+    }
+    
+    private String getResetTableNamesCacheKey(final E2ETestParameter 
testParam) {
+        StringBuilder result = new StringBuilder();
+        appendCacheKeyValue(result, testParam.getKey());
+        appendCacheKeyValue(result, testParam.getMode().name());
+        appendCacheKeyValue(result, 
testParam.getTestCaseContext().getParentPath());
+        appendCacheKeyValue(result, 
testParam.getTestCaseContext().getTestCase().getSql());
+        for (SQLE2ETestCaseAssertion each : getAssertions(testParam)) {
+            appendAssertionCacheKey(result, each);
+        }
+        return result.toString();
+    }
+    
+    private void appendAssertionCacheKey(final StringBuilder cacheKey, final 
SQLE2ETestCaseAssertion assertion) {
+        if (null == assertion) {
+            appendCacheKeyValue(cacheKey, "");
+            return;
+        }
+        appendCacheKeyValue(cacheKey, assertion.getExpectedDataFile());
+        appendCacheKeyValue(cacheKey, 
assertion.getExpectedGeneratedKeyDataFile());
+        appendSQLCacheKey(cacheKey, assertion.getInitialSQL());
+        appendSQLCacheKey(cacheKey, assertion.getAssertionSQL());
+    }
+    
+    private void appendSQLCacheKey(final StringBuilder cacheKey, final 
SQLE2ETestCaseAssertionSQL sql) {
+        appendCacheKeyValue(cacheKey, null == sql ? "" : sql.getSql());
+    }
+    
+    private void appendCacheKeyValue(final StringBuilder cacheKey, final 
String value) {
+        String actualValue = null == value ? "" : value;
+        cacheKey.append(actualValue.length()).append(':').append(actualValue);
+    }
+    
+    private boolean addAssertionScope(final Collection<String> tableNames, 
final E2ETestParameter testParam, final SQLE2ETestCaseAssertion assertion) {
+        if (null == assertion) {
+            return true;
+        }
+        if (!addSQLTableNames(tableNames, assertion.getInitialSQL(), 
testParam.getDatabaseType()) || !addSQLTableNames(tableNames, 
assertion.getAssertionSQL(), testParam.getDatabaseType())) {
+            return false;
+        }
+        return addDataSetTableNames(tableNames, testParam, 
assertion.getExpectedDataFile());
+    }
+    
+    private Collection<SQLE2ETestCaseAssertion> getAssertions(final 
E2ETestParameter testParam) {
+        if (testParam instanceof AssertionTestParameter) {
+            return Collections.singleton(((AssertionTestParameter) 
testParam).getAssertion());
+        }
+        if (testParam instanceof CaseTestParameter) {
+            return 
testParam.getTestCaseContext().getTestCase().getAssertions();
+        }
+        return Collections.emptyList();
+    }
+    
+    private boolean addSQLTableNames(final Collection<String> tableNames, 
final SQLE2ETestCaseAssertionSQL sql, final DatabaseType databaseType) {
+        return null == sql || null == sql.getSql() || 
addParsedTableNames(tableNames, sql.getSql(), databaseType);
+    }
+    
+    private boolean addParsedTableNames(final Collection<String> tableNames, 
final String sql, final DatabaseType databaseType) {
+        if (null == sql || sql.isEmpty()) {
+            return true;
+        }
+        for (String each : MultiSQLSplitter.split(sql)) {
+            if (!addParsedSingleSQLTableNames(tableNames, each, databaseType)) 
{
+                return false;
+            }
+        }
+        return true;
+    }
+    
+    private boolean addParsedSingleSQLTableNames(final Collection<String> 
tableNames, final String sql, final DatabaseType databaseType) {
+        try {
+            SQLStatement sqlStatement = 
getSQLParserEngine(databaseType).parse(sql, true);
+            if (isUnextractedWithSQL(sql, sqlStatement)) {
+                return false;
+            }
+            Collection<String> virtualTableNames = new LinkedHashSet<>();
+            Collection<String> parsedTableNames = new LinkedHashSet<>();
+            TableExtractor tableExtractor = new TableExtractor();
+            tableExtractor.extractTablesFromSQLStatement(sqlStatement);
+            addExtractedRewriteTableNames(parsedTableNames, tableExtractor);
+            tableExtractor.getTableContext().forEach(each -> 
addVirtualTableNames(virtualTableNames, each));
+            addSupplementalTableNames(parsedTableNames, virtualTableNames, 
sqlStatement);
+            removeVirtualTableNames(parsedTableNames, virtualTableNames);
+            tableNames.addAll(parsedTableNames);
+            return true;
+        } catch (final SQLParsingException | ParseCancellationException | 
DialectSQLParsingException ignored) {
+            return isTransactionControlSQL(sql);
+        }
+    }
+    
+    private void addSupplementalTableNames(final Collection<String> 
tableNames, final Collection<String> virtualTableNames, final SQLStatement 
sqlStatement) {
+        if (sqlStatement instanceof InsertStatement) {
+            addInsertStatementTableNames(tableNames, virtualTableNames, 
(InsertStatement) sqlStatement);
+        } else if (sqlStatement instanceof UpdateStatement) {
+            addUpdateStatementTableNames(tableNames, virtualTableNames, 
(UpdateStatement) sqlStatement);
+        } else if (sqlStatement instanceof DeleteStatement) {
+            addDeleteStatementTableNames(tableNames, virtualTableNames, 
(DeleteStatement) sqlStatement);
+        } else if (sqlStatement instanceof MergeStatement) {
+            addMergeStatementTableNames(tableNames, virtualTableNames, 
(MergeStatement) sqlStatement);
+        } else if (sqlStatement instanceof SelectStatement) {
+            addSelectStatementTableNames(tableNames, virtualTableNames, 
(SelectStatement) sqlStatement);
+        }
+    }
+    
+    private void addSelectStatementTableNames(final Collection<String> 
tableNames, final Collection<String> virtualTableNames, final SelectStatement 
selectStatement) {
+        selectStatement.getWith().ifPresent(optional -> 
addWithTableNames(tableNames, virtualTableNames, optional));
+    }
+    
+    private void addInsertStatementTableNames(final Collection<String> 
tableNames, final Collection<String> virtualTableNames, final InsertStatement 
insertStatement) {
+        insertStatement.getTable().ifPresent(optional -> 
addTableSegmentTableNames(tableNames, virtualTableNames, optional));
+        insertStatement.getInsertSelect().ifPresent(optional -> 
addSubqueryTableNames(tableNames, virtualTableNames, optional));
+        insertStatement.getWith().ifPresent(optional -> 
addWithTableNames(tableNames, virtualTableNames, optional));
+        insertStatement.getMultiTableInsertInto().ifPresent(optional -> 
addMultiTableInsertIntoTableNames(tableNames, virtualTableNames, optional));
+        insertStatement.getMultiTableConditionalInto().ifPresent(optional -> 
addMultiTableConditionalIntoTableNames(tableNames, virtualTableNames, 
optional));
+        insertStatement.getValues().forEach(each -> 
addInsertValuesTableNames(tableNames, virtualTableNames, each));
+        insertStatement.getSetAssignment().ifPresent(optional -> 
addSetAssignmentTableNames(tableNames, virtualTableNames, optional));
+        insertStatement.getOnDuplicateKeyColumns().ifPresent(optional -> 
addOnDuplicateKeyColumnsTableNames(tableNames, virtualTableNames, optional));
+    }
+    
+    private void addUpdateStatementTableNames(final Collection<String> 
tableNames, final Collection<String> virtualTableNames, final UpdateStatement 
updateStatement) {
+        addTableSegmentTableNames(tableNames, virtualTableNames, 
updateStatement.getTable());
+        updateStatement.getFrom().ifPresent(optional -> 
addTableSegmentTableNames(tableNames, virtualTableNames, optional));
+        updateStatement.getWith().ifPresent(optional -> 
addWithTableNames(tableNames, virtualTableNames, optional));
+        updateStatement.getAssignment().ifPresent(optional -> 
addSetAssignmentTableNames(tableNames, virtualTableNames, optional));
+    }
+    
+    private void addDeleteStatementTableNames(final Collection<String> 
tableNames, final Collection<String> virtualTableNames, final DeleteStatement 
deleteStatement) {
+        addTableSegmentTableNames(tableNames, virtualTableNames, 
deleteStatement.getTable());
+        deleteStatement.getWith().ifPresent(optional -> 
addWithTableNames(tableNames, virtualTableNames, optional));
+    }
+    
+    private void addMergeStatementTableNames(final Collection<String> 
tableNames, final Collection<String> virtualTableNames, final MergeStatement 
mergeStatement) {
+        addTableSegmentTableNames(tableNames, virtualTableNames, 
mergeStatement.getTarget());
+        addTableSegmentTableNames(tableNames, virtualTableNames, 
mergeStatement.getSource());
+        mergeStatement.getUpdate().ifPresent(optional -> 
addUpdateStatementTableNames(tableNames, virtualTableNames, optional));
+        mergeStatement.getInsert().ifPresent(optional -> 
addInsertStatementTableNames(tableNames, virtualTableNames, optional));
+        mergeStatement.getWith().ifPresent(optional -> 
addWithTableNames(tableNames, virtualTableNames, optional));
+    }
+    
+    private void addMultiTableInsertIntoTableNames(final Collection<String> 
tableNames, final Collection<String> virtualTableNames, final 
MultiTableInsertIntoSegment insertIntoSegment) {
+        insertIntoSegment.getInsertStatements().forEach(each -> 
addInsertStatementTableNames(tableNames, virtualTableNames, each));
+    }
+    
+    private void addMultiTableConditionalIntoTableNames(final 
Collection<String> tableNames, final Collection<String> virtualTableNames,
+                                                        final 
MultiTableConditionalIntoSegment conditionalIntoSegment) {
+        conditionalIntoSegment.getWhenThenSegments()
+                .forEach(each -> 
each.getThenSegment().getInsertStatements().forEach(optional -> 
addInsertStatementTableNames(tableNames, virtualTableNames, optional)));
+        conditionalIntoSegment.getElseSegment().ifPresent(optional -> 
optional.getInsertStatements().forEach(each -> 
addInsertStatementTableNames(tableNames, virtualTableNames, each)));
+    }
+    
+    private void addInsertValuesTableNames(final Collection<String> 
tableNames, final Collection<String> virtualTableNames, final 
InsertValuesSegment valuesSegment) {
+        addExpressionTableNames(tableNames, virtualTableNames, 
valuesSegment.getValues());
+    }
+    
+    private void addSetAssignmentTableNames(final Collection<String> 
tableNames, final Collection<String> virtualTableNames, final 
SetAssignmentSegment setAssignmentSegment) {
+        addColumnAssignmentTableNames(tableNames, virtualTableNames, 
setAssignmentSegment.getAssignments());
+    }
+    
+    private void addOnDuplicateKeyColumnsTableNames(final Collection<String> 
tableNames, final Collection<String> virtualTableNames, final 
OnDuplicateKeyColumnsSegment onDuplicateKeyColumnsSegment) {
+        addColumnAssignmentTableNames(tableNames, virtualTableNames, 
onDuplicateKeyColumnsSegment.getColumns());
+    }
+    
+    private void addColumnAssignmentTableNames(final Collection<String> 
tableNames, final Collection<String> virtualTableNames, final 
Collection<ColumnAssignmentSegment> assignments) {
+        assignments.forEach(each -> addExpressionTableNames(tableNames, 
virtualTableNames, each.getValue()));
+    }
+    
+    private void addExpressionTableNames(final Collection<String> tableNames, 
final Collection<String> virtualTableNames, final Collection<ExpressionSegment> 
expressionSegments) {
+        expressionSegments.forEach(each -> addExpressionTableNames(tableNames, 
virtualTableNames, each));
+    }
+    
+    private void addExpressionTableNames(final Collection<String> tableNames, 
final Collection<String> virtualTableNames, final ExpressionSegment 
expressionSegment) {
+        if (null == expressionSegment) {
+            return;
+        }
+        if (expressionSegment instanceof SubquerySegment) {
+            addSubqueryTableNames(tableNames, virtualTableNames, 
(SubquerySegment) expressionSegment);
+        } else if (expressionSegment instanceof SubqueryExpressionSegment) {
+            addSubqueryTableNames(tableNames, virtualTableNames, 
((SubqueryExpressionSegment) expressionSegment).getSubquery());
+        } else if (expressionSegment instanceof ExistsSubqueryExpression) {
+            addSubqueryTableNames(tableNames, virtualTableNames, 
((ExistsSubqueryExpression) expressionSegment).getSubquery());
+        } else if (expressionSegment instanceof QuantifySubqueryExpression) {
+            addSubqueryTableNames(tableNames, virtualTableNames, 
((QuantifySubqueryExpression) expressionSegment).getSubquery());
+        } else {
+            addNestedExpressionTableNames(tableNames, virtualTableNames, 
expressionSegment);
+        }
+    }
+    
+    private void addNestedExpressionTableNames(final Collection<String> 
tableNames, final Collection<String> virtualTableNames, final ExpressionSegment 
expressionSegment) {
+        if (expressionSegment instanceof BinaryOperationExpression) {
+            BinaryOperationExpression binaryOperationExpression = 
(BinaryOperationExpression) expressionSegment;
+            addExpressionTableNames(tableNames, virtualTableNames, 
binaryOperationExpression.getLeft());
+            addExpressionTableNames(tableNames, virtualTableNames, 
binaryOperationExpression.getRight());
+        } else if (expressionSegment instanceof FunctionSegment) {
+            addExpressionTableNames(tableNames, virtualTableNames, 
((FunctionSegment) expressionSegment).getParameters());
+        } else if (expressionSegment instanceof ListExpression) {
+            addExpressionTableNames(tableNames, virtualTableNames, 
((ListExpression) expressionSegment).getItems());
+        } else if (expressionSegment instanceof RowExpression) {
+            addExpressionTableNames(tableNames, virtualTableNames, 
((RowExpression) expressionSegment).getItems());
+        } else if (expressionSegment instanceof InExpression) {
+            InExpression inExpression = (InExpression) expressionSegment;
+            addExpressionTableNames(tableNames, virtualTableNames, 
inExpression.getLeft());
+            addExpressionTableNames(tableNames, virtualTableNames, 
inExpression.getRight());
+        } else if (expressionSegment instanceof BetweenExpression) {
+            BetweenExpression betweenExpression = (BetweenExpression) 
expressionSegment;
+            addExpressionTableNames(tableNames, virtualTableNames, 
betweenExpression.getLeft());
+            addExpressionTableNames(tableNames, virtualTableNames, 
betweenExpression.getBetweenExpr());
+            addExpressionTableNames(tableNames, virtualTableNames, 
betweenExpression.getAndExpr());
+        } else if (expressionSegment instanceof CaseWhenExpression) {
+            addCaseWhenTableNames(tableNames, virtualTableNames, 
(CaseWhenExpression) expressionSegment);
+        } else {
+            addOtherExpressionTableNames(tableNames, virtualTableNames, 
expressionSegment);
+        }
+    }
+    
+    private void addCaseWhenTableNames(final Collection<String> tableNames, 
final Collection<String> virtualTableNames, final CaseWhenExpression 
caseWhenExpression) {
+        addExpressionTableNames(tableNames, virtualTableNames, 
caseWhenExpression.getCaseExpr());
+        addExpressionTableNames(tableNames, virtualTableNames, 
caseWhenExpression.getWhenExprs());
+        addExpressionTableNames(tableNames, virtualTableNames, 
caseWhenExpression.getThenExprs());
+        addExpressionTableNames(tableNames, virtualTableNames, 
caseWhenExpression.getElseExpr());
+    }
+    
+    private void addOtherExpressionTableNames(final Collection<String> 
tableNames, final Collection<String> virtualTableNames, final ExpressionSegment 
expressionSegment) {
+        if (expressionSegment instanceof TypeCastExpression) {
+            addExpressionTableNames(tableNames, virtualTableNames, 
((TypeCastExpression) expressionSegment).getExpression());
+        } else if (expressionSegment instanceof UnaryOperationExpression) {
+            addExpressionTableNames(tableNames, virtualTableNames, 
((UnaryOperationExpression) expressionSegment).getExpression());
+        } else if (expressionSegment instanceof NotExpression) {
+            addExpressionTableNames(tableNames, virtualTableNames, 
((NotExpression) expressionSegment).getExpression());
+        } else if (expressionSegment instanceof CollateExpression) {
+            ((CollateExpression) 
expressionSegment).getExpr().ifPresent(optional -> 
addExpressionTableNames(tableNames, virtualTableNames, optional));
+        } else if (expressionSegment instanceof ValuesExpression) {
+            ((ValuesExpression) 
expressionSegment).getRowConstructorList().forEach(each -> 
addInsertValuesTableNames(tableNames, virtualTableNames, each));
+        } else if (expressionSegment instanceof KeyValueSegment) {
+            KeyValueSegment keyValueSegment = (KeyValueSegment) 
expressionSegment;
+            addExpressionTableNames(tableNames, virtualTableNames, 
keyValueSegment.getKey());
+            addExpressionTableNames(tableNames, virtualTableNames, 
keyValueSegment.getValue());
+        } else if (expressionSegment instanceof ColumnAssignmentSegment) {
+            addExpressionTableNames(tableNames, virtualTableNames, 
((ColumnAssignmentSegment) expressionSegment).getValue());
+        }
+    }
+    
+    private void addWithTableNames(final Collection<String> tableNames, final 
Collection<String> virtualTableNames, final WithSegment withSegment) {
+        for (CommonTableExpressionSegment each : 
withSegment.getCommonTableExpressions()) {
+            each.getAliasName().ifPresent(optional -> 
virtualTableNames.add(optional.toLowerCase(Locale.ENGLISH)));
+            addSubqueryTableNames(tableNames, virtualTableNames, 
each.getSubquery());
+        }
+    }
+    
+    private void addTableSegmentTableNames(final Collection<String> 
tableNames, final Collection<String> virtualTableNames, final TableSegment 
tableSegment) {
+        addVirtualTableNames(virtualTableNames, tableSegment);
+        if (tableSegment instanceof SimpleTableSegment) {
+            addTableName(tableNames, (SimpleTableSegment) tableSegment);
+        } else if (tableSegment instanceof JoinTableSegment) {
+            JoinTableSegment joinTableSegment = (JoinTableSegment) 
tableSegment;
+            addTableSegmentTableNames(tableNames, virtualTableNames, 
joinTableSegment.getLeft());
+            addTableSegmentTableNames(tableNames, virtualTableNames, 
joinTableSegment.getRight());
+        } else if (tableSegment instanceof SubqueryTableSegment) {
+            addSubqueryTableNames(tableNames, virtualTableNames, 
((SubqueryTableSegment) tableSegment).getSubquery());
+        } else if (tableSegment instanceof DeleteMultiTableSegment) {
+            DeleteMultiTableSegment deleteMultiTableSegment = 
(DeleteMultiTableSegment) tableSegment;
+            deleteMultiTableSegment.getActualDeleteTables().forEach(each -> 
addTableName(tableNames, each));
+            addTableSegmentTableNames(tableNames, virtualTableNames, 
deleteMultiTableSegment.getRelationTable());
+        }
+    }
+    
+    private void addSubqueryTableNames(final Collection<String> tableNames, 
final Collection<String> virtualTableNames, final SubquerySegment 
subquerySegment) {
+        if (null != subquerySegment.getSelect()) {
+            TableExtractor tableExtractor = new TableExtractor();
+            
tableExtractor.extractTablesFromSelect(subquerySegment.getSelect());
+            tableExtractor.getTableContext().forEach(each -> 
addVirtualTableNames(virtualTableNames, each));
+            addSelectStatementTableNames(tableNames, virtualTableNames, 
subquerySegment.getSelect());
+            addExtractedRewriteTableNames(tableNames, tableExtractor);
+        }
+        if (null != subquerySegment.getMerge()) {
+            addMergeStatementTableNames(tableNames, virtualTableNames, 
subquerySegment.getMerge());
+        }
+    }
+    
+    private void addExtractedRewriteTableNames(final Collection<String> 
tableNames, final TableExtractor tableExtractor) {
+        for (SimpleTableSegment each : tableExtractor.getRewriteTables()) {
+            addTableName(tableNames, each);
+        }
+    }
+    
+    private void addTableName(final Collection<String> tableNames, final 
SimpleTableSegment tableSegment) {
+        tableNames.add(tableSegment.getTableName().getIdentifier().getValue());
+    }
+    
+    private void addVirtualTableNames(final Collection<String> 
virtualTableNames, final TableSegment tableSegment) {
+        if (tableSegment instanceof SimpleTableSegment) {
+            SimpleTableSegment simpleTableSegment = (SimpleTableSegment) 
tableSegment;
+            simpleTableSegment.getAliasName().filter(optional -> 
!optional.equalsIgnoreCase(simpleTableSegment.getTableName().getIdentifier().getValue()))
+                    .ifPresent(optional -> 
virtualTableNames.add(optional.toLowerCase(Locale.ENGLISH)));
+        } else if (tableSegment instanceof JoinTableSegment) {
+            JoinTableSegment joinTableSegment = (JoinTableSegment) 
tableSegment;
+            joinTableSegment.getAliasName().ifPresent(optional -> 
virtualTableNames.add(optional.toLowerCase(Locale.ENGLISH)));
+            addVirtualTableNames(virtualTableNames, 
joinTableSegment.getLeft());
+            addVirtualTableNames(virtualTableNames, 
joinTableSegment.getRight());
+        } else if (tableSegment instanceof SubqueryTableSegment) {
+            tableSegment.getAliasName().ifPresent(optional -> 
virtualTableNames.add(optional.toLowerCase(Locale.ENGLISH)));
+        }
+    }
+    
+    private void removeVirtualTableNames(final Collection<String> tableNames, 
final Collection<String> virtualTableNames) {
+        tableNames.removeIf(each -> 
virtualTableNames.contains(each.toLowerCase(Locale.ENGLISH)));
+    }
+    
+    private boolean isUnextractedWithSQL(final String sql, final SQLStatement 
sqlStatement) {
+        return sql.trim().toUpperCase(Locale.ENGLISH).startsWith("WITH") && 
!containsWithSegment(sqlStatement);
+    }
+    
+    private boolean containsWithSegment(final SQLStatement sqlStatement) {
+        if (sqlStatement instanceof SelectStatement) {
+            return ((SelectStatement) sqlStatement).getWith().isPresent();
+        }
+        if (sqlStatement instanceof InsertStatement) {
+            return ((InsertStatement) sqlStatement).getWith().isPresent();
+        }
+        if (sqlStatement instanceof UpdateStatement) {
+            return ((UpdateStatement) sqlStatement).getWith().isPresent();
+        }
+        if (sqlStatement instanceof DeleteStatement) {
+            return ((DeleteStatement) sqlStatement).getWith().isPresent();
+        }
+        return sqlStatement instanceof MergeStatement && ((MergeStatement) 
sqlStatement).getWith().isPresent();
+    }
+    
+    private boolean isTransactionControlSQL(final String sql) {
+        String trimSQL = sql.trim().toUpperCase(Locale.ENGLISH);
+        return trimSQL.startsWith("BEGIN") || trimSQL.startsWith("COMMIT") || 
trimSQL.startsWith("ROLLBACK") || trimSQL.startsWith("START TRANSACTION")
+                || trimSQL.startsWith("SAVEPOINT") || 
trimSQL.startsWith("RELEASE SAVEPOINT") || trimSQL.startsWith("SET AUTOCOMMIT");
+    }
+    
+    private SQLParserEngine getSQLParserEngine(final DatabaseType 
databaseType) {
+        return SQL_PARSER_ENGINE_MAP.computeIfAbsent(databaseType.getType(), 
unused -> new ShardingSphereSQLParserEngine(databaseType, CACHE_OPTION, 
CACHE_OPTION));
+    }
+    
+    private boolean addDataSetTableNames(final Collection<String> tableNames, 
final E2ETestParameter testParam, final String expectedDataFile) {
+        if (null == expectedDataFile) {
+            return true;
+        }
+        try {
+            for (DataSetMetaData each : 
DataSetLoader.load(testParam.getTestCaseContext().getParentPath(), 
testParam.getScenario(), testParam.getDatabaseType(), testParam.getMode(), 
expectedDataFile)
+                    .getMetaDataList()) {
+                addDataSetMetaDataTableNames(tableNames, each);
+            }
+            return true;
+        } catch (final IllegalArgumentException ignored) {
+            return false;
+        }
+    }
+    
+    private void addDataSetMetaDataTableNames(final Collection<String> 
tableNames, final DataSetMetaData dataSetMetaData) {
+        if (null != dataSetMetaData.getTableName()) {
+            tableNames.add(dataSetMetaData.getTableName());
+        }
+        for (String each : 
InlineExpressionParserFactory.newInstance(dataSetMetaData.getDataNodes()).splitAndEvaluate())
 {
+            String tableName = new DataNode(each).getTableName();
+            tableNames.add(tableName);
+            tableNames.add(tableName.replaceFirst("_[0-9]+$", ""));
+            tableNames.add(tableName.replaceFirst("[0-9]+$", ""));
+        }
+    }
+}
diff --git 
a/test/e2e/sql/src/test/java/org/apache/shardingsphere/test/e2e/sql/env/SQLE2EEnvironmentEngine.java
 
b/test/e2e/sql/src/test/java/org/apache/shardingsphere/test/e2e/sql/env/SQLE2EEnvironmentEngine.java
index 3262e4d2f2d..db29f45cae6 100644
--- 
a/test/e2e/sql/src/test/java/org/apache/shardingsphere/test/e2e/sql/env/SQLE2EEnvironmentEngine.java
+++ 
b/test/e2e/sql/src/test/java/org/apache/shardingsphere/test/e2e/sql/env/SQLE2EEnvironmentEngine.java
@@ -31,9 +31,9 @@ import 
org.apache.shardingsphere.test.e2e.sql.env.container.compose.ContainerCom
 
 import javax.sql.DataSource;
 import java.util.Collection;
-import java.util.HashSet;
 import java.util.Map;
 import java.util.Optional;
+import java.util.concurrent.ConcurrentHashMap;
 
 /**
  * SQL E2E environment engine.
@@ -43,7 +43,7 @@ public final class SQLE2EEnvironmentEngine {
     
     private static final ContainerComposerRegistry CONTAINER_COMPOSER_REGISTRY 
= new ContainerComposerRegistry();
     
-    private static final Collection<String> INITIALIZED_SUITES = new 
HashSet<>();
+    private static final Collection<String> INITIALIZED_SUITES = 
ConcurrentHashMap.newKeySet();
     
     @Getter(AccessLevel.NONE)
     private final ContainerComposer containerComposer;
@@ -58,7 +58,7 @@ public final class SQLE2EEnvironmentEngine {
         containerComposer = 
CONTAINER_COMPOSER_REGISTRY.getContainerComposer(key, scenario, databaseType, 
mode, adapter);
         containerComposer.start();
         actualDataSourceMap = containerComposer.getActualDataSourceMap();
-        targetDataSource = containerComposer.getTargetDataSource();
+        targetDataSource = 
CONTAINER_COMPOSER_REGISTRY.getTargetDataSource(key);
         expectedDataSourceMap = containerComposer.getExpectedDataSourceMap();
         executeLogicDatabaseInitSQLFileOnlyOnce(key, scenario, databaseType);
     }
diff --git 
a/test/e2e/sql/src/test/java/org/apache/shardingsphere/test/e2e/sql/env/container/compose/ContainerComposerRegistry.java
 
b/test/e2e/sql/src/test/java/org/apache/shardingsphere/test/e2e/sql/env/container/compose/ContainerComposerRegistry.java
index 4dd926852ce..15c28e3f07a 100644
--- 
a/test/e2e/sql/src/test/java/org/apache/shardingsphere/test/e2e/sql/env/container/compose/ContainerComposerRegistry.java
+++ 
b/test/e2e/sql/src/test/java/org/apache/shardingsphere/test/e2e/sql/env/container/compose/ContainerComposerRegistry.java
@@ -40,6 +40,8 @@ public final class ContainerComposerRegistry implements 
AutoCloseable {
     
     private final Map<String, ContainerComposer> containerComposers = new 
HashMap<>(7, 1F);
     
+    private final Map<String, DataSource> targetDataSources = new HashMap<>(7, 
1F);
+    
     /**
      * Get container composer.
      *
@@ -51,9 +53,6 @@ public final class ContainerComposerRegistry implements 
AutoCloseable {
      * @return composed container
      */
     public ContainerComposer getContainerComposer(final String key, final 
String scenario, final DatabaseType databaseType, final Mode mode, final 
Adapter adapter) {
-        if (containerComposers.containsKey(key)) {
-            return containerComposers.get(key);
-        }
         synchronized (containerComposers) {
             if (!containerComposers.containsKey(key)) {
                 close();
@@ -63,6 +62,22 @@ public final class ContainerComposerRegistry implements 
AutoCloseable {
         }
     }
     
+    /**
+     * Get target data source.
+     *
+     * @param key key
+     * @return target data source
+     */
+    public DataSource getTargetDataSource(final String key) {
+        synchronized (containerComposers) {
+            return targetDataSources.computeIfAbsent(key, unused -> {
+                ContainerComposer containerComposer = 
containerComposers.get(key);
+                Preconditions.checkNotNull(containerComposer, "Container 
composer `%s` does not exist.", key);
+                return containerComposer.getTargetDataSource();
+            });
+        }
+    }
+    
     private boolean isClusterMode(final Mode mode, final Adapter adapter) {
         return Mode.CLUSTER == mode && Adapter.PROXY == adapter;
     }
@@ -74,11 +89,14 @@ public final class ContainerComposerRegistry implements 
AutoCloseable {
     @Override
     public void close() {
         synchronized (containerComposers) {
+            for (DataSource each : targetDataSources.values()) {
+                closeTargetDataSource(each);
+            }
             for (ContainerComposer each : containerComposers.values()) {
-                closeTargetDataSource(each.getTargetDataSource());
                 closeActualDataSourceMap(each.getActualDataSourceMap());
                 closeContainer(each);
             }
+            targetDataSources.clear();
             containerComposers.clear();
         }
     }
diff --git 
a/test/e2e/sql/src/test/java/org/apache/shardingsphere/test/e2e/sql/it/sql/ddl/DDLE2EIT.java
 
b/test/e2e/sql/src/test/java/org/apache/shardingsphere/test/e2e/sql/it/sql/ddl/DDLE2EIT.java
index 000aa309fd0..a3cc20f9cba 100644
--- 
a/test/e2e/sql/src/test/java/org/apache/shardingsphere/test/e2e/sql/it/sql/ddl/DDLE2EIT.java
+++ 
b/test/e2e/sql/src/test/java/org/apache/shardingsphere/test/e2e/sql/it/sql/ddl/DDLE2EIT.java
@@ -44,23 +44,41 @@ import java.sql.PreparedStatement;
 import java.sql.ResultSet;
 import java.sql.SQLException;
 import java.sql.Statement;
+import java.time.Duration;
 import java.util.Collection;
 import java.util.LinkedHashSet;
 import java.util.LinkedList;
 import java.util.List;
-import java.util.Set;
-import java.util.concurrent.TimeUnit;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
 import java.util.stream.Collectors;
 
 import static org.hamcrest.Matchers.is;
 import static org.hamcrest.MatcherAssert.assertThat;
 import static org.junit.jupiter.api.Assertions.assertFalse;
 import static org.junit.jupiter.api.Assertions.assertNotNull;
+import static org.junit.jupiter.api.Assertions.assertTrue;
 
 @SQLE2EITSettings(SQLCommandType.DDL)
 @Setter
 class DDLE2EIT implements SQLE2EIT {
     
+    private static final Duration META_DATA_WAIT_TIMEOUT = 
Duration.ofSeconds(30L);
+    
+    private static final Duration META_DATA_POLL_INTERVAL = 
Duration.ofMillis(100L);
+    
+    private static final Duration DDL_COMPLETION_DELAY = 
Duration.ofMillis(1500L);
+    
+    private static final Pattern CREATE_TABLE_OR_VIEW_PATTERN = 
Pattern.compile("(?is)^\\s*CREATE\\s+(?:OR\\s+REPLACE\\s+)?(?:TABLE|VIEW)\\s+(?:IF\\s+NOT\\s+EXISTS\\s+)?([^\\s(]+).*");
+    
+    private static final Pattern DROP_TABLE_OR_VIEW_PATTERN = 
Pattern.compile("(?is)^\\s*DROP\\s+(?:TABLE|VIEW)\\s+(?:IF\\s+EXISTS\\s+)?([^\\s(;]+).*");
+    
+    private static final Pattern CREATE_INDEX_PATTERN = 
Pattern.compile("(?is)^\\s*CREATE\\s+(?:UNIQUE\\s+)?INDEX\\s+([^\\s(]+)\\s+ON\\s+([^\\s(]+).*");
+    
+    private static final Pattern DROP_INDEX_WITH_TABLE_PATTERN = 
Pattern.compile("(?is)^\\s*DROP\\s+INDEX\\s+([^\\s(]+)\\s+ON\\s+([^\\s(]+).*");
+    
+    private static final Pattern DROP_INDEX_PATTERN = 
Pattern.compile("(?is)^\\s*DROP\\s+INDEX\\s+([^\\s(]+).*");
+    
     private SQLE2EEnvironmentEngine environmentEngine;
     
     @ParameterizedTest(name = "{0}")
@@ -79,7 +97,7 @@ class DDLE2EIT implements SQLE2EIT {
             } else {
                 executeUpdateForPreparedStatement(context, connection);
             }
-            assertTableMetaData(testParam, context);
+            assertTableMetaDataEventually(testParam, context);
         } finally {
             tearDown(context);
         }
@@ -89,14 +107,12 @@ class DDLE2EIT implements SQLE2EIT {
         try (Statement statement = connection.createStatement()) {
             assertFalse(statement.executeUpdate(context.getSQL()) > 0, "Not a 
DDL statement.");
         }
-        waitCompleted();
     }
     
     private void executeUpdateForPreparedStatement(final SQLE2EITContext 
context, final Connection connection) throws SQLException {
         try (PreparedStatement preparedStatement = 
connection.prepareStatement(context.getSQL())) {
             assertFalse(preparedStatement.executeUpdate() > 0, "Not a DDL 
statement.");
         }
-        waitCompleted();
     }
     
     @ParameterizedTest(name = "{0}")
@@ -115,7 +131,7 @@ class DDLE2EIT implements SQLE2EIT {
             } else {
                 executeForPreparedStatement(context, connection);
             }
-            assertTableMetaData(testParam, context);
+            assertTableMetaDataEventually(testParam, context);
         } finally {
             tearDown(context);
         }
@@ -125,14 +141,12 @@ class DDLE2EIT implements SQLE2EIT {
         try (Statement statement = connection.createStatement()) {
             assertFalse(statement.execute(context.getSQL()), "Not a DDL 
statement.");
         }
-        waitCompleted();
     }
     
     private void executeForPreparedStatement(final SQLE2EITContext context, 
final Connection connection) throws SQLException {
         try (PreparedStatement preparedStatement = 
connection.prepareStatement(context.getSQL())) {
             assertFalse(preparedStatement.execute(), "Not a DDL statement.");
         }
-        waitCompleted();
     }
     
     private void init(final SQLE2EITContext context) throws SQLException {
@@ -151,7 +165,7 @@ class DDLE2EIT implements SQLE2EIT {
             try (PreparedStatement preparedStatement = 
connection.prepareStatement(each)) {
                 preparedStatement.executeUpdate();
             }
-            waitCompleted();
+            waitSQLCompleted(each, context);
         }
     }
     
@@ -171,14 +185,112 @@ class DDLE2EIT implements SQLE2EIT {
             try (PreparedStatement preparedStatement = 
connection.prepareStatement(each)) {
                 preparedStatement.executeUpdate();
             }
-            waitCompleted();
+            waitSQLCompleted(each, context);
+        }
+    }
+    
+    private void waitSQLCompleted(final String sql, final SQLE2EITContext 
context) {
+        Matcher createTableOrViewMatcher = 
CREATE_TABLE_OR_VIEW_PATTERN.matcher(sql);
+        if (createTableOrViewMatcher.matches() && waitTableExists(context, 
createTableOrViewMatcher.group(1), true)) {
+            return;
+        }
+        Matcher dropTableOrViewMatcher = 
DROP_TABLE_OR_VIEW_PATTERN.matcher(sql);
+        if (dropTableOrViewMatcher.matches() && waitTableExists(context, 
dropTableOrViewMatcher.group(1), false)) {
+            return;
+        }
+        Matcher createIndexMatcher = CREATE_INDEX_PATTERN.matcher(sql);
+        if (createIndexMatcher.matches() && waitIndexExists(context, 
createIndexMatcher.group(2), createIndexMatcher.group(1), true)) {
+            return;
+        }
+        Matcher dropIndexWithTableMatcher = 
DROP_INDEX_WITH_TABLE_PATTERN.matcher(sql);
+        if (dropIndexWithTableMatcher.matches() && waitIndexExists(context, 
dropIndexWithTableMatcher.group(2), dropIndexWithTableMatcher.group(1), false)) 
{
+            return;
+        }
+        Matcher dropIndexMatcher = DROP_INDEX_PATTERN.matcher(sql);
+        if (dropIndexMatcher.matches() && waitIndexExists(context, 
context.getAssertion().getInitialSQL().getAffectedTable(), 
dropIndexMatcher.group(1), false)) {
+            return;
+        }
+        waitCompleted();
+    }
+    
+    private boolean waitTableExists(final SQLE2EITContext context, final 
String tableName, final boolean exists) {
+        Collection<DataNode> dataNodes = findDataNodes(context, 
getIdentifierValue(tableName));
+        if (dataNodes.isEmpty()) {
+            return false;
+        }
+        
Awaitility.await().atMost(META_DATA_WAIT_TIMEOUT).pollInterval(META_DATA_POLL_INTERVAL).untilAsserted(()
 -> assertTableState(dataNodes, exists));
+        return true;
+    }
+    
+    private void assertTableState(final Collection<DataNode> dataNodes, final 
boolean exists) throws SQLException {
+        if (!exists) {
+            assertNotContainsTable(environmentEngine, dataNodes);
+            return;
+        }
+        boolean tableExists = false;
+        for (DataNode each : dataNodes) {
+            try (Connection connection = 
environmentEngine.getActualDataSourceMap().get(each.getDataSourceName()).getConnection())
 {
+                if (containsTable(connection, each.getTableName())) {
+                    tableExists = true;
+                    break;
+                }
+            }
         }
+        assertTrue(tableExists, "Expected table does not exist");
+    }
+    
+    private boolean waitIndexExists(final SQLE2EITContext context, final 
String tableName, final String indexName, final boolean exists) {
+        Collection<DataNode> dataNodes = findDataNodes(context, 
getIdentifierValue(tableName));
+        if (dataNodes.isEmpty()) {
+            return false;
+        }
+        String actualTableName = getIdentifierValue(tableName);
+        String actualIndexName = getIdentifierValue(indexName);
+        
Awaitility.await().atMost(META_DATA_WAIT_TIMEOUT).pollInterval(META_DATA_POLL_INTERVAL)
+                .untilAsserted(() -> assertThat(String.format("Index `%s` 
existed state mismatch", indexName), containsIndex(dataNodes, actualTableName, 
actualIndexName), is(exists)));
+        return true;
+    }
+    
+    private boolean containsIndex(final Collection<DataNode> dataNodes, final 
String tableName, final String indexName) throws SQLException {
+        return getActualIndexes(dataNodes).stream().anyMatch(each -> 
isSameIndex(each.getName(), tableName, indexName));
+    }
+    
+    private boolean isSameIndex(final String actualIndexName, final String 
tableName, final String indexName) {
+        if (null == actualIndexName) {
+            return false;
+        }
+        if (indexName.equalsIgnoreCase(actualIndexName)) {
+            return true;
+        }
+        String shardingIndexName = indexName + "_" + tableName;
+        return shardingIndexName.equalsIgnoreCase(actualIndexName) || 
actualIndexName.length() > shardingIndexName.length()
+                && '_' == actualIndexName.charAt(shardingIndexName.length()) 
&& actualIndexName.regionMatches(true, 0, shardingIndexName, 0, 
shardingIndexName.length());
+    }
+    
+    private Collection<DataNode> findDataNodes(final SQLE2EITContext context, 
final String tableName) {
+        try {
+            return getDataNodes(context.getDataSet().findMetaData(tableName));
+        } catch (final IllegalArgumentException ignored) {
+            return new LinkedList<>();
+        }
+    }
+    
+    private String getIdentifierValue(final String identifier) {
+        String result = identifier.trim();
+        if (result.contains(".")) {
+            result = result.substring(result.lastIndexOf('.') + 1);
+        }
+        return result.replace("`", "").replace("\"", "").replace("[", 
"").replace("]", "");
+    }
+    
+    private void assertTableMetaDataEventually(final AssertionTestParameter 
testParam, final SQLE2EITContext context) {
+        
Awaitility.await().atMost(META_DATA_WAIT_TIMEOUT).pollInterval(META_DATA_POLL_INTERVAL).untilAsserted(()
 -> assertTableMetaData(testParam, context));
     }
     
     private void assertTableMetaData(final AssertionTestParameter testParam, 
final SQLE2EITContext context) throws SQLException {
         String tableName = 
context.getAssertion().getInitialSQL().getAffectedTable();
         DataSetMetaData expected = 
context.getDataSet().findMetaData(tableName);
-        Collection<DataNode> dataNodes = 
InlineExpressionParserFactory.newInstance(expected.getDataNodes()).splitAndEvaluate().stream().map(DataNode::new).collect(Collectors.toList());
+        Collection<DataNode> dataNodes = getDataNodes(expected);
         if (expected.getColumns().isEmpty()) {
             assertNotContainsTable(environmentEngine, dataNodes);
             return;
@@ -191,6 +303,10 @@ class DDLE2EIT implements SQLE2EIT {
         assertIndexMetaData(actualIndexes, expected.getIndexes());
     }
     
+    private Collection<DataNode> getDataNodes(final DataSetMetaData metaData) {
+        return 
InlineExpressionParserFactory.newInstance(metaData.getDataNodes()).splitAndEvaluate().stream().map(DataNode::new).collect(Collectors.toList());
+    }
+    
     private void assertNotContainsTable(final SQLE2EEnvironmentEngine 
environmentEngine, final Collection<DataNode> dataNodes) throws SQLException {
         for (DataNode each : dataNodes) {
             try (Connection connection = 
environmentEngine.getActualDataSourceMap().get(each.getDataSourceName()).getConnection())
 {
@@ -200,12 +316,16 @@ class DDLE2EIT implements SQLE2EIT {
     }
     
     private void assertNotContainsTable(final Connection connection, final 
String tableName) throws SQLException {
-        assertFalse(connection.getMetaData().getTables(null, null, tableName, 
new String[]{"TABLE"}).next(), String.format("Table `%s` should not existed", 
tableName));
+        assertFalse(containsTable(connection, tableName), String.format("Table 
`%s` should not existed", tableName));
+    }
+    
+    private boolean containsTable(final Connection connection, final String 
tableName) throws SQLException {
+        return connection.getMetaData().getTables(null, null, tableName, new 
String[]{"TABLE", "VIEW"}).next();
     }
     
     @SuppressWarnings("CollectionWithoutInitialCapacity")
     private List<DataSetColumn> getActualColumns(final Collection<DataNode> 
dataNodes) throws SQLException {
-        Set<DataSetColumn> result = new LinkedHashSet<>();
+        Collection<DataSetColumn> result = new LinkedHashSet<>();
         for (DataNode each : dataNodes) {
             try (Connection connection = 
environmentEngine.getActualDataSourceMap().get(each.getDataSourceName()).getConnection())
 {
                 result.addAll(getActualColumns(connection, 
each.getTableName()));
@@ -231,7 +351,7 @@ class DDLE2EIT implements SQLE2EIT {
     
     @SuppressWarnings("CollectionWithoutInitialCapacity")
     private List<DataSetIndex> getActualIndexes(final Collection<DataNode> 
dataNodes) throws SQLException {
-        Set<DataSetIndex> result = new LinkedHashSet<>();
+        Collection<DataSetIndex> result = new LinkedHashSet<>();
         for (DataNode each : dataNodes) {
             try (Connection connection = 
environmentEngine.getActualDataSourceMap().get(each.getDataSourceName()).getConnection())
 {
                 result.addAll(getActualIndexes(connection, 
each.getTableName()));
@@ -290,7 +410,7 @@ class DDLE2EIT implements SQLE2EIT {
     }
     
     private void waitCompleted() {
-        Awaitility.await().pollDelay(1500L, TimeUnit.MILLISECONDS).until(() -> 
true);
+        Awaitility.await().pollDelay(DDL_COMPLETION_DELAY).until(() -> true);
     }
     
     private static boolean isEnabled() {
diff --git 
a/test/e2e/sql/src/test/java/org/apache/shardingsphere/test/e2e/sql/it/sql/dml/BaseDMLE2EIT.java
 
b/test/e2e/sql/src/test/java/org/apache/shardingsphere/test/e2e/sql/it/sql/dml/BaseDMLE2EIT.java
index c9369cf7ebb..d2cd78bd6f8 100644
--- 
a/test/e2e/sql/src/test/java/org/apache/shardingsphere/test/e2e/sql/it/sql/dml/BaseDMLE2EIT.java
+++ 
b/test/e2e/sql/src/test/java/org/apache/shardingsphere/test/e2e/sql/it/sql/dml/BaseDMLE2EIT.java
@@ -36,6 +36,7 @@ import 
org.apache.shardingsphere.test.e2e.sql.cases.dataset.metadata.DataSetColu
 import 
org.apache.shardingsphere.test.e2e.sql.cases.dataset.metadata.DataSetMetaData;
 import org.apache.shardingsphere.test.e2e.sql.cases.dataset.row.DataSetRow;
 import org.apache.shardingsphere.test.e2e.sql.env.DataSetEnvironmentManager;
+import org.apache.shardingsphere.test.e2e.sql.env.DataSetResetScopeCalculator;
 import org.apache.shardingsphere.test.e2e.sql.env.SQLE2EEnvironmentEngine;
 import 
org.apache.shardingsphere.test.e2e.sql.framework.metadata.DialectDatabaseAssertionMetaDataFactory;
 import 
org.apache.shardingsphere.test.e2e.sql.framework.metadata.DialectQueryBehaviorProvider;
@@ -81,8 +82,12 @@ public abstract class BaseDMLE2EIT implements SQLE2EIT {
     
     private static final String DATA_COLUMN_DELIMITER = ", ";
     
+    private static final DataSetResetScopeCalculator 
DATA_SET_RESET_SCOPE_CALCULATOR = new DataSetResetScopeCalculator();
+    
     private DataSetEnvironmentManager dataSetEnvironmentManager;
     
+    private Collection<String> resetTableNames = Collections.emptyList();
+    
     @Getter
     @Setter
     private SQLE2EEnvironmentEngine environmentEngine;
@@ -97,13 +102,18 @@ public abstract class BaseDMLE2EIT implements SQLE2EIT {
     protected void init(final E2ETestParameter testParam) throws IOException, 
JAXBException {
         dataSetEnvironmentManager = new DataSetEnvironmentManager(
                 new ScenarioDataPath(testParam.getScenario(), 
Type.ACTUAL).getDataSetFile(), getEnvironmentEngine().getActualDataSourceMap(), 
testParam.getDatabaseType());
-        dataSetEnvironmentManager.fillData();
+        resetTableNames = 
DATA_SET_RESET_SCOPE_CALCULATOR.getResetTableNames(testParam);
+        fillActualDataSet(resetTableNames);
+    }
+    
+    private void fillActualDataSet(final Collection<String> tableNames) {
+        dataSetEnvironmentManager.fillData(tableNames);
     }
     
     void tearDown() {
         // TODO make sure test case can not be null
         if (null != dataSetEnvironmentManager) {
-            dataSetEnvironmentManager.cleanData();
+            dataSetEnvironmentManager.cleanData(resetTableNames);
         }
     }
     
diff --git 
a/test/e2e/sql/src/test/java/org/apache/shardingsphere/test/e2e/sql/it/sql/dml/BatchDMLE2EIT.java
 
b/test/e2e/sql/src/test/java/org/apache/shardingsphere/test/e2e/sql/it/sql/dml/BatchDMLE2EIT.java
index 8de207c9456..3fd1552c594 100644
--- 
a/test/e2e/sql/src/test/java/org/apache/shardingsphere/test/e2e/sql/it/sql/dml/BatchDMLE2EIT.java
+++ 
b/test/e2e/sql/src/test/java/org/apache/shardingsphere/test/e2e/sql/it/sql/dml/BatchDMLE2EIT.java
@@ -106,7 +106,6 @@ class BatchDMLE2EIT extends BaseDMLE2EIT {
         if (null == testParam.getTestCaseContext()) {
             return;
         }
-        init(testParam);
         try (
                 Connection connection = 
getEnvironmentEngine().getTargetDataSource().getConnection();
                 PreparedStatement preparedStatement = 
connection.prepareStatement(testParam.getTestCaseContext().getTestCase().getSql()))
 {
@@ -115,8 +114,6 @@ class BatchDMLE2EIT extends BaseDMLE2EIT {
             }
             preparedStatement.clearBatch();
             assertThat(preparedStatement.executeBatch().length, is(0));
-        } finally {
-            tearDown(testParam);
         }
     }
     
diff --git 
a/test/e2e/sql/src/test/java/org/apache/shardingsphere/test/e2e/sql/it/sql/dql/AdditionalDQLE2EIT.java
 
b/test/e2e/sql/src/test/java/org/apache/shardingsphere/test/e2e/sql/it/sql/dql/AdditionalDQLE2EIT.java
index 0e44981d0a3..b02b97ff190 100644
--- 
a/test/e2e/sql/src/test/java/org/apache/shardingsphere/test/e2e/sql/it/sql/dql/AdditionalDQLE2EIT.java
+++ 
b/test/e2e/sql/src/test/java/org/apache/shardingsphere/test/e2e/sql/it/sql/dql/AdditionalDQLE2EIT.java
@@ -26,6 +26,8 @@ import 
org.apache.shardingsphere.test.e2e.sql.framework.type.SQLCommandType;
 import org.apache.shardingsphere.test.e2e.sql.framework.type.SQLExecuteType;
 import org.apache.shardingsphere.test.e2e.sql.it.SQLE2EITContext;
 import org.junit.jupiter.api.condition.EnabledIf;
+import org.junit.jupiter.api.parallel.Execution;
+import org.junit.jupiter.api.parallel.ExecutionMode;
 import org.junit.jupiter.params.ParameterizedTest;
 import org.junit.jupiter.params.provider.ArgumentsSource;
 
@@ -44,6 +46,7 @@ import static org.junit.jupiter.api.Assertions.assertTrue;
 class AdditionalDQLE2EIT extends BaseDQLE2EIT {
     
     @ParameterizedTest(name = "{0}")
+    @Execution(ExecutionMode.CONCURRENT)
     @EnabledIf("isEnabled")
     @ArgumentsSource(SQLE2EITArgumentsProvider.class)
     void assertExecuteQueryWithResultSetTypeAndConcurrency(final 
AssertionTestParameter testParam) throws SQLException, JAXBException, 
IOException {
@@ -52,19 +55,11 @@ class AdditionalDQLE2EIT extends BaseDQLE2EIT {
             return;
         }
         SQLE2EITContext context = new SQLE2EITContext(testParam);
-        init(testParam, context);
-        // TODO fix e2e test blocked exception with PostgreSQL or openGauss in 
#23643
-        if (isPostgreSQLOrOpenGauss(testParam.getDatabaseType().getType())) {
-            return;
-        }
-        if (isUseXMLAsExpectedDataset()) {
-            assertExecuteQueryWithXMLExpected(testParam, context, 
ResultSet.TYPE_FORWARD_ONLY, ResultSet.CONCUR_READ_ONLY);
-        } else {
-            assertExecuteQueryWithExpectedDataSource(testParam, context, 
ResultSet.TYPE_FORWARD_ONLY, ResultSet.CONCUR_READ_ONLY);
-        }
+        executeDQL(context, () -> 
assertExecuteQueryWithResultSetTypes(testParam, context, 
ResultSet.TYPE_FORWARD_ONLY, ResultSet.CONCUR_READ_ONLY));
     }
     
     @ParameterizedTest(name = "{0}")
+    @Execution(ExecutionMode.CONCURRENT)
     @EnabledIf("isEnabled")
     @ArgumentsSource(SQLE2EITArgumentsProvider.class)
     void assertExecuteQueryWithResultSetTypeAndConcurrencyAndHoldability(final 
AssertionTestParameter testParam) throws SQLException, JAXBException, 
IOException {
@@ -73,20 +68,11 @@ class AdditionalDQLE2EIT extends BaseDQLE2EIT {
             return;
         }
         SQLE2EITContext context = new SQLE2EITContext(testParam);
-        init(testParam, context);
-        // TODO fix e2e test blocked exception with PostgreSQL or openGauss in 
#23643
-        if (isPostgreSQLOrOpenGauss(testParam.getDatabaseType().getType())) {
-            return;
-        }
-        if (isUseXMLAsExpectedDataset()) {
-            assertExecuteQueryWithXMLExpected(testParam, context, 
ResultSet.TYPE_FORWARD_ONLY, ResultSet.CONCUR_READ_ONLY, 
ResultSet.HOLD_CURSORS_OVER_COMMIT);
-        } else {
-            assertExecuteQueryWithExpectedDataSource(testParam, context, 
ResultSet.TYPE_FORWARD_ONLY, ResultSet.CONCUR_READ_ONLY,
-                    ResultSet.HOLD_CURSORS_OVER_COMMIT);
-        }
+        executeDQL(context, () -> 
assertExecuteQueryWithResultSetTypes(testParam, context, 
ResultSet.TYPE_FORWARD_ONLY, ResultSet.CONCUR_READ_ONLY, 
ResultSet.HOLD_CURSORS_OVER_COMMIT));
     }
     
     @ParameterizedTest(name = "{0}")
+    @Execution(ExecutionMode.CONCURRENT)
     @EnabledIf("isEnabled")
     @ArgumentsSource(SQLE2EITArgumentsProvider.class)
     void assertExecuteWithResultSetTypeAndConcurrency(final 
AssertionTestParameter testParam) throws SQLException, JAXBException, 
IOException {
@@ -95,19 +81,11 @@ class AdditionalDQLE2EIT extends BaseDQLE2EIT {
             return;
         }
         SQLE2EITContext context = new SQLE2EITContext(testParam);
-        init(testParam, context);
-        // TODO fix e2e test blocked exception with PostgreSQL or openGauss in 
#23643
-        if (isPostgreSQLOrOpenGauss(testParam.getDatabaseType().getType())) {
-            return;
-        }
-        if (isUseXMLAsExpectedDataset()) {
-            assertExecuteWithXMLExpected(testParam, context, 
ResultSet.TYPE_FORWARD_ONLY, ResultSet.CONCUR_READ_ONLY);
-        } else {
-            assertExecuteWithExpectedDataSource(testParam, context, 
ResultSet.TYPE_FORWARD_ONLY, ResultSet.CONCUR_READ_ONLY);
-        }
+        executeDQL(context, () -> assertExecuteWithResultSetTypes(testParam, 
context, ResultSet.TYPE_FORWARD_ONLY, ResultSet.CONCUR_READ_ONLY));
     }
     
     @ParameterizedTest(name = "{0}")
+    @Execution(ExecutionMode.CONCURRENT)
     @EnabledIf("isEnabled")
     @ArgumentsSource(SQLE2EITArgumentsProvider.class)
     void assertExecuteWithResultSetTypeAndConcurrencyAndHoldability(final 
AssertionTestParameter testParam) throws SQLException, JAXBException, 
IOException {
@@ -116,15 +94,33 @@ class AdditionalDQLE2EIT extends BaseDQLE2EIT {
             return;
         }
         SQLE2EITContext context = new SQLE2EITContext(testParam);
+        executeDQL(context, () -> assertExecuteWithResultSetTypes(testParam, 
context, ResultSet.TYPE_FORWARD_ONLY, ResultSet.CONCUR_READ_ONLY, 
ResultSet.HOLD_CURSORS_OVER_COMMIT));
+    }
+    
+    private void assertExecuteQueryWithResultSetTypes(final 
AssertionTestParameter testParam, final SQLE2EITContext context,
+                                                      final int... 
resultSetTypes) throws IOException, JAXBException, SQLException {
+        init(testParam, context);
+        // TODO fix e2e test blocked exception with PostgreSQL or openGauss in 
#23643
+        if (isPostgreSQLOrOpenGauss(testParam.getDatabaseType().getType())) {
+            return;
+        }
+        if (isUseXMLAsExpectedDataset()) {
+            assertExecuteQueryWithXMLExpected(testParam, context, 
resultSetTypes);
+        } else {
+            assertExecuteQueryWithExpectedDataSource(testParam, context, 
resultSetTypes);
+        }
+    }
+    
+    private void assertExecuteWithResultSetTypes(final AssertionTestParameter 
testParam, final SQLE2EITContext context, final int... resultSetTypes) throws 
IOException, JAXBException, SQLException {
         init(testParam, context);
         // TODO fix e2e test blocked exception with PostgreSQL or openGauss in 
#23643
         if (isPostgreSQLOrOpenGauss(testParam.getDatabaseType().getType())) {
             return;
         }
         if (isUseXMLAsExpectedDataset()) {
-            assertExecuteWithXMLExpected(testParam, context, 
ResultSet.TYPE_FORWARD_ONLY, ResultSet.CONCUR_READ_ONLY, 
ResultSet.HOLD_CURSORS_OVER_COMMIT);
+            assertExecuteWithXMLExpected(testParam, context, resultSetTypes);
         } else {
-            assertExecuteWithExpectedDataSource(testParam, context, 
ResultSet.TYPE_FORWARD_ONLY, ResultSet.CONCUR_READ_ONLY, 
ResultSet.HOLD_CURSORS_OVER_COMMIT);
+            assertExecuteWithExpectedDataSource(testParam, context, 
resultSetTypes);
         }
     }
     
diff --git 
a/test/e2e/sql/src/test/java/org/apache/shardingsphere/test/e2e/sql/it/sql/dql/BaseDQLE2EIT.java
 
b/test/e2e/sql/src/test/java/org/apache/shardingsphere/test/e2e/sql/it/sql/dql/BaseDQLE2EIT.java
index c692a9c0507..3c2e7e7ace6 100644
--- 
a/test/e2e/sql/src/test/java/org/apache/shardingsphere/test/e2e/sql/it/sql/dql/BaseDQLE2EIT.java
+++ 
b/test/e2e/sql/src/test/java/org/apache/shardingsphere/test/e2e/sql/it/sql/dql/BaseDQLE2EIT.java
@@ -47,10 +47,15 @@ import java.sql.Types;
 import java.time.Duration;
 import java.time.LocalDateTime;
 import java.util.Collection;
-import java.util.HashSet;
+import java.util.Collections;
 import java.util.LinkedList;
 import java.util.List;
+import java.util.Map;
+import java.util.concurrent.ConcurrentHashMap;
 import java.util.concurrent.TimeUnit;
+import java.util.concurrent.locks.Lock;
+import java.util.concurrent.locks.ReadWriteLock;
+import java.util.concurrent.locks.ReentrantReadWriteLock;
 import java.util.stream.Collectors;
 
 import static org.hamcrest.MatcherAssert.assertThat;
@@ -61,7 +66,11 @@ import static org.junit.jupiter.api.Assertions.assertTrue;
 @Getter(AccessLevel.PROTECTED)
 public abstract class BaseDQLE2EIT implements SQLE2EIT {
     
-    private static final Collection<String> FILLED_SUITES = new HashSet<>();
+    private static final Collection<String> FILLED_SUITES = 
ConcurrentHashMap.newKeySet();
+    
+    private static final Map<String, Object> FILL_DATA_LOCKS = new 
ConcurrentHashMap<>();
+    
+    private static final ReadWriteLock DQL_EXECUTION_LOCK = new 
ReentrantReadWriteLock(true);
     
     private DataSource expectedDataSource;
     
@@ -70,6 +79,25 @@ public abstract class BaseDQLE2EIT implements SQLE2EIT {
     @Setter
     private SQLE2EEnvironmentEngine environmentEngine;
     
+    /**
+     * Execute DQL with concurrent execution control.
+     *
+     * @param context SQL E2E IT context
+     * @param callback DQL execution callback
+     * @throws SQLException SQL exception
+     * @throws IOException IO exception
+     * @throws JAXBException JAXB exception
+     */
+    protected final void executeDQL(final SQLE2EITContext context, final 
DQLExecutionCallback callback) throws SQLException, IOException, JAXBException {
+        Lock lock = 
DQLExclusiveExecutionDetector.requiresExclusiveExecution(context.getSQL()) ? 
DQL_EXECUTION_LOCK.writeLock() : DQL_EXECUTION_LOCK.readLock();
+        lock.lock();
+        try {
+            callback.execute();
+        } finally {
+            lock.unlock();
+        }
+    }
+    
     protected final void init(final AssertionTestParameter testParam, final 
SQLE2EITContext context) throws IOException, JAXBException {
         fillDataOnlyOnce(testParam);
         expectedDataSource = getExpectedDataSource(testParam, context);
@@ -92,16 +120,16 @@ public abstract class BaseDQLE2EIT implements SQLE2EIT {
         if (FILLED_SUITES.contains(cacheKey)) {
             return;
         }
-        synchronized (FILLED_SUITES) {
+        synchronized (FILL_DATA_LOCKS.computeIfAbsent(cacheKey, unused -> new 
Object())) {
             if (FILLED_SUITES.contains(cacheKey)) {
                 return;
             }
             new DataSetEnvironmentManager(
                     new ScenarioDataPath(testParam.getScenario(), 
Type.ACTUAL).getDataSetFile(), getEnvironmentEngine().getActualDataSourceMap(),
-                    testParam.getDatabaseType()).fillData();
+                    
testParam.getDatabaseType()).fillData(Collections.emptyList());
             new DataSetEnvironmentManager(
                     new ScenarioDataPath(testParam.getScenario(), 
Type.EXPECTED).getDataSetFile(), 
getEnvironmentEngine().getExpectedDataSourceMap(),
-                    testParam.getDatabaseType()).fillData();
+                    
testParam.getDatabaseType()).fillData(Collections.emptyList());
             FILLED_SUITES.add(cacheKey);
         }
     }
@@ -229,4 +257,20 @@ public abstract class BaseDQLE2EIT implements SQLE2EIT {
         assertThat(String.valueOf(actual.getObject(columnIndex)).trim(), 
is(expected));
         assertThat(String.valueOf(actual.getObject(columnLabel)).trim(), 
is(expected));
     }
+    
+    /**
+     * DQL execution callback.
+     */
+    @FunctionalInterface
+    protected interface DQLExecutionCallback {
+        
+        /**
+         * Execute DQL.
+         *
+         * @throws SQLException SQL exception
+         * @throws IOException IO exception
+         * @throws JAXBException JAXB exception
+         */
+        void execute() throws SQLException, IOException, JAXBException;
+    }
 }
diff --git 
a/test/e2e/sql/src/test/java/org/apache/shardingsphere/test/e2e/sql/it/sql/dql/DQLExclusiveExecutionDetector.java
 
b/test/e2e/sql/src/test/java/org/apache/shardingsphere/test/e2e/sql/it/sql/dql/DQLExclusiveExecutionDetector.java
new file mode 100644
index 00000000000..df1688267b1
--- /dev/null
+++ 
b/test/e2e/sql/src/test/java/org/apache/shardingsphere/test/e2e/sql/it/sql/dql/DQLExclusiveExecutionDetector.java
@@ -0,0 +1,143 @@
+/*
+ * 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.test.e2e.sql.it.sql.dql;
+
+import java.util.Locale;
+import java.util.regex.Pattern;
+
+/**
+ * Exclusive execution detector for SQL E2E tests.
+ */
+public final class DQLExclusiveExecutionDetector {
+    
+    private static final Pattern SELECT_PATTERN = 
Pattern.compile("\\bSELECT\\b");
+    
+    private static final Pattern DML_RETURNING_PATTERN = 
Pattern.compile("\\b(?:INSERT|UPDATE|DELETE|MERGE)\\b.*\\bRETURNING\\b");
+    
+    private static final Pattern FOR_UPDATE_PATTERN = 
Pattern.compile("\\bFOR\\s+(?:NO\\s+KEY\\s+)?UPDATE\\b");
+    
+    private static final Pattern FOR_SHARE_PATTERN = 
Pattern.compile("\\bFOR\\s+(?:KEY\\s+)?SHARE\\b");
+    
+    private static final Pattern LOCK_IN_SHARE_MODE_PATTERN = 
Pattern.compile("\\bLOCK\\s+IN\\s+SHARE\\s+MODE\\b");
+    
+    private static final Pattern SQL_SERVER_LOCK_HINT_PATTERN = 
Pattern.compile(
+            
"\\bWITH\\s*\\([^)]*\\b(?:UPDLOCK|HOLDLOCK|XLOCK|TABLOCKX|TABLOCK|PAGLOCK|ROWLOCK|READCOMMITTEDLOCK|REPEATABLEREAD|SERIALIZABLE)\\b[^)]*\\)");
+    
+    private static final Pattern KEEP_LOCKS_PATTERN = 
Pattern.compile("\\bWITH\\s+(?:RR|RS|CS)\\s+USE\\s+AND\\s+KEEP\\s+(?:UPDATE|EXCLUSIVE|SHARE)\\s+LOCKS\\b");
+    
+    private static final Pattern WITH_LOCK_PATTERN = 
Pattern.compile("\\bWITH\\s+LOCK\\b(?!\\s+AS\\b)");
+    
+    private DQLExclusiveExecutionDetector() {
+    }
+    
+    /**
+     * Judge whether SQL requires exclusive execution.
+     *
+     * @param sql SQL
+     * @return requires exclusive execution or not
+     */
+    public static boolean requiresExclusiveExecution(final String sql) {
+        String normalizedSQL = normalize(sql);
+        return DML_RETURNING_PATTERN.matcher(normalizedSQL).find() || 
containsLockingSelect(normalizedSQL);
+    }
+    
+    private static boolean containsLockingSelect(final String normalizedSQL) {
+        return SELECT_PATTERN.matcher(normalizedSQL).find()
+                && (FOR_UPDATE_PATTERN.matcher(normalizedSQL).find() || 
FOR_SHARE_PATTERN.matcher(normalizedSQL).find() || 
LOCK_IN_SHARE_MODE_PATTERN.matcher(normalizedSQL).find()
+                        || 
SQL_SERVER_LOCK_HINT_PATTERN.matcher(normalizedSQL).find() || 
KEEP_LOCKS_PATTERN.matcher(normalizedSQL).find()
+                        || WITH_LOCK_PATTERN.matcher(normalizedSQL).find());
+    }
+    
+    private static String normalize(final String sql) {
+        StringBuilder result = new StringBuilder(sql.length());
+        int i = 0;
+        while (i < sql.length()) {
+            char each = sql.charAt(i);
+            if ('\'' == each || '"' == each || '`' == each) {
+                result.append(' ');
+                i = skipQuotedIdentifier(sql, i, each);
+            } else if ('[' == each) {
+                result.append(' ');
+                i = skipSquareQuotedIdentifier(sql, i);
+            } else if ('-' == each && i + 1 < sql.length() && '-' == 
sql.charAt(i + 1)) {
+                result.append(' ');
+                i = skipLineComment(sql, i + 2);
+            } else if ('#' == each) {
+                result.append(' ');
+                i = skipLineComment(sql, i + 1);
+            } else if ('/' == each && i + 1 < sql.length() && '*' == 
sql.charAt(i + 1)) {
+                result.append(' ');
+                i = skipBlockComment(sql, i);
+            } else {
+                result.append(each);
+            }
+            i++;
+        }
+        return 
result.toString().toUpperCase(Locale.ENGLISH).replaceAll("\\s+", " ");
+    }
+    
+    private static int skipQuotedIdentifier(final String sql, final int 
startIndex, final char quoteCharacter) {
+        int i = startIndex + 1;
+        while (i < sql.length()) {
+            if ('\\' == sql.charAt(i)) {
+                i++;
+            } else if (quoteCharacter == sql.charAt(i)) {
+                if (i + 1 < sql.length() && quoteCharacter == sql.charAt(i + 
1)) {
+                    i++;
+                } else {
+                    return i;
+                }
+            }
+            i++;
+        }
+        return sql.length() - 1;
+    }
+    
+    private static int skipSquareQuotedIdentifier(final String sql, final int 
startIndex) {
+        int i = startIndex + 1;
+        while (i < sql.length()) {
+            if (']' == sql.charAt(i)) {
+                return i;
+            }
+            i++;
+        }
+        return sql.length() - 1;
+    }
+    
+    private static int skipLineComment(final String sql, final int startIndex) 
{
+        int i = startIndex;
+        while (i < sql.length()) {
+            if ('\n' == sql.charAt(i)) {
+                return i;
+            }
+            i++;
+        }
+        return sql.length() - 1;
+    }
+    
+    private static int skipBlockComment(final String sql, final int 
startIndex) {
+        int i = startIndex + 2;
+        while (i < sql.length() - 1) {
+            if ('*' == sql.charAt(i) && '/' == sql.charAt(i + 1)) {
+                return i + 1;
+            }
+            i++;
+        }
+        return sql.length() - 1;
+    }
+}
diff --git 
a/test/e2e/sql/src/test/java/org/apache/shardingsphere/test/e2e/sql/it/sql/dql/DQLExclusiveExecutionDetectorTest.java
 
b/test/e2e/sql/src/test/java/org/apache/shardingsphere/test/e2e/sql/it/sql/dql/DQLExclusiveExecutionDetectorTest.java
new file mode 100644
index 00000000000..34a82556244
--- /dev/null
+++ 
b/test/e2e/sql/src/test/java/org/apache/shardingsphere/test/e2e/sql/it/sql/dql/DQLExclusiveExecutionDetectorTest.java
@@ -0,0 +1,75 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.shardingsphere.test.e2e.sql.it.sql.dql;
+
+import org.junit.jupiter.params.ParameterizedTest;
+import org.junit.jupiter.params.provider.Arguments;
+import org.junit.jupiter.params.provider.MethodSource;
+
+import java.util.stream.Stream;
+
+import static org.junit.jupiter.api.Assertions.assertFalse;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+
+class DQLExclusiveExecutionDetectorTest {
+    
+    @ParameterizedTest(name = "{0}")
+    @MethodSource("exclusiveExecutionSQLProvider")
+    void assertRequiresExclusiveExecution(final String name, final String sql) 
{
+        
assertTrue(DQLExclusiveExecutionDetector.requiresExclusiveExecution(sql));
+    }
+    
+    private static Stream<Arguments> exclusiveExecutionSQLProvider() {
+        return Stream.of(
+                Arguments.of("For update", "SELECT * FROM t_order WHERE 
order_id = 1 FOR UPDATE"),
+                Arguments.of("For no key update", "SELECT * FROM t_order FOR 
NO KEY UPDATE"),
+                Arguments.of("For share", "SELECT * FROM t_order FOR SHARE"),
+                Arguments.of("For key share", "SELECT * FROM t_order FOR KEY 
SHARE"),
+                Arguments.of("Lock in share mode", "SELECT * FROM t_order LOCK 
IN SHARE MODE"),
+                Arguments.of("SQL Server updlock", "SELECT * FROM t_order WITH 
(UPDLOCK) WHERE order_id = 1"),
+                Arguments.of("SQL Server holdlock", "SELECT * FROM t_order 
WITH (ROWLOCK, HOLDLOCK)"),
+                Arguments.of("SQL Server xlock", "SELECT * FROM t_order WITH 
(XLOCK, TABLOCKX)"),
+                Arguments.of("DB2 keep update locks", "SELECT * FROM t_order 
WITH RS USE AND KEEP UPDATE LOCKS"),
+                Arguments.of("DB2 keep share locks", "SELECT * FROM t_order 
WITH CS USE AND KEEP SHARE LOCKS"),
+                Arguments.of("With lock", "SELECT * FROM t_order WITH LOCK"),
+                Arguments.of("Insert returning", "INSERT INTO 
t_with_generated_id (val) VALUES (?) RETURNING *, id, val aliased_val"),
+                Arguments.of("Update returning", "UPDATE t_order SET status = 
'OK' WHERE order_id = 1 RETURNING order_id"),
+                Arguments.of("Delete returning", "DELETE FROM t_order WHERE 
order_id = 1 RETURNING order_id"));
+    }
+    
+    @ParameterizedTest(name = "{0}")
+    @MethodSource("nonExclusiveExecutionSQLProvider")
+    void assertDoesNotRequireExclusiveExecution(final String name, final 
String sql) {
+        
assertFalse(DQLExclusiveExecutionDetector.requiresExclusiveExecution(sql));
+    }
+    
+    private static Stream<Arguments> nonExclusiveExecutionSQLProvider() {
+        return Stream.of(
+                Arguments.of("Plain select", "SELECT * FROM t_order"),
+                Arguments.of("For update literal", "SELECT 'FOR UPDATE' AS 
content FROM t_order"),
+                Arguments.of("For update comment", "SELECT * FROM t_order -- 
FOR UPDATE"),
+                Arguments.of("For update block comment", "SELECT * FROM 
t_order /* FOR UPDATE */"),
+                Arguments.of("For update quoted identifier", "SELECT \"FOR 
UPDATE\" FROM t_order"),
+                Arguments.of("Non select DML", "UPDATE t_order SET status = 
'FOR UPDATE' WHERE order_id = 1"),
+                Arguments.of("No lock hint", "SELECT * FROM t_order WITH 
(NOLOCK)"),
+                Arguments.of("For read only", "SELECT * FROM t_order FOR READ 
ONLY"),
+                Arguments.of("CTE named lock", "WITH lock AS (SELECT * FROM 
t_order) SELECT * FROM lock"),
+                Arguments.of("Returning literal", "SELECT 'INSERT INTO t_order 
RETURNING order_id' AS content FROM t_order"),
+                Arguments.of("Returning comment", "SELECT * FROM t_order -- 
INSERT INTO t_order RETURNING order_id"));
+    }
+}
diff --git 
a/test/e2e/sql/src/test/java/org/apache/shardingsphere/test/e2e/sql/it/sql/dql/GeneralDQLE2EIT.java
 
b/test/e2e/sql/src/test/java/org/apache/shardingsphere/test/e2e/sql/it/sql/dql/GeneralDQLE2EIT.java
index df2fe7d5f0c..cbb60ed12b3 100644
--- 
a/test/e2e/sql/src/test/java/org/apache/shardingsphere/test/e2e/sql/it/sql/dql/GeneralDQLE2EIT.java
+++ 
b/test/e2e/sql/src/test/java/org/apache/shardingsphere/test/e2e/sql/it/sql/dql/GeneralDQLE2EIT.java
@@ -26,6 +26,8 @@ import 
org.apache.shardingsphere.test.e2e.sql.framework.type.SQLCommandType;
 import org.apache.shardingsphere.test.e2e.sql.framework.type.SQLExecuteType;
 import org.apache.shardingsphere.test.e2e.sql.it.SQLE2EITContext;
 import org.junit.jupiter.api.condition.EnabledIf;
+import org.junit.jupiter.api.parallel.Execution;
+import org.junit.jupiter.api.parallel.ExecutionMode;
 import org.junit.jupiter.params.ParameterizedTest;
 import org.junit.jupiter.params.provider.ArgumentsSource;
 
@@ -43,6 +45,7 @@ import static org.junit.jupiter.api.Assertions.assertTrue;
 class GeneralDQLE2EIT extends BaseDQLE2EIT {
     
     @ParameterizedTest(name = "{0}")
+    @Execution(ExecutionMode.CONCURRENT)
     @EnabledIf("isEnabled")
     @ArgumentsSource(SQLE2EITArgumentsProvider.class)
     void assertExecuteQuery(final AssertionTestParameter testParam) throws 
SQLException, IOException, JAXBException {
@@ -51,8 +54,10 @@ class GeneralDQLE2EIT extends BaseDQLE2EIT {
             return;
         }
         SQLE2EITContext context = new SQLE2EITContext(testParam);
-        init(testParam, context);
-        assertExecuteQuery(testParam, context);
+        executeDQL(context, () -> {
+            init(testParam, context);
+            assertExecuteQuery(testParam, context);
+        });
     }
     
     private void assertExecuteQuery(final AssertionTestParameter testParam, 
final SQLE2EITContext context) throws SQLException {
@@ -144,6 +149,7 @@ class GeneralDQLE2EIT extends BaseDQLE2EIT {
     }
     
     @ParameterizedTest(name = "{0}")
+    @Execution(ExecutionMode.CONCURRENT)
     @EnabledIf("isEnabled")
     @ArgumentsSource(SQLE2EITArgumentsProvider.class)
     void assertExecute(final AssertionTestParameter testParam) throws 
SQLException, JAXBException, IOException {
@@ -152,8 +158,10 @@ class GeneralDQLE2EIT extends BaseDQLE2EIT {
             return;
         }
         SQLE2EITContext context = new SQLE2EITContext(testParam);
-        init(testParam, context);
-        assertExecute(testParam, context);
+        executeDQL(context, () -> {
+            init(testParam, context);
+            assertExecute(testParam, context);
+        });
     }
     
     private void assertExecute(final AssertionTestParameter testParam, final 
SQLE2EITContext context) throws SQLException {
diff --git a/test/e2e/sql/src/test/resources/junit-platform.properties 
b/test/e2e/sql/src/test/resources/junit-platform.properties
new file mode 100644
index 00000000000..5a10faf99c6
--- /dev/null
+++ b/test/e2e/sql/src/test/resources/junit-platform.properties
@@ -0,0 +1,22 @@
+#
+# 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.
+#
+
+junit.jupiter.execution.parallel.enabled=true
+junit.jupiter.execution.parallel.mode.default=same_thread
+junit.jupiter.execution.parallel.mode.classes.default=same_thread
+junit.jupiter.execution.parallel.config.strategy=fixed
+junit.jupiter.execution.parallel.config.fixed.parallelism=4

Reply via email to