diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml
index fe3cadc4595e5e25ebb3b413fef90a771839e8ad..d257cae269b0d179e6576777d82382abbbce2e6d 100644
--- a/.github/workflows/build.yml
+++ b/.github/workflows/build.yml
@@ -2,18 +2,19 @@ name: build
 
 on:
   push:
-    branches: [ develop ]
+    branches: [ develop,master ]
   pull_request:
-    branches: [ develop ]
+    branches: [ develop,master ]
 
 jobs:
   build:
 
-    runs-on: ubuntu-latest
+    runs-on: ${{ matrix.os }}
     strategy:
+      max-parallel: 1
       matrix:
         java: [8, 11]
-
+        os: [ ubuntu-18.04 ]
     steps:
       - uses: actions/checkout@v2
       - name: Set up JDK
@@ -33,9 +34,9 @@ jobs:
           REGISTRY_PASSWORD: ${{ secrets.REGISTRY_PASSWORD }}
         # https://docs.github.com/cn/free-pro-team@latest/actions/reference/context-and-expression-syntax-for-github-actions#github-context
         run: if [ "${{github.event_name}}" == "push" ] && [ "${{github.ref}}" == "refs/heads/develop" ]; then
-                mvn clean install -DskipTests=false -Dcheckstyle.skip=false -Dlicense.skip=false -P image -B -Dorg.slf4j.simpleLogger.log.org.apache.maven.cli.transfer.Slf4jMavenTransferListener=warn;
-             else
-                mvn clean install -DskipTests=false -Dcheckstyle.skip=false -Dlicense.skip=false -B -Dorg.slf4j.simpleLogger.log.org.apache.maven.cli.transfer.Slf4jMavenTransferListener=warn;
+            ./mvnw clean install -DskipTests=false -Dcheckstyle.skip=false -Dlicense.skip=false -P image -B -Dorg.slf4j.simpleLogger.log.org.apache.maven.cli.transfer.Slf4jMavenTransferListener=warn;
+            else
+             ./mvnw clean install -DskipTests=false -Dcheckstyle.skip=false -Dlicense.skip=false -B -Dorg.slf4j.simpleLogger.log.org.apache.maven.cli.transfer.Slf4jMavenTransferListener=warn;
              fi
       - name: Codecov
         uses: codecov/codecov-action@v1
\ No newline at end of file
diff --git a/rm-datasource/src/test/java/io/seata/rm/datasource/PreparedStatementProxyTest.java b/rm-datasource/src/test/java/io/seata/rm/datasource/PreparedStatementProxyTest.java
index 28352303ff05d3d06da5b39800fc4c0759f88b90..cd7c58cb9df31945de9f5627ff5da3d40787b49c 100644
--- a/rm-datasource/src/test/java/io/seata/rm/datasource/PreparedStatementProxyTest.java
+++ b/rm-datasource/src/test/java/io/seata/rm/datasource/PreparedStatementProxyTest.java
@@ -39,11 +39,18 @@ import com.alibaba.druid.mock.MockSQLXML;
 import com.alibaba.druid.pool.DruidDataSource;
 
 import com.google.common.collect.Lists;
+import io.seata.common.loader.EnhancedServiceLoader;
 import io.seata.rm.datasource.mock.MockBlob;
 import io.seata.rm.datasource.mock.MockClob;
 import io.seata.rm.datasource.mock.MockConnection;
 import io.seata.rm.datasource.mock.MockDriver;
+import io.seata.sqlparser.SQLRecognizerFactory;
+import io.seata.sqlparser.SqlParserType;
+import io.seata.sqlparser.druid.DruidDelegatingSQLRecognizerFactory;
+import io.seata.sqlparser.druid.SQLOperateRecognizerHolder;
+import io.seata.sqlparser.druid.SQLOperateRecognizerHolderFactory;
 import io.seata.sqlparser.struct.Null;
+import io.seata.sqlparser.util.JdbcConstants;
 import org.junit.jupiter.api.Assertions;
 import org.junit.jupiter.api.BeforeAll;
 import org.junit.jupiter.api.Test;
@@ -84,13 +91,17 @@ public class PreparedStatementProxyTest {
 
         ConnectionProxy connectionProxy = new ConnectionProxy(dataSourceProxy, dataSource.getConnection().getConnection());
 
-        String sql = "update from prepared_statement_proxy set name = ?";
+        String sql = "update prepared_statement_proxy set name = ?";
 
         PreparedStatement preparedStatement = mockDriver.createSeataMockPreparedStatement(
             (MockConnection)connectionProxy.getTargetConnection(), sql);
 
         preparedStatementProxy = new PreparedStatementProxy(connectionProxy, preparedStatement, sql);
         unusedConstructorPreparedStatementProxy = new TestUnusedConstructorPreparedStatementProxy(connectionProxy, preparedStatement);
+        EnhancedServiceLoader.load(SQLOperateRecognizerHolder.class, JdbcConstants.MYSQL,
+            SQLOperateRecognizerHolderFactory.class.getClassLoader());
+        DruidDelegatingSQLRecognizerFactory recognizerFactory = (DruidDelegatingSQLRecognizerFactory) EnhancedServiceLoader
+            .load(SQLRecognizerFactory.class, SqlParserType.SQL_PARSER_TYPE_DRUID);
     }
 
     @Test
diff --git a/rm-datasource/src/test/java/io/seata/rm/datasource/StatementProxyTest.java b/rm-datasource/src/test/java/io/seata/rm/datasource/StatementProxyTest.java
index 9e6684bfcf331d5e63a152d3a46b564db446c61b..4a9ff503513fc1268db12add22d2ad1417f3ed53 100644
--- a/rm-datasource/src/test/java/io/seata/rm/datasource/StatementProxyTest.java
+++ b/rm-datasource/src/test/java/io/seata/rm/datasource/StatementProxyTest.java
@@ -25,16 +25,27 @@ import com.alibaba.druid.mock.MockResultSet;
 import com.alibaba.druid.mock.MockStatement;
 import com.alibaba.druid.pool.DruidDataSource;
 import com.alibaba.druid.util.jdbc.ResultSetMetaDataBase;
+
 import com.google.common.collect.Lists;
+import io.seata.common.loader.EnhancedServiceLoader;
 import io.seata.rm.datasource.mock.MockConnection;
 import io.seata.rm.datasource.mock.MockDriver;
+import io.seata.sqlparser.SQLRecognizerFactory;
+import io.seata.sqlparser.SqlParserType;
+import io.seata.sqlparser.druid.DruidDelegatingSQLRecognizerFactory;
+import io.seata.sqlparser.druid.SQLOperateRecognizerHolder;
+import io.seata.sqlparser.druid.SQLOperateRecognizerHolderFactory;
+import io.seata.sqlparser.util.JdbcConstants;
 import org.junit.jupiter.api.Assertions;
 import org.junit.jupiter.api.BeforeAll;
+import org.junit.jupiter.api.MethodOrderer;
 import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.TestMethodOrder;
 
 /**
  * @author will
  */
+@TestMethodOrder(MethodOrderer.Alphanumeric.class)
 public class StatementProxyTest {
 
     private static List<String> returnValueColumnLabels = Lists.newArrayList("id", "name");
@@ -76,6 +87,10 @@ public class StatementProxyTest {
         ((MockStatement) statement).setGeneratedKeys(mockResultSet);
 
         statementProxy = new StatementProxy(connectionProxy, statement);
+        EnhancedServiceLoader.load(SQLOperateRecognizerHolder.class, JdbcConstants.MYSQL,
+            SQLOperateRecognizerHolderFactory.class.getClassLoader());
+        DruidDelegatingSQLRecognizerFactory recognizerFactory = (DruidDelegatingSQLRecognizerFactory) EnhancedServiceLoader
+            .load(SQLRecognizerFactory.class, SqlParserType.SQL_PARSER_TYPE_DRUID);
     }
 
     @Test
diff --git a/rm-datasource/src/test/java/io/seata/rm/datasource/exec/SelectForUpdateExecutorTest.java b/rm-datasource/src/test/java/io/seata/rm/datasource/exec/SelectForUpdateExecutorTest.java
index 87f30fd58f6920bcb715775a9191f361f8dda938..46d4ca0f75a5abb66ade648baa4416671bc5da08 100644
--- a/rm-datasource/src/test/java/io/seata/rm/datasource/exec/SelectForUpdateExecutorTest.java
+++ b/rm-datasource/src/test/java/io/seata/rm/datasource/exec/SelectForUpdateExecutorTest.java
@@ -50,6 +50,7 @@ public class SelectForUpdateExecutorTest {
 
     @BeforeAll
     public static void init() {
+        RootContext.unbind();
         List<String> returnValueColumnLabels = Lists.newArrayList("id", "name");
         Object[][] returnValue = new Object[][] {
             new Object[] {1, "Tom"},
diff --git a/rm-datasource/src/test/java/io/seata/rm/datasource/undo/mysql/MySQLUndoLogManagerTest.java b/rm-datasource/src/test/java/io/seata/rm/datasource/undo/mysql/MySQLUndoLogManagerTest.java
index c6a9f26613bd4a1b5e9a2ab79d0cad2c036fc45b..b0741ace6ae81b1bc55f731cce8b703d21a033a3 100644
--- a/rm-datasource/src/test/java/io/seata/rm/datasource/undo/mysql/MySQLUndoLogManagerTest.java
+++ b/rm-datasource/src/test/java/io/seata/rm/datasource/undo/mysql/MySQLUndoLogManagerTest.java
@@ -26,9 +26,10 @@ import java.util.Date;
 import java.util.List;
 
 import com.alibaba.druid.pool.DruidDataSource;
+
 import com.google.common.collect.Lists;
 import com.google.common.collect.Sets;
-import io.seata.core.compressor.CompressorType;
+import io.seata.common.loader.EnhancedServiceLoader;
 import io.seata.rm.datasource.ConnectionContext;
 import io.seata.rm.datasource.ConnectionProxy;
 import io.seata.rm.datasource.DataSourceProxy;
@@ -42,8 +43,15 @@ import io.seata.rm.datasource.undo.SQLUndoLog;
 import io.seata.rm.datasource.undo.UndoLogParser;
 import io.seata.rm.datasource.undo.UndoLogParserFactory;
 import io.seata.rm.datasource.undo.parser.JacksonUndoLogParser;
+import io.seata.sqlparser.SQLRecognizerFactory;
 import io.seata.sqlparser.SQLType;
+import io.seata.sqlparser.SqlParserType;
+import io.seata.sqlparser.druid.DruidDelegatingSQLRecognizerFactory;
+import io.seata.sqlparser.druid.SQLOperateRecognizerHolder;
+import io.seata.sqlparser.druid.SQLOperateRecognizerHolderFactory;
+import io.seata.sqlparser.util.JdbcConstants;
 import org.junit.jupiter.api.Assertions;
+import org.junit.jupiter.api.BeforeAll;
 import org.junit.jupiter.api.BeforeEach;
 import org.junit.jupiter.api.Test;
 
@@ -75,6 +83,14 @@ public class MySQLUndoLogManagerTest {
 
     private TableMeta tableMeta;
 
+    @BeforeAll
+    public static void setup(){
+        EnhancedServiceLoader.load(SQLOperateRecognizerHolder.class, JdbcConstants.MYSQL,
+            SQLOperateRecognizerHolderFactory.class.getClassLoader());
+        DruidDelegatingSQLRecognizerFactory recognizerFactory = (DruidDelegatingSQLRecognizerFactory) EnhancedServiceLoader
+            .load(SQLRecognizerFactory.class, SqlParserType.SQL_PARSER_TYPE_DRUID);
+    }
+
     @BeforeEach
     public void init() throws SQLException {
         MockDriver mockDriver = new MockDriver(returnValueColumnLabels, returnValue, columnMetas, indexMetas);
diff --git a/rm-datasource/src/test/java/io/seata/rm/xa/ConnectionProxyXATest.java b/rm-datasource/src/test/java/io/seata/rm/xa/ConnectionProxyXATest.java
index 8960a70023068b4300add11f670686179949331a..66eea05ac436574766f071a46ef4b96741e4d8a1 100644
--- a/rm-datasource/src/test/java/io/seata/rm/xa/ConnectionProxyXATest.java
+++ b/rm-datasource/src/test/java/io/seata/rm/xa/ConnectionProxyXATest.java
@@ -15,6 +15,7 @@
  */
 package io.seata.rm.xa;
 
+import io.seata.core.context.RootContext;
 import io.seata.core.model.BranchType;
 import io.seata.core.model.Resource;
 import io.seata.core.model.ResourceManager;
@@ -22,6 +23,7 @@ import io.seata.rm.BaseDataSourceResource;
 import io.seata.rm.DefaultResourceManager;
 import io.seata.rm.datasource.xa.ConnectionProxyXA;
 import io.seata.rm.datasource.xa.StatementProxyXA;
+import org.junit.jupiter.api.AfterAll;
 import org.junit.jupiter.api.Assertions;
 import org.junit.jupiter.api.Test;
 import org.mockito.Mockito;
@@ -203,4 +205,9 @@ public class ConnectionProxyXATest {
         Statement statement = connectionProxyXA.createStatement();
         Assertions.assertTrue(statement instanceof StatementProxyXA);
     }
+
+    @AfterAll
+    public static void tearDown(){
+        RootContext.unbind();
+    }
 }
diff --git a/rm-datasource/src/test/java/io/seata/rm/xa/DataSourceProxyXATest.java b/rm-datasource/src/test/java/io/seata/rm/xa/DataSourceProxyXATest.java
index bc6af43bd9146ab9b47531b6837882284efa5332..f0d83c1682074056cc328e00dbe2d564160b8e27 100644
--- a/rm-datasource/src/test/java/io/seata/rm/xa/DataSourceProxyXATest.java
+++ b/rm-datasource/src/test/java/io/seata/rm/xa/DataSourceProxyXATest.java
@@ -22,6 +22,7 @@ import io.seata.core.context.RootContext;
 import io.seata.rm.datasource.mock.MockDataSource;
 import io.seata.rm.datasource.xa.ConnectionProxyXA;
 import io.seata.rm.datasource.xa.DataSourceProxyXA;
+import org.junit.jupiter.api.AfterAll;
 import org.junit.jupiter.api.Assertions;
 import org.junit.jupiter.api.Test;
 import org.mockito.Mockito;
@@ -86,4 +87,9 @@ public class DataSourceProxyXATest {
         Connection connectionInXA = xaConnection.getConnection();
         Assertions.assertTrue(connectionInXA instanceof JDBC4ConnectionWrapper);
     }
+
+    @AfterAll
+    public static void tearDown(){
+        RootContext.unbind();
+    }
 }
diff --git a/rm-datasource/src/test/resources/META-INF/services/io.seata.sqlparser.SQLRecognizerFactory b/rm-datasource/src/test/resources/META-INF/services/io.seata.sqlparser.SQLRecognizerFactory
new file mode 100644
index 0000000000000000000000000000000000000000..f4b2f829968434de12833097eb9039c3a80f4dca
--- /dev/null
+++ b/rm-datasource/src/test/resources/META-INF/services/io.seata.sqlparser.SQLRecognizerFactory
@@ -0,0 +1,17 @@
+#
+#  Copyright 1999-2019 Seata.io Group.
+#
+#  Licensed 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.
+#
+
+io.seata.sqlparser.druid.DruidDelegatingSQLRecognizerFactory
diff --git a/rm-datasource/src/test/resources/META-INF/services/io.seata.sqlparser.druid.SQLOperateRecognizerHolder b/rm-datasource/src/test/resources/META-INF/services/io.seata.sqlparser.druid.SQLOperateRecognizerHolder
new file mode 100644
index 0000000000000000000000000000000000000000..e7a870fdf3197ebc81881cddd467b0bbf5078883
--- /dev/null
+++ b/rm-datasource/src/test/resources/META-INF/services/io.seata.sqlparser.druid.SQLOperateRecognizerHolder
@@ -0,0 +1,19 @@
+#
+#  Copyright 1999-2019 Seata.io Group.
+#
+#  Licensed 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.
+#
+
+io.seata.sqlparser.druid.mysql.MySQLOperateRecognizerHolder
+io.seata.sqlparser.druid.oracle.OracleOperateRecognizerHolder
+io.seata.sqlparser.druid.postgresql.PostgresqlOperateRecognizerHolder
\ No newline at end of file
diff --git a/rm-datasource/src/test/resources/META-INF/services/io.seata.sqlparser.util.DbTypeParser b/rm-datasource/src/test/resources/META-INF/services/io.seata.sqlparser.util.DbTypeParser
new file mode 100644
index 0000000000000000000000000000000000000000..983f0c35c929ddc8bc771b0404251c220316ec49
--- /dev/null
+++ b/rm-datasource/src/test/resources/META-INF/services/io.seata.sqlparser.util.DbTypeParser
@@ -0,0 +1,17 @@
+#
+#  Copyright 1999-2019 Seata.io Group.
+#
+#  Licensed 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.
+#
+
+io.seata.sqlparser.druid.DruidDelegatingDbTypeParser