You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@rocketmq.apache.org by zh...@apache.org on 2022/05/18 07:25:45 UTC

[rocketmq-connect] branch master updated: Upgrade rocketmq connect jdbc (#139)

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

zhoubo pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/rocketmq-connect.git


The following commit(s) were added to refs/heads/master by this push:
     new 8a72f1c  Upgrade rocketmq connect jdbc (#139)
8a72f1c is described below

commit 8a72f1c4038ad9042a0e09aa9bbdd386ebf081d9
Author: xiaoyi <su...@163.com>
AuthorDate: Wed May 18 15:25:39 2022 +0800

    Upgrade rocketmq connect jdbc (#139)
    
    * Upgrade rocketmq connect JDBC and be compatible with openmldb JDBC write mode
    
    * add OpenMLDBDatabaseDialect
    
    * Upgrade rocketmq connect API to increase scalability; Add support for openmldb #114
    
    * Update README.md
    
    * delete author information
    
    * remove deprecated method
    
    * reference package expand
    
    * finish debugging openmldb compatibility
    
    * fixed
    
    * reformat code and Check by checkstyle
    
    Co-authored-by: “sunxiaojian” <“sunxiaojian926@163.com”>
---
 connectors/rocketmq-connect-jdbc/README.md         |   37 +-
 connectors/rocketmq-connect-jdbc/pom.xml           |   74 +-
 .../rocketmq/connect/jdbc/common/Dialect.java      |    3 +
 .../jdbc/common/JdbcSourceConfigConstants.java     |   26 +-
 .../connect/jdbc/config/AbstractConfig.java        |   26 +-
 .../rocketmq/connect/jdbc/config/ConfigDef.java    |   33 +-
 .../connect/jdbc/connector/JdbcSinkConfig.java     |   45 +-
 .../connect/jdbc/connector/JdbcSinkConnector.java  |   16 +-
 .../connect/jdbc/connector/JdbcSinkTask.java       |   11 +-
 .../connect/jdbc/connector/JdbcSourceConfig.java   |   45 +-
 .../jdbc/connector/JdbcSourceConnector.java        |   22 +-
 .../connect/jdbc/connector/JdbcSourceTask.java     |   39 +-
 .../jdbc/connector/JdbcSourceTaskConfig.java       |   22 +-
 .../connect/jdbc/dialect/DatabaseDialect.java      |  548 +--
 .../jdbc/dialect/DatabaseDialectFactory.java       |  269 +-
 .../rocketmq/connect/jdbc/dialect/DropOptions.java |  134 +-
 .../jdbc/dialect/PreparedStatementBinder.java      |  156 +-
 .../jdbc/dialect/impl/GenericDatabaseDialect.java  | 3491 ++++++++++----------
 .../jdbc/dialect/impl/MySqlDatabaseDialect.java    |  194 +-
 .../jdbc/dialect/impl/OpenMLDBDatabaseDialect.java |   55 +-
 .../dialect/provider/CachedConnectionProvider.java |  162 +-
 .../jdbc/dialect/provider/ConnectionProvider.java  |   71 +-
 .../dialect/provider/DatabaseDialectProvider.java  |   56 +-
 .../connect/jdbc/dialect/provider/JdbcUrlInfo.java |    2 +-
 .../connect/jdbc/exception/ConfigException.java    |    2 +-
 .../exception/TableAlterOrCreateException.java     |   12 +-
 .../jdbc/schema/column/ColumnDefAdjuster.java      |   12 +-
 .../jdbc/schema/column/ColumnDefinition.java       |  560 ++--
 .../connect/jdbc/schema/column/ColumnId.java       |  126 +-
 .../schema/column/parser/DateColumnParser.java     |    4 +-
 .../schema/column/parser/TimeColumnParser.java     |    8 +-
 .../connect/jdbc/schema/db/DbStructure.java        |  469 +--
 .../connect/jdbc/schema/table/TableDefinition.java |  156 +-
 .../jdbc/schema/table/TableDefinitions.java        |  106 +-
 .../connect/jdbc/schema/table/TableId.java         |  204 +-
 .../connect/jdbc/sink/BufferedRecords.java         |  531 +--
 .../apache/rocketmq/connect/jdbc/sink/Updater.java |  114 +-
 .../connect/jdbc/sink/metadata/FieldsMetadata.java |  362 +-
 .../connect/jdbc/sink/metadata/SchemaPair.java     |   64 +-
 .../jdbc/sink/metadata/SinkRecordField.java        |  114 +-
 .../jdbc/source/TimestampIncrementingCriteria.java |  546 +--
 .../jdbc/source/metadata/ColumnMapping.java        |  122 +-
 .../jdbc/source/metadata/SchemaMapping.java        |  199 +-
 .../jdbc/source/offset/SourceOffsetCompute.java    |  200 +-
 .../source/offset/TimestampIncrementingOffset.java |  140 +-
 .../connect/jdbc/source/querier/BulkQuerier.java   |  174 +-
 .../connect/jdbc/source/querier/Querier.java       |  250 +-
 .../querier/TimestampIncrementingQuerier.java      |  340 +-
 .../rocketmq/connect/jdbc/util/BytesUtil.java      |   17 +-
 .../connect/jdbc/util/ConnectorGroupUtils.java     |    4 +-
 .../rocketmq/connect/jdbc/util/DateTimeUtils.java  |   68 +-
 .../connect/jdbc/util/ExpressionBuilder.java       | 1061 +++---
 .../connect/jdbc/util/IdentifierRules.java         |  294 +-
 .../rocketmq/connect/jdbc/util/JdbcDriverInfo.java |  202 +-
 .../rocketmq/connect/jdbc/util/NumericMapping.java |   29 +-
 .../rocketmq/connect/jdbc/util/QuoteMethod.java    |   34 +-
 .../rocketmq/connect/jdbc/util/TableType.java      |   77 +-
 .../dialect/DatabaseDialectFactoryTest.java        |    5 +-
 .../jdbc/connector/sink/OpenMLDBJdbcSinkTest.java  |  141 +
 .../{ => source}/JdbcSourceConnectorTest.java      |    2 +-
 .../jdbc/connector/source/JdbcSourceTaskTest.java  |   15 +-
 61 files changed, 6306 insertions(+), 5995 deletions(-)

diff --git a/connectors/rocketmq-connect-jdbc/README.md b/connectors/rocketmq-connect-jdbc/README.md
index fb8b8f5..cf7ca7b 100644
--- a/connectors/rocketmq-connect-jdbc/README.md
+++ b/connectors/rocketmq-connect-jdbc/README.md
@@ -1,8 +1,11 @@
 # rocketmq-connect-jdbc
+
 ```  
 注: 目前支持的数据库类型为 mysql ,openMLDB ,其它数据库的jdbc模式持续扩展中
 ```  
+
 ## rocketmq-connect-jdbc 打包
+
 ```
 mvn clean package -Dmaven.test.skip=true
 ```
@@ -10,6 +13,7 @@ mvn clean package -Dmaven.test.skip=true
 ## rocketmq-connect-jdbc 启动
 
 * **jdbc-source-connector** 启动
+
 ```
 POST  http://${runtime-ip}:${runtime-port}/connectors/${rocketmq-jdbc-source-connector-name}
 {
@@ -26,7 +30,6 @@ POST  http://${runtime-ip}:${runtime-port}/connectors/${rocketmq-jdbc-source-con
 }
 ```
 
-
 * **jdbc-sink-connector** 启动
 
 ```
@@ -45,7 +48,7 @@ POST  http://${runtime-ip}:${runtime-port}/connectors/${rocketmq-jdbc-sink-conne
 }
 ```
 
->**注:** `rocketmq-jdbc-connect` 的启动依赖于`rocketmq-connect-runtime`项目的启动,需将打好的所有`jar`包放置到`runtime`项目中`pluginPaths`配置的路径后再执行上面的启动请求,该值配置在`runtime`项目下的`connect.conf`文件中
+> **注:** `rocketmq-jdbc-connect` 的启动依赖于`rocketmq-connect-runtime`项目的启动,需将打好的所有`jar`包放置到`runtime`项目中`pluginPaths`配置的路径后再执行上面的启动请求,该值配置在`runtime`项目下的`connect.conf`文件中
 
 ## rocketmq-connect-jdbc 停止
 
@@ -54,8 +57,8 @@ http://${runtime-ip}:${runtime-port}/connectors/${rocketmq-jdbc-connector-name}/
 ```
 
 ## rocketmq-connect-jdbc 参数说明
-* **jdbc-source-connector 参数说明**
 
+* **jdbc-source-connector 参数说明**
 
 |         KEY                 |  TYPE   | Must be filled | Description| Example
 |------------------------|----|---------|---------------|------------------|
@@ -72,12 +75,13 @@ http://${runtime-ip}:${runtime-port}/connectors/${rocketmq-jdbc-connector-name}/
 |table.whitelist              | String  | YES           |需要扫描的表 | db.table,db.table01 |
 |max-task                     | Integer | YES           |任务数量,最大不能大于表的数量 | 2 |
 |source-record-converter      | Integer | YES           |data转换器  | org.apache.rocketmq.connect.runtime.converter.JsonConverter |
+
 ```  
 注:1.source拉取的数据写入到以表名自动创建的topic中,如果需要写入特定的topic中则需要指定"connect-topicname" 参数
    2.topic.prefix参数可以为自动创建的topic增加前缀,用来进行逻辑的隔离
 ```  
-* **jdbc-sink-connector 参数说明**
 
+* **jdbc-sink-connector 参数说明**
 
 |         KEY                 |  TYPE   | Must be filled | Description| Example
 |------------------------|----|---------|---------------|------------------|
@@ -94,3 +98,28 @@ http://${runtime-ip}:${runtime-port}/connectors/${rocketmq-jdbc-connector-name}/
 |source-record-converter      | Integer | YES          |data转换器  | org.apache.rocketmq.connect.runtime.converter.JsonConverter |
 
 ```  
+注: openMLDB maven包的引入:
+---------MacOS 系统下运行-------------
+     <dependency>
+            <groupId>com.4paradigm.openmldb</groupId>
+            <artifactId>openmldb-native</artifactId>
+            <version>0.5.0-macos</version>
+        </dependency>
+        <dependency>
+            <groupId>com.4paradigm.openmldb</groupId>
+            <artifactId>openmldb-jdbc</artifactId>
+            <version>0.5.0</version>
+            <exclusions>
+                <exclusion>
+                    <groupId>com.4paradigm.openmldb</groupId>
+                    <artifactId>openmldb-native</artifactId>
+                </exclusion>
+            </exclusions>
+        </dependency>
+---------Linux 系统下运行-------------
+        <dependency>
+            <groupId>com.4paradigm.openmldb</groupId>
+            <artifactId>openmldb-jdbc</artifactId>
+            <version>0.5.0</version>
+        </dependency>
+```
diff --git a/connectors/rocketmq-connect-jdbc/pom.xml b/connectors/rocketmq-connect-jdbc/pom.xml
index 651f5a7..8abed39 100644
--- a/connectors/rocketmq-connect-jdbc/pom.xml
+++ b/connectors/rocketmq-connect-jdbc/pom.xml
@@ -44,7 +44,6 @@
     </properties>
 
 
-
     <build>
         <resources>
             <resource>
@@ -162,6 +161,27 @@
                     </execution>
                 </executions>
             </plugin>
+            <plugin>
+                <artifactId>maven-checkstyle-plugin</artifactId>
+                <version>2.17</version>
+                <executions>
+                    <execution>
+                        <id>verify</id>
+                        <phase>verify</phase>
+                        <configuration>
+                            <configLocation>../../style/rmq_checkstyle.xml</configLocation>
+                            <encoding>UTF-8</encoding>
+                            <consoleOutput>true</consoleOutput>
+                            <failsOnError>true</failsOnError>
+                            <includeTestSourceDirectory>false</includeTestSourceDirectory>
+                            <includeTestResources>false</includeTestResources>
+                        </configuration>
+                        <goals>
+                            <goal>check</goal>
+                        </goals>
+                    </execution>
+                </executions>
+            </plugin>
         </plugins>
     </build>
 
@@ -185,21 +205,21 @@
             <scope>test</scope>
         </dependency>
         <dependency>
-    		<groupId>commons-codec</groupId>
-    		<artifactId>commons-codec</artifactId>
-    		<version>1.12</version>
-		</dependency>
+            <groupId>commons-codec</groupId>
+            <artifactId>commons-codec</artifactId>
+            <version>1.12</version>
+        </dependency>
         <dependency>
             <groupId>io.openmessaging</groupId>
             <artifactId>openmessaging-connector</artifactId>
             <version>0.1.2</version>
             <scope>provided</scope>
         </dependency>
-		<dependency>
-			<groupId>io.openmessaging</groupId>
-			<artifactId>openmessaging-api</artifactId>
-			<version>0.3.1-alpha</version>
-		</dependency>
+        <dependency>
+            <groupId>io.openmessaging</groupId>
+            <artifactId>openmessaging-api</artifactId>
+            <version>0.3.1-alpha</version>
+        </dependency>
         <dependency>
             <groupId>com.alibaba</groupId>
             <artifactId>fastjson</artifactId>
@@ -246,16 +266,16 @@
             <artifactId>commons-cli</artifactId>
             <version>1.2</version>
         </dependency>
-		<dependency>
-			<groupId>mysql</groupId>
-			<artifactId>mysql-connector-java</artifactId>
-			<version>8.0.16</version>
-		</dependency>
-		<dependency>
-			<groupId>io.javalin</groupId>
-			<artifactId>javalin</artifactId>
-			<version>1.3.0</version>
-		</dependency>
+        <dependency>
+            <groupId>mysql</groupId>
+            <artifactId>mysql-connector-java</artifactId>
+            <version>8.0.16</version>
+        </dependency>
+        <dependency>
+            <groupId>io.javalin</groupId>
+            <artifactId>javalin</artifactId>
+            <version>1.3.0</version>
+        </dependency>
 
         <dependency>
             <groupId>com.github.shyiko</groupId>
@@ -271,13 +291,19 @@
 
         <dependency>
             <groupId>com.4paradigm.openmldb</groupId>
-            <artifactId>openmldb-jdbc</artifactId>
-            <version>0.4.4-hotfix1</version>
+            <artifactId>openmldb-native</artifactId>
+            <version>0.5.0-macos</version>
         </dependency>
         <dependency>
             <groupId>com.4paradigm.openmldb</groupId>
-            <artifactId>openmldb-native</artifactId>
-            <version>0.4.4-hotfix1</version>
+            <artifactId>openmldb-jdbc</artifactId>
+            <version>0.5.0</version>
+            <exclusions>
+                <exclusion>
+                    <groupId>com.4paradigm.openmldb</groupId>
+                    <artifactId>openmldb-native</artifactId>
+                </exclusion>
+            </exclusions>
         </dependency>
     </dependencies>
 </project>
diff --git a/connectors/rocketmq-connect-jdbc/src/main/java/org/apache/rocketmq/connect/jdbc/common/Dialect.java b/connectors/rocketmq-connect-jdbc/src/main/java/org/apache/rocketmq/connect/jdbc/common/Dialect.java
index c164462..f6f7dd2 100644
--- a/connectors/rocketmq-connect-jdbc/src/main/java/org/apache/rocketmq/connect/jdbc/common/Dialect.java
+++ b/connectors/rocketmq-connect-jdbc/src/main/java/org/apache/rocketmq/connect/jdbc/common/Dialect.java
@@ -14,6 +14,7 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
+
 package org.apache.rocketmq.connect.jdbc.common;
 
 import java.lang.annotation.ElementType;
@@ -29,12 +30,14 @@ import java.lang.annotation.Target;
 public @interface Dialect {
     /**
      * dialect name
+     *
      * @return
      */
     String name() default "";
 
     /**
      * support protocol
+     *
      * @return
      */
     String protocol() default "";
diff --git a/connectors/rocketmq-connect-jdbc/src/main/java/org/apache/rocketmq/connect/jdbc/common/JdbcSourceConfigConstants.java b/connectors/rocketmq-connect-jdbc/src/main/java/org/apache/rocketmq/connect/jdbc/common/JdbcSourceConfigConstants.java
index fbe00ec..3b1039b 100644
--- a/connectors/rocketmq-connect-jdbc/src/main/java/org/apache/rocketmq/connect/jdbc/common/JdbcSourceConfigConstants.java
+++ b/connectors/rocketmq-connect-jdbc/src/main/java/org/apache/rocketmq/connect/jdbc/common/JdbcSourceConfigConstants.java
@@ -19,21 +19,21 @@ package org.apache.rocketmq.connect.jdbc.common;
 import org.apache.commons.lang3.StringUtils;
 
 public class JdbcSourceConfigConstants {
-  private static final String TABLE_NAME_KEY_PREFIX = "table";
-  private static final String QUERY_NAME_KEY_PREFIX = "query";
-  public static final String QUERY_NAME_VALUE = "query";
+    private static final String TABLE_NAME_KEY_PREFIX = "table";
+    private static final String QUERY_NAME_KEY_PREFIX = "query";
+    public static final String QUERY_NAME_VALUE = "query";
 
-  public static String TABLE_NAME_KEY(String suffix){
-    if(StringUtils.isEmpty(suffix)){
-      return TABLE_NAME_KEY_PREFIX;
+    public static String TABLE_NAME_KEY(String suffix) {
+        if (StringUtils.isEmpty(suffix)) {
+            return TABLE_NAME_KEY_PREFIX;
+        }
+        return TABLE_NAME_KEY_PREFIX + "-" + suffix;
     }
-    return TABLE_NAME_KEY_PREFIX + "-" + suffix;
-  }
 
-  public static String QUERY_NAME_KEY(String suffix){
-    if(StringUtils.isEmpty(suffix)){
-      return QUERY_NAME_KEY_PREFIX;
+    public static String QUERY_NAME_KEY(String suffix) {
+        if (StringUtils.isEmpty(suffix)) {
+            return QUERY_NAME_KEY_PREFIX;
+        }
+        return QUERY_NAME_KEY_PREFIX + "-" + suffix;
     }
-    return QUERY_NAME_KEY_PREFIX + "-" + suffix;
-  }
 }
diff --git a/connectors/rocketmq-connect-jdbc/src/main/java/org/apache/rocketmq/connect/jdbc/config/AbstractConfig.java b/connectors/rocketmq-connect-jdbc/src/main/java/org/apache/rocketmq/connect/jdbc/config/AbstractConfig.java
index 6daf89e..e4fd758 100644
--- a/connectors/rocketmq-connect-jdbc/src/main/java/org/apache/rocketmq/connect/jdbc/config/AbstractConfig.java
+++ b/connectors/rocketmq-connect-jdbc/src/main/java/org/apache/rocketmq/connect/jdbc/config/AbstractConfig.java
@@ -66,13 +66,13 @@ public abstract class AbstractConfig {
     private Long backoffMs;
     private String quoteSqlIdentifiers;
 
-    public AbstractConfig(KeyValue config){
-        connectionDbUrl=config.getString(CONNECTION_URL_CONFIG);
-        connectionDbUser=config.getString(CONNECTION_USER_CONFIG);
-        connectionDbPassword=config.getString(CONNECTION_PASSWORD_CONFIG);
-        attempts=config.getInt(CONNECTION_ATTEMPTS_CONFIG,CONNECTION_ATTEMPTS_DEFAULT);
-        backoffMs=config.getLong(CONNECTION_BACKOFF_CONFIG,CONNECTION_BACKOFF_DEFAULT);
-        quoteSqlIdentifiers=config.getString(QUOTE_SQL_IDENTIFIERS_CONFIG,QUOTE_SQL_IDENTIFIERS_DEFAULT);
+    public AbstractConfig(KeyValue config) {
+        connectionDbUrl = config.getString(CONNECTION_URL_CONFIG);
+        connectionDbUser = config.getString(CONNECTION_USER_CONFIG);
+        connectionDbPassword = config.getString(CONNECTION_PASSWORD_CONFIG);
+        attempts = config.getInt(CONNECTION_ATTEMPTS_CONFIG, CONNECTION_ATTEMPTS_DEFAULT);
+        backoffMs = config.getLong(CONNECTION_BACKOFF_CONFIG, CONNECTION_BACKOFF_DEFAULT);
+        quoteSqlIdentifiers = config.getString(QUOTE_SQL_IDENTIFIERS_CONFIG, QUOTE_SQL_IDENTIFIERS_DEFAULT);
     }
 
 
@@ -102,12 +102,13 @@ public abstract class AbstractConfig {
 
     /**
      * get list
+     *
      * @param config
      * @param key
      * @return
      */
-    protected List<String> getList(KeyValue config, String key){
-        if (!config.containsKey(key) || Objects.isNull(config.getString(key))){
+    protected List<String> getList(KeyValue config, String key) {
+        if (!config.containsKey(key) || Objects.isNull(config.getString(key))) {
             return Lists.newArrayList();
         }
         return Arrays.asList(COMMA_WITH_WHITESPACE.split(config.getString(key), -1));
@@ -115,18 +116,19 @@ public abstract class AbstractConfig {
 
     /**
      * get list
+     *
      * @param config
      * @param key
      * @return
      */
-    protected List<String> getList(KeyValue config, String key,String defaultValue){
-        if (config.containsKey(key) ||Objects.isNull(config.getString(key))){
+    protected List<String> getList(KeyValue config, String key, String defaultValue) {
+        if (config.containsKey(key) || Objects.isNull(config.getString(key))) {
             return Lists.newArrayList(defaultValue);
         }
         return Arrays.asList(COMMA_WITH_WHITESPACE.split(config.getString(key), -1));
     }
 
-    protected Boolean getBoolean(KeyValue config, String key,Boolean defaultValue){
+    protected Boolean getBoolean(KeyValue config, String key, Boolean defaultValue) {
         return config.containsKey(key) ? Boolean.getBoolean(config.getString(key)) : defaultValue;
     }
 
diff --git a/connectors/rocketmq-connect-jdbc/src/main/java/org/apache/rocketmq/connect/jdbc/config/ConfigDef.java b/connectors/rocketmq-connect-jdbc/src/main/java/org/apache/rocketmq/connect/jdbc/config/ConfigDef.java
index c54ce11..504e225 100644
--- a/connectors/rocketmq-connect-jdbc/src/main/java/org/apache/rocketmq/connect/jdbc/config/ConfigDef.java
+++ b/connectors/rocketmq-connect-jdbc/src/main/java/org/apache/rocketmq/connect/jdbc/config/ConfigDef.java
@@ -45,6 +45,7 @@ public class ConfigDef {
     public static final Object NO_DEFAULT_VALUE = new Object();
 
     private final Map<String, ConfigKey> configKeys;
+
     public ConfigDef() {
         configKeys = new LinkedHashMap<>();
     }
@@ -59,7 +60,7 @@ public class ConfigDef {
     }
 
     /**
-     * 不验证
+     * no validate
      * @param name
      * @param type
      * @param defaultValue
@@ -67,12 +68,12 @@ public class ConfigDef {
      * @param documentation
      * @return
      */
-    public ConfigDef define(String name, Type type, Object defaultValue, Validator validator,  String documentation) {
+    public ConfigDef define(String name, Type type, Object defaultValue, Validator validator, String documentation) {
         return define(new ConfigKey(name, type, defaultValue, validator, documentation));
     }
 
     /**
-     * 加验证
+     * validate config
      * @param name
      * @param type
      * @param defaultValue
@@ -86,6 +87,7 @@ public class ConfigDef {
 
     /**
      * Get the configuration keys
+     *
      * @return a map containing all configuration keys
      */
     public Map<String, ConfigKey> configKeys() {
@@ -94,6 +96,7 @@ public class ConfigDef {
 
     /**
      * parse props
+     *
      * @param props
      * @return
      */
@@ -108,6 +111,7 @@ public class ConfigDef {
 
     /**
      * convert + validate
+     *
      * @param key
      * @param value
      * @param isSet
@@ -130,9 +134,9 @@ public class ConfigDef {
     }
 
 
-
     /**
      * Parse a value according to its expected type.
+     *
      * @param name  The config name
      * @param value The config value
      * @param type  The expected type
@@ -260,16 +264,14 @@ public class ConfigDef {
     }
 
 
-
-
-
     /**
      * Validation logic the user may provide to perform single configuration validation.
      */
     public interface Validator {
         /**
          * Perform single configuration validation.
-         * @param name The name of the configuration
+         *
+         * @param name  The name of the configuration
          * @param value The value of the configuration
          * @throws ConfigException if the value is invalid.
          */
@@ -282,17 +284,21 @@ public class ConfigDef {
     public static class Range implements Validator {
         private final Number min;
         private final Number max;
+
         /**
-         *  A numeric range with inclusive upper bound and inclusive lower bound
-         * @param min  the lower bound
-         * @param max  the upper bound
+         * A numeric range with inclusive upper bound and inclusive lower bound
+         *
+         * @param min the lower bound
+         * @param max the upper bound
          */
         private Range(Number min, Number max) {
             this.min = min;
             this.max = max;
         }
+
         /**
          * A numeric range that checks only the lower bound
+         *
          * @param min The minimum acceptable value
          */
         public static Range atLeast(Number min) {
@@ -469,7 +475,7 @@ public class ConfigDef {
 
         @Override
         public void ensureValid(String name, Object value) {
-            for (Validator validator: validators) {
+            for (Validator validator : validators) {
                 validator.ensureValid(name, value);
             }
         }
@@ -480,7 +486,7 @@ public class ConfigDef {
                 return "";
             }
             StringBuilder desc = new StringBuilder();
-            for (Validator v: validators) {
+            for (Validator v : validators) {
                 if (desc.length() > 0) {
                     desc.append(',').append(' ');
                 }
@@ -511,6 +517,7 @@ public class ConfigDef {
         public static NonEmptyStringWithoutControlChars nonEmptyStringWithoutControlChars() {
             return new NonEmptyStringWithoutControlChars();
         }
+
         @Override
         public void ensureValid(String name, Object value) {
             String s = (String) value;
diff --git a/connectors/rocketmq-connect-jdbc/src/main/java/org/apache/rocketmq/connect/jdbc/connector/JdbcSinkConfig.java b/connectors/rocketmq-connect-jdbc/src/main/java/org/apache/rocketmq/connect/jdbc/connector/JdbcSinkConfig.java
index d211a23..7c1f0b3 100644
--- a/connectors/rocketmq-connect-jdbc/src/main/java/org/apache/rocketmq/connect/jdbc/connector/JdbcSinkConfig.java
+++ b/connectors/rocketmq-connect-jdbc/src/main/java/org/apache/rocketmq/connect/jdbc/connector/JdbcSinkConfig.java
@@ -72,7 +72,7 @@ public class JdbcSinkConfig extends AbstractConfig {
     private static final int RETRY_BACKOFF_MS_DEFAULT = 3000;
 
     public static final String BATCH_SIZE = "batch.size";
-    private static final int BATCH_SIZE_DEFAULT = 3000;
+    private static final int BATCH_SIZE_DEFAULT = 100;
 
 
     public static final String DELETE_ENABLED = "delete.enabled";
@@ -184,27 +184,27 @@ public class JdbcSinkConfig extends AbstractConfig {
 
     public JdbcSinkConfig(KeyValue config) {
         super(config);
-        tableNameFormat = config.getString(TABLE_NAME_FORMAT,TABLE_NAME_FORMAT_DEFAULT).trim();
-        tableFromHeader=getBoolean(config,TABLE_NAME_FROM_HEADER,false);
-        batchSize = config.getInt(BATCH_SIZE);
-        deleteEnabled = getBoolean(config,DELETE_ENABLED,DELETE_ENABLED_DEFAULT);
-        maxRetries = config.getInt(MAX_RETRIES,MAX_RETRIES_DEFAULT);
-        retryBackoffMs =config.getInt(RETRY_BACKOFF_MS,RETRY_BACKOFF_MS_DEFAULT);
-        autoCreate = getBoolean(config,AUTO_CREATE,AUTO_CREATE_DEFAULT);
-        autoEvolve = getBoolean(config,AUTO_EVOLVE,AUTO_EVOLVE_DEFAULT);
-        if (Objects.nonNull(config.getString(INSERT_MODE))){
-            insertMode = InsertMode.valueOf(config.getString(INSERT_MODE,INSERT_MODE_DEFAULT).toUpperCase());
+        tableNameFormat = config.getString(TABLE_NAME_FORMAT, TABLE_NAME_FORMAT_DEFAULT).trim();
+        tableFromHeader = getBoolean(config, TABLE_NAME_FROM_HEADER, false);
+        batchSize = config.getInt(BATCH_SIZE, BATCH_SIZE_DEFAULT);
+        deleteEnabled = getBoolean(config, DELETE_ENABLED, DELETE_ENABLED_DEFAULT);
+        maxRetries = config.getInt(MAX_RETRIES, MAX_RETRIES_DEFAULT);
+        retryBackoffMs = config.getInt(RETRY_BACKOFF_MS, RETRY_BACKOFF_MS_DEFAULT);
+        autoCreate = getBoolean(config, AUTO_CREATE, AUTO_CREATE_DEFAULT);
+        autoEvolve = getBoolean(config, AUTO_EVOLVE, AUTO_EVOLVE_DEFAULT);
+        if (Objects.nonNull(config.getString(INSERT_MODE))) {
+            insertMode = InsertMode.valueOf(config.getString(INSERT_MODE, INSERT_MODE_DEFAULT).toUpperCase());
         }
 
-        pkMode = PrimaryKeyMode.valueOf(config.getString(PK_MODE,PK_MODE_DEFAULT).toUpperCase());
-        pkFields = getList(config,PK_FIELDS);
+        pkMode = PrimaryKeyMode.valueOf(config.getString(PK_MODE, PK_MODE_DEFAULT).toUpperCase());
+        pkFields = getList(config, PK_FIELDS);
         dialectName = config.getString(DIALECT_NAME_CONFIG);
-        fieldsWhitelist = new HashSet<>(getList(config,FIELDS_WHITELIST));
+        fieldsWhitelist = new HashSet<>(getList(config, FIELDS_WHITELIST));
         // table white list
-        tableWhitelist =new HashSet<>(getList(config,TABLE_WHITE_LIST_CONFIG));
-        String dbTimeZone = config.getString(DB_TIMEZONE_CONFIG,DB_TIMEZONE_DEFAULT);
+        tableWhitelist = new HashSet<>(getList(config, TABLE_WHITE_LIST_CONFIG));
+        String dbTimeZone = config.getString(DB_TIMEZONE_CONFIG, DB_TIMEZONE_DEFAULT);
         timeZone = TimeZone.getTimeZone(ZoneId.of(dbTimeZone));
-        tableTypes = TableType.parse(getList(config,TABLE_TYPES_CONFIG,TABLE_TYPES_DEFAULT));
+        tableTypes = TableType.parse(getList(config, TABLE_TYPES_CONFIG, TABLE_TYPES_DEFAULT));
 
     }
 
@@ -274,21 +274,22 @@ public class JdbcSinkConfig extends AbstractConfig {
 
     /**
      * filter white table
+     *
      * @param dbDialect
      * @param tableId
      * @return
      */
     public boolean filterWhiteTable(DatabaseDialect dbDialect, TableId tableId) {
         // not filter table
-        if (tableWhitelist.isEmpty()){
+        if (tableWhitelist.isEmpty()) {
             return true;
         }
-        for (String tableName: tableWhitelist){
-            TableId table=dbDialect.parseToTableId(tableName);
-            if(table.catalogName() != null && table.catalogName().equals(tableId.catalogName())){
+        for (String tableName : tableWhitelist) {
+            TableId table = dbDialect.parseToTableId(tableName);
+            if (table.catalogName() != null && table.catalogName().equals(tableId.catalogName())) {
                 return true;
             }
-            if (table.tableName().equals(tableId.tableName())){
+            if (table.tableName().equals(tableId.tableName())) {
                 return true;
             }
         }
diff --git a/connectors/rocketmq-connect-jdbc/src/main/java/org/apache/rocketmq/connect/jdbc/connector/JdbcSinkConnector.java b/connectors/rocketmq-connect-jdbc/src/main/java/org/apache/rocketmq/connect/jdbc/connector/JdbcSinkConnector.java
index 0fbbf8d..aaf8560 100644
--- a/connectors/rocketmq-connect-jdbc/src/main/java/org/apache/rocketmq/connect/jdbc/connector/JdbcSinkConnector.java
+++ b/connectors/rocketmq-connect-jdbc/src/main/java/org/apache/rocketmq/connect/jdbc/connector/JdbcSinkConnector.java
@@ -38,11 +38,12 @@ public class JdbcSinkConnector extends SinkConnector {
 
     @Override
     public void start(ConnectorContext context) {
-        this.context=context;
+        this.context = context;
     }
 
     /**
      * Should invoke before start the connector.
+     *
      * @param config
      * @return error message
      */
@@ -58,17 +59,20 @@ public class JdbcSinkConnector extends SinkConnector {
      */
     @Override
     public void init(KeyValue config) {
-        this.connectConfig=config;
+        this.connectConfig = config;
     }
 
     @Override
-    public void stop() {}
+    public void stop() {
+    }
 
     @Override
-    public void pause() {}
+    public void pause() {
+    }
 
     @Override
-    public void resume() {}
+    public void resume() {
+    }
 
     /**
      * Returns a set of configurations for Tasks based on the current configuration,
@@ -81,7 +85,7 @@ public class JdbcSinkConnector extends SinkConnector {
     public List<KeyValue> taskConfigs(int maxTasks) {
         log.info("Starting task config !!! ");
         List<KeyValue> configs = new ArrayList<>();
-        for (int i =0 ; i< maxTasks; i++){
+        for (int i = 0; i < maxTasks; i++) {
             configs.add(this.connectConfig);
         }
         return configs;
diff --git a/connectors/rocketmq-connect-jdbc/src/main/java/org/apache/rocketmq/connect/jdbc/connector/JdbcSinkTask.java b/connectors/rocketmq-connect-jdbc/src/main/java/org/apache/rocketmq/connect/jdbc/connector/JdbcSinkTask.java
index 4fd844a..d69c80a 100644
--- a/connectors/rocketmq-connect-jdbc/src/main/java/org/apache/rocketmq/connect/jdbc/connector/JdbcSinkTask.java
+++ b/connectors/rocketmq-connect-jdbc/src/main/java/org/apache/rocketmq/connect/jdbc/connector/JdbcSinkTask.java
@@ -51,6 +51,7 @@ public class JdbcSinkTask extends SinkTask {
 
     /**
      * Put the records to the sink
+     *
      * @param records
      */
     @Override
@@ -89,7 +90,7 @@ public class JdbcSinkTask extends SinkTask {
 
     @Override
     public void start(SinkTaskContext context) {
-        this.context=context;
+        this.context = context;
     }
 
     /**
@@ -105,6 +106,7 @@ public class JdbcSinkTask extends SinkTask {
 
     /**
      * Init the component
+     *
      * @param keyValue
      */
     @Override
@@ -141,8 +143,11 @@ public class JdbcSinkTask extends SinkTask {
     }
 
     @Override
-    public void pause() {}
+    public void pause() {
+    }
+
     @Override
-    public void resume() {}
+    public void resume() {
+    }
 
 }
diff --git a/connectors/rocketmq-connect-jdbc/src/main/java/org/apache/rocketmq/connect/jdbc/connector/JdbcSourceConfig.java b/connectors/rocketmq-connect-jdbc/src/main/java/org/apache/rocketmq/connect/jdbc/connector/JdbcSourceConfig.java
index bc3028f..3c34c5e 100644
--- a/connectors/rocketmq-connect-jdbc/src/main/java/org/apache/rocketmq/connect/jdbc/connector/JdbcSourceConfig.java
+++ b/connectors/rocketmq-connect-jdbc/src/main/java/org/apache/rocketmq/connect/jdbc/connector/JdbcSourceConfig.java
@@ -38,11 +38,12 @@ public class JdbcSourceConfig extends AbstractConfig {
     public enum TableLoadMode {
         MODE_BULK("bulk"),
         MODE_TIMESTAMP("timestamp"),
-        MODE_INCREMENTING( "incrementing"),
+        MODE_INCREMENTING("incrementing"),
         MODE_TIMESTAMP_INCREMENTING("timestamp+incrementing");
         private String name;
+
         TableLoadMode(String name) {
-            this.name=name;
+            this.name = name;
         }
 
         public String getName() {
@@ -52,13 +53,14 @@ public class JdbcSourceConfig extends AbstractConfig {
         public void setName(String name) {
             this.name = name;
         }
-        public static TableLoadMode findTableLoadModeByName(String name){
-            for (TableLoadMode mode : TableLoadMode.values()){
-                if (mode.getName().equals(name)){
+
+        public static TableLoadMode findTableLoadModeByName(String name) {
+            for (TableLoadMode mode : TableLoadMode.values()) {
+                if (mode.getName().equals(name)) {
                     return mode;
                 }
             }
-            throw new IllegalArgumentException("Unsupports mode "+ name);
+            throw new IllegalArgumentException("Unsupports mode " + name);
         }
     }
 
@@ -217,30 +219,31 @@ public class JdbcSourceConfig extends AbstractConfig {
     private TimeZone timeZone;
     private String offsetSuffix;
     private String querySuffix;
+
     public JdbcSourceConfig(KeyValue config) {
         super(config);
-        this.pollIntervalMs = config.getInt(POLL_INTERVAL_MS_CONFIG,POLL_INTERVAL_MS_DEFAULT);
-        this.batchMaxRows = config.getInt(BATCH_MAX_ROWS_CONFIG,BATCH_MAX_ROWS_DEFAULT);
-        this.numericPrecisionMapping = getBoolean(config,NUMERIC_PRECISION_MAPPING_CONFIG,NUMERIC_PRECISION_MAPPING_DEFAULT);
-        this.numericMapping = config.getString(NUMERIC_MAPPING_CONFIG,NUMERIC_MAPPING_DEFAULT);
-        this.dialectName = config.getString(DIALECT_NAME_CONFIG, DIALECT_NAME_DEFAULT );
+        this.pollIntervalMs = config.getInt(POLL_INTERVAL_MS_CONFIG, POLL_INTERVAL_MS_DEFAULT);
+        this.batchMaxRows = config.getInt(BATCH_MAX_ROWS_CONFIG, BATCH_MAX_ROWS_DEFAULT);
+        this.numericPrecisionMapping = getBoolean(config, NUMERIC_PRECISION_MAPPING_CONFIG, NUMERIC_PRECISION_MAPPING_DEFAULT);
+        this.numericMapping = config.getString(NUMERIC_MAPPING_CONFIG, NUMERIC_MAPPING_DEFAULT);
+        this.dialectName = config.getString(DIALECT_NAME_CONFIG, DIALECT_NAME_DEFAULT);
         this.mode = config.getString(MODE_CONFIG);
         this.incrementingColumnName = config.getString(INCREMENTING_COLUMN_NAME_CONFIG);
-        this.timestampColumnNames = getList(config,TIMESTAMP_COLUMN_NAME_CONFIG);
-        timestampDelayIntervalMs=config.getLong(TIMESTAMP_DELAY_INTERVAL_MS_CONFIG);
+        this.timestampColumnNames = getList(config, TIMESTAMP_COLUMN_NAME_CONFIG);
+        timestampDelayIntervalMs = config.getLong(TIMESTAMP_DELAY_INTERVAL_MS_CONFIG);
         this.timestampInitial = config.containsKey(TIMESTAMP_INITIAL_CONFIG) ? config.getLong(TIMESTAMP_INITIAL_CONFIG) : TIMESTAMP_INITIAL_DEFAULT;
-        this.tableWhitelist = new HashSet<>(getList(config,TABLE_WHITELIST_CONFIG));
-        this.tableBlacklist = new HashSet<>(getList(config,TABLE_BLACKLIST_CONFIG));
+        this.tableWhitelist = new HashSet<>(getList(config, TABLE_WHITELIST_CONFIG));
+        this.tableBlacklist = new HashSet<>(getList(config, TABLE_BLACKLIST_CONFIG));
         this.schemaPattern = config.getString(SCHEMA_PATTERN_CONFIG);
         this.catalogPattern = config.getString(CATALOG_PATTERN_CONFIG);
         this.query = config.getString(QUERY_CONFIG);
         this.topicPrefix = config.getString(TOPIC_PREFIX_CONFIG);
-        this.validateNonNull = getBoolean(config,VALIDATE_NON_NULL_CONFIG,VALIDATE_NON_NULL_DEFAULT);
-        tableTypes = TableType.parse(getList(config,TABLE_TYPE_CONFIG,TABLE_TYPE_DEFAULT)) ;
-        String dbTimeZone =config.getString(DB_TIMEZONE_CONFIG,DB_TIMEZONE_DEFAULT);
-        this.timeZone=TimeZone.getTimeZone(ZoneId.of(dbTimeZone));
-        this.querySuffix=config.getString(QUERY_SUFFIX_CONFIG,QUERY_SUFFIX_DEFAULT);
-        this.offsetSuffix=config.getString(OFFSET_SUFFIX_CONFIG,OFFSET_SUFFIX_DEFAULT);
+        this.validateNonNull = getBoolean(config, VALIDATE_NON_NULL_CONFIG, VALIDATE_NON_NULL_DEFAULT);
+        tableTypes = TableType.parse(getList(config, TABLE_TYPE_CONFIG, TABLE_TYPE_DEFAULT));
+        String dbTimeZone = config.getString(DB_TIMEZONE_CONFIG, DB_TIMEZONE_DEFAULT);
+        this.timeZone = TimeZone.getTimeZone(ZoneId.of(dbTimeZone));
+        this.querySuffix = config.getString(QUERY_SUFFIX_CONFIG, QUERY_SUFFIX_DEFAULT);
+        this.offsetSuffix = config.getString(OFFSET_SUFFIX_CONFIG, OFFSET_SUFFIX_DEFAULT);
     }
 
 
diff --git a/connectors/rocketmq-connect-jdbc/src/main/java/org/apache/rocketmq/connect/jdbc/connector/JdbcSourceConnector.java b/connectors/rocketmq-connect-jdbc/src/main/java/org/apache/rocketmq/connect/jdbc/connector/JdbcSourceConnector.java
index f7f55eb..c48da71 100644
--- a/connectors/rocketmq-connect-jdbc/src/main/java/org/apache/rocketmq/connect/jdbc/connector/JdbcSourceConnector.java
+++ b/connectors/rocketmq-connect-jdbc/src/main/java/org/apache/rocketmq/connect/jdbc/connector/JdbcSourceConnector.java
@@ -55,8 +55,8 @@ public class JdbcSourceConnector extends SourceConnector {
      */
     @Override
     public void init(KeyValue config) {
-        if (config.containsKey("connect-topicname")){
-            config.put("connect-topicname","");
+        if (config.containsKey("connect-topicname")) {
+            config.put("connect-topicname", "");
         }
         originalConfig = config;
     }
@@ -83,23 +83,23 @@ public class JdbcSourceConnector extends SourceConnector {
     @Override
     public List<KeyValue> taskConfigs(int maxTasks) {
 
-        log.info("Connector task config divide["+maxTasks+"]" );
-        List<KeyValue> keyValues=Lists.newArrayList();
+        log.info("Connector task config divide[" + maxTasks + "]");
+        List<KeyValue> keyValues = Lists.newArrayList();
         List<String> tables = Lists.newArrayList();
-        log.info("Connector table white list["+jdbcSourceConfig.getTableWhitelist()+"]" );
-        jdbcSourceConfig.getTableWhitelist().forEach(table->{
+        log.info("Connector table white list[" + jdbcSourceConfig.getTableWhitelist() + "]");
+        jdbcSourceConfig.getTableWhitelist().forEach(table -> {
             tables.add(table);
         });
-        maxTasks = tables.size() > maxTasks ? maxTasks: tables.size();
+        maxTasks = tables.size() > maxTasks ? maxTasks : tables.size();
 
         List<List<String>> tablesGrouped =
                 ConnectorGroupUtils.groupPartitions(tables, maxTasks);
-        for (List<String> tableGroup:tablesGrouped) {
+        for (List<String> tableGroup : tablesGrouped) {
             KeyValue keyValue = new DefaultKeyValue();
-            for (String key: originalConfig.keySet()){
-                keyValue.put(key,originalConfig.getString(key));
+            for (String key : originalConfig.keySet()) {
+                keyValue.put(key, originalConfig.getString(key));
             }
-            keyValue.put(JdbcSourceTaskConfig.TABLES_CONFIG,StringUtils.join(tableGroup,","));
+            keyValue.put(JdbcSourceTaskConfig.TABLES_CONFIG, StringUtils.join(tableGroup, ","));
             keyValues.add(keyValue);
         }
         return keyValues;
diff --git a/connectors/rocketmq-connect-jdbc/src/main/java/org/apache/rocketmq/connect/jdbc/connector/JdbcSourceTask.java b/connectors/rocketmq-connect-jdbc/src/main/java/org/apache/rocketmq/connect/jdbc/connector/JdbcSourceTask.java
index 6ea61b1..80a7117 100644
--- a/connectors/rocketmq-connect-jdbc/src/main/java/org/apache/rocketmq/connect/jdbc/connector/JdbcSourceTask.java
+++ b/connectors/rocketmq-connect-jdbc/src/main/java/org/apache/rocketmq/connect/jdbc/connector/JdbcSourceTask.java
@@ -1,4 +1,3 @@
-
 /*
  * Licensed to the Apache Software Foundation (ASF) under one or more
  * contributor license agreements.  See the NOTICE file distributed with
@@ -21,7 +20,7 @@ package org.apache.rocketmq.connect.jdbc.connector;
 import io.openmessaging.KeyValue;
 import io.openmessaging.connector.api.component.task.source.SourceTask;
 import io.openmessaging.connector.api.component.task.source.SourceTaskContext;
-import io.openmessaging.connector.api.data.*;
+import io.openmessaging.connector.api.data.ConnectRecord;
 import org.apache.commons.lang3.StringUtils;
 import org.apache.rocketmq.connect.jdbc.dialect.DatabaseDialect;
 import org.apache.rocketmq.connect.jdbc.dialect.DatabaseDialectFactory;
@@ -65,7 +64,6 @@ public class JdbcSourceTask extends SourceTask {
     @Override
     public List<ConnectRecord> poll() {
         log.trace(" Polling for new data");
-        // 连续空的次数
         Map<Querier, Integer> consecutiveEmptyResults = tableQueue.stream().collect(Collectors.toMap(Function.identity(), (q) -> 0));
         while (running.get()) {
             final Querier querier = tableQueue.peek();
@@ -138,9 +136,9 @@ public class JdbcSourceTask extends SourceTask {
     private void resetAndRequeueHead(Querier querier) {
         log.debug("Resetting querier {}", querier.toString());
         tableQueue.poll();
-        if(running.get()){
+        if (running.get()) {
             querier.reset(System.currentTimeMillis());
-        }else{  //不跑了,可能是暂停,也可能是停止
+        } else {
             querier.reset(0);
         }
         tableQueue.add(querier);
@@ -149,21 +147,24 @@ public class JdbcSourceTask extends SourceTask {
 
     /**
      * Should invoke before start the connector.
+     *
      * @param config
      * @return error message
      */
     @Override
-    public void validate(KeyValue config) { }
+    public void validate(KeyValue config) {
+    }
 
     /**
      * start jdbc task
+     *
      * @param context
      */
     @Override
     public void start(SourceTaskContext context) {
         // compute table offset
-        Map<String,Map<String, Object>> offsetValues= SourceOffsetCompute.initOffset(config, context, dialect, cachedConnectionProvider);
-        for (String tableOrQuery: offsetValues.keySet()) {
+        Map<String, Map<String, Object>> offsetValues = SourceOffsetCompute.initOffset(config, context, dialect, cachedConnectionProvider);
+        for (String tableOrQuery : offsetValues.keySet()) {
             this.buildAndAddQuerier(
                     JdbcSourceConfig.TableLoadMode.findTableLoadModeByName(this.config.getMode()),
                     this.config.getQuerySuffix(),
@@ -180,6 +181,7 @@ public class JdbcSourceTask extends SourceTask {
 
     /**
      * build and add querier
+     *
      * @param loadMode
      * @param querySuffix
      * @param incrementingColumn
@@ -193,7 +195,7 @@ public class JdbcSourceTask extends SourceTask {
         String topicPrefix = config.getTopicPrefix();
         Querier.QueryMode queryMode = !StringUtils.isEmpty(config.getQuery()) ? Querier.QueryMode.QUERY : Querier.QueryMode.TABLE;
         Querier querier = null;
-        switch (loadMode){
+        switch (loadMode) {
             case MODE_BULK:
                 querier = new BulkQuerier(
                         dialect,
@@ -222,7 +224,7 @@ public class JdbcSourceTask extends SourceTask {
                 tableQueue.add(querier);
                 break;
             case MODE_TIMESTAMP:
-                 querier = new TimestampIncrementingQuerier(
+                querier = new TimestampIncrementingQuerier(
                         dialect,
                         queryMode,
                         tableOrQuery,
@@ -238,7 +240,7 @@ public class JdbcSourceTask extends SourceTask {
                 tableQueue.add(querier);
                 break;
             case MODE_TIMESTAMP_INCREMENTING:
-                 querier = new TimestampIncrementingQuerier(
+                querier = new TimestampIncrementingQuerier(
                         dialect,
                         queryMode,
                         tableOrQuery,
@@ -249,7 +251,7 @@ public class JdbcSourceTask extends SourceTask {
                         timestampDelayInterval,
                         timeZone,
                         querySuffix,
-                         this.config.getOffsetSuffix()
+                        this.config.getOffsetSuffix()
                 );
                 tableQueue.add(querier);
                 break;
@@ -257,13 +259,14 @@ public class JdbcSourceTask extends SourceTask {
     }
 
     /**
-         * Init the component
-         * @param props
-         */
+     * Init the component
+     *
+     * @param props
+     */
     @Override
     public void init(KeyValue props) {
         try {
-            config=new JdbcSourceTaskConfig(props);
+            config = new JdbcSourceTaskConfig(props);
             final String dialectName = config.getDialectName();
             final String url = config.getConnectionDbUrl();
             if (dialectName != null && !dialectName.trim().isEmpty()) {
@@ -321,11 +324,11 @@ public class JdbcSourceTask extends SourceTask {
 
     @Override
     public void pause() {
-        //TODO do nothing
+        // do nothing
     }
 
     @Override
     public void resume() {
-        //TODO do nothing
+        // do nothing
     }
 }
diff --git a/connectors/rocketmq-connect-jdbc/src/main/java/org/apache/rocketmq/connect/jdbc/connector/JdbcSourceTaskConfig.java b/connectors/rocketmq-connect-jdbc/src/main/java/org/apache/rocketmq/connect/jdbc/connector/JdbcSourceTaskConfig.java
index d0a2d8e..001beb8 100644
--- a/connectors/rocketmq-connect-jdbc/src/main/java/org/apache/rocketmq/connect/jdbc/connector/JdbcSourceTaskConfig.java
+++ b/connectors/rocketmq-connect-jdbc/src/main/java/org/apache/rocketmq/connect/jdbc/connector/JdbcSourceTaskConfig.java
@@ -17,6 +17,7 @@
 package org.apache.rocketmq.connect.jdbc.connector;
 
 import io.openmessaging.KeyValue;
+
 import java.util.List;
 
 
@@ -24,16 +25,17 @@ import java.util.List;
  * task config
  */
 public class JdbcSourceTaskConfig extends JdbcSourceConfig {
-  public static final String TABLES_CONFIG = "tables";
-  private static final String TABLES_DOC = "List of tables for this task to watch for changes.";
+    public static final String TABLES_CONFIG = "tables";
+    private static final String TABLES_DOC = "List of tables for this task to watch for changes.";
+
+    private List<String> tables;
 
-  private List<String> tables;
-  public JdbcSourceTaskConfig(KeyValue config) {
-    super(config);
-    this.tables=getList(config,TABLES_CONFIG);
-  }
+    public JdbcSourceTaskConfig(KeyValue config) {
+        super(config);
+        this.tables = getList(config, TABLES_CONFIG);
+    }
 
-  public List<String> getTables() {
-    return tables;
-  }
+    public List<String> getTables() {
+        return tables;
+    }
 }
diff --git a/connectors/rocketmq-connect-jdbc/src/main/java/org/apache/rocketmq/connect/jdbc/dialect/DatabaseDialect.java b/connectors/rocketmq-connect-jdbc/src/main/java/org/apache/rocketmq/connect/jdbc/dialect/DatabaseDialect.java
index fc15fa8..61c9d6e 100644
--- a/connectors/rocketmq-connect-jdbc/src/main/java/org/apache/rocketmq/connect/jdbc/dialect/DatabaseDialect.java
+++ b/connectors/rocketmq-connect-jdbc/src/main/java/org/apache/rocketmq/connect/jdbc/dialect/DatabaseDialect.java
@@ -49,271 +49,299 @@ import java.util.Map;
  */
 public interface DatabaseDialect extends ConnectionProvider {
 
-  /**
-   * dialect name
-   * @return
-   */
-  String name();
-
-  /**
-   * create jdbc prepared statement
-   * @param connection
-   * @param query
-   * @return
-   * @throws SQLException
-   */
-  PreparedStatement createPreparedStatement(Connection connection, String query) throws SQLException;
-
-  /**
-   * parse to Table Id
-   * @param fqn
-   * @return
-   */
-  TableId parseToTableId(String fqn);
-
-  /**
-   * Get the identifier rules for this database.
-   * @return the identifier rules
-   */
-  IdentifierRules identifierRules();
-
-  /**
-   *  Get a new expression builder that can be used to build expressions with quoted identifiers.
-   * @return
-   */
-  ExpressionBuilder expressionBuilder();
-  Timestamp currentTimeOnDB(Connection connection, Calendar cal) throws SQLException;
-
-  /**
-   *  Get a list of identifiers of the non-system tables in the database.
-   * @param connection
-   * @return
-   * @throws SQLException
-   */
-  List<TableId> tableIds(Connection connection) throws SQLException;
-
-  /**
-   * table exists
-   * @param connection
-   * @param tableId
-   * @return
-   * @throws SQLException
-   */
-  boolean tableExists(Connection connection, TableId tableId) throws SQLException;
-
-
-  /**
-   * Create the definition for the columns described by the database metadata.
-   */
-  Map<ColumnId, ColumnDefinition> describeColumns(Connection connection, String tablePattern, String columnPattern) throws SQLException;
-
-  /**
-   * Create the definition for the columns described by the database metadata.
-   */
-  Map<ColumnId, ColumnDefinition> describeColumns(Connection connection, String catalogPattern, String schemaPattern, String tablePattern, String columnPattern) throws SQLException;
-
-  /**
-   * Create the definition for the columns in the result set.
-   */
-  Map<ColumnId, ColumnDefinition> describeColumns(Connection conn, TableId tableId, ResultSetMetaData rsMetadata) throws SQLException;
-
-  /**
-   * describe table info
-   * @param connection
-   * @param tableId
-   * @return
-   * @throws SQLException
-   */
-  TableDefinition describeTable(Connection connection, TableId tableId) throws SQLException;
-
-  /**
-   * describe columns by query sql
-   * @param connection
-   * @param tableId
-   * @return
-   * @throws SQLException
-   */
-  Map<ColumnId, ColumnDefinition> describeColumnsByQuerying(Connection connection, TableId tableId) throws SQLException;
-
-  /**
-   * 添加字段到schema中
-   * @param column
-   * @param schema
-   * @param index
-   * @return
-   */
-  String addFieldToSchema(ColumnDefinition column, Schema schema,int index);
-
-  /**
-   * Apply the supplied DDL statements using the given connection. This gives the dialect the
-   * opportunity to execute the statements with a different autocommit setting.
-   */
-  void applyDdlStatements(Connection connection, List<String> statements) throws SQLException;
-
-  /**
-   * build dml statement
-   * @param table
-   * @param keyColumns
-   * @param nonKeyColumns
-   * @return
-   */
-  String buildInsertStatement(TableId table, Collection<ColumnId> keyColumns, Collection<ColumnId> nonKeyColumns);
-
-  /**
-   * update statement
-   * @param table
-   * @param keyColumns
-   * @param nonKeyColumns
-   * @return
-   */
-  String buildUpdateStatement(TableId table, Collection<ColumnId> keyColumns, Collection<ColumnId> nonKeyColumns);
-
-  /**
-   * upsert statment
-   * @param table
-   * @param keyColumns
-   * @param nonKeyColumns
-   * @return
-   */
-  String buildUpsertQueryStatement(TableId table, Collection<ColumnId> keyColumns, Collection<ColumnId> nonKeyColumns);
-
-  /**
-   * delete statement
-   * @param table
-   * @param keyColumns
-   * @return
-   */
-  default String buildDeleteStatement(TableId table, Collection<ColumnId> keyColumns) {
-    throw new UnsupportedOperationException();
-  }
-
-
-  /**
-   * build select table
-   */
-  String buildSelectTableMode();
-  void buildSelectTable(ExpressionBuilder builder, TableId tableId);
-
-
-  /** drop table
-   * @param table
-   * @param options
-   * @return
-   */
-  String buildDropTableStatement(TableId table, DropOptions options);
-
-  /**
-   * create table
-   * @param table
-   * @param fields
-   * @return
-   */
-  String buildCreateTableStatement(TableId table, Collection<SinkRecordField> fields);
-  /**
-   * build alter table
-   * @param table
-   * @param fields
-   * @return
-   */
-  List<String> buildAlterTable(TableId table, Collection<SinkRecordField> fields);
-
-  /**
-   * Create a component that can bind record values into the supplied prepared statement.
-   *
-   * @param statement      the prepared statement
-   * @param pkMode         the primary key mode; may not be null
-   * @param schemaPair     the key and value schemas; may not be null
-   * @param fieldsMetadata the field metadata; may not be null
-   * @param tableDefinition the table definition; may be null
-   * @param insertMode     the insert mode; may not be null
-   * @return the statement binder; may not be null
-   */
-  StatementBinder statementBinder(
-      PreparedStatement statement,
-      JdbcSinkConfig.PrimaryKeyMode pkMode,
-      SchemaPair schemaPair,
-      FieldsMetadata fieldsMetadata,
-      TableDefinition tableDefinition,
-      JdbcSinkConfig.InsertMode insertMode
-  );
-
-  /**
-   * value column types
-   * @param rsMetadata
-   * @param columns
-   * @throws io.openmessaging.connector.api.errors.ConnectException
-   */
-  void validateColumnTypes(
-          ResultSetMetaData rsMetadata,
-          List<ColumnId> columns
-  ) throws io.openmessaging.connector.api.errors.ConnectException;
-
-
-  /**
-   * bind field
-   * @param statement
-   * @param index
-   * @param schema
-   * @param value
-   * @param colDef
-   * @throws SQLException
-   */
-  void bindField(PreparedStatement statement, int index, Schema schema, Object value, ColumnDefinition colDef) throws SQLException ;
-
-  /**
-   * criteria for
-   * @param incrementingColumn
-   * @param timestampColumns
-   * @return
-   */
-  TimestampIncrementingCriteria criteriaFor(
-          ColumnId incrementingColumn,
-          List<ColumnId> timestampColumns
-  );
-
-  /**
-   * get min timestamp value
-   * @param con
-   * @param tableOrQuery
-   * @param timestampColumns
-   * @return
-   * @throws SQLException
-   */
-  Long getMinTimestampValue(Connection con, String tableOrQuery, List<String> timestampColumns) throws SQLException;
-
-  /**
-   * A function to bind the values from a sink record into a prepared statement.
-   */
-  @FunctionalInterface
-  interface StatementBinder {
     /**
-     * bind record
-     * @param record
+     * dialect name
+     *
+     * @return
+     */
+    String name();
+
+    /**
+     * create jdbc prepared statement
+     *
+     * @param connection
+     * @param query
+     * @return
+     * @throws SQLException
+     */
+    PreparedStatement createPreparedStatement(Connection connection, String query) throws SQLException;
+
+    /**
+     * parse to Table Id
+     *
+     * @param fqn
+     * @return
+     */
+    TableId parseToTableId(String fqn);
+
+    /**
+     * Get the identifier rules for this database.
+     *
+     * @return the identifier rules
+     */
+    IdentifierRules identifierRules();
+
+    /**
+     * Get a new expression builder that can be used to build expressions with quoted identifiers.
+     *
+     * @return
+     */
+    ExpressionBuilder expressionBuilder();
+
+    Timestamp currentTimeOnDB(Connection connection, Calendar cal) throws SQLException;
+
+    /**
+     * Get a list of identifiers of the non-system tables in the database.
+     *
+     * @param connection
+     * @return
+     * @throws SQLException
+     */
+    List<TableId> tableIds(Connection connection) throws SQLException;
+
+    /**
+     * table exists
+     *
+     * @param connection
+     * @param tableId
+     * @return
+     * @throws SQLException
+     */
+    boolean tableExists(Connection connection, TableId tableId) throws SQLException;
+
+
+    /**
+     * Create the definition for the columns described by the database metadata.
+     */
+    Map<ColumnId, ColumnDefinition> describeColumns(Connection connection, String tablePattern, String columnPattern) throws SQLException;
+
+    /**
+     * Create the definition for the columns described by the database metadata.
+     */
+    Map<ColumnId, ColumnDefinition> describeColumns(Connection connection, String catalogPattern, String schemaPattern, String tablePattern, String columnPattern) throws SQLException;
+
+    /**
+     * Create the definition for the columns in the result set.
+     */
+    Map<ColumnId, ColumnDefinition> describeColumns(Connection conn, TableId tableId, ResultSetMetaData rsMetadata) throws SQLException;
+
+    /**
+     * describe table info
+     *
+     * @param connection
+     * @param tableId
+     * @return
      * @throws SQLException
      */
-    void bindRecord(ConnectRecord record) throws SQLException;
-  }
-
-
-  /**
-   * Create a function that converts column values for the column defined by the specified mapping.
-   * @param mapping
-   * @return
-   */
-  ColumnConverter createColumnConverter(ColumnMapping mapping);
-
-  /**
-   * A function that obtains a column value from the current row of the specified result set.
-   */
-  @FunctionalInterface
-  interface ColumnConverter {
+    TableDefinition describeTable(Connection connection, TableId tableId) throws SQLException;
+
     /**
-     * convert
-     * @param resultSet
+     * describe columns by query sql
+     *
+     * @param connection
+     * @param tableId
      * @return
      * @throws SQLException
-     * @throws IOException
      */
-    Object convert(ResultSet resultSet) throws SQLException, IOException;
-  }
+    Map<ColumnId, ColumnDefinition> describeColumnsByQuerying(Connection connection, TableId tableId) throws SQLException;
+
+    /**
+     * add field to schema
+     *
+     * @param column
+     * @param schema
+     * @param index
+     * @return
+     */
+    String addFieldToSchema(ColumnDefinition column, Schema schema, int index);
+
+    /**
+     * Apply the supplied DDL statements using the given connection. This gives the dialect the
+     * opportunity to execute the statements with a different autocommit setting.
+     */
+    void applyDdlStatements(Connection connection, List<String> statements) throws SQLException;
+
+    /**
+     * build dml statement
+     *
+     * @param table
+     * @param keyColumns
+     * @param nonKeyColumns
+     * @return
+     */
+    String buildInsertStatement(TableId table, Collection<ColumnId> keyColumns, Collection<ColumnId> nonKeyColumns);
+
+    /**
+     * update statement
+     *
+     * @param table
+     * @param keyColumns
+     * @param nonKeyColumns
+     * @return
+     */
+    String buildUpdateStatement(TableId table, Collection<ColumnId> keyColumns, Collection<ColumnId> nonKeyColumns);
+
+    /**
+     * upsert statment
+     *
+     * @param table
+     * @param keyColumns
+     * @param nonKeyColumns
+     * @return
+     */
+    String buildUpsertQueryStatement(TableId table, Collection<ColumnId> keyColumns, Collection<ColumnId> nonKeyColumns);
+
+    /**
+     * delete statement
+     *
+     * @param table
+     * @param keyColumns
+     * @return
+     */
+    default String buildDeleteStatement(TableId table, Collection<ColumnId> keyColumns) {
+        throw new UnsupportedOperationException();
+    }
+
+
+    /**
+     * build select table
+     */
+    String buildSelectTableMode();
+
+    void buildSelectTable(ExpressionBuilder builder, TableId tableId);
+
+
+    /**
+     * drop table
+     *
+     * @param table
+     * @param options
+     * @return
+     */
+    String buildDropTableStatement(TableId table, DropOptions options);
+
+    /**
+     * create table
+     *
+     * @param table
+     * @param fields
+     * @return
+     */
+    String buildCreateTableStatement(TableId table, Collection<SinkRecordField> fields);
+
+    /**
+     * build alter table
+     *
+     * @param table
+     * @param fields
+     * @return
+     */
+    List<String> buildAlterTable(TableId table, Collection<SinkRecordField> fields);
+
+    /**
+     * Create a component that can bind record values into the supplied prepared statement.
+     *
+     * @param statement       the prepared statement
+     * @param pkMode          the primary key mode; may not be null
+     * @param schemaPair      the key and value schemas; may not be null
+     * @param fieldsMetadata  the field metadata; may not be null
+     * @param tableDefinition the table definition; may be null
+     * @param insertMode      the insert mode; may not be null
+     * @return the statement binder; may not be null
+     */
+    StatementBinder statementBinder(
+            PreparedStatement statement,
+            JdbcSinkConfig.PrimaryKeyMode pkMode,
+            SchemaPair schemaPair,
+            FieldsMetadata fieldsMetadata,
+            TableDefinition tableDefinition,
+            JdbcSinkConfig.InsertMode insertMode
+    );
+
+    /**
+     * value column types
+     *
+     * @param rsMetadata
+     * @param columns
+     * @throws io.openmessaging.connector.api.errors.ConnectException
+     */
+    void validateColumnTypes(
+            ResultSetMetaData rsMetadata,
+            List<ColumnId> columns
+    ) throws io.openmessaging.connector.api.errors.ConnectException;
+
+
+    /**
+     * bind field
+     *
+     * @param statement
+     * @param index
+     * @param schema
+     * @param value
+     * @param colDef
+     * @throws SQLException
+     */
+    void bindField(PreparedStatement statement, int index, Schema schema, Object value, ColumnDefinition colDef) throws SQLException;
+
+    /**
+     * criteria for
+     *
+     * @param incrementingColumn
+     * @param timestampColumns
+     * @return
+     */
+    TimestampIncrementingCriteria criteriaFor(
+            ColumnId incrementingColumn,
+            List<ColumnId> timestampColumns
+    );
+
+    /**
+     * get min timestamp value
+     *
+     * @param con
+     * @param tableOrQuery
+     * @param timestampColumns
+     * @return
+     * @throws SQLException
+     */
+    Long getMinTimestampValue(Connection con, String tableOrQuery, List<String> timestampColumns) throws SQLException;
+
+    /**
+     * A function to bind the values from a sink record into a prepared statement.
+     */
+    @FunctionalInterface
+    interface StatementBinder {
+        /**
+         * bind record
+         *
+         * @param record
+         * @throws SQLException
+         */
+        void bindRecord(ConnectRecord record) throws SQLException;
+    }
+
+
+    /**
+     * Create a function that converts column values for the column defined by the specified mapping.
+     *
+     * @param mapping
+     * @return
+     */
+    ColumnConverter createColumnConverter(ColumnMapping mapping);
+
+    /**
+     * A function that obtains a column value from the current row of the specified result set.
+     */
+    @FunctionalInterface
+    interface ColumnConverter {
+        /**
+         * convert
+         *
+         * @param resultSet
+         * @return
+         * @throws SQLException
+         * @throws IOException
+         */
+        Object convert(ResultSet resultSet) throws SQLException, IOException;
+    }
 }
\ No newline at end of file
diff --git a/connectors/rocketmq-connect-jdbc/src/main/java/org/apache/rocketmq/connect/jdbc/dialect/DatabaseDialectFactory.java b/connectors/rocketmq-connect-jdbc/src/main/java/org/apache/rocketmq/connect/jdbc/dialect/DatabaseDialectFactory.java
index 61d37b5..0938684 100644
--- a/connectors/rocketmq-connect-jdbc/src/main/java/org/apache/rocketmq/connect/jdbc/dialect/DatabaseDialectFactory.java
+++ b/connectors/rocketmq-connect-jdbc/src/main/java/org/apache/rocketmq/connect/jdbc/dialect/DatabaseDialectFactory.java
@@ -40,153 +40,154 @@ import java.util.regex.Pattern;
  */
 public class DatabaseDialectFactory {
 
-  /**
-   * regex jdbc protocol
-   */
-  private static final Pattern PROTOCOL_PATTERN = Pattern.compile("jdbc:([^:]+):(.*)");
-  private static final Logger LOG = LoggerFactory.getLogger(DatabaseDialectFactory.class);
-  private static final ConcurrentMap<String, DatabaseDialectProvider> REGISTRY = new ConcurrentSkipListMap<>();
-
-  static {
-    loadAllDialects();
-  }
-
-  /**
-   * load all database dialect
-   */
-  private static void loadAllDialects() {
-    LOG.debug("Searching for and loading all JDBC source dialects on the classpath");
-    final AtomicInteger count = new AtomicInteger();
-    AccessController.doPrivileged(new PrivilegedAction<Void>() {
-      @Override
-      public Void run() {
-        ServiceLoader<DatabaseDialectProvider> loadedDialects =
-                ServiceLoader.load(
-                        DatabaseDialectProvider.class,
-                        this.getClass().getClassLoader()
-                );
-        Iterator<DatabaseDialectProvider> dialectIterator = loadedDialects.iterator();
-        try {
-          while (dialectIterator.hasNext()) {
-            try {
-              DatabaseDialectProvider provider = dialectIterator.next();
-              REGISTRY.put(provider.getClass().getName(), provider);
-              count.incrementAndGet();
-              LOG.debug("Found '{}' provider {}", provider, provider.getClass());
-            } catch (Throwable t) {
-              LOG.debug("Skipping dialect provider after error while loading", t);
+    /**
+     * regex jdbc protocol
+     */
+    private static final Pattern PROTOCOL_PATTERN = Pattern.compile("jdbc:([^:]+):(.*)");
+    private static final Logger LOG = LoggerFactory.getLogger(DatabaseDialectFactory.class);
+    private static final ConcurrentMap<String, DatabaseDialectProvider> REGISTRY = new ConcurrentSkipListMap<>();
+
+    static {
+        loadAllDialects();
+    }
+
+    /**
+     * load all database dialect
+     */
+    private static void loadAllDialects() {
+        LOG.debug("Searching for and loading all JDBC source dialects on the classpath");
+        final AtomicInteger count = new AtomicInteger();
+        AccessController.doPrivileged(new PrivilegedAction<Void>() {
+            @Override
+            public Void run() {
+                ServiceLoader<DatabaseDialectProvider> loadedDialects =
+                        ServiceLoader.load(
+                                DatabaseDialectProvider.class,
+                                this.getClass().getClassLoader()
+                        );
+                Iterator<DatabaseDialectProvider> dialectIterator = loadedDialects.iterator();
+                try {
+                    while (dialectIterator.hasNext()) {
+                        try {
+                            DatabaseDialectProvider provider = dialectIterator.next();
+                            REGISTRY.put(provider.getClass().getName(), provider);
+                            count.incrementAndGet();
+                            LOG.debug("Found '{}' provider {}", provider, provider.getClass());
+                        } catch (Throwable t) {
+                            LOG.debug("Skipping dialect provider after error while loading", t);
+                        }
+                    }
+                } catch (Throwable t) {
+                    LOG.debug("Error loading dialect providers", t);
+                }
+                return null;
             }
-          }
-        } catch (Throwable t) {
-          LOG.debug("Error loading dialect providers", t);
-        }
-        return null;
-      }
-    });
-    LOG.debug("Registered {} source dialects", count.get());
-  }
-
-
-  /**
-   * find dialect for
-   * @param jdbcUrl
-   * @param config
-   * @return
-   * @throws ConnectException
-   */
-  public static DatabaseDialect findDialectFor(
-          String jdbcUrl,
-          AbstractConfig config
-  ) throws ConnectException {
-    final JdbcUrlInfo info = extractJdbcUrlInfo(jdbcUrl);
-    LOG.debug("Finding best dialect for {}", info);
-    DatabaseDialectProvider bestMatch = null;
-    for (DatabaseDialectProvider provider : REGISTRY.values()) {
-      if (provider.protocolName().equals(info.subProtocol())){
-        bestMatch=provider;
-        break;
-      }
+        });
+        LOG.debug("Registered {} source dialects", count.get());
     }
-    LOG.debug("Using dialect {}  against {}", bestMatch, info);
-    return bestMatch.create(config);
-  }
-
-  /**
-   * create database dialect
-   * @param dialectName
-   * @param config
-   * @return
-   * @throws ConnectException
-   */
-  public static DatabaseDialect create(
-          String dialectName,
-          AbstractConfig config
-  ) throws ConnectException {
-    LOG.debug("Looking for named dialect '{}'", dialectName);
-    Set<String> dialectNames = new HashSet<>();
-    for (DatabaseDialectProvider provider : REGISTRY.values()) {
-      dialectNames.add(provider.dialectName());
-      if (provider.dialectName().equals(dialectName)) {
-        return provider.create(config);
-      }
+
+
+    /**
+     * find dialect for
+     *
+     * @param jdbcUrl
+     * @param config
+     * @return
+     * @throws ConnectException
+     */
+    public static DatabaseDialect findDialectFor(
+            String jdbcUrl,
+            AbstractConfig config
+    ) throws ConnectException {
+        final JdbcUrlInfo info = extractJdbcUrlInfo(jdbcUrl);
+        LOG.debug("Finding best dialect for {}", info);
+        DatabaseDialectProvider bestMatch = null;
+        for (DatabaseDialectProvider provider : REGISTRY.values()) {
+            if (provider.protocolName().equals(info.subProtocol())) {
+                bestMatch = provider;
+                break;
+            }
+        }
+        LOG.debug("Using dialect {}  against {}", bestMatch, info);
+        return bestMatch.create(config);
     }
-    for (DatabaseDialectProvider provider : REGISTRY.values()) {
-      if (provider.dialectName().equalsIgnoreCase(dialectName)) {
-        return provider.create(config);
-      }
+
+    /**
+     * create database dialect
+     *
+     * @param dialectName
+     * @param config
+     * @return
+     * @throws ConnectException
+     */
+    public static DatabaseDialect create(
+            String dialectName,
+            AbstractConfig config
+    ) throws ConnectException {
+        LOG.debug("Looking for named dialect '{}'", dialectName);
+        Set<String> dialectNames = new HashSet<>();
+        for (DatabaseDialectProvider provider : REGISTRY.values()) {
+            dialectNames.add(provider.dialectName());
+            if (provider.dialectName().equals(dialectName)) {
+                return provider.create(config);
+            }
+        }
+        for (DatabaseDialectProvider provider : REGISTRY.values()) {
+            if (provider.dialectName().equalsIgnoreCase(dialectName)) {
+                return provider.create(config);
+            }
+        }
+        throw new ConnectException(
+                "Unable to find dialect with name '" + dialectName + "' in the available dialects: "
+                        + dialectNames
+        );
     }
-    throw new ConnectException(
-        "Unable to find dialect with name '" + dialectName + "' in the available dialects: "
-        + dialectNames
-    );
-  }
-
-  public static JdbcUrlInfo extractJdbcUrlInfo(final String url) {
-    Matcher matcher = PROTOCOL_PATTERN.matcher(url);
-    if (matcher.matches()) {
-      return new JdbcUrlDetails(matcher.group(1), matcher.group(2), url);
+
+    public static JdbcUrlInfo extractJdbcUrlInfo(final String url) {
+        Matcher matcher = PROTOCOL_PATTERN.matcher(url);
+        if (matcher.matches()) {
+            return new JdbcUrlDetails(matcher.group(1), matcher.group(2), url);
+        }
+        throw new ConnectException("Not a valid JDBC URL: " + url);
     }
-    throw new ConnectException("Not a valid JDBC URL: " + url);
-  }
 
 
+    static class JdbcUrlDetails implements JdbcUrlInfo {
+        final String subprotocol;
+        final String subname;
+        final String url;
 
-  static class JdbcUrlDetails implements JdbcUrlInfo {
-    final String subprotocol;
-    final String subname;
-    final String url;
+        public JdbcUrlDetails(
+                String subprotocol,
+                String subname,
+                String url
+        ) {
+            this.subprotocol = subprotocol;
+            this.subname = subname;
+            this.url = url;
+        }
 
-    public JdbcUrlDetails(
-        String subprotocol,
-        String subname,
-        String url
-    ) {
-      this.subprotocol = subprotocol;
-      this.subname = subname;
-      this.url = url;
-    }
+        @Override
+        public String subProtocol() {
+            return subprotocol;
+        }
 
-    @Override
-    public String subProtocol() {
-      return subprotocol;
-    }
+        @Override
+        public String subName() {
+            return subname;
+        }
 
-    @Override
-    public String subName() {
-      return subname;
-    }
+        @Override
+        public String url() {
+            return url;
+        }
 
-    @Override
-    public String url() {
-      return url;
+        @Override
+        public String toString() {
+            return "JDBC subprotocol '" + subprotocol + "' and source '" + url + "'";
+        }
     }
 
-    @Override
-    public String toString() {
-      return "JDBC subprotocol '" + subprotocol + "' and source '" + url + "'";
+    private DatabaseDialectFactory() {
     }
-  }
-
-  private DatabaseDialectFactory() {
-  }
 }
diff --git a/connectors/rocketmq-connect-jdbc/src/main/java/org/apache/rocketmq/connect/jdbc/dialect/DropOptions.java b/connectors/rocketmq-connect-jdbc/src/main/java/org/apache/rocketmq/connect/jdbc/dialect/DropOptions.java
index 4e8122f..11d6674 100644
--- a/connectors/rocketmq-connect-jdbc/src/main/java/org/apache/rocketmq/connect/jdbc/dialect/DropOptions.java
+++ b/connectors/rocketmq-connect-jdbc/src/main/java/org/apache/rocketmq/connect/jdbc/dialect/DropOptions.java
@@ -20,81 +20,81 @@ import java.util.Objects;
 
 public class DropOptions {
 
-  private final boolean ifExists;
-  private final boolean cascade;
+    private final boolean ifExists;
+    private final boolean cascade;
 
-  /**
-   * Create a new instance with the default settings.
-   */
-  public DropOptions() {
-    this(false, false);
-  }
-
-  protected DropOptions(
-      boolean ifExists,
-      boolean cascade
-  ) {
-    this.ifExists = ifExists;
-    this.cascade = cascade;
-  }
+    /**
+     * Create a new instance with the default settings.
+     */
+    public DropOptions() {
+        this(false, false);
+    }
 
-  /**
-   * Get whether the 'IF EXISTS' clause should be used with the 'DROP' statement.
-   *
-   * @return true if the object should be dropped only if it already exists, or false otherwise
-   */
-  public boolean ifExists() {
-    return ifExists;
-  }
+    protected DropOptions(
+            boolean ifExists,
+            boolean cascade
+    ) {
+        this.ifExists = ifExists;
+        this.cascade = cascade;
+    }
 
-  /**
-   * Get whether the 'DROP' statement should cascade to dependent objects.
-   *
-   * @return true if dependent objects should also be dropped, or false otherwise
-   */
-  public boolean cascade() {
-    return cascade;
-  }
+    /**
+     * Get whether the 'IF EXISTS' clause should be used with the 'DROP' statement.
+     *
+     * @return true if the object should be dropped only if it already exists, or false otherwise
+     */
+    public boolean ifExists() {
+        return ifExists;
+    }
 
-  /**
-   * Set whether the 'IF EXISTS' clause should be used with the 'DROP' statement.
-   *
-   * @param ifExists true if the object should be dropped only if it already exists
-   * @return a new options object with the current state plus the new if-exists state; never null
-   */
-  public DropOptions setIfExists(boolean ifExists) {
-    return new DropOptions(ifExists, cascade);
-  }
+    /**
+     * Get whether the 'DROP' statement should cascade to dependent objects.
+     *
+     * @return true if dependent objects should also be dropped, or false otherwise
+     */
+    public boolean cascade() {
+        return cascade;
+    }
 
-  /**
-   * Set whether the 'DROP' statement should cascade to dependent objects.
-   *
-   * @param cascade true if dependent objects should also be dropped, or false otherwise
-   * @return a new options object with the current state plus the new cascade state; never null
-   */
-  public DropOptions setCascade(boolean cascade) {
-    return new DropOptions(ifExists, cascade);
-  }
+    /**
+     * Set whether the 'IF EXISTS' clause should be used with the 'DROP' statement.
+     *
+     * @param ifExists true if the object should be dropped only if it already exists
+     * @return a new options object with the current state plus the new if-exists state; never null
+     */
+    public DropOptions setIfExists(boolean ifExists) {
+        return new DropOptions(ifExists, cascade);
+    }
 
-  @Override
-  public String toString() {
-    return "DropOptions{ifExists=" + ifExists + ", cascade=" + cascade + "}";
-  }
+    /**
+     * Set whether the 'DROP' statement should cascade to dependent objects.
+     *
+     * @param cascade true if dependent objects should also be dropped, or false otherwise
+     * @return a new options object with the current state plus the new cascade state; never null
+     */
+    public DropOptions setCascade(boolean cascade) {
+        return new DropOptions(ifExists, cascade);
+    }
 
-  @Override
-  public int hashCode() {
-    return Objects.hash(ifExists, cascade);
-  }
+    @Override
+    public String toString() {
+        return "DropOptions{ifExists=" + ifExists + ", cascade=" + cascade + "}";
+    }
 
-  @Override
-  public boolean equals(Object obj) {
-    if (obj == this) {
-      return true;
+    @Override
+    public int hashCode() {
+        return Objects.hash(ifExists, cascade);
     }
-    if (obj instanceof DropOptions) {
-      DropOptions that = (DropOptions) obj;
-      return this.ifExists() == that.ifExists() && this.cascade() == that.cascade();
+
+    @Override
+    public boolean equals(Object obj) {
+        if (obj == this) {
+            return true;
+        }
+        if (obj instanceof DropOptions) {
+            DropOptions that = (DropOptions) obj;
+            return this.ifExists() == that.ifExists() && this.cascade() == that.cascade();
+        }
+        return false;
     }
-    return false;
-  }
 }
diff --git a/connectors/rocketmq-connect-jdbc/src/main/java/org/apache/rocketmq/connect/jdbc/dialect/PreparedStatementBinder.java b/connectors/rocketmq-connect-jdbc/src/main/java/org/apache/rocketmq/connect/jdbc/dialect/PreparedStatementBinder.java
index d74ec52..dc9c0ae 100644
--- a/connectors/rocketmq-connect-jdbc/src/main/java/org/apache/rocketmq/connect/jdbc/dialect/PreparedStatementBinder.java
+++ b/connectors/rocketmq-connect-jdbc/src/main/java/org/apache/rocketmq/connect/jdbc/dialect/PreparedStatementBinder.java
@@ -37,94 +37,94 @@ import java.util.Objects;
  */
 public class PreparedStatementBinder implements DatabaseDialect.StatementBinder {
 
-  private final JdbcSinkConfig.PrimaryKeyMode pkMode;
-  private final PreparedStatement statement;
-  private final SchemaPair schemaPair;
-  private final FieldsMetadata fieldsMetadata;
-  private final JdbcSinkConfig.InsertMode insertMode;
-  private final DatabaseDialect dialect;
-  private final TableDefinition tabDef;
+    private final JdbcSinkConfig.PrimaryKeyMode pkMode;
+    private final PreparedStatement statement;
+    private final SchemaPair schemaPair;
+    private final FieldsMetadata fieldsMetadata;
+    private final JdbcSinkConfig.InsertMode insertMode;
+    private final DatabaseDialect dialect;
+    private final TableDefinition tabDef;
 
-  public PreparedStatementBinder(
-      DatabaseDialect dialect,
-      PreparedStatement statement,
-      JdbcSinkConfig.PrimaryKeyMode pkMode,
-      SchemaPair schemaPair,
-      FieldsMetadata fieldsMetadata,
-      TableDefinition tabDef,
-      JdbcSinkConfig.InsertMode insertMode
-  ) {
-    this.dialect = dialect;
-    this.pkMode = pkMode;
-    this.statement = statement;
-    this.schemaPair = schemaPair;
-    this.fieldsMetadata = fieldsMetadata;
-    this.insertMode = insertMode;
-    this.tabDef = tabDef;
-  }
+    public PreparedStatementBinder(
+            DatabaseDialect dialect,
+            PreparedStatement statement,
+            JdbcSinkConfig.PrimaryKeyMode pkMode,
+            SchemaPair schemaPair,
+            FieldsMetadata fieldsMetadata,
+            TableDefinition tabDef,
+            JdbcSinkConfig.InsertMode insertMode
+    ) {
+        this.dialect = dialect;
+        this.pkMode = pkMode;
+        this.statement = statement;
+        this.schemaPair = schemaPair;
+        this.fieldsMetadata = fieldsMetadata;
+        this.insertMode = insertMode;
+        this.tabDef = tabDef;
+    }
 
-  @Override
-  public void bindRecord(ConnectRecord record) throws SQLException {
-    final boolean isDelete = Objects.isNull(record.getData());
+    @Override
+    public void bindRecord(ConnectRecord record) throws SQLException {
+        final boolean isDelete = Objects.isNull(record.getData());
 
-    int index = 1;
-    if (isDelete) {
-      bindKeyFields(record, index);
-    } else {
-      switch (insertMode) {
-        case INSERT:
-        case UPSERT:
-          index = bindKeyFields(record, index);
-          bindNonKeyFields(record,index);
-          break;
+        int index = 1;
+        if (isDelete) {
+            bindKeyFields(record, index);
+        } else {
+            switch (insertMode) {
+                case INSERT:
+                case UPSERT:
+                    index = bindKeyFields(record, index);
+                    bindNonKeyFields(record, index);
+                    break;
 
-        case UPDATE:
-          index = bindNonKeyFields(record, index);
-          bindKeyFields(record, index);
-          break;
-        default:
-          throw new AssertionError();
+                case UPDATE:
+                    index = bindNonKeyFields(record, index);
+                    bindKeyFields(record, index);
+                    break;
+                default:
+                    throw new AssertionError();
 
-      }
+            }
+        }
+        statement.addBatch();
     }
-    statement.addBatch();
-  }
 
-  protected int bindKeyFields(ConnectRecord record, int index) throws SQLException {
-    switch (pkMode) {
-      case NONE:
-        if (!fieldsMetadata.keyFieldNames.isEmpty()) {
-          throw new AssertionError();
-        }
-        break;
-      case RECORD_VALUE: {
-        Object[] data=JSONArray.parseArray(JSON.toJSONString(record.getData())).stream().toArray();
-        for (String fieldName : fieldsMetadata.keyFieldNames) {
-          final Field field = schemaPair.schema.getField(fieldName);
-          bindField(index++, field.getSchema(), data[field.getIndex()] , fieldName);
+    protected int bindKeyFields(ConnectRecord record, int index) throws SQLException {
+        switch (pkMode) {
+            case NONE:
+                if (!fieldsMetadata.keyFieldNames.isEmpty()) {
+                    throw new AssertionError();
+                }
+                break;
+            case RECORD_VALUE: {
+                Object[] data = JSONArray.parseArray(JSON.toJSONString(record.getData())).stream().toArray();
+                for (String fieldName : fieldsMetadata.keyFieldNames) {
+                    final Field field = schemaPair.schema.getField(fieldName);
+                    bindField(index++, field.getSchema(), data[field.getIndex()], fieldName);
+                }
+            }
+            break;
+            default:
+                throw new ConnectException("Unknown primary key mode: " + pkMode);
         }
-      }
-      break;
-      default:
-        throw new ConnectException("Unknown primary key mode: " + pkMode);
+        return index;
     }
-    return index;
-  }
 
-  protected int bindNonKeyFields(
-          ConnectRecord record,
-          int index
-  ) throws SQLException {
-    for (final String fieldName : fieldsMetadata.nonKeyFieldNames) {
-      final Field field = record.getSchema().getField(fieldName);
-      bindField(index++, field.getSchema(), ((Object[])record.getData())[field.getIndex()] , fieldName);
+    protected int bindNonKeyFields(
+            ConnectRecord record,
+            int index
+    ) throws SQLException {
+        for (final String fieldName : fieldsMetadata.nonKeyFieldNames) {
+            final Field field = record.getSchema().getField(fieldName);
+            bindField(index++, field.getSchema(), ((Object[]) record.getData())[field.getIndex()], fieldName);
+        }
+        return index;
     }
-    return index;
-  }
 
-  protected void bindField(int index, Schema schema, Object value, String fieldName)
-      throws SQLException {
-    ColumnDefinition colDef = tabDef == null ? null : tabDef.definitionForColumn(fieldName);
-    dialect.bindField(statement, index, schema, value, colDef);
-  }
+    protected void bindField(int index, Schema schema, Object value, String fieldName)
+            throws SQLException {
+        ColumnDefinition colDef = tabDef == null ? null : tabDef.definitionForColumn(fieldName);
+        dialect.bindField(statement, index, schema, value, colDef);
+    }
 }
diff --git a/connectors/rocketmq-connect-jdbc/src/main/java/org/apache/rocketmq/connect/jdbc/dialect/impl/GenericDatabaseDialect.java b/connectors/rocketmq-connect-jdbc/src/main/java/org/apache/rocketmq/connect/jdbc/dialect/impl/GenericDatabaseDialect.java
index f177148..d9e8dee 100644
--- a/connectors/rocketmq-connect-jdbc/src/main/java/org/apache/rocketmq/connect/jdbc/dialect/impl/GenericDatabaseDialect.java
+++ b/connectors/rocketmq-connect-jdbc/src/main/java/org/apache/rocketmq/connect/jdbc/dialect/impl/GenericDatabaseDialect.java
@@ -43,7 +43,13 @@ import org.apache.rocketmq.connect.jdbc.schema.column.parser.DateColumnParser;
 import org.apache.rocketmq.connect.jdbc.schema.column.parser.TimeColumnParser;
 import org.apache.rocketmq.connect.jdbc.schema.column.parser.TimestampColumnParser;
 import org.apache.rocketmq.connect.jdbc.source.TimestampIncrementingCriteria;
-import org.apache.rocketmq.connect.jdbc.util.*;
+import org.apache.rocketmq.connect.jdbc.util.DateTimeUtils;
+import org.apache.rocketmq.connect.jdbc.util.ExpressionBuilder;
+import org.apache.rocketmq.connect.jdbc.util.IdentifierRules;
+import org.apache.rocketmq.connect.jdbc.util.JdbcDriverInfo;
+import org.apache.rocketmq.connect.jdbc.util.NumericMapping;
+import org.apache.rocketmq.connect.jdbc.util.QuoteMethod;
+import org.apache.rocketmq.connect.jdbc.util.TableType;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
@@ -88,790 +94,808 @@ import java.util.stream.Collectors;
  */
 public class GenericDatabaseDialect implements DatabaseDialect {
 
-  protected static final int NUMERIC_TYPE_SCALE_LOW = -84;
-  protected static final int NUMERIC_TYPE_SCALE_HIGH = 127;
-  protected static final int NUMERIC_TYPE_SCALE_UNSET = -127;
+    protected static final int NUMERIC_TYPE_SCALE_LOW = -84;
+    protected static final int NUMERIC_TYPE_SCALE_HIGH = 127;
+    protected static final int NUMERIC_TYPE_SCALE_UNSET = -127;
 
-  private static final int MAX_INTEGER_TYPE_PRECISION = 18;
+    private static final int MAX_INTEGER_TYPE_PRECISION = 18;
 
-  /**
-   * The provider for GenericDatabaseDialect
-   */
-  public static class Provider extends DatabaseDialectProvider {
-    public Provider() {
-      super(GenericDatabaseDialect.class.getSimpleName(),"");
+    /**
+     * The provider for GenericDatabaseDialect
+     */
+    public static class Provider extends DatabaseDialectProvider {
+        public Provider() {
+            super(GenericDatabaseDialect.class.getSimpleName(), "");
+        }
+
+        @Override
+        public DatabaseDialect create(AbstractConfig config) {
+            return new GenericDatabaseDialect(config);
+        }
+    }
+
+    /**
+     * The provider for GenericDatabaseDialect
+     */
+    public static class DialectName {
+        public static String generateDialectName(Class clazz) {
+            return clazz.getSimpleName().replace("DatabaseDialect", "");
+        }
     }
+
+    private static final Logger log = LoggerFactory.getLogger(GenericDatabaseDialect.class);
+
+//    @Deprecated
+//    protected final Logger log = LoggerFactory.getLogger(GenericDatabaseDialect.class);
+    protected AbstractConfig config;
+    /**
+     * Whether to map {@code NUMERIC} JDBC types by precision.
+     */
+    protected NumericMapping mapNumerics = NumericMapping.NONE;
+    protected String catalogPattern;
+    protected String schemaPattern;
+    protected Set<String> tableTypes;
+    protected String jdbcUrl;
+    protected JdbcUrlInfo jdbcUrlInfo;
+    private QuoteMethod quoteSqlIdentifiers = QuoteMethod.ALWAYS;
+    private IdentifierRules defaultIdentifierRules;
+    private AtomicReference<IdentifierRules> identifierRules = new AtomicReference<>();
+    private Queue<Connection> connections = new ConcurrentLinkedQueue<>();
+    private volatile JdbcDriverInfo jdbcDriverInfo;
+    private int batchMaxRows;
+    private TimeZone timeZone;
+
+    public GenericDatabaseDialect(AbstractConfig config) {
+        this(config, IdentifierRules.DEFAULT);
+    }
+
+    protected GenericDatabaseDialect(
+            AbstractConfig config, IdentifierRules defaultIdentifierRules
+    ) {
+        this.config = config;
+        this.defaultIdentifierRules = defaultIdentifierRules;
+        this.jdbcUrl = config.getConnectionDbUrl();
+        this.jdbcUrlInfo = DatabaseDialectFactory.extractJdbcUrlInfo(jdbcUrl);
+        if (config instanceof JdbcSinkConfig) {
+            JdbcSinkConfig sinkConfig = (JdbcSinkConfig) config;
+            catalogPattern = JdbcSourceConfig.CATALOG_PATTERN_DEFAULT;
+            schemaPattern = JdbcSourceConfig.SCHEMA_PATTERN_DEFAULT;
+            tableTypes = sinkConfig.tableTypeNames();
+            quoteSqlIdentifiers = QuoteMethod.get(config.getQuoteSqlIdentifiers());
+            mapNumerics = NumericMapping.NONE;
+            batchMaxRows = 0;
+            timeZone = sinkConfig.getTimeZone();
+        } else {
+            JdbcSourceConfig sourceConfig = (JdbcSourceConfig) config;
+            catalogPattern = sourceConfig.getCatalogPattern();
+            schemaPattern = sourceConfig.getSchemaPattern();
+            tableTypes = sourceConfig.getTableTypes().stream().map(TableType::toString).collect(Collectors.toSet());
+            quoteSqlIdentifiers = QuoteMethod.get(config.getQuoteSqlIdentifiers());
+            mapNumerics = sourceConfig.numericMapping();
+            batchMaxRows = sourceConfig.getBatchMaxRows();
+            timeZone = sourceConfig.getTimeZone();
+        }
+
+    }
+
+    @Override
+    public String name() {
+        return DialectName.generateDialectName(getClass());
+//    return getClass().getSimpleName().replace("DatabaseDialect", "");
+    }
+
+    /**
+     * init jdbc connection
+     *
+     * @return
+     * @throws SQLException
+     */
+    @SneakyThrows
     @Override
-    public DatabaseDialect create(AbstractConfig config) {
-      return new GenericDatabaseDialect(config);
-    }
-  }
-
-  private static final Logger glog = LoggerFactory.getLogger(GenericDatabaseDialect.class);
-
-  @Deprecated
-  protected final Logger log = LoggerFactory.getLogger(GenericDatabaseDialect.class);
-  protected AbstractConfig config;
-  /**
-   * Whether to map {@code NUMERIC} JDBC types by precision.
-   */
-  protected NumericMapping mapNumerics=NumericMapping.NONE;
-  protected String catalogPattern;
-  protected  String schemaPattern;
-  protected Set<String> tableTypes;
-  protected String jdbcUrl;
-  protected JdbcUrlInfo jdbcUrlInfo;
-  private QuoteMethod quoteSqlIdentifiers=QuoteMethod.ALWAYS;
-  private IdentifierRules defaultIdentifierRules;
-  private AtomicReference<IdentifierRules> identifierRules = new AtomicReference<>();
-  private Queue<Connection> connections = new ConcurrentLinkedQueue<>();
-  private volatile JdbcDriverInfo jdbcDriverInfo;
-  private int batchMaxRows;
-  private TimeZone timeZone;
-
-  public GenericDatabaseDialect(AbstractConfig config) {
-    this(config, IdentifierRules.DEFAULT);
-  }
-
-  protected GenericDatabaseDialect(
-          AbstractConfig config, IdentifierRules defaultIdentifierRules
-  ) {
-    this.config = config;
-    this.defaultIdentifierRules = defaultIdentifierRules;
-    this.jdbcUrl = config.getConnectionDbUrl();
-    this.jdbcUrlInfo = DatabaseDialectFactory.extractJdbcUrlInfo(jdbcUrl);
-    if (config instanceof JdbcSinkConfig) {
-      JdbcSinkConfig sinkConfig = (JdbcSinkConfig) config;
-      catalogPattern = JdbcSourceConfig.CATALOG_PATTERN_DEFAULT;
-      schemaPattern = JdbcSourceConfig.SCHEMA_PATTERN_DEFAULT;
-      tableTypes = sinkConfig.tableTypeNames();
-      quoteSqlIdentifiers = QuoteMethod.get(config.getQuoteSqlIdentifiers());
-      mapNumerics = NumericMapping.NONE;
-      batchMaxRows = 0;
-      timeZone = sinkConfig.getTimeZone();
-    } else {
-      JdbcSourceConfig sourceConfig = (JdbcSourceConfig) config;
-      catalogPattern = sourceConfig.getCatalogPattern();
-      schemaPattern = sourceConfig.getSchemaPattern();
-      tableTypes = sourceConfig.getTableTypes().stream().map(TableType::toString).collect(Collectors.toSet());
-      quoteSqlIdentifiers = QuoteMethod.get(config.getQuoteSqlIdentifiers());
-      mapNumerics = sourceConfig.numericMapping();
-      batchMaxRows = sourceConfig.getBatchMaxRows();
-      timeZone = sourceConfig.getTimeZone();
-    }
-
-  }
-
-  @Override
-  public String name() {
-    return getClass().getSimpleName().replace("DatabaseDialect", "");
-  }
-
-  /**
-   * init jdbc connection
-   * @return
-   * @throws SQLException
-   */
-  @SneakyThrows
-  @Override
-  public Connection getConnection() throws SQLException {
-    // These config names are the same for both source and sink configs ...
-    String username = config.getConnectionDbUser();
-    String dbPassword = config.getConnectionDbPassword();
-    Properties properties = new Properties();
-    if (username != null) {
-      properties.setProperty("user", username);
-    }
-    if (dbPassword != null) {
-      properties.setProperty("password", dbPassword);
-    }
-    properties = addConnectionProperties(properties);
-    DriverManager.setLoginTimeout(40);
-    Connection connection = DriverManager.getConnection(jdbcUrl, properties);
-    if (jdbcDriverInfo == null) {
-      jdbcDriverInfo = createJdbcDriverInfo(connection);
-    }
-    connections.add(connection);
-    return connection;
-  }
-
-  @Override
-  public void close() {
-    Connection conn;
-    while ((conn = connections.poll()) != null) {
-      try {
-        conn.close();
-      } catch (Throwable e) {
-        glog.warn("Error while closing connection to {}", jdbcDriverInfo, e);
-      }
-    }
-  }
-
-  @Override
-  public boolean isConnectionValid(
-          Connection connection,
-          int timeout
-  ) throws SQLException {
-    if (jdbcDriverInfo().jdbcMajorVersion() >= 4) {
-      return connection.isValid(timeout);
-    }
-    // issue a test query ...
-    String query = checkConnectionQuery();
-    if (query != null) {
-      try (Statement statement = connection.createStatement()) {
-        if (statement.execute(query)) {
-          ResultSet rs = null;
-          try {
-            // do nothing with the result set
-            rs = statement.getResultSet();
-          } finally {
-            if (rs != null) {
-              rs.close();
+    public Connection getConnection() throws SQLException {
+        // These config names are the same for both source and sink configs ...
+        String username = config.getConnectionDbUser();
+        String dbPassword = config.getConnectionDbPassword();
+        Properties properties = new Properties();
+        if (username != null) {
+            properties.setProperty("user", username);
+        }
+        if (dbPassword != null) {
+            properties.setProperty("password", dbPassword);
+        }
+        properties = addConnectionProperties(properties);
+        DriverManager.setLoginTimeout(40);
+        Connection connection = DriverManager.getConnection(jdbcUrl, properties);
+        if (jdbcDriverInfo == null) {
+            jdbcDriverInfo = createJdbcDriverInfo(connection);
+        }
+        connections.add(connection);
+        return connection;
+    }
+
+    @Override
+    public void close() {
+        Connection conn;
+        while ((conn = connections.poll()) != null) {
+            try {
+                conn.close();
+            } catch (Throwable e) {
+                log.warn("Error while closing connection to {}", jdbcDriverInfo, e);
+            }
+        }
+    }
+
+    @Override
+    public boolean isConnectionValid(
+            Connection connection,
+            int timeout
+    ) throws SQLException {
+        if (jdbcDriverInfo().jdbcMajorVersion() >= 4) {
+            return connection.isValid(timeout);
+        }
+        // issue a test query ...
+        String query = checkConnectionQuery();
+        if (query != null) {
+            try (Statement statement = connection.createStatement()) {
+                if (statement.execute(query)) {
+                    ResultSet rs = null;
+                    try {
+                        // do nothing with the result set
+                        rs = statement.getResultSet();
+                    } finally {
+                        if (rs != null) {
+                            rs.close();
+                        }
+                    }
+                }
+            }
+        }
+        return true;
+    }
+
+    /**
+     * check connection query
+     *
+     * @return the check connection query; may be null if the connection should not be queried
+     */
+    protected String checkConnectionQuery() {
+        return "SELECT 1";
+    }
+
+    protected JdbcDriverInfo jdbcDriverInfo() {
+        if (jdbcDriverInfo == null) {
+            try (Connection connection = getConnection()) {
+                jdbcDriverInfo = createJdbcDriverInfo(connection);
+            } catch (SQLException e) {
+                throw new io.openmessaging.connector.api.errors.ConnectException("Unable to get JDBC driver information", e);
             }
-          }
         }
-      }
-    }
-    return true;
-  }
-
-  /**
-   * check connection query
-   * @return the check connection query; may be null if the connection should not be queried
-   */
-  protected String checkConnectionQuery() {
-    return "SELECT 1";
-  }
-
-  protected JdbcDriverInfo jdbcDriverInfo() {
-    if (jdbcDriverInfo == null) {
-      try (Connection connection = getConnection()) {
-        jdbcDriverInfo = createJdbcDriverInfo(connection);
-      } catch (SQLException e) {
-        throw new io.openmessaging.connector.api.errors.ConnectException("Unable to get JDBC driver information", e);
-      }
-    }
-    return jdbcDriverInfo;
-  }
-
-  protected JdbcDriverInfo createJdbcDriverInfo(Connection connection) throws SQLException {
-    DatabaseMetaData metadata = connection.getMetaData();
-    return new JdbcDriverInfo(
-            metadata.getJDBCMajorVersion(),
-            metadata.getJDBCMinorVersion(),
-            metadata.getDriverName(),
-            metadata.getDatabaseProductName(),
-            metadata.getDatabaseProductVersion()
-    );
-  }
-
-  /**
-   * add connect properties
-   * @param properties
-   * @return
-   */
-  protected Properties addConnectionProperties(Properties properties) {
-    // todo 设置 config中的属性
-    return properties;
-  }
-
-  @Override
-  public PreparedStatement createPreparedStatement(
-          Connection db,
-          String query
-  ) throws SQLException {
-    glog.trace("Creating a PreparedStatement '{}'", query);
-    PreparedStatement stmt = db.prepareStatement(query);
-    initializePreparedStatement(stmt);
-    return stmt;
-  }
-
-  /**
-   * init PreparedStatement
-   * @param stmt
-   * @throws SQLException
-   */
-  protected void initializePreparedStatement(PreparedStatement stmt) throws SQLException {
-    if (batchMaxRows > 0) {
-      stmt.setFetchSize(batchMaxRows);
-    }
-  }
-
-  @Override
-  public TableId parseToTableId(String fqn) {
-    List<String> parts = identifierRules().parseQualifiedIdentifier(fqn);
-    if (parts.isEmpty()) {
-      throw new IllegalArgumentException("Invalid fully qualified name: '" + fqn + "'");
-    }
-    if (parts.size() == 1) {
-      return new TableId(null, null, parts.get(0));
-    }
-    if (parts.size() == 3) {
-      return new TableId(parts.get(0), parts.get(1), parts.get(2));
-    }
-    assert parts.size() >= 2;
-    if (useCatalog()) {
-      return new TableId(parts.get(0), null, parts.get(1));
-    }
-    return new TableId(null, parts.get(0), parts.get(1));
-  }
-
-  /**
-   * Return whether the database uses JDBC catalogs.
-   * @return true if catalogs are used, or false otherwise
-   */
-  protected boolean useCatalog() {
-    return false;
-  }
-
-  @Override
-  public List<TableId> tableIds(Connection conn) throws SQLException {
-    DatabaseMetaData metadata = conn.getMetaData();
-    String[] tableTypes = tableTypes(metadata, this.tableTypes);
-    String tableTypeDisplay = displayableTableTypes(tableTypes, ", ");
-    glog.debug("Using {} dialect to get {}", this, tableTypeDisplay);
-    try (ResultSet rs = metadata.getTables(catalogPattern(), schemaPattern(), "%", tableTypes)) {
-      List<TableId> tableIds = new ArrayList<>();
-      while (rs.next()) {
-        String catalogName = rs.getString(1);
-        String schemaName = rs.getString(2);
-        String tableName = rs.getString(3);
-        TableId tableId = new TableId(catalogName, schemaName, tableName);
-        if (includeTable(tableId)) {
-          tableIds.add(tableId);
+        return jdbcDriverInfo;
+    }
+
+    protected JdbcDriverInfo createJdbcDriverInfo(Connection connection) throws SQLException {
+        DatabaseMetaData metadata = connection.getMetaData();
+        return new JdbcDriverInfo(
+                metadata.getJDBCMajorVersion(),
+                metadata.getJDBCMinorVersion(),
+                metadata.getDriverName(),
+                metadata.getDatabaseProductName(),
+                metadata.getDatabaseProductVersion()
+        );
+    }
+
+    /**
+     * add connect properties
+     *
+     * @param properties
+     * @return
+     */
+    protected Properties addConnectionProperties(Properties properties) {
+        // do nothiong
+        return properties;
+    }
+
+    @Override
+    public PreparedStatement createPreparedStatement(
+            Connection db,
+            String query
+    ) throws SQLException {
+        log.trace("Creating a PreparedStatement '{}'", query);
+        PreparedStatement stmt = db.prepareStatement(query);
+        initializePreparedStatement(stmt);
+        return stmt;
+    }
+
+    /**
+     * init PreparedStatement
+     *
+     * @param stmt
+     * @throws SQLException
+     */
+    protected void initializePreparedStatement(PreparedStatement stmt) throws SQLException {
+        if (batchMaxRows > 0) {
+            stmt.setFetchSize(batchMaxRows);
+        }
+    }
+
+    @Override
+    public TableId parseToTableId(String fqn) {
+        List<String> parts = identifierRules().parseQualifiedIdentifier(fqn);
+        if (parts.isEmpty()) {
+            throw new IllegalArgumentException("Invalid fully qualified name: '" + fqn + "'");
         }
-      }
-      glog.debug("Used {} dialect to find {} {}", this, tableIds.size(), tableTypeDisplay);
-      return tableIds;
-    }
-  }
-
-  protected String catalogPattern() {
-    return catalogPattern;
-  }
-
-  protected String schemaPattern() {
-    return schemaPattern;
-  }
-
-  /**
-   * Determine whether the table with the specific name is to be included in the tables.
-   *
-   * <p>This method can be overridden to exclude certain database tables.
-   *
-   * @param table the identifier of the table; may be null
-   * @return true if the table should be included; false otherwise
-   */
-  protected boolean includeTable(TableId table) {
-    return true;
-  }
-
-  /**
-   * Find the available table types that are returned by the JDBC driver that case insensitively
-   * match the specified types.
-   *
-   * @param metadata the database metadata; may not be null but may be empty if no table types
-   * @param types    the case-independent table types that are desired
-   * @return the array of table types take directly from the list of available types returned by the
-   *     JDBC driver; never null
-   * @throws SQLException if there is an error with the database connection
-   */
-  protected String[] tableTypes(
-          DatabaseMetaData metadata,
-          Set<String> types
-  ) throws SQLException {
-    glog.debug("Using {} dialect to check support for {}", this, types);
-    // Compute the uppercase form of the desired types ...
-    Set<String> uppercaseTypes = new HashSet<>();
-    for (String type : types) {
-      if (type != null) {
-        uppercaseTypes.add(type.toUpperCase(Locale.ROOT));
-      }
-    }
-    // Now find out the available table types ...
-    Set<String> matchingTableTypes = new HashSet<>();
-    try (ResultSet rs = metadata.getTableTypes()) {
-      while (rs.next()) {
-        String tableType = rs.getString(1);
-        if (tableType != null && uppercaseTypes.contains(tableType.toUpperCase(Locale.ROOT))) {
-          matchingTableTypes.add(tableType);
+        if (parts.size() == 1) {
+            return new TableId(null, null, parts.get(0));
         }
-      }
-    }
-    String[] result = matchingTableTypes.toArray(new String[matchingTableTypes.size()]);
-    glog.debug("Used {} dialect to find table types: {}", this, result);
-    return result;
-  }
-
-  @Override
-  public IdentifierRules identifierRules() {
-    if (identifierRules.get() == null) {
-      // Otherwise try to get the actual quote string and separator from the database, since
-      // many databases allow them to be changed
-      try (Connection connection = getConnection()) {
-        DatabaseMetaData metaData = connection.getMetaData();
-        String leadingQuoteStr = metaData.getIdentifierQuoteString();
-        String trailingQuoteStr = leadingQuoteStr; // JDBC does not distinguish
-        String separator = metaData.getCatalogSeparator();
-        if (leadingQuoteStr == null || leadingQuoteStr.isEmpty()) {
-          leadingQuoteStr = defaultIdentifierRules.leadingQuoteString();
-          trailingQuoteStr = defaultIdentifierRules.trailingQuoteString();
+        if (parts.size() == 3) {
+            return new TableId(parts.get(0), parts.get(1), parts.get(2));
         }
-        if (separator == null || separator.isEmpty()) {
-          separator = defaultIdentifierRules.identifierDelimiter();
+        assert parts.size() >= 2;
+        if (useCatalog()) {
+            return new TableId(parts.get(0), null, parts.get(1));
         }
-        identifierRules.set(new IdentifierRules(separator, leadingQuoteStr, trailingQuoteStr));
-      } catch (SQLException e) {
-        if (defaultIdentifierRules != null) {
-          identifierRules.set(defaultIdentifierRules);
-          glog.warn("Unable to get identifier metadata; using default rules", e);
-        } else {
-          throw new io.openmessaging.connector.api.errors.ConnectException("Unable to get identifier metadata", e);
+        return new TableId(null, parts.get(0), parts.get(1));
+    }
+
+    /**
+     * Return whether the database uses JDBC catalogs.
+     *
+     * @return true if catalogs are used, or false otherwise
+     */
+    protected boolean useCatalog() {
+        return false;
+    }
+
+    @Override
+    public List<TableId> tableIds(Connection conn) throws SQLException {
+        DatabaseMetaData metadata = conn.getMetaData();
+        String[] tableTypes = tableTypes(metadata, this.tableTypes);
+        String tableTypeDisplay = displayableTableTypes(tableTypes, ", ");
+        log.debug("Using {} dialect to get {}", this, tableTypeDisplay);
+        try (ResultSet rs = metadata.getTables(catalogPattern(), schemaPattern(), "%", tableTypes)) {
+            List<TableId> tableIds = new ArrayList<>();
+            while (rs.next()) {
+                String catalogName = rs.getString(1);
+                String schemaName = rs.getString(2);
+                String tableName = rs.getString(3);
+                TableId tableId = new TableId(catalogName, schemaName, tableName);
+                if (includeTable(tableId)) {
+                    tableIds.add(tableId);
+                }
+            }
+            log.debug("Used {} dialect to find {} {}", this, tableIds.size(), tableTypeDisplay);
+            return tableIds;
+        }
+    }
+
+    protected String catalogPattern() {
+        return catalogPattern;
+    }
+
+    protected String schemaPattern() {
+        return schemaPattern;
+    }
+
+    /**
+     * Determine whether the table with the specific name is to be included in the tables.
+     *
+     * <p>This method can be overridden to exclude certain database tables.
+     *
+     * @param table the identifier of the table; may be null
+     * @return true if the table should be included; false otherwise
+     */
+    protected boolean includeTable(TableId table) {
+        return true;
+    }
+
+    /**
+     * Find the available table types that are returned by the JDBC driver that case insensitively
+     * match the specified types.
+     *
+     * @param metadata the database metadata; may not be null but may be empty if no table types
+     * @param types    the case-independent table types that are desired
+     * @return the array of table types take directly from the list of available types returned by the
+     * JDBC driver; never null
+     * @throws SQLException if there is an error with the database connection
+     */
+    protected String[] tableTypes(
+            DatabaseMetaData metadata,
+            Set<String> types
+    ) throws SQLException {
+        log.debug("Using {} dialect to check support for {}", this, types);
+        // Compute the uppercase form of the desired types ...
+        Set<String> uppercaseTypes = new HashSet<>();
+        for (String type : types) {
+            if (type != null) {
+                uppercaseTypes.add(type.toUpperCase(Locale.ROOT));
+            }
+        }
+        // Now find out the available table types ...
+        Set<String> matchingTableTypes = new HashSet<>();
+        try (ResultSet rs = metadata.getTableTypes()) {
+            while (rs.next()) {
+                String tableType = rs.getString(1);
+                if (tableType != null && uppercaseTypes.contains(tableType.toUpperCase(Locale.ROOT))) {
+                    matchingTableTypes.add(tableType);
+                }
+            }
+        }
+        String[] result = matchingTableTypes.toArray(new String[matchingTableTypes.size()]);
+        log.debug("Used {} dialect to find table types: {}", this, result);
+        return result;
+    }
+
+    @Override
+    public IdentifierRules identifierRules() {
+        if (identifierRules.get() == null) {
+            // Otherwise try to get the actual quote string and separator from the database, since
+            // many databases allow them to be changed
+            try (Connection connection = getConnection()) {
+                DatabaseMetaData metaData = connection.getMetaData();
+                String leadingQuoteStr = metaData.getIdentifierQuoteString();
+                String trailingQuoteStr = leadingQuoteStr; // JDBC does not distinguish
+                String separator = metaData.getCatalogSeparator();
+                if (leadingQuoteStr == null || leadingQuoteStr.isEmpty()) {
+                    leadingQuoteStr = defaultIdentifierRules.leadingQuoteString();
+                    trailingQuoteStr = defaultIdentifierRules.trailingQuoteString();
+                }
+                if (separator == null || separator.isEmpty()) {
+                    separator = defaultIdentifierRules.identifierDelimiter();
+                }
+                identifierRules.set(new IdentifierRules(separator, leadingQuoteStr, trailingQuoteStr));
+            } catch (SQLException e) {
+                if (defaultIdentifierRules != null) {
+                    identifierRules.set(defaultIdentifierRules);
+                    log.warn("Unable to get identifier metadata; using default rules", e);
+                } else {
+                    throw new io.openmessaging.connector.api.errors.ConnectException("Unable to get identifier metadata", e);
+                }
+            }
+        }
+        return identifierRules.get();
+    }
+
+    @Override
+    public ExpressionBuilder expressionBuilder() {
+        return identifierRules().expressionBuilder()
+                .setQuoteIdentifiers(quoteSqlIdentifiers);
+    }
+
+    /**
+     * Return current time at the database
+     *
+     * @param conn database connection
+     * @param cal  calendar
+     * @return the current time at the database
+     */
+    @Override
+    public Timestamp currentTimeOnDB(
+            Connection conn,
+            Calendar cal
+    ) throws SQLException {
+        String query = currentTimestampDatabaseQuery();
+        assert query != null;
+        assert !query.isEmpty();
+        try (Statement stmt = conn.createStatement()) {
+            log.debug("executing query " + query + " to get current time from database");
+            try (ResultSet rs = stmt.executeQuery(query)) {
+                if (rs.next()) {
+                    return rs.getTimestamp(1, cal);
+                } else {
+                    throw new io.openmessaging.connector.api.errors.ConnectException(
+                            "Unable to get current time from DB using " + this + " and query '" + query + "'"
+                    );
+                }
+            }
+        } catch (SQLException e) {
+            log.error("Failed to get current time from DB using {} and query '{}'", this, query, e);
+            throw e;
         }
-      }
-    }
-    return identifierRules.get();
-  }
-
-  @Override
-  public ExpressionBuilder expressionBuilder() {
-    return identifierRules().expressionBuilder()
-            .setQuoteIdentifiers(quoteSqlIdentifiers);
-  }
-
-  /**
-   * Return current time at the database
-   * @param conn database connection
-   * @param cal  calendar
-   * @return the current time at the database
-   */
-  @Override
-  public Timestamp currentTimeOnDB(
-          Connection conn,
-          Calendar cal
-  ) throws SQLException {
-    String query = currentTimestampDatabaseQuery();
-    assert query != null;
-    assert !query.isEmpty();
-    try (Statement stmt = conn.createStatement()) {
-      glog.debug("executing query " + query + " to get current time from database");
-      try (ResultSet rs = stmt.executeQuery(query)) {
-        if (rs.next()) {
-          return rs.getTimestamp(1, cal);
+    }
+
+    /**
+     * Get the query string to determine the current timestamp in the database.
+     *
+     * @return the query string; never null or empty
+     */
+    protected String currentTimestampDatabaseQuery() {
+        return "SELECT CURRENT_TIMESTAMP";
+    }
+
+    @Override
+    public boolean tableExists(
+            Connection connection,
+            TableId tableId
+    ) throws SQLException {
+        DatabaseMetaData metadata = connection.getMetaData();
+        String[] tableTypes = tableTypes(metadata, this.tableTypes);
+        String tableTypeDisplay = displayableTableTypes(tableTypes, "/");
+        log.info("Checking {} dialect for existence of {} {}", this, tableTypeDisplay, tableId);
+        try (ResultSet rs = connection.getMetaData().getTables(
+                tableId.catalogName(),
+                tableId.schemaName(),
+                tableId.tableName(),
+                tableTypes
+        )) {
+            final boolean exists = rs.next();
+            log.info(
+                    "Using {} dialect {} {} {}",
+                    this,
+                    tableTypeDisplay,
+                    tableId,
+                    exists ? "present" : "absent"
+            );
+            return exists;
+        }
+    }
+
+    protected String displayableTableTypes(String[] types, String delim) {
+        return Arrays.stream(types).sorted().collect(Collectors.joining(delim));
+    }
+
+    @Override
+    public Map<ColumnId, ColumnDefinition> describeColumns(
+            Connection connection,
+            String tablePattern,
+            String columnPattern
+    ) throws SQLException {
+        //if the table pattern is fqn, then just use the actual table name
+        TableId tableId = parseToTableId(tablePattern);
+        String catalog = tableId.catalogName() != null ? tableId.catalogName() : catalogPattern;
+        String schema = tableId.schemaName() != null ? tableId.schemaName() : schemaPattern;
+        return describeColumns(connection, catalog, schema, tableId.tableName(), columnPattern);
+    }
+
+    @Override
+    public Map<ColumnId, ColumnDefinition> describeColumns(
+            Connection connection,
+            String catalogPattern,
+            String schemaPattern,
+            String tablePattern,
+            String columnPattern
+    ) throws SQLException {
+        log.debug(
+                "Querying {} dialect column metadata for catalog:{} schema:{} table:{}",
+                this,
+                catalogPattern,
+                schemaPattern,
+                tablePattern);
+
+        // Get the primary keys of the table(s) ...
+        final Set<ColumnId> pkColumns = primaryKeyColumns(
+                connection,
+                catalogPattern,
+                schemaPattern,
+                tablePattern
+        );
+        Map<ColumnId, ColumnDefinition> results = new HashMap<>();
+        try (ResultSet rs = connection.getMetaData().getColumns(
+                catalogPattern,
+                schemaPattern,
+                tablePattern,
+                columnPattern
+        )) {
+            final int rsColumnCount = rs.getMetaData().getColumnCount();
+            while (rs.next()) {
+                final String catalogName = rs.getString(1);
+                final String schemaName = rs.getString(2);
+                final String tableName = rs.getString(3);
+                final TableId tableId = new TableId(catalogName, schemaName, tableName);
+                final String columnName = rs.getString(4);
+                final ColumnId columnId = new ColumnId(tableId, columnName, null);
+                final int jdbcType = rs.getInt(5);
+                final String typeName = rs.getString(6);
+                final int precision = rs.getInt(7);
+                final int scale = rs.getInt(9);
+                final String typeClassName = null;
+                ColumnDefinition.Nullability nullability;
+                final int nullableValue = rs.getInt(11);
+                switch (nullableValue) {
+                    case DatabaseMetaData.columnNoNulls:
+                        nullability = ColumnDefinition.Nullability.NOT_NULL;
+                        break;
+                    case DatabaseMetaData.columnNullable:
+                        nullability = ColumnDefinition.Nullability.NULL;
+                        break;
+                    case DatabaseMetaData.columnNullableUnknown:
+                    default:
+                        nullability = ColumnDefinition.Nullability.UNKNOWN;
+                        break;
+                }
+                Boolean autoIncremented = null;
+                if (rsColumnCount >= 23) {
+                    // Not all drivers include all columns ...
+                    String isAutoIncremented = rs.getString(23);
+                    if ("yes".equalsIgnoreCase(isAutoIncremented)) {
+                        autoIncremented = Boolean.TRUE;
+                    } else if ("no".equalsIgnoreCase(isAutoIncremented)) {
+                        autoIncremented = Boolean.FALSE;
+                    }
+                }
+                Boolean signed = null;
+                Boolean caseSensitive = null;
+                Boolean searchable = null;
+                Boolean currency = null;
+                Integer displaySize = null;
+                boolean isPrimaryKey = pkColumns.contains(columnId);
+                if (isPrimaryKey) {
+                    // Some DBMSes report pks as null
+                    nullability = ColumnDefinition.Nullability.NOT_NULL;
+                }
+                ColumnDefinition defn = columnDefinition(
+                        rs,
+                        columnId,
+                        jdbcType,
+                        typeName,
+                        typeClassName,
+                        nullability,
+                        ColumnDefinition.Mutability.UNKNOWN,
+                        precision,
+                        scale,
+                        signed,
+                        displaySize,
+                        autoIncremented,
+                        caseSensitive,
+                        searchable,
+                        currency,
+                        isPrimaryKey
+                );
+                results.put(columnId, defn);
+            }
+            return results;
+        }
+    }
+
+    @Override
+    public Map<ColumnId, ColumnDefinition> describeColumns(Connection conn,
+                                                           TableId tableId, ResultSetMetaData rsMetadata) throws SQLException {
+        ColumnDefAdjuster adjuster;
+        if (tableId == null) {
+            adjuster = new ColumnDefAdjuster();
         } else {
-          throw new io.openmessaging.connector.api.errors.ConnectException(
-                  "Unable to get current time from DB using " + this + " and query '" + query + "'"
-          );
+            String catalog = tableId.catalogName() != null ? tableId.catalogName() : catalogPattern;
+            String schema = tableId.schemaName() != null ? tableId.schemaName() : schemaPattern;
+            adjuster = ColumnDefAdjuster.create(
+                    conn, catalog, schema, tableId.tableName(), null);
         }
-      }
-    } catch (SQLException e) {
-      glog.error("Failed to get current time from DB using {} and query '{}'", this, query, e);
-      throw e;
-    }
-  }
-
-  /**
-   * Get the query string to determine the current timestamp in the database.
-   * @return the query string; never null or empty
-   */
-  protected String currentTimestampDatabaseQuery() {
-    return "SELECT CURRENT_TIMESTAMP";
-  }
-
-  @Override
-  public boolean tableExists(
-          Connection connection,
-          TableId tableId
-  ) throws SQLException {
-    DatabaseMetaData metadata = connection.getMetaData();
-    String[] tableTypes = tableTypes(metadata, this.tableTypes);
-    String tableTypeDisplay = displayableTableTypes(tableTypes, "/");
-    glog.info("Checking {} dialect for existence of {} {}", this, tableTypeDisplay, tableId);
-    try (ResultSet rs = connection.getMetaData().getTables(
-            tableId.catalogName(),
-            tableId.schemaName(),
-            tableId.tableName(),
-            tableTypes
-    )) {
-      final boolean exists = rs.next();
-      glog.info(
-              "Using {} dialect {} {} {}",
-              this,
-              tableTypeDisplay,
-              tableId,
-              exists ? "present" : "absent"
-      );
-      return exists;
-    }
-  }
-
-  protected String displayableTableTypes(String[] types, String delim) {
-    return Arrays.stream(types).sorted().collect(Collectors.joining(delim));
-  }
-
-  @Override
-  public Map<ColumnId, ColumnDefinition> describeColumns(
-          Connection connection,
-          String tablePattern,
-          String columnPattern
-  ) throws SQLException {
-    //if the table pattern is fqn, then just use the actual table name
-    TableId tableId = parseToTableId(tablePattern);
-    String catalog = tableId.catalogName() != null ? tableId.catalogName() : catalogPattern;
-    String schema = tableId.schemaName() != null ? tableId.schemaName() : schemaPattern;
-    return describeColumns(connection, catalog , schema, tableId.tableName(), columnPattern);
-  }
-
-  @Override
-  public Map<ColumnId, ColumnDefinition> describeColumns(
-          Connection connection,
-          String catalogPattern,
-          String schemaPattern,
-          String tablePattern,
-          String columnPattern
-  ) throws SQLException {
-    glog.debug(
-            "Querying {} dialect column metadata for catalog:{} schema:{} table:{}",
-            this,
-            catalogPattern,
-            schemaPattern,
-            tablePattern);
-
-    // Get the primary keys of the table(s) ...
-    final Set<ColumnId> pkColumns = primaryKeyColumns(
-            connection,
-            catalogPattern,
-            schemaPattern,
-            tablePattern
-    );
-    Map<ColumnId, ColumnDefinition> results = new HashMap<>();
-    try (ResultSet rs = connection.getMetaData().getColumns(
-            catalogPattern,
-            schemaPattern,
-            tablePattern,
-            columnPattern
-    )) {
-      final int rsColumnCount = rs.getMetaData().getColumnCount();
-      while (rs.next()) {
-        final String catalogName = rs.getString(1);
-        final String schemaName = rs.getString(2);
-        final String tableName = rs.getString(3);
-        final TableId tableId = new TableId(catalogName, schemaName, tableName);
-        final String columnName = rs.getString(4);
-        final ColumnId columnId = new ColumnId(tableId, columnName, null);
-        final int jdbcType = rs.getInt(5);
-        final String typeName = rs.getString(6);
-        final int precision = rs.getInt(7);
-        final int scale = rs.getInt(9);
-        final String typeClassName = null;
-        ColumnDefinition.Nullability nullability;
-        final int nullableValue = rs.getInt(11);
-        switch (nullableValue) {
-          case DatabaseMetaData.columnNoNulls:
-            nullability = ColumnDefinition.Nullability.NOT_NULL;
-            break;
-          case DatabaseMetaData.columnNullable:
-            nullability = ColumnDefinition.Nullability.NULL;
-            break;
-          case DatabaseMetaData.columnNullableUnknown:
-          default:
-            nullability = ColumnDefinition.Nullability.UNKNOWN;
-            break;
+        Map<ColumnId, ColumnDefinition> result = new LinkedHashMap<>();
+        for (int i = 1; i <= rsMetadata.getColumnCount(); ++i) {
+            ColumnDefinition defn = describeColumn(rsMetadata, adjuster, i);
+            result.put(defn.id(), defn);
         }
-        Boolean autoIncremented = null;
-        if (rsColumnCount >= 23) {
-          // Not all drivers include all columns ...
-          String isAutoIncremented = rs.getString(23);
-          if ("yes".equalsIgnoreCase(isAutoIncremented)) {
-            autoIncremented = Boolean.TRUE;
-          } else if ("no".equalsIgnoreCase(isAutoIncremented)) {
-            autoIncremented = Boolean.FALSE;
-          }
+        return result;
+    }
+
+    /**
+     * Create a definition for the specified column in the result set.
+     *
+     * @param rsMetadata the result set metadata; may not be null
+     * @param column     the column number, starting at 1 for the first column
+     * @return the column definition; never null
+     * @throws SQLException if there is an error accessing the result set metadata
+     */
+    protected ColumnDefinition describeColumn(
+            ResultSetMetaData rsMetadata,
+            ColumnDefAdjuster adjuster,
+            int column
+    ) throws SQLException {
+        String catalog = rsMetadata.getCatalogName(column);
+        String schema = rsMetadata.getSchemaName(column);
+        String tableName = rsMetadata.getTableName(column);
+        TableId tableId = new TableId(catalog, schema, tableName);
+        String name = rsMetadata.getColumnName(column);
+        String alias = rsMetadata.getColumnLabel(column);
+        ColumnId id = new ColumnId(tableId, name, alias);
+
+        ColumnDefinition.Nullability nullability = adjuster.nullable(name);
+        if (nullability == null) {
+            nullability = columnIsNullAble(rsMetadata, column);
+        }
+
+        ColumnDefinition.Mutability mutability = ColumnDefinition.Mutability.MAYBE_WRITABLE;
+        if (rsMetadata.isReadOnly(column)) {
+            mutability = ColumnDefinition.Mutability.READ_ONLY;
+        } else if (rsMetadata.isWritable(column)) {
+            mutability = ColumnDefinition.Mutability.MAYBE_WRITABLE;
+        } else if (rsMetadata.isDefinitelyWritable(column)) {
+            mutability = ColumnDefinition.Mutability.WRITABLE;
+        }
+        return new ColumnDefinition(
+                id,
+                rsMetadata.getColumnType(column),
+                rsMetadata.getColumnTypeName(column),
+                rsMetadata.getColumnClassName(column),
+                nullability,
+                mutability,
+                rsMetadata.getPrecision(column),
+                rsMetadata.getScale(column),
+                rsMetadata.isSigned(column),
+                rsMetadata.getColumnDisplaySize(column),
+                rsMetadata.isAutoIncrement(column),
+                false,
+                rsMetadata.isSearchable(column),
+                rsMetadata.isCurrency(column),
+                false
+        );
+    }
+
+    protected ColumnDefinition.Nullability columnIsNullAble(ResultSetMetaData rsMetadata, int column) throws SQLException {
+        switch (rsMetadata.isNullable(column)) {
+            case ResultSetMetaData.columnNullable:
+                return ColumnDefinition.Nullability.NULL;
+            case ResultSetMetaData.columnNoNulls:
+                return ColumnDefinition.Nullability.NOT_NULL;
+            case ResultSetMetaData.columnNullableUnknown:
+            default:
+                return ColumnDefinition.Nullability.UNKNOWN;
+        }
+    }
+
+    protected Set<ColumnId> primaryKeyColumns(
+            Connection connection,
+            String catalogPattern,
+            String schemaPattern,
+            String tablePattern
+    ) throws SQLException {
+
+        // Get the primary keys of the table(s) ...
+        final Set<ColumnId> pkColumns = new HashSet<>();
+        try (ResultSet rs = connection.getMetaData().getPrimaryKeys(
+                catalogPattern, schemaPattern, tablePattern)) {
+            while (rs.next()) {
+                String catalogName = rs.getString(1);
+                String schemaName = rs.getString(2);
+                String tableName = rs.getString(3);
+                TableId tableId = new TableId(catalogName, schemaName, tableName);
+                final String colName = rs.getString(4);
+                ColumnId columnId = new ColumnId(tableId, colName);
+                pkColumns.add(columnId);
+            }
+        }
+        return pkColumns;
+    }
+
+    @Override
+    public Map<ColumnId, ColumnDefinition> describeColumnsByQuerying(
+            Connection db,
+            TableId tableId
+    ) throws SQLException {
+        String queryStr = "SELECT * FROM {} LIMIT 1";
+        String quotedName = expressionBuilder().append(tableId).toString();
+        try (PreparedStatement stmt = db.prepareStatement(queryStr)) {
+            stmt.setString(1, quotedName);
+            try (ResultSet rs = stmt.executeQuery()) {
+                ResultSetMetaData rsmd = rs.getMetaData();
+                return describeColumns(db, tableId, rsmd);
+            }
+        }
+    }
+
+
+    @Override
+    public TableDefinition describeTable(
+            Connection connection,
+            TableId tableId
+    ) throws SQLException {
+        Map<ColumnId, ColumnDefinition> columnDefns = describeColumns(connection, tableId.catalogName(),
+                tableId.schemaName(),
+                tableId.tableName(), null
+        );
+        if (columnDefns.isEmpty()) {
+            return null;
         }
-        Boolean signed = null;
-        Boolean caseSensitive = null;
-        Boolean searchable = null;
-        Boolean currency = null;
-        Integer displaySize = null;
-        boolean isPrimaryKey = pkColumns.contains(columnId);
-        if (isPrimaryKey) {
-          // Some DBMSes report pks as null
-          nullability = ColumnDefinition.Nullability.NOT_NULL;
+        TableType tableType = tableTypeFor(connection, tableId);
+        return new TableDefinition(tableId, columnDefns.values(), tableType);
+    }
+
+    protected TableType tableTypeFor(
+            Connection connection,
+            TableId tableId
+    ) throws SQLException {
+        DatabaseMetaData metadata = connection.getMetaData();
+        String[] tableTypes = tableTypes(metadata, this.tableTypes);
+        String tableTypeDisplay = displayableTableTypes(tableTypes, "/");
+        log.info("Checking {} dialect for type of {} {}", this, tableTypeDisplay, tableId);
+        try (ResultSet rs = connection.getMetaData().getTables(
+                tableId.catalogName(),
+                tableId.schemaName(),
+                tableId.tableName(),
+                tableTypes
+        )) {
+            if (rs.next()) {
+                //final String catalogName = rs.getString(1);
+                //final String schemaName = rs.getString(2);
+                //final String tableName = rs.getString(3);
+                final String tableType = rs.getString(4);
+                try {
+                    return TableType.get(tableType);
+                } catch (IllegalArgumentException e) {
+                    log.warn(
+                            "{} dialect found unknown type '{}' for {} {}; using TABLE",
+                            this,
+                            tableType,
+                            tableTypeDisplay,
+                            tableId
+                    );
+                    return TableType.TABLE;
+                }
+            }
         }
-        ColumnDefinition defn = columnDefinition(
-                rs,
-                columnId,
+        log.warn(
+                "{} dialect did not find type for {} {}; using TABLE",
+                this,
+                tableTypeDisplay,
+                tableId
+        );
+        return TableType.TABLE;
+    }
+
+    /**
+     * Create a ColumnDefinition with supplied values and the result set from the {@link
+     * DatabaseMetaData#getColumns(String, String, String, String)} call. By default that method does
+     * not describe whether the column is signed, case sensitive, searchable, currency, or the
+     * preferred display size.
+     *
+     * <p>Subclasses can override this method to extract additional non-standard characteristics from
+     * the result set, and override the characteristics determined using the standard JDBC metadata
+     * columns and supplied as parameters.
+     *
+     * @param resultSet        the result set
+     * @param id               the column identifier
+     * @param jdbcType         the JDBC type of the column
+     * @param typeName         the name of the column's type
+     * @param classNameForType the name of the class used as instances of the value when {@link
+     *                         ResultSet#getObject(int)} is called
+     * @param nullability      the nullability of the column
+     * @param mutability       the mutability of the column
+     * @param precision        the precision of the column for numeric values, or the length for
+     *                         non-numeric values
+     * @param scale            the scale of the column for numeric values; ignored for other values
+     * @param signedNumbers    true if the column holds signed numeric values; null if not known
+     * @param displaySize      the preferred display size for the column values; null if not known
+     * @param autoIncremented  true if the column is auto-incremented; null if not known
+     * @param caseSensitive    true if the column values are case-sensitive; null if not known
+     * @param searchable       true if the column is searchable; null if no; null if not known known
+     * @param currency         true if the column is a currency value
+     * @param isPrimaryKey     true if the column is part of the primary key; null if not known known
+     * @return the column definition; never null
+     */
+    protected ColumnDefinition columnDefinition(
+            ResultSet resultSet,
+            ColumnId id,
+            int jdbcType,
+            String typeName,
+            String classNameForType,
+            ColumnDefinition.Nullability nullability,
+            ColumnDefinition.Mutability mutability,
+            int precision,
+            int scale,
+            Boolean signedNumbers,
+            Integer displaySize,
+            Boolean autoIncremented,
+            Boolean caseSensitive,
+            Boolean searchable,
+            Boolean currency,
+            Boolean isPrimaryKey
+    ) {
+        return new ColumnDefinition(
+                id,
                 jdbcType,
                 typeName,
-                typeClassName,
+                classNameForType,
                 nullability,
-                ColumnDefinition.Mutability.UNKNOWN,
+                mutability,
                 precision,
                 scale,
-                signed,
-                displaySize,
-                autoIncremented,
-                caseSensitive,
-                searchable,
-                currency,
-                isPrimaryKey
+                signedNumbers != null ? signedNumbers.booleanValue() : false,
+                displaySize != null ? displaySize.intValue() : 0,
+                autoIncremented != null ? autoIncremented.booleanValue() : false,
+                caseSensitive != null ? caseSensitive.booleanValue() : false,
+                searchable != null ? searchable.booleanValue() : false,
+                currency != null ? currency.booleanValue() : false,
+                isPrimaryKey != null ? isPrimaryKey.booleanValue() : false
         );
-        results.put(columnId, defn);
-      }
-      return results;
-    }
-  }
-
-  @Override
-  public Map<ColumnId, ColumnDefinition> describeColumns(Connection conn,
-        TableId tableId,ResultSetMetaData rsMetadata) throws SQLException {
-    ColumnDefAdjuster adjuster;
-    //querySql模式tableId会是空的
-    if(tableId == null){
-      adjuster = new ColumnDefAdjuster();
-    }else{
-      String catalog = tableId.catalogName() != null ? tableId.catalogName() : catalogPattern;
-      String schema = tableId.schemaName() != null ? tableId.schemaName() : schemaPattern;
-      adjuster = ColumnDefAdjuster.create(
-              conn, catalog, schema, tableId.tableName(), null);
-    }
-    Map<ColumnId, ColumnDefinition> result = new LinkedHashMap<>();
-    for (int i = 1; i <= rsMetadata.getColumnCount(); ++i) {
-      ColumnDefinition defn = describeColumn(rsMetadata, adjuster, i);
-      result.put(defn.id(), defn);
-    }
-    return result;
-  }
-
-  /**
-   * Create a definition for the specified column in the result set.
-   * @param rsMetadata the result set metadata; may not be null
-   * @param column     the column number, starting at 1 for the first column
-   * @return the column definition; never null
-   * @throws SQLException if there is an error accessing the result set metadata
-   */
-  protected ColumnDefinition describeColumn(
-          ResultSetMetaData rsMetadata,
-          ColumnDefAdjuster adjuster,
-          int column
-  ) throws SQLException {
-    String catalog = rsMetadata.getCatalogName(column);
-    String schema = rsMetadata.getSchemaName(column);
-    String tableName = rsMetadata.getTableName(column);
-    TableId tableId = new TableId(catalog, schema, tableName);
-    String name = rsMetadata.getColumnName(column);
-    String alias = rsMetadata.getColumnLabel(column);
-    ColumnId id = new ColumnId(tableId, name, alias);
-
-    ColumnDefinition.Nullability nullability = adjuster.nullable(name);
-    if(nullability == null){
-      nullability = columnIsNullAble(rsMetadata, column);
-    }
-
-    ColumnDefinition.Mutability mutability = ColumnDefinition.Mutability.MAYBE_WRITABLE;
-    if (rsMetadata.isReadOnly(column)) {
-      mutability = ColumnDefinition.Mutability.READ_ONLY;
-    } else if (rsMetadata.isWritable(column)) {
-      mutability = ColumnDefinition.Mutability.MAYBE_WRITABLE;
-    } else if (rsMetadata.isDefinitelyWritable(column)) {
-      mutability = ColumnDefinition.Mutability.WRITABLE;
-    }
-    return new ColumnDefinition(
-            id,
-            rsMetadata.getColumnType(column),
-            rsMetadata.getColumnTypeName(column),
-            rsMetadata.getColumnClassName(column),
-            nullability,
-            mutability,
-            rsMetadata.getPrecision(column),
-            rsMetadata.getScale(column),
-            rsMetadata.isSigned(column),
-            rsMetadata.getColumnDisplaySize(column),
-            rsMetadata.isAutoIncrement(column),
-            false,
-            rsMetadata.isSearchable(column),
-            rsMetadata.isCurrency(column),
-            false
-    );
-  }
-
-  protected ColumnDefinition.Nullability columnIsNullAble(ResultSetMetaData rsMetadata, int column) throws SQLException {
-    switch (rsMetadata.isNullable(column)) {
-      case ResultSetMetaData.columnNullable:
-        return ColumnDefinition.Nullability.NULL;
-      case ResultSetMetaData.columnNoNulls:
-        return ColumnDefinition.Nullability.NOT_NULL;
-      case ResultSetMetaData.columnNullableUnknown:
-      default:
-        return ColumnDefinition.Nullability.UNKNOWN;
-    }
-  }
-
-  protected Set<ColumnId> primaryKeyColumns(
-          Connection connection,
-          String catalogPattern,
-          String schemaPattern,
-          String tablePattern
-  ) throws SQLException {
-
-    // Get the primary keys of the table(s) ...
-    final Set<ColumnId> pkColumns = new HashSet<>();
-    try (ResultSet rs = connection.getMetaData().getPrimaryKeys(
-            catalogPattern, schemaPattern, tablePattern)) {
-      while (rs.next()) {
-        String catalogName = rs.getString(1);
-        String schemaName = rs.getString(2);
-        String tableName = rs.getString(3);
-        TableId tableId = new TableId(catalogName, schemaName, tableName);
-        final String colName = rs.getString(4);
-        ColumnId columnId = new ColumnId(tableId, colName);
-        pkColumns.add(columnId);
-      }
-    }
-    return pkColumns;
-  }
-
-  @Override
-  public Map<ColumnId, ColumnDefinition> describeColumnsByQuerying(
-          Connection db,
-          TableId tableId
-  ) throws SQLException {
-    String queryStr = "SELECT * FROM {} LIMIT 1";
-    String quotedName = expressionBuilder().append(tableId).toString();
-    try (PreparedStatement stmt = db.prepareStatement(queryStr)) {
-      stmt.setString(1, quotedName);
-      try (ResultSet rs = stmt.executeQuery()) {
-        ResultSetMetaData rsmd = rs.getMetaData();
-        return describeColumns(db, tableId, rsmd);
-      }
-    }
-  }
-
-
-  @Override
-  public TableDefinition describeTable(
-          Connection connection,
-          TableId tableId
-  ) throws SQLException {
-    Map<ColumnId, ColumnDefinition> columnDefns = describeColumns(connection, tableId.catalogName(),
-            tableId.schemaName(),
-            tableId.tableName(), null
-    );
-    if (columnDefns.isEmpty()) {
-      return null;
-    }
-    TableType tableType = tableTypeFor(connection, tableId);
-    return new TableDefinition(tableId, columnDefns.values(), tableType);
-  }
-
-  protected TableType tableTypeFor(
-          Connection connection,
-          TableId tableId
-  ) throws SQLException {
-    DatabaseMetaData metadata = connection.getMetaData();
-    String[] tableTypes = tableTypes(metadata, this.tableTypes);
-    String tableTypeDisplay = displayableTableTypes(tableTypes, "/");
-    glog.info("Checking {} dialect for type of {} {}", this, tableTypeDisplay, tableId);
-    try (ResultSet rs = connection.getMetaData().getTables(
-            tableId.catalogName(),
-            tableId.schemaName(),
-            tableId.tableName(),
-            tableTypes
-    )) {
-      if (rs.next()) {
-        //final String catalogName = rs.getString(1);
-        //final String schemaName = rs.getString(2);
-        //final String tableName = rs.getString(3);
-        final String tableType = rs.getString(4);
-        try {
-          return TableType.get(tableType);
-        } catch (IllegalArgumentException e) {
-          glog.warn(
-                  "{} dialect found unknown type '{}' for {} {}; using TABLE",
-                  this,
-                  tableType,
-                  tableTypeDisplay,
-                  tableId
-          );
-          return TableType.TABLE;
-        }
-      }
-    }
-    glog.warn(
-            "{} dialect did not find type for {} {}; using TABLE",
-            this,
-            tableTypeDisplay,
-            tableId
-    );
-    return TableType.TABLE;
-  }
-
-  /**
-   * Create a ColumnDefinition with supplied values and the result set from the {@link
-   * DatabaseMetaData#getColumns(String, String, String, String)} call. By default that method does
-   * not describe whether the column is signed, case sensitive, searchable, currency, or the
-   * preferred display size.
-   *
-   * <p>Subclasses can override this method to extract additional non-standard characteristics from
-   * the result set, and override the characteristics determined using the standard JDBC metadata
-   * columns and supplied as parameters.
-   *
-   * @param resultSet        the result set
-   * @param id               the column identifier
-   * @param jdbcType         the JDBC type of the column
-   * @param typeName         the name of the column's type
-   * @param classNameForType the name of the class used as instances of the value when {@link
-   *                         ResultSet#getObject(int)} is called
-   * @param nullability      the nullability of the column
-   * @param mutability       the mutability of the column
-   * @param precision        the precision of the column for numeric values, or the length for
-   *                         non-numeric values
-   * @param scale            the scale of the column for numeric values; ignored for other values
-   * @param signedNumbers    true if the column holds signed numeric values; null if not known
-   * @param displaySize      the preferred display size for the column values; null if not known
-   * @param autoIncremented  true if the column is auto-incremented; null if not known
-   * @param caseSensitive    true if the column values are case-sensitive; null if not known
-   * @param searchable       true if the column is searchable; null if no; null if not known known
-   * @param currency         true if the column is a currency value
-   * @param isPrimaryKey     true if the column is part of the primary key; null if not known known
-   * @return the column definition; never null
-   */
-  protected ColumnDefinition columnDefinition(
-          ResultSet resultSet,
-          ColumnId id,
-          int jdbcType,
-          String typeName,
-          String classNameForType,
-          ColumnDefinition.Nullability nullability,
-          ColumnDefinition.Mutability mutability,
-          int precision,
-          int scale,
-          Boolean signedNumbers,
-          Integer displaySize,
-          Boolean autoIncremented,
-          Boolean caseSensitive,
-          Boolean searchable,
-          Boolean currency,
-          Boolean isPrimaryKey
-  ) {
-    return new ColumnDefinition(
-            id,
-            jdbcType,
-            typeName,
-            classNameForType,
-            nullability,
-            mutability,
-            precision,
-            scale,
-            signedNumbers != null ? signedNumbers.booleanValue() : false,
-            displaySize != null ? displaySize.intValue() : 0,
-            autoIncremented != null ? autoIncremented.booleanValue() : false,
-            caseSensitive != null ? caseSensitive.booleanValue() : false,
-            searchable != null ? searchable.booleanValue() : false,
-            currency != null ? currency.booleanValue() : false,
-            isPrimaryKey != null ? isPrimaryKey.booleanValue() : false
-    );
-  }
+    }
 
 //  @Override
 //  public TimestampIncrementingCriteria criteriaFor(
@@ -884,1005 +908,1006 @@ public class GenericDatabaseDialect implements DatabaseDialect {
 //            incrementingColumn, incrementingColumnStep, timestampColumns,timestampColumnStep, timeZone);
 //  }
 
-  /**
-   * Determine the name of the field. By default this is the column alias or name.
-   * @param columnDefinition the column definition; never null
-   * @return the field name; never null
-   */
-  protected String fieldNameFor(ColumnDefinition columnDefinition) {
-    return columnDefinition.id().aliasOrName();
-  }
-
-
-  @Override
-  public String addFieldToSchema(
-          ColumnDefinition columnDefn,
-          Schema schema,
-          int index
-  ) {
-    return addFieldToSchema(columnDefn, schema, fieldNameFor(columnDefn), index,columnDefn.type(),
-            columnDefn.isOptional()
-    );
-  }
-
-  /**
-   * add field to schema
-   * @param columnDefn
-   * @param builder
-   * @param fieldName
-   * @param index
-   * @param sqlType
-   * @param optional
-   * @return
-   */
-  @SuppressWarnings("fallthrough")
-  protected String addFieldToSchema(
-          final ColumnDefinition columnDefn,
-          final Schema builder,
-          final String fieldName,
-          final int index,
-          final int sqlType,
-          final boolean optional
-  ) {
-    int precision = columnDefn.precision();
-    int scale = columnDefn.scale();
-    switch (sqlType) {
-      case Types.NULL: {
-        glog.debug("JDBC type 'NULL' not currently supported for column '{}'", fieldName);
-        return null;
-      }
-      case Types.BOOLEAN: {
-        builder.addField(new Field(index, fieldName, SchemaBuilder.bool().build()));
-        break;
-      }
-
-      // ints <= 8 bits
-      case Types.BIT: {
-        builder.addField(new Field(index, fieldName, SchemaBuilder.int8().build()));
-        break;
-      }
-
-      case Types.TINYINT: {
-        if (columnDefn.isSignedNumber()) {
-          builder.addField(new Field(index, fieldName, SchemaBuilder.int8().build()));
-        } else {
-          builder.addField(new Field(index, fieldName, SchemaBuilder.int32().build()));
+    /**
+     * Determine the name of the field. By default this is the column alias or name.
+     *
+     * @param columnDefinition the column definition; never null
+     * @return the field name; never null
+     */
+    protected String fieldNameFor(ColumnDefinition columnDefinition) {
+        return columnDefinition.id().aliasOrName();
+    }
+
+
+    @Override
+    public String addFieldToSchema(
+            ColumnDefinition columnDefn,
+            Schema schema,
+            int index
+    ) {
+        return addFieldToSchema(columnDefn, schema, fieldNameFor(columnDefn), index, columnDefn.type(),
+                columnDefn.isOptional()
+        );
+    }
+
+    /**
+     * add field to schema
+     *
+     * @param columnDefn
+     * @param builder
+     * @param fieldName
+     * @param index
+     * @param sqlType
+     * @param optional
+     * @return
+     */
+    @SuppressWarnings("fallthrough")
+    protected String addFieldToSchema(
+            final ColumnDefinition columnDefn,
+            final Schema builder,
+            final String fieldName,
+            final int index,
+            final int sqlType,
+            final boolean optional
+    ) {
+        int precision = columnDefn.precision();
+        int scale = columnDefn.scale();
+        switch (sqlType) {
+            case Types.NULL: {
+                log.debug("JDBC type 'NULL' not currently supported for column '{}'", fieldName);
+                return null;
+            }
+            case Types.BOOLEAN: {
+                builder.addField(new Field(index, fieldName, SchemaBuilder.bool().build()));
+                break;
+            }
+
+            // ints <= 8 bits
+            case Types.BIT: {
+                builder.addField(new Field(index, fieldName, SchemaBuilder.int8().build()));
+                break;
+            }
+
+            case Types.TINYINT: {
+                if (columnDefn.isSignedNumber()) {
+                    builder.addField(new Field(index, fieldName, SchemaBuilder.int8().build()));
+                } else {
+                    builder.addField(new Field(index, fieldName, SchemaBuilder.int32().build()));
+                }
+                break;
+            }
+
+            // 16 bit ints
+            case Types.SMALLINT: {
+                builder.addField(new Field(index, fieldName, SchemaBuilder.int32().build()));
+                break;
+            }
+
+            // 32 bit ints
+            case Types.INTEGER: {
+                if (columnDefn.isSignedNumber()) {
+                    builder.addField(new Field(index, fieldName, SchemaBuilder.int32().build()));
+                } else {
+                    builder.addField(new Field(index, fieldName, SchemaBuilder.int64().build()));
+                }
+                break;
+            }
+
+            // 64 bit ints
+            case Types.BIGINT: {
+                builder.addField(new Field(index, fieldName, SchemaBuilder.int64().build()));
+                break;
+            }
+
+            // REAL is a single precision floating point value, i.e. a Java float
+            case Types.REAL: {
+                builder.addField(new Field(index, fieldName, SchemaBuilder.float32().build()));
+                break;
+            }
+
+            // FLOAT is, confusingly, double precision and effectively the same as DOUBLE. See REAL
+            // for single precision
+            case Types.FLOAT:
+            case Types.DOUBLE:
+            case Types.DECIMAL:
+                builder.addField(new Field(index, fieldName, SchemaBuilder.float64().build()));
+                break;
+
+            /**
+             * numeric
+             */
+            case Types.NUMERIC:
+                if (mapNumerics == NumericMapping.PRECISION_ONLY) {
+                    log.debug("NUMERIC with precision: '{}' and scale: '{}'", precision, scale);
+                    if (scale == 0 && precision <= MAX_INTEGER_TYPE_PRECISION) { // integer
+                        builder.addField(new Field(index, fieldName, integerSchema(optional, precision)));
+                        break;
+                    }
+                } else if (mapNumerics == NumericMapping.BEST_FIT) {
+                    log.debug("NUMERIC with precision: '{}' and scale: '{}'", precision, scale);
+                    if (precision <= MAX_INTEGER_TYPE_PRECISION) { // fits in primitive data types.
+                        if (scale < 1 && scale >= NUMERIC_TYPE_SCALE_LOW) { // integer
+                            builder.addField(new Field(index, fieldName, integerSchema(optional, precision)));
+                            break;
+                        } else if (scale > 0) { // floating point - use double in all cases
+                            builder.addField(new Field(index, fieldName, SchemaBuilder.float64().build()));
+                            break;
+                        }
+                    }
+                } else if (mapNumerics == NumericMapping.BEST_FIT_EAGER_DOUBLE) {
+                    log.debug("NUMERIC with precision: '{}' and scale: '{}'", precision, scale);
+                    if (scale < 1 && scale >= NUMERIC_TYPE_SCALE_LOW) { // integer
+                        if (precision <= MAX_INTEGER_TYPE_PRECISION) { // fits in primitive data types.
+                            builder.addField(new Field(index, fieldName, integerSchema(optional, precision)));
+                            break;
+                        }
+                    } else if (scale > 0) { // floating point - use double in all cases
+                        builder.addField(new Field(index, fieldName, SchemaBuilder.float64().build()));
+                        break;
+                    }
+                }
+
+            case Types.CHAR:
+            case Types.VARCHAR:
+            case Types.LONGVARCHAR:
+            case Types.NCHAR:
+            case Types.NVARCHAR:
+            case Types.LONGNVARCHAR:
+            case Types.CLOB:
+            case Types.NCLOB:
+            case Types.DATALINK:
+            case Types.SQLXML: {
+                // Some of these types will have fixed size, but we drop this from the schema conversion
+                // since only fixed byte arrays can have a fixed size
+                builder.addField(new Field(index, fieldName, SchemaBuilder.string().build()));
+                break;
+            }
+
+            // Binary == fixed bytes
+            // BLOB, VARBINARY, LONGVARBINARY == bytes
+            case Types.BINARY:
+            case Types.BLOB:
+            case Types.VARBINARY:
+            case Types.LONGVARBINARY: {
+                builder.addField(new Field(index, fieldName, SchemaBuilder.bytes().build()));
+                break;
+            }
+
+            // Date is day + moth + year
+            case Types.DATE: {
+                SchemaBuilder dateSchemaBuilder = DateColumnParser.builder();
+                builder.addField(new Field(index, fieldName, dateSchemaBuilder.build()));
+                break;
+            }
+
+            // Time is a time of day -- hour, minute, seconds, nanoseconds
+            case Types.TIME: {
+                SchemaBuilder timeSchemaBuilder = TimestampColumnParser.builder();
+                builder.addField(new Field(index, fieldName, timeSchemaBuilder.build()));
+                break;
+            }
+
+            // Timestamp is a date + time
+            case Types.TIMESTAMP: {
+                SchemaBuilder tsSchemaBuilder = TimestampColumnParser.builder();
+                builder.addField(new Field(index, fieldName, tsSchemaBuilder.build()));
+                break;
+            }
+
+            case Types.ARRAY:
+            case Types.JAVA_OBJECT:
+            case Types.OTHER:
+            case Types.DISTINCT:
+            case Types.STRUCT:
+            case Types.REF:
+            case Types.ROWID:
+            default: {
+                log.warn("JDBC type {} ({}) not currently supported", sqlType, columnDefn.typeName());
+                return null;
+            }
         }
-        break;
-      }
-
-      // 16 bit ints
-      case Types.SMALLINT: {
-        builder.addField(new Field(index, fieldName, SchemaBuilder.int32().build()));
-        break;
-      }
-
-      // 32 bit ints
-      case Types.INTEGER: {
-        if (columnDefn.isSignedNumber()) {
-          builder.addField(new Field(index, fieldName, SchemaBuilder.int32().build()));
+        return fieldName;
+    }
+
+    private Schema integerSchema(boolean optional, int precision) {
+        Schema schema;
+        if (precision > 9) {
+            schema = SchemaBuilder.int64().build();
+        } else if (precision > 2) {
+            schema = SchemaBuilder.int32().build();
         } else {
-          builder.addField(new Field(index, fieldName, SchemaBuilder.int64().build()));
+            schema = SchemaBuilder.int8().build();
         }
-        break;
-      }
-
-      // 64 bit ints
-      case Types.BIGINT: {
-        builder.addField(new Field(index, fieldName, SchemaBuilder.int64().build()));
-        break;
-      }
-
-      // REAL is a single precision floating point value, i.e. a Java float
-      case Types.REAL: {
-        builder.addField(new Field(index, fieldName, SchemaBuilder.float32().build()));
-        break;
-      }
-
-      // FLOAT is, confusingly, double precision and effectively the same as DOUBLE. See REAL
-      // for single precision
-      case Types.FLOAT:
-      case Types.DOUBLE:
-      case Types.DECIMAL:
-        builder.addField(new Field(index, fieldName, SchemaBuilder.float64().build()));
-        break;
-
-      /**
-       * numeric
-       */
-      case Types.NUMERIC:
-        if (mapNumerics == NumericMapping.PRECISION_ONLY) {
-          glog.debug("NUMERIC with precision: '{}' and scale: '{}'", precision, scale);
-          if (scale == 0 && precision <= MAX_INTEGER_TYPE_PRECISION) { // integer
-            builder.addField(new Field(index, fieldName, integerSchema(optional, precision)));
-            break;
-          }
-        } else if (mapNumerics == NumericMapping.BEST_FIT) {
-          glog.debug("NUMERIC with precision: '{}' and scale: '{}'", precision, scale);
-          if (precision <= MAX_INTEGER_TYPE_PRECISION) { // fits in primitive data types.
-            if (scale < 1 && scale >= NUMERIC_TYPE_SCALE_LOW) { // integer
-              builder.addField(new Field(index,fieldName, integerSchema(optional, precision)));
-              break;
-            } else if (scale > 0) { // floating point - use double in all cases
-              builder.addField(new Field(index, fieldName, SchemaBuilder.float64().build()));
-              break;
-            }
-          }
-        } else if (mapNumerics == NumericMapping.BEST_FIT_EAGER_DOUBLE) {
-          glog.debug("NUMERIC with precision: '{}' and scale: '{}'", precision, scale);
-          if (scale < 1 && scale >= NUMERIC_TYPE_SCALE_LOW) { // integer
-            if (precision <= MAX_INTEGER_TYPE_PRECISION) { // fits in primitive data types.
-              builder.addField(new Field(index, fieldName, integerSchema(optional, precision)));
-              break;
+        return schema;
+    }
+
+    /**
+     * execute ddl
+     *
+     * @param connection the connection to use
+     * @param statements the list of DDL statements to execute
+     * @throws SQLException
+     */
+    @Override
+    public void applyDdlStatements(
+            Connection connection,
+            List<String> statements
+    ) throws SQLException {
+        try (Statement statement = connection.createStatement()) {
+            for (String ddlStatement : statements) {
+                statement.executeUpdate(ddlStatement);
             }
-          } else if (scale > 0) { // floating point - use double in all cases
-            builder.addField(new Field(index, fieldName, SchemaBuilder.float64().build()));
-            break;
-          }
         }
+    }
 
-      case Types.CHAR:
-      case Types.VARCHAR:
-      case Types.LONGVARCHAR:
-      case Types.NCHAR:
-      case Types.NVARCHAR:
-      case Types.LONGNVARCHAR:
-      case Types.CLOB:
-      case Types.NCLOB:
-      case Types.DATALINK:
-      case Types.SQLXML: {
-        // Some of these types will have fixed size, but we drop this from the schema conversion
-        // since only fixed byte arrays can have a fixed size
-        builder.addField(new Field(index, fieldName, SchemaBuilder.string().build()));
-        break;
-      }
-
-      // Binary == fixed bytes
-      // BLOB, VARBINARY, LONGVARBINARY == bytes
-      case Types.BINARY:
-      case Types.BLOB:
-      case Types.VARBINARY:
-      case Types.LONGVARBINARY: {
-        builder.addField(new Field(index, fieldName, SchemaBuilder.bytes().build()));
-        break;
-      }
-
-      // Date is day + moth + year
-      case Types.DATE: {
-        SchemaBuilder dateSchemaBuilder = DateColumnParser.builder();
-        builder.addField(new Field(index, fieldName,dateSchemaBuilder.build()));
-        break;
-      }
-
-      // Time is a time of day -- hour, minute, seconds, nanoseconds
-      case Types.TIME: {
-        SchemaBuilder timeSchemaBuilder = TimestampColumnParser.builder();
-        builder.addField(new Field(index, fieldName, timeSchemaBuilder.build()));
-        break;
-      }
-
-      // Timestamp is a date + time
-      case Types.TIMESTAMP: {
-        SchemaBuilder tsSchemaBuilder = TimestampColumnParser.builder();
-        builder.addField(new Field(index, fieldName,tsSchemaBuilder.build()));
-        break;
-      }
-
-      case Types.ARRAY:
-      case Types.JAVA_OBJECT:
-      case Types.OTHER:
-      case Types.DISTINCT:
-      case Types.STRUCT:
-      case Types.REF:
-      case Types.ROWID:
-      default: {
-        glog.warn("JDBC type {} ({}) not currently supported", sqlType, columnDefn.typeName());
-        return null;
-      }
-    }
-    return fieldName;
-  }
-
-  private Schema integerSchema(boolean optional, int precision) {
-    Schema schema;
-    if (precision > 9) {
-      schema =SchemaBuilder.int64().build();
-    } else if (precision > 2) {
-      schema =SchemaBuilder.int32().build();
-    } else {
-      schema =SchemaBuilder.int8().build();
-    }
-    return schema;
-  }
-
-  /**
-   * execute ddl
-   * @param connection the connection to use
-   * @param statements the list of DDL statements to execute
-   * @throws SQLException
-   */
-  @Override
-  public void applyDdlStatements(
-          Connection connection,
-          List<String> statements
-  ) throws SQLException {
-    try (Statement statement = connection.createStatement()) {
-      for (String ddlStatement : statements) {
-        statement.executeUpdate(ddlStatement);
-      }
-    }
-  }
-
-  @Override
-  public ColumnConverter createColumnConverter(
-          ColumnMapping mapping
-  ) {
-    return columnConverterFor(
-            mapping,
-            mapping.columnDefn(),
-            mapping.columnNumber(),
-            jdbcDriverInfo().jdbcVersionAtLeast(4, 0)
-    );
-  }
-
-  @SuppressWarnings({"deprecation", "fallthrough"})
-  protected ColumnConverter columnConverterFor(
-          final ColumnMapping mapping,
-          final ColumnDefinition defn,
-          final int col,
-          final boolean isJdbc4
-  ) {
-    switch (mapping.columnDefn().type()) {
-
-      case Types.BOOLEAN: {
-        return rs -> rs.getBoolean(col);
-      }
-
-      case Types.BIT: {
-        /**
-         * BIT should be either 0 or 1.
-         * TODO: Postgres handles this differently, returning a string "t" or "f". See the
-         * elasticsearch-jdbc plugin for an example of how this is handled
-         */
-        return rs -> rs.getByte(col);
-      }
-
-      // 8 bits int
-      case Types.TINYINT: {
-        if (defn.isSignedNumber()) {
-          return rs -> rs.getByte(col);
-        } else {
-          return rs -> rs.getShort(col);
+    @Override
+    public ColumnConverter createColumnConverter(
+            ColumnMapping mapping
+    ) {
+        return columnConverterFor(
+                mapping,
+                mapping.columnDefn(),
+                mapping.columnNumber(),
+                jdbcDriverInfo().jdbcVersionAtLeast(4, 0)
+        );
+    }
+
+    @SuppressWarnings({"deprecation", "fallthrough"})
+    protected ColumnConverter columnConverterFor(
+            final ColumnMapping mapping,
+            final ColumnDefinition defn,
+            final int col,
+            final boolean isJdbc4
+    ) {
+        switch (mapping.columnDefn().type()) {
+
+            case Types.BOOLEAN: {
+                return rs -> rs.getBoolean(col);
+            }
+
+            case Types.BIT: {
+                /**
+                 * BIT should be either 0 or 1.
+                 * TODO: Postgres handles this differently, returning a string "t" or "f". See the
+                 * elasticsearch-jdbc plugin for an example of how this is handled
+                 */
+                return rs -> rs.getByte(col);
+            }
+
+            // 8 bits int
+            case Types.TINYINT: {
+                if (defn.isSignedNumber()) {
+                    return rs -> rs.getByte(col);
+                } else {
+                    return rs -> rs.getShort(col);
+                }
+            }
+
+            // 16 bits int
+            case Types.SMALLINT: {
+                if (defn.isSignedNumber()) {
+                    return rs -> rs.getShort(col);
+                } else {
+                    return rs -> rs.getInt(col);
+                }
+            }
+
+            // 32 bits int
+            case Types.INTEGER: {
+                if (defn.isSignedNumber()) {
+                    return rs -> rs.getInt(col);
+                } else {
+                    return rs -> rs.getLong(col);
+                }
+            }
+
+            // 64 bits int
+            case Types.BIGINT: {
+                return rs -> rs.getLong(col);
+            }
+
+            // REAL is a single precision floating point value, i.e. a Java float
+            case Types.REAL: {
+                return rs -> rs.getFloat(col);
+            }
+
+            // FLOAT is, confusingly, double precision and effectively the same as DOUBLE. See REAL
+            // for single precision
+            case Types.FLOAT:
+            case Types.DOUBLE: {
+                return rs -> rs.getDouble(col);
+            }
+
+            case Types.NUMERIC:
+                if (mapNumerics == NumericMapping.PRECISION_ONLY) {
+                    int precision = defn.precision();
+                    int scale = defn.scale();
+                    log.trace("NUMERIC with precision: '{}' and scale: '{}'", precision, scale);
+                    if (scale == 0 && precision <= MAX_INTEGER_TYPE_PRECISION) { // integer
+                        if (precision > 9) {
+                            return rs -> rs.getLong(col);
+                        } else if (precision > 4) {
+                            return rs -> rs.getInt(col);
+                        } else if (precision > 2) {
+                            return rs -> rs.getShort(col);
+                        } else {
+                            return rs -> rs.getByte(col);
+                        }
+                    }
+                } else if (mapNumerics == NumericMapping.BEST_FIT) {
+                    int precision = defn.precision();
+                    int scale = defn.scale();
+                    log.trace("NUMERIC with precision: '{}' and scale: '{}'", precision, scale);
+                    if (precision <= MAX_INTEGER_TYPE_PRECISION) { // fits in primitive data types.
+                        if (scale < 1 && scale >= NUMERIC_TYPE_SCALE_LOW) { // integer
+                            if (precision > 9) {
+                                return rs -> rs.getLong(col);
+                            } else if (precision > 4) {
+                                return rs -> rs.getInt(col);
+                            } else if (precision > 2) {
+                                return rs -> rs.getShort(col);
+                            } else {
+                                return rs -> rs.getByte(col);
+                            }
+                        } else if (scale > 0) { // floating point - use double in all cases
+                            return rs -> rs.getDouble(col);
+                        }
+                    }
+                } else if (mapNumerics == NumericMapping.BEST_FIT_EAGER_DOUBLE) {
+                    int precision = defn.precision();
+                    int scale = defn.scale();
+                    log.trace("NUMERIC with precision: '{}' and scale: '{}'", precision, scale);
+                    if (scale < 1 && scale >= NUMERIC_TYPE_SCALE_LOW) { // integer
+                        if (precision <= MAX_INTEGER_TYPE_PRECISION) { // fits in primitive data types.
+                            if (precision > 9) {
+                                return rs -> rs.getLong(col);
+                            } else if (precision > 4) {
+                                return rs -> rs.getInt(col);
+                            } else if (precision > 2) {
+                                return rs -> rs.getShort(col);
+                            } else {
+                                return rs -> rs.getByte(col);
+                            }
+                        }
+                    } else if (scale > 0) { // floating point - use double in all cases
+                        return rs -> rs.getDouble(col);
+                    }
+                }
+                // fallthrough
+
+            case Types.DECIMAL: {
+                final int precision = defn.precision();
+                log.debug("DECIMAL with precision: '{}' and scale: '{}'", precision, defn.scale());
+                final int scale = decimalScale(defn);
+                return rs -> rs.getBigDecimal(col, scale);
+            }
+
+            case Types.CHAR:
+            case Types.VARCHAR:
+            case Types.LONGVARCHAR: {
+                return rs -> rs.getString(col);
+            }
+
+            case Types.NCHAR:
+            case Types.NVARCHAR:
+            case Types.LONGNVARCHAR: {
+                return rs -> rs.getNString(col);
+            }
+
+            // Binary == fixed, VARBINARY and LONGVARBINARY == bytes
+            case Types.BINARY:
+            case Types.VARBINARY:
+            case Types.LONGVARBINARY: {
+                return rs -> rs.getBytes(col);
+            }
+
+            // Date is day + month + year
+            case Types.DATE: {
+                return rs -> rs.getDate(col,
+                        DateTimeUtils.getTimeZoneCalendar(TimeZone.getTimeZone(ZoneOffset.UTC)));
+            }
+
+            // Time is a time of day -- hour, minute, seconds, nanoseconds
+            case Types.TIME: {
+                return rs -> rs.getTime(col, DateTimeUtils.getTimeZoneCalendar(timeZone));
+            }
+
+            // Timestamp is a date + time
+            case Types.TIMESTAMP: {
+                return rs -> rs.getTimestamp(col, DateTimeUtils.getTimeZoneCalendar(timeZone));
+            }
+
+            // Datalink is basically a URL -> string
+            case Types.DATALINK: {
+                return rs -> {
+                    URL url = rs.getURL(col);
+                    return url != null ? url.toString() : null;
+                };
+            }
+
+            // BLOB == fixed
+            case Types.BLOB: {
+                return rs -> {
+                    Blob blob = rs.getBlob(col);
+                    if (blob == null) {
+                        return null;
+                    } else {
+                        try {
+                            if (blob.length() > Integer.MAX_VALUE) {
+                                throw new IOException("Can't process BLOBs longer than " + Integer.MAX_VALUE);
+                            }
+                            return blob.getBytes(1, (int) blob.length());
+                        } finally {
+                            if (isJdbc4) {
+                                free(blob);
+                            }
+                        }
+                    }
+                };
+            }
+            case Types.CLOB:
+                return rs -> {
+                    Clob clob = rs.getClob(col);
+                    if (clob == null) {
+                        return null;
+                    } else {
+                        try {
+                            if (clob.length() > Integer.MAX_VALUE) {
+                                throw new IOException("Can't process CLOBs longer than " + Integer.MAX_VALUE);
+                            }
+                            return clob.getSubString(1, (int) clob.length());
+                        } finally {
+                            if (isJdbc4) {
+                                free(clob);
+                            }
+                        }
+                    }
+                };
+            case Types.NCLOB: {
+                return rs -> {
+                    Clob clob = rs.getNClob(col);
+                    if (clob == null) {
+                        return null;
+                    } else {
+                        try {
+                            if (clob.length() > Integer.MAX_VALUE) {
+                                throw new IOException("Can't process NCLOBs longer than " + Integer.MAX_VALUE);
+                            }
+                            return clob.getSubString(1, (int) clob.length());
+                        } finally {
+                            if (isJdbc4) {
+                                free(clob);
+                            }
+                        }
+                    }
+                };
+            }
+
+            // XML -> string
+            case Types.SQLXML: {
+                return rs -> {
+                    SQLXML xml = rs.getSQLXML(col);
+                    return xml != null ? xml.getString() : null;
+                };
+            }
+
+            case Types.NULL:
+            case Types.ARRAY:
+            case Types.JAVA_OBJECT:
+            case Types.OTHER:
+            case Types.DISTINCT:
+            case Types.STRUCT:
+            case Types.REF:
+            case Types.ROWID:
+            default: {
+                // These are not currently supported, but we don't want to log something for every single
+                // record we translate. There will already be errors logged for the schema translation
+                break;
+            }
         }
-      }
+        return null;
+    }
 
-      // 16 bits int
-      case Types.SMALLINT: {
-        if (defn.isSignedNumber()) {
-          return rs -> rs.getShort(col);
-        } else {
-          return rs -> rs.getInt(col);
+    protected int decimalScale(ColumnDefinition defn) {
+        return defn.scale() == NUMERIC_TYPE_SCALE_UNSET ? NUMERIC_TYPE_SCALE_HIGH : defn.scale();
+    }
+
+    /**
+     * Called when the object has been fully read and {@link Blob#free()} should be called.
+     *
+     * @param blob the Blob; never null
+     * @throws SQLException if there is a problem calling free()
+     */
+    protected void free(Blob blob) throws SQLException {
+        blob.free();
+    }
+
+    /**
+     * Called when the object has been fully read and {@link Clob#free()} should be called.
+     *
+     * @param clob the Clob; never null
+     * @throws SQLException if there is a problem calling free()
+     */
+    protected void free(Clob clob) throws SQLException {
+        clob.free();
+    }
+
+    @Override
+    public String buildInsertStatement(
+            TableId table,
+            Collection<ColumnId> keyColumns,
+            Collection<ColumnId> nonKeyColumns
+    ) {
+        ExpressionBuilder builder = expressionBuilder();
+        builder.append("INSERT INTO ");
+        builder.append(table);
+        builder.append("(");
+        builder.appendList()
+                .delimitedBy(",")
+                .transformedBy(ExpressionBuilder.columnNames())
+                .of(keyColumns, nonKeyColumns);
+        builder.append(") VALUES(");
+        builder.appendMultiple(",", "?", keyColumns.size() + nonKeyColumns.size());
+        builder.append(")");
+        return builder.toString();
+    }
+
+    @Override
+    public String buildUpdateStatement(
+            TableId table,
+            Collection<ColumnId> keyColumns,
+            Collection<ColumnId> nonKeyColumns
+    ) {
+        ExpressionBuilder builder = expressionBuilder();
+        builder.append("UPDATE ");
+        builder.append(table);
+        builder.append(" SET ");
+        builder.appendList()
+                .delimitedBy(", ")
+                .transformedBy(ExpressionBuilder.columnNamesWith(" = ?"))
+                .of(nonKeyColumns);
+        if (!keyColumns.isEmpty()) {
+            builder.append(" WHERE ");
+            builder.appendList()
+                    .delimitedBy(" AND ")
+                    .transformedBy(ExpressionBuilder.columnNamesWith(" = ?"))
+                    .of(keyColumns);
         }
-      }
+        return builder.toString();
+    }
 
-      // 32 bits int
-      case Types.INTEGER: {
-        if (defn.isSignedNumber()) {
-          return rs -> rs.getInt(col);
-        } else {
-          return rs -> rs.getLong(col);
+    @Override
+    public String buildUpsertQueryStatement(
+            TableId table,
+            Collection<ColumnId> keyColumns,
+            Collection<ColumnId> nonKeyColumns
+    ) {
+        throw new UnsupportedOperationException();
+    }
+
+    @Override
+    public final String buildDeleteStatement(
+            TableId table,
+            Collection<ColumnId> keyColumns
+    ) {
+        ExpressionBuilder builder = expressionBuilder();
+        builder.append("DELETE FROM ");
+        builder.append(table);
+        if (!keyColumns.isEmpty()) {
+            builder.append(" WHERE ");
+            builder.appendList()
+                    .delimitedBy(" AND ")
+                    .transformedBy(ExpressionBuilder.columnNamesWith(" = ?"))
+                    .of(keyColumns);
         }
-      }
-
-      // 64 bits int
-      case Types.BIGINT: {
-        return rs -> rs.getLong(col);
-      }
-
-      // REAL is a single precision floating point value, i.e. a Java float
-      case Types.REAL: {
-        return rs -> rs.getFloat(col);
-      }
-
-      // FLOAT is, confusingly, double precision and effectively the same as DOUBLE. See REAL
-      // for single precision
-      case Types.FLOAT:
-      case Types.DOUBLE: {
-        return rs -> rs.getDouble(col);
-      }
-
-      case Types.NUMERIC:
-        if (mapNumerics == NumericMapping.PRECISION_ONLY) {
-          int precision = defn.precision();
-          int scale = defn.scale();
-          glog.trace("NUMERIC with precision: '{}' and scale: '{}'", precision, scale);
-          if (scale == 0 && precision <= MAX_INTEGER_TYPE_PRECISION) { // integer
-            if (precision > 9) {
-              return rs -> rs.getLong(col);
-            } else if (precision > 4) {
-              return rs -> rs.getInt(col);
-            } else if (precision > 2) {
-              return rs -> rs.getShort(col);
+        return builder.toString();
+    }
+
+    /**
+     * table moode
+     *
+     * @return
+     */
+    @Override
+    public String buildSelectTableMode() {
+        return "SELECT * FROM ";
+    }
+
+    @Override
+    public void buildSelectTable(ExpressionBuilder builder, TableId tableId) {
+        String mode = buildSelectTableMode();
+        builder.append(mode).append(tableId);
+    }
+
+    @Override
+    public StatementBinder statementBinder(
+            PreparedStatement statement,
+            JdbcSinkConfig.PrimaryKeyMode pkMode,
+            SchemaPair schemaPair,
+            FieldsMetadata fieldsMetadata,
+            TableDefinition tableDefinition,
+            JdbcSinkConfig.InsertMode insertMode) {
+        return new PreparedStatementBinder(
+                this,
+                statement,
+                pkMode,
+                schemaPair,
+                fieldsMetadata,
+                tableDefinition,
+                insertMode
+        );
+    }
+
+    @Override
+    public void bindField(
+            PreparedStatement statement,
+            int index, Schema schema,
+            Object value,
+            ColumnDefinition colDef) throws SQLException {
+        if (value == null) {
+            Integer type = getSqlTypeForSchema(schema);
+            if (type != null) {
+                statement.setNull(index, type);
             } else {
-              return rs -> rs.getByte(col);
+                statement.setObject(index, null);
             }
-          }
-        } else if (mapNumerics == NumericMapping.BEST_FIT) {
-          int precision = defn.precision();
-          int scale = defn.scale();
-          glog.trace("NUMERIC with precision: '{}' and scale: '{}'", precision, scale);
-          if (precision <= MAX_INTEGER_TYPE_PRECISION) { // fits in primitive data types.
-            if (scale < 1 && scale >= NUMERIC_TYPE_SCALE_LOW) { // integer
-              if (precision > 9) {
-                return rs -> rs.getLong(col);
-              } else if (precision > 4) {
-                return rs -> rs.getInt(col);
-              } else if (precision > 2) {
-                return rs -> rs.getShort(col);
-              } else {
-                return rs -> rs.getByte(col);
-              }
-            } else if (scale > 0) { // floating point - use double in all cases
-              return rs -> rs.getDouble(col);
+        } else {
+            boolean bound = maybeBindLogical(statement, index, schema, value, null);
+            if (!bound) {
+                bound = maybeBindPrimitive(statement, index, schema, value);
             }
-          }
-        } else if (mapNumerics == NumericMapping.BEST_FIT_EAGER_DOUBLE) {
-          int precision = defn.precision();
-          int scale = defn.scale();
-          glog.trace("NUMERIC with precision: '{}' and scale: '{}'", precision, scale);
-          if (scale < 1 && scale >= NUMERIC_TYPE_SCALE_LOW) { // integer
-            if (precision <= MAX_INTEGER_TYPE_PRECISION) { // fits in primitive data types.
-              if (precision > 9) {
-                return rs -> rs.getLong(col);
-              } else if (precision > 4) {
-                return rs -> rs.getInt(col);
-              } else if (precision > 2) {
-                return rs -> rs.getShort(col);
-              } else {
-                return rs -> rs.getByte(col);
-              }
+            if (!bound) {
+                throw new io.openmessaging.connector.api.errors.ConnectException("Unsupported source data type: " + schema.getFieldType());
             }
-          } else if (scale > 0) { // floating point - use double in all cases
-            return rs -> rs.getDouble(col);
-          }
         }
-        // fallthrough
-
-      case Types.DECIMAL: {
-        final int precision = defn.precision();
-        glog.debug("DECIMAL with precision: '{}' and scale: '{}'", precision, defn.scale());
-        final int scale = decimalScale(defn);
-        return rs -> rs.getBigDecimal(col, scale);
-      }
-
-      case Types.CHAR:
-      case Types.VARCHAR:
-      case Types.LONGVARCHAR: {
-        return rs -> rs.getString(col);
-      }
-
-      case Types.NCHAR:
-      case Types.NVARCHAR:
-      case Types.LONGNVARCHAR: {
-        return rs -> rs.getNString(col);
-      }
-
-      // Binary == fixed, VARBINARY and LONGVARBINARY == bytes
-      case Types.BINARY:
-      case Types.VARBINARY:
-      case Types.LONGVARBINARY: {
-        return rs -> rs.getBytes(col);
-      }
-
-      // Date is day + month + year
-      case Types.DATE: {
-        return rs -> rs.getDate(col,
-                DateTimeUtils.getTimeZoneCalendar(TimeZone.getTimeZone(ZoneOffset.UTC)));
-      }
-
-      // Time is a time of day -- hour, minute, seconds, nanoseconds
-      case Types.TIME: {
-        return rs -> rs.getTime(col, DateTimeUtils.getTimeZoneCalendar(timeZone));
-      }
-
-      // Timestamp is a date + time
-      case Types.TIMESTAMP: {
-        return rs -> rs.getTimestamp(col, DateTimeUtils.getTimeZoneCalendar(timeZone));
-      }
-
-      // Datalink is basically a URL -> string
-      case Types.DATALINK: {
-        return rs -> {
-          URL url = rs.getURL(col);
-          return (url != null ? url.toString() : null);
-        };
-      }
+    }
 
-      // BLOB == fixed
-      case Types.BLOB: {
-        return rs -> {
-          Blob blob = rs.getBlob(col);
-          if (blob == null) {
-            return null;
-          } else {
-            try {
-              if (blob.length() > Integer.MAX_VALUE) {
-                throw new IOException("Can't process BLOBs longer than " + Integer.MAX_VALUE);
-              }
-              return blob.getBytes(1, (int) blob.length());
-            } finally {
-              if (isJdbc4) {
-                free(blob);
-              }
+    protected boolean maybeBindLogical(
+            PreparedStatement statement,
+            int index,
+            Schema schema,
+            Object value,
+            ColumnDefinition colDef
+    ) throws SQLException {
+        if (schema.getName() != null) {
+            switch (schema.getName()) {
+                case DateColumnParser.LOGICAL_NAME:
+                    java.sql.Date date;
+                    if (value instanceof java.util.Date) {
+                        date = new java.sql.Date(((java.util.Date) value).getTime());
+                    } else {
+                        date = new java.sql.Date((int) value);
+                    }
+                    statement.setDate(
+                            index, date,
+                            DateTimeUtils.getTimeZoneCalendar(timeZone)
+                    );
+                    return true;
+                case TimeColumnParser.LOGICAL_NAME:
+                    java.sql.Time time;
+                    if (value instanceof java.util.Date) {
+                        time = new java.sql.Time(((java.util.Date) value).getTime());
+                    } else {
+                        time = new java.sql.Time((int) value);
+                    }
+                    statement.setTime(
+                            index, time,
+                            DateTimeUtils.getTimeZoneCalendar(timeZone)
+                    );
+                    return true;
+                case TimestampColumnParser.LOGICAL_NAME:
+                    java.sql.Timestamp timestamp;
+                    if (value instanceof java.util.Date) {
+                        timestamp = new java.sql.Timestamp(((java.util.Date) value).getTime());
+                    } else {
+                        timestamp = new java.sql.Timestamp((long) value);
+                    }
+                    statement.setTimestamp(
+                            index, timestamp,
+                            DateTimeUtils.getTimeZoneCalendar(timeZone)
+                    );
+                    return true;
+                default:
+                    return false;
             }
-          }
-        };
-      }
-      case Types.CLOB:
-        return rs -> {
-          Clob clob = rs.getClob(col);
-          if (clob == null) {
+        }
+        return false;
+    }
+
+
+    @Override
+    public TimestampIncrementingCriteria criteriaFor(ColumnId incrementingColumn, List<ColumnId> timestampColumns) {
+        return new TimestampIncrementingCriteria(
+                incrementingColumn, timestampColumns, timeZone);
+    }
+
+    @Override
+    public Long getMinTimestampValue(Connection con, String tableOrQuery, List<String> timestampColumns) throws SQLException {
+        if (timestampColumns == null || timestampColumns.isEmpty()) {
             return null;
-          } else {
-            try {
-              if (clob.length() > Integer.MAX_VALUE) {
-                throw new IOException("Can't process CLOBs longer than " + Integer.MAX_VALUE);
-              }
-              return clob.getSubString(1, (int) clob.length());
-            } finally {
-              if (isJdbc4) {
-                free(clob);
-              }
+        }
+        StringBuilder builder = new StringBuilder();
+        builder.append("SELECT ");
+        boolean appendComma = false;
+        for (String column : timestampColumns) {
+            builder.append("MIN(");
+            builder.append(column);
+            builder.append(")");
+            if (appendComma) {
+                builder.append(",");
+            } else {
+                appendComma = true;
             }
-          }
-        };
-      case Types.NCLOB: {
-        return rs -> {
-          Clob clob = rs.getNClob(col);
-          if (clob == null) {
-            return null;
-          } else {
-            try {
-              if (clob.length() > Integer.MAX_VALUE) {
-                throw new IOException("Can't process NCLOBs longer than " + Integer.MAX_VALUE);
-              }
-              return clob.getSubString(1, (int) clob.length());
-            } finally {
-              if (isJdbc4) {
-                free(clob);
-              }
+        }
+        builder.append(" FROM ");
+        builder.append(tableOrQuery);
+        String querySql = builder.toString();
+        PreparedStatement st = con.prepareStatement(querySql);
+        ResultSet resultSet = st.executeQuery();
+        ResultSetMetaData metaData = resultSet.getMetaData();
+        long minTimestampValue = Long.MAX_VALUE;
+        for (int i = 1; i <= metaData.getColumnCount(); ++i) {
+            long t = resultSet.getLong(i);
+            minTimestampValue = Math.min(minTimestampValue, t);
+        }
+        st.close();
+        return minTimestampValue;
+    }
+
+    /**
+     * Dialects not supporting `setObject(index, null)` can override this method
+     * to provide a specific sqlType, as per the JDBC documentation
+     * https://docs.oracle.com/javase/7/docs/api/java/sql/PreparedStatement.html
+     *
+     * @param schema the schema
+     * @return the SQL type
+     */
+    protected Integer getSqlTypeForSchema(Schema schema) {
+        return null;
+    }
+
+    protected boolean maybeBindPrimitive(
+            PreparedStatement statement,
+            int index,
+            Schema schema,
+            Object value
+    ) throws SQLException {
+        switch (schema.getFieldType()) {
+            case INT8:
+                statement.setByte(index, (Byte) value);
+                break;
+            case INT32:
+                statement.setInt(index, (Integer) value);
+                break;
+            case INT64:
+                statement.setLong(index, (Long) value);
+                break;
+            case FLOAT32:
+                statement.setFloat(index, (Float) value);
+                break;
+            case FLOAT64:
+                statement.setDouble(index, (Double) value);
+                break;
+            case BOOLEAN:
+                statement.setBoolean(index, (Boolean) value);
+                break;
+            case STRING:
+                statement.setString(index, (String) value);
+                break;
+            case BYTES:
+                final byte[] bytes;
+                if (value instanceof ByteBuffer) {
+                    final ByteBuffer buffer = ((ByteBuffer) value).slice();
+                    bytes = new byte[buffer.remaining()];
+                    buffer.get(bytes);
+                } else {
+                    bytes = (byte[]) value;
+                }
+                statement.setBytes(index, bytes);
+                break;
+            case DATETIME:
+                java.sql.Date date;
+                if (value instanceof java.util.Date) {
+                    date = new java.sql.Date(((java.util.Date) value).getTime());
+                } else {
+                    date = new java.sql.Date((int) value);
+                }
+                statement.setDate(
+                        index, date,
+                        DateTimeUtils.getTimeZoneCalendar(timeZone)
+                );
+
+
+                break;
+            default:
+                return false;
+        }
+        return true;
+    }
+
+    @Override
+    public String buildCreateTableStatement(
+            TableId table,
+            Collection<SinkRecordField> fields) {
+        ExpressionBuilder builder = expressionBuilder();
+        final List<String> pkFieldNames = extractPrimaryKeyFieldNames(fields);
+        builder.append("CREATE TABLE ");
+        builder.append(table);
+        builder.append(" (");
+        writeColumnsSpec(builder, fields);
+        if (!pkFieldNames.isEmpty()) {
+            builder.append(",");
+            builder.append(System.lineSeparator());
+            builder.append("PRIMARY KEY(");
+            builder.appendList()
+                    .delimitedBy(",")
+                    .transformedBy(ExpressionBuilder.quote())
+                    .of(pkFieldNames);
+            builder.append(")");
+        }
+        builder.append(")");
+        return builder.toString();
+    }
+
+    @Override
+    public String buildDropTableStatement(
+            TableId table,
+            DropOptions options
+    ) {
+        ExpressionBuilder builder = expressionBuilder();
+
+        builder.append("DROP TABLE ");
+        builder.append(table);
+        if (options.ifExists()) {
+            builder.append(" IF EXISTS");
+        }
+        if (options.cascade()) {
+            builder.append(" CASCADE");
+        }
+        return builder.toString();
+    }
+
+    @Override
+    public List<String> buildAlterTable(
+            TableId table,
+            Collection<SinkRecordField> fields
+    ) {
+        final boolean newlines = fields.size() > 1;
+
+        final ExpressionBuilder.Transform<SinkRecordField> transform = (builder, field) -> {
+            if (newlines) {
+                builder.appendNewLine();
             }
-          }
+            builder.append("ADD ");
+            writeColumnSpec(builder, field);
         };
-      }
 
-      // XML -> string
-      case Types.SQLXML: {
-        return rs -> {
-          SQLXML xml = rs.getSQLXML(col);
-          return xml != null ? xml.getString() : null;
+        ExpressionBuilder builder = expressionBuilder();
+        builder.append("ALTER TABLE ");
+        builder.append(table);
+        builder.append(" ");
+        builder.appendList()
+                .delimitedBy(",")
+                .transformedBy(transform)
+                .of(fields);
+        return Collections.singletonList(builder.toString());
+    }
+
+    @Override
+    public void validateColumnTypes(
+            ResultSetMetaData rsMetadata,
+            List<ColumnId> columns
+    ) throws io.openmessaging.connector.api.errors.ConnectException {
+    }
+
+    protected List<String> extractPrimaryKeyFieldNames(Collection<SinkRecordField> fields) {
+        final List<String> pks = new ArrayList<>();
+        for (SinkRecordField f : fields) {
+            // todo add check column info
+            if (f.isPrimaryKey()) {
+                pks.add(f.name());
+            }
+        }
+        return pks;
+    }
+
+    protected void writeColumnsSpec(
+            ExpressionBuilder builder,
+            Collection<SinkRecordField> fields
+    ) {
+        ExpressionBuilder.Transform<SinkRecordField> transform = (b, field) -> {
+            b.append(System.lineSeparator());
+            writeColumnSpec(b, field);
         };
-      }
-
-      case Types.NULL:
-      case Types.ARRAY:
-      case Types.JAVA_OBJECT:
-      case Types.OTHER:
-      case Types.DISTINCT:
-      case Types.STRUCT:
-      case Types.REF:
-      case Types.ROWID:
-      default: {
-        // These are not currently supported, but we don't want to log something for every single
-        // record we translate. There will already be errors logged for the schema translation
-        break;
-      }
-    }
-    return null;
-  }
-
-  protected int decimalScale(ColumnDefinition defn) {
-    return defn.scale() == NUMERIC_TYPE_SCALE_UNSET ? NUMERIC_TYPE_SCALE_HIGH : defn.scale();
-  }
-
-  /**
-   * Called when the object has been fully read and {@link Blob#free()} should be called.
-   *
-   * @param blob the Blob; never null
-   * @throws SQLException if there is a problem calling free()
-   */
-  protected void free(Blob blob) throws SQLException {
-    blob.free();
-  }
-
-  /**
-   * Called when the object has been fully read and {@link Clob#free()} should be called.
-   *
-   * @param clob the Clob; never null
-   * @throws SQLException if there is a problem calling free()
-   */
-  protected void free(Clob clob) throws SQLException {
-    clob.free();
-  }
-
-  @Override
-  @SuppressWarnings("deprecation")
-  public String buildInsertStatement(
-          TableId table,
-          Collection<ColumnId> keyColumns,
-          Collection<ColumnId> nonKeyColumns
-  ) {
-    ExpressionBuilder builder = expressionBuilder();
-    builder.append("INSERT INTO ");
-    builder.append(table);
-    builder.append("(");
-    builder.appendList()
-            .delimitedBy(",")
-            .transformedBy(ExpressionBuilder.columnNames())
-            .of(keyColumns, nonKeyColumns);
-    builder.append(") VALUES(");
-    builder.appendMultiple(",", "?", keyColumns.size() + nonKeyColumns.size());
-    builder.append(")");
-    return builder.toString();
-  }
-
-  @Override
-  public String buildUpdateStatement(
-          TableId table,
-          Collection<ColumnId> keyColumns,
-          Collection<ColumnId> nonKeyColumns
-  ) {
-    ExpressionBuilder builder = expressionBuilder();
-    builder.append("UPDATE ");
-    builder.append(table);
-    builder.append(" SET ");
-    builder.appendList()
-            .delimitedBy(", ")
-            .transformedBy(ExpressionBuilder.columnNamesWith(" = ?"))
-            .of(nonKeyColumns);
-    if (!keyColumns.isEmpty()) {
-      builder.append(" WHERE ");
-      builder.appendList()
-              .delimitedBy(" AND ")
-              .transformedBy(ExpressionBuilder.columnNamesWith(" = ?"))
-              .of(keyColumns);
-    }
-    return builder.toString();
-  }
-
-  @Override
-  @SuppressWarnings("deprecation")
-  public String buildUpsertQueryStatement(
-          TableId table,
-          Collection<ColumnId> keyColumns,
-          Collection<ColumnId> nonKeyColumns
-  ) {
-    throw new UnsupportedOperationException();
-  }
-
-  @Override
-  public final String buildDeleteStatement(
-          TableId table,
-          Collection<ColumnId> keyColumns
-  ) {
-    ExpressionBuilder builder = expressionBuilder();
-    builder.append("DELETE FROM ");
-    builder.append(table);
-    if (!keyColumns.isEmpty()) {
-      builder.append(" WHERE ");
-      builder.appendList()
-              .delimitedBy(" AND ")
-              .transformedBy(ExpressionBuilder.columnNamesWith(" = ?"))
-              .of(keyColumns);
-    }
-    return builder.toString();
-  }
-
-  /**
-   * table moode
-   * @return
-   */
-  @Override
-  public String buildSelectTableMode() {
-    return "SELECT * FROM ";
-  }
-
-  @Override
-  public void buildSelectTable(ExpressionBuilder builder, TableId tableId) {
-    String mode = buildSelectTableMode();
-    builder.append(mode).append(tableId);
-  }
-
-  @Override
-  public StatementBinder statementBinder(
-          PreparedStatement statement,
-          JdbcSinkConfig.PrimaryKeyMode pkMode,
-          SchemaPair schemaPair,
-          FieldsMetadata fieldsMetadata,
-          TableDefinition tableDefinition,
-          JdbcSinkConfig.InsertMode insertMode) {
-    return new PreparedStatementBinder(
-            this,
-            statement,
-            pkMode,
-            schemaPair,
-            fieldsMetadata,
-            tableDefinition,
-            insertMode
-    );
-  }
-
-  @Override
-  public void bindField(
-          PreparedStatement statement,
-          int index, Schema schema,
-          Object value,
-          ColumnDefinition colDef) throws SQLException {
-    if (value == null) {
-      Integer type = getSqlTypeForSchema(schema);
-      if (type != null) {
-        statement.setNull(index, type);
-      } else {
-        statement.setObject(index, null);
-      }
-    } else {
-      boolean bound = maybeBindLogical(statement, index, schema, value, null);
-      if (!bound) {
-        bound = maybeBindPrimitive(statement, index, schema, value);
-      }
-      if (!bound) {
-        throw new io.openmessaging.connector.api.errors.ConnectException("Unsupported source data type: " + schema.getFieldType());
-      }
-    }
-  }
-
-  protected boolean maybeBindLogical(
-          PreparedStatement statement,
-          int index,
-          Schema schema,
-          Object value,
-          ColumnDefinition colDef
-  ) throws SQLException {
-    if (schema.getName() != null) {
-      switch (schema.getName()) {
-        case DateColumnParser.LOGICAL_NAME:
-          java.sql.Date date;
-          if(value instanceof java.util.Date){
-            date = new java.sql.Date(((java.util.Date) value).getTime());
-          }else{
-            date = new java.sql.Date((int)value);
-          }
-          statement.setDate(
-                  index, date,
-                  DateTimeUtils.getTimeZoneCalendar(timeZone)
-          );
-          return true;
-        case TimeColumnParser.LOGICAL_NAME:
-          java.sql.Time time;
-          if(value instanceof java.util.Date){
-            time = new java.sql.Time(((java.util.Date) value).getTime());
-          }else{
-            time = new java.sql.Time((int)value);
-          }
-          statement.setTime(
-                  index, time,
-                  DateTimeUtils.getTimeZoneCalendar(timeZone)
-          );
-          return true;
-        case TimestampColumnParser.LOGICAL_NAME:
-          java.sql.Timestamp timestamp;
-          if(value instanceof java.util.Date){
-            timestamp = new java.sql.Timestamp(((java.util.Date) value).getTime());
-          }else{
-            timestamp = new java.sql.Timestamp((long)value);
-          }
-          statement.setTimestamp(
-                  index, timestamp,
-                  DateTimeUtils.getTimeZoneCalendar(timeZone)
-          );
-          return true;
-        default:
-          return false;
-      }
-    }
-    return false;
-  }
-
-
-  @Override
-  public TimestampIncrementingCriteria criteriaFor(ColumnId incrementingColumn, List<ColumnId> timestampColumns) {
-    return new TimestampIncrementingCriteria(
-            incrementingColumn, timestampColumns, timeZone);
-  }
-
-  @Override
-  public Long getMinTimestampValue(Connection con, String tableOrQuery, List<String> timestampColumns) throws SQLException {
-    if(timestampColumns==null || timestampColumns.isEmpty()){
-      return null;
-    }
-    StringBuilder builder = new StringBuilder();
-    builder.append("SELECT ");
-    boolean appendComma = false;
-    for(String column : timestampColumns){
-      builder.append("MIN(");
-      builder.append(column);
-      builder.append(")");
-      if(appendComma){
-        builder.append(",");
-      }else{
-        appendComma = true;
-      }
-    }
-    builder.append(" FROM ");
-    builder.append(tableOrQuery);
-    String querySql = builder.toString();
-    PreparedStatement st = con.prepareStatement(querySql);
-    ResultSet resultSet = st.executeQuery();
-    ResultSetMetaData metaData = resultSet.getMetaData();
-    long minTimestampValue = Long.MAX_VALUE;
-    for(int i = 1; i <= metaData.getColumnCount(); ++i){
-      long t = resultSet.getLong(i);
-      minTimestampValue = Math.min(minTimestampValue, t);
-    }
-    st.close();
-    return minTimestampValue;
-  }
-
-  /**
-   * Dialects not supporting `setObject(index, null)` can override this method
-   * to provide a specific sqlType, as per the JDBC documentation
-   * https://docs.oracle.com/javase/7/docs/api/java/sql/PreparedStatement.html
-   *
-   * @param schema the schema
-   * @return the SQL type
-   */
-  protected Integer getSqlTypeForSchema(Schema schema) {
-    return null;
-  }
-
-  protected boolean maybeBindPrimitive(
-          PreparedStatement statement,
-          int index,
-          Schema schema,
-          Object value
-  ) throws SQLException {
-    switch (schema.getFieldType()) {
-      case INT8:
-        statement.setByte(index, (Byte) value);
-        break;
-      case INT32:
-        statement.setInt(index, (Integer) value);
-        break;
-      case INT64:
-        statement.setLong(index, (Long) value);
-        break;
-      case FLOAT32:
-        statement.setFloat(index, (Float) value);
-        break;
-      case FLOAT64:
-        statement.setDouble(index, (Double) value);
-        break;
-      case BOOLEAN:
-        statement.setBoolean(index, (Boolean) value);
-        break;
-      case STRING:
-        statement.setString(index, (String) value);
-        break;
-      case BYTES:
-        final byte[] bytes;
-        if (value instanceof ByteBuffer) {
-          final ByteBuffer buffer = ((ByteBuffer) value).slice();
-          bytes = new byte[buffer.remaining()];
-          buffer.get(bytes);
+        builder.appendList().delimitedBy(",").transformedBy(transform).of(fields);
+    }
+
+    protected void writeColumnSpec(
+            ExpressionBuilder builder,
+            SinkRecordField f
+    ) {
+        builder.appendColumnName(f.name());
+        builder.append(" ");
+        String sqlType = getSqlType(f);
+        builder.append(sqlType);
+        if (f.defaultValue() != null) {
+            builder.append(" DEFAULT ");
+            formatColumnValue(
+                    builder,
+                    f.schemaType(),
+                    f.defaultValue()
+            );
+        } else if (isColumnOptional(f)) {
+            builder.append(" NULL");
         } else {
-          bytes = (byte[]) value;
+            builder.append(" NOT NULL");
         }
-        statement.setBytes(index, bytes);
-        break;
-      case DATETIME:
-        java.sql.Date date;
-        if(value instanceof java.util.Date){
-          date = new java.sql.Date(((java.util.Date) value).getTime());
-        }else{
-          date = new java.sql.Date((int)value);
+    }
+
+    protected boolean isColumnOptional(SinkRecordField field) {
+        return field.isOptional();
+    }
+
+    protected void formatColumnValue(
+            ExpressionBuilder builder,
+            FieldType type,
+            Object value) {
+        switch (type) {
+            case INT8:
+            case INT32:
+            case INT64:
+            case FLOAT32:
+            case FLOAT64:
+                // no escaping required
+                builder.append(value);
+                break;
+            case BOOLEAN:
+                // 1 & 0 for boolean is more portable rather than TRUE/FALSE
+                builder.append((Boolean) value ? '1' : '0');
+                break;
+            case STRING:
+                builder.appendStringQuoted(value);
+                break;
+            case BYTES:
+                final byte[] bytes;
+                if (value instanceof ByteBuffer) {
+                    final ByteBuffer buffer = ((ByteBuffer) value).slice();
+                    bytes = new byte[buffer.remaining()];
+                    buffer.get(bytes);
+                } else {
+                    bytes = (byte[]) value;
+                }
+                builder.appendBinaryLiteral(bytes);
+                break;
+            default:
+                throw new io.openmessaging.connector.api.errors.ConnectException("Unsupported type for column value: " + type);
         }
-        statement.setDate(
-                index, date,
-                DateTimeUtils.getTimeZoneCalendar(timeZone)
-        );
+    }
 
+    protected String getSqlType(SinkRecordField f) {
+        throw new io.openmessaging.connector.api.errors.ConnectException(String.format(
+                "%s (%s) type doesn't have a mapping to the SQL database column type", f.schemaName(),
+                f.schemaType()
+        ));
+    }
 
 
-        break;
-      default:
-        return false;
+    /**
+     * @param url
+     * @return
+     */
+    protected String sanitizedUrl(String url) {
+        // Only replace standard URL-type properties ...
+        return url.replaceAll("(?i)([?&]([^=&]*)password([^=&]*)=)[^&]*", "$1****");
+    }
+
+    @Override
+    public String identifier() {
+        return name() + " database " + sanitizedUrl(jdbcUrl);
+    }
+
+    @Override
+    public String toString() {
+        return name();
     }
-    return true;
-  }
-
-  @Override
-  public String buildCreateTableStatement(
-          TableId table,
-          Collection<SinkRecordField> fields) {
-    ExpressionBuilder builder = expressionBuilder();
-    final List<String> pkFieldNames = extractPrimaryKeyFieldNames(fields);
-    builder.append("CREATE TABLE ");
-    builder.append(table);
-    builder.append(" (");
-    writeColumnsSpec(builder, fields);
-    if (!pkFieldNames.isEmpty()) {
-      builder.append(",");
-      builder.append(System.lineSeparator());
-      builder.append("PRIMARY KEY(");
-      builder.appendList()
-              .delimitedBy(",")
-              .transformedBy(ExpressionBuilder.quote())
-              .of(pkFieldNames);
-      builder.append(")");
-    }
-    builder.append(")");
-    return builder.toString();
-  }
-
-  @Override
-  public String buildDropTableStatement(
-          TableId table,
-          DropOptions options
-  ) {
-    ExpressionBuilder builder = expressionBuilder();
-
-    builder.append("DROP TABLE ");
-    builder.append(table);
-    if (options.ifExists()) {
-      builder.append(" IF EXISTS");
-    }
-    if (options.cascade()) {
-      builder.append(" CASCADE");
-    }
-    return builder.toString();
-  }
-
-  @Override
-  public List<String> buildAlterTable(
-          TableId table,
-          Collection<SinkRecordField> fields
-  ) {
-    final boolean newlines = fields.size() > 1;
-
-    final ExpressionBuilder.Transform<SinkRecordField> transform = (builder, field) -> {
-      if (newlines) {
-        builder.appendNewLine();
-      }
-      builder.append("ADD ");
-      writeColumnSpec(builder, field);
-    };
-
-    ExpressionBuilder builder = expressionBuilder();
-    builder.append("ALTER TABLE ");
-    builder.append(table);
-    builder.append(" ");
-    builder.appendList()
-            .delimitedBy(",")
-            .transformedBy(transform)
-            .of(fields);
-    return Collections.singletonList(builder.toString());
-  }
-
-  @Override
-  public void validateColumnTypes(
-          ResultSetMetaData rsMetadata,
-          List<ColumnId> columns
-  ) throws io.openmessaging.connector.api.errors.ConnectException { }
-
-  protected List<String> extractPrimaryKeyFieldNames(Collection<SinkRecordField> fields) {
-    final List<String> pks = new ArrayList<>();
-    for (SinkRecordField f : fields) {
-      // todo add check column info
-      if (f.isPrimaryKey()) {
-        pks.add(f.name());
-      }
-    }
-    return pks;
-  }
-
-  protected void writeColumnsSpec(
-          ExpressionBuilder builder,
-          Collection<SinkRecordField> fields
-  ) {
-    ExpressionBuilder.Transform<SinkRecordField> transform = (b, field) -> {
-      b.append(System.lineSeparator());
-      writeColumnSpec(b, field);
-    };
-    builder.appendList().delimitedBy(",").transformedBy(transform).of(fields);
-  }
-
-  protected void writeColumnSpec(
-          ExpressionBuilder builder,
-          SinkRecordField f
-  ) {
-    builder.appendColumnName(f.name());
-    builder.append(" ");
-    String sqlType = getSqlType(f);
-    builder.append(sqlType);
-    if (f.defaultValue() != null) {
-      builder.append(" DEFAULT ");
-      formatColumnValue(
-              builder,
-              f.schemaType(),
-              f.defaultValue()
-      );
-    } else if (isColumnOptional(f)) {
-      builder.append(" NULL");
-    } else {
-      builder.append(" NOT NULL");
-    }
-  }
-
-  protected boolean isColumnOptional(SinkRecordField field) {
-    return field.isOptional();
-  }
-
-  protected void formatColumnValue(
-          ExpressionBuilder builder,
-          FieldType type,
-          Object value) {
-    switch (type) {
-      case INT8:
-      case INT32:
-      case INT64:
-      case FLOAT32:
-      case FLOAT64:
-        // no escaping required
-        builder.append(value);
-        break;
-      case BOOLEAN:
-        // 1 & 0 for boolean is more portable rather than TRUE/FALSE
-        builder.append((Boolean) value ? '1' : '0');
-        break;
-      case STRING:
-        builder.appendStringQuoted(value);
-        break;
-      case BYTES:
-        final byte[] bytes;
-        if (value instanceof ByteBuffer) {
-          final ByteBuffer buffer = ((ByteBuffer) value).slice();
-          bytes = new byte[buffer.remaining()];
-          buffer.get(bytes);
-        } else {
-          bytes = (byte[]) value;
-        }
-        builder.appendBinaryLiteral(bytes);
-        break;
-      default:
-        throw new io.openmessaging.connector.api.errors.ConnectException("Unsupported type for column value: " + type);
-    }
-  }
-
-  protected String getSqlType(SinkRecordField f) {
-    throw new io.openmessaging.connector.api.errors.ConnectException(String.format(
-            "%s (%s) type doesn't have a mapping to the SQL database column type", f.schemaName(),
-            f.schemaType()
-    ));
-  }
-
-
-  /**
-   * 隐藏密码
-   * @param url
-   * @return
-   */
-  protected String sanitizedUrl(String url) {
-    // Only replace standard URL-type properties ...
-    return url.replaceAll("(?i)([?&]([^=&]*)password([^=&]*)=)[^&]*", "$1****");
-  }
-
-  @Override
-  public String identifier() {
-    return name() + " database " + sanitizedUrl(jdbcUrl);
-  }
-
-  @Override
-  public String toString() {
-    return name();
-  }
 }
diff --git a/connectors/rocketmq-connect-jdbc/src/main/java/org/apache/rocketmq/connect/jdbc/dialect/impl/MySqlDatabaseDialect.java b/connectors/rocketmq-connect-jdbc/src/main/java/org/apache/rocketmq/connect/jdbc/dialect/impl/MySqlDatabaseDialect.java
index b6bd1dc..0eb8724 100644
--- a/connectors/rocketmq-connect-jdbc/src/main/java/org/apache/rocketmq/connect/jdbc/dialect/impl/MySqlDatabaseDialect.java
+++ b/connectors/rocketmq-connect-jdbc/src/main/java/org/apache/rocketmq/connect/jdbc/dialect/impl/MySqlDatabaseDialect.java
@@ -38,112 +38,114 @@ import java.util.Collection;
  */
 public class MySqlDatabaseDialect extends GenericDatabaseDialect {
 
-  private final Logger log = LoggerFactory.getLogger(MySqlDatabaseDialect.class);
+    private final Logger log = LoggerFactory.getLogger(MySqlDatabaseDialect.class);
 
-  /**
-   * The provider for {@link MySqlDatabaseDialect}.
-   */
-  public static class Provider extends DatabaseDialectProvider {
-    @SneakyThrows
-    public Provider() {
-      super(MySqlDatabaseDialect.class.getSimpleName(), "mysql");
-      Class.forName("com.mysql.cj.jdbc.Driver");
-    }
+    /**
+     * The provider for {@link MySqlDatabaseDialect}.
+     */
+    public static class Provider extends DatabaseDialectProvider {
+        @SneakyThrows
+        public Provider() {
+            super(MySqlDatabaseDialect.class.getSimpleName(), "mysql");
+            Class.forName("com.mysql.cj.jdbc.Driver");
+        }
 
-    @Override
-    public DatabaseDialect create(AbstractConfig config) {
-      return new MySqlDatabaseDialect(config);
+        @Override
+        public DatabaseDialect create(AbstractConfig config) {
+            return new MySqlDatabaseDialect(config);
+        }
     }
-  }
 
-  /**
-   * Create a new dialect instance with the given connector configuration.
-   *
-   * @param config the connector configuration; may not be null
-   */
-  public MySqlDatabaseDialect(AbstractConfig config) {
-    super(config, new IdentifierRules(".", "`", "`"));
-  }
+    /**
+     * Create a new dialect instance with the given connector configuration.
+     *
+     * @param config the connector configuration; may not be null
+     */
+    public MySqlDatabaseDialect(AbstractConfig config) {
+        super(config, new IdentifierRules(".", "`", "`"));
+    }
 
-  /**
-   * initialize prepared statement
-   * @param stmt
-   * @throws SQLException
-   */
-  @Override
-  protected void initializePreparedStatement(PreparedStatement stmt) throws SQLException {
-    stmt.setFetchSize(Integer.MIN_VALUE);
-    log.trace("Initializing PreparedStatement fetch direction to FETCH_FORWARD for '{}'", stmt);
-    stmt.setFetchDirection(ResultSet.FETCH_FORWARD);
-  }
+    /**
+     * initialize prepared statement
+     *
+     * @param stmt
+     * @throws SQLException
+     */
+    @Override
+    protected void initializePreparedStatement(PreparedStatement stmt) throws SQLException {
+        stmt.setFetchSize(Integer.MIN_VALUE);
+        log.trace("Initializing PreparedStatement fetch direction to FETCH_FORWARD for '{}'", stmt);
+        stmt.setFetchDirection(ResultSet.FETCH_FORWARD);
+    }
 
-  /**
-   * get sql type
-   * @param field
-   * @return
-   */
-  @Override
-  protected String getSqlType(SinkRecordField field) {
-    switch (field.schemaType()) {
-      case INT8:
-        return "TINYINT";
-      case INT32:
-        return "INT";
-      case INT64:
-        return "BIGINT";
-      case FLOAT32:
-        return "FLOAT";
-      case FLOAT64:
-        return "DOUBLE";
-      case BOOLEAN:
-        return "TINYINT";
-      case STRING:
-        return "TEXT";
-      case BYTES:
-        return "VARBINARY(1024)";
-      default:
-        return super.getSqlType(field);
+    /**
+     * get sql type
+     *
+     * @param field
+     * @return
+     */
+    @Override
+    protected String getSqlType(SinkRecordField field) {
+        switch (field.schemaType()) {
+            case INT8:
+                return "TINYINT";
+            case INT32:
+                return "INT";
+            case INT64:
+                return "BIGINT";
+            case FLOAT32:
+                return "FLOAT";
+            case FLOAT64:
+                return "DOUBLE";
+            case BOOLEAN:
+                return "TINYINT";
+            case STRING:
+                return "TEXT";
+            case BYTES:
+                return "VARBINARY(1024)";
+            default:
+                return super.getSqlType(field);
+        }
     }
-  }
 
-  @Override
-  public String buildUpsertQueryStatement(
-      TableId table,
-      Collection<ColumnId> keyColumns,
-      Collection<ColumnId> nonKeyColumns
-  ) {
-    //MySql doesn't support SQL 2003:merge so here how the upsert is handled
-    final ExpressionBuilder.Transform<ColumnId> transform = (builder, col) -> {
-      builder.appendColumnName(col.name());
-      builder.append("=values(");
-      builder.appendColumnName(col.name());
-      builder.append(")");
-    };
+    @Override
+    public String buildUpsertQueryStatement(
+            TableId table,
+            Collection<ColumnId> keyColumns,
+            Collection<ColumnId> nonKeyColumns
+    ) {
+        //MySql doesn't support SQL 2003:merge so here how the upsert is handled
+        final ExpressionBuilder.Transform<ColumnId> transform = (builder, col) -> {
+            builder.appendColumnName(col.name());
+            builder.append("=values(");
+            builder.appendColumnName(col.name());
+            builder.append(")");
+        };
 
-    ExpressionBuilder builder = expressionBuilder();
-    builder.append("insert into ");
-    builder.append(table);
-    builder.append("(");
-    builder.appendList()
-           .delimitedBy(",")
-           .transformedBy(ExpressionBuilder.columnNames())
-           .of(keyColumns, nonKeyColumns);
-    builder.append(") values(");
-    builder.appendMultiple(",", "?", keyColumns.size() + nonKeyColumns.size());
-    builder.append(") on duplicate key update ");
-    builder.appendList()
-           .delimitedBy(",")
-           .transformedBy(transform)
-           .of(nonKeyColumns.isEmpty() ? keyColumns : nonKeyColumns);
-    return builder.toString();
-  }
+        ExpressionBuilder builder = expressionBuilder();
+        builder.append("insert into ");
+        builder.append(table);
+        builder.append("(");
+        builder.appendList()
+                .delimitedBy(",")
+                .transformedBy(ExpressionBuilder.columnNames())
+                .of(keyColumns, nonKeyColumns);
+        builder.append(") values(");
+        builder.appendMultiple(",", "?", keyColumns.size() + nonKeyColumns.size());
+        builder.append(") on duplicate key update ");
+        builder.appendList()
+                .delimitedBy(",")
+                .transformedBy(transform)
+                .of(nonKeyColumns.isEmpty() ? keyColumns : nonKeyColumns);
+        return builder.toString();
+    }
 
-  @Override
-  protected String sanitizedUrl(String url) {
-    // MySQL can also have "username:password@" at the beginning of the host list and
-    // in parenthetical properties
-    return super.sanitizedUrl(url)
+    @Override
+    protected String sanitizedUrl(String url) {
+        // MySQL can also have "username:password@" at the beginning of the host list and
+        // in parenthetical properties
+        return super.sanitizedUrl(url)
                 .replaceAll("(?i)([(,]password=)[^,)]*", "$1****")
                 .replaceAll("(://[^:]*:)([^@]*)@", "$1****@");
-  }
+    }
 }
diff --git a/connectors/rocketmq-connect-jdbc/src/main/java/org/apache/rocketmq/connect/jdbc/dialect/impl/OpenMLDBDatabaseDialect.java b/connectors/rocketmq-connect-jdbc/src/main/java/org/apache/rocketmq/connect/jdbc/dialect/impl/OpenMLDBDatabaseDialect.java
index daf4ace..0c70626 100644
--- a/connectors/rocketmq-connect-jdbc/src/main/java/org/apache/rocketmq/connect/jdbc/dialect/impl/OpenMLDBDatabaseDialect.java
+++ b/connectors/rocketmq-connect-jdbc/src/main/java/org/apache/rocketmq/connect/jdbc/dialect/impl/OpenMLDBDatabaseDialect.java
@@ -30,39 +30,40 @@ import org.slf4j.LoggerFactory;
  */
 public class OpenMLDBDatabaseDialect extends GenericDatabaseDialect {
 
-  private final Logger log = LoggerFactory.getLogger(OpenMLDBDatabaseDialect.class);
+    private final Logger log = LoggerFactory.getLogger(OpenMLDBDatabaseDialect.class);
 
-  /**
-   * The provider for {@link OpenMLDBDatabaseDialect}.
-   */
-  public static class Provider extends DatabaseDialectProvider {
-    @SneakyThrows
-    public Provider() {
-      super(OpenMLDBDatabaseDialect.class.getSimpleName(), "openmldb");
-      Class.forName("com._4paradigm.openmldb.jdbc.SQLDriver");
-    }
+    /**
+     * The provider for {@link OpenMLDBDatabaseDialect}.
+     */
+    public static class Provider extends DatabaseDialectProvider {
+        @SneakyThrows
+        public Provider() {
+            super(OpenMLDBDatabaseDialect.class.getSimpleName(), "openmldb");
+            Class.forName("com._4paradigm.openmldb.jdbc.SQLDriver");
+        }
 
-    @Override
-    public DatabaseDialect create(AbstractConfig config) {
-      return new OpenMLDBDatabaseDialect(config);
+        @Override
+        public DatabaseDialect create(AbstractConfig config) {
+            return new OpenMLDBDatabaseDialect(config);
+        }
     }
-  }
 
-  /**
-   * create openMLDB database dialect
-   * @param config
-   */
-  public OpenMLDBDatabaseDialect(AbstractConfig config) {
-    super(config, new IdentifierRules(".", "`", "`"));
-  }
+    /**
+     * create openMLDB database dialect
+     *
+     * @param config
+     */
+    public OpenMLDBDatabaseDialect(AbstractConfig config) {
+        super(config, new IdentifierRules(".", "`", "`"));
+    }
 
 
-  @Override
-  protected String sanitizedUrl(String url) {
-    // MySQL can also have "username:password@" at the beginning of the host list and
-    // in parenthetical properties
-    return super.sanitizedUrl(url)
+    @Override
+    protected String sanitizedUrl(String url) {
+        // MySQL can also have "username:password@" at the beginning of the host list and
+        // in parenthetical properties
+        return super.sanitizedUrl(url)
                 .replaceAll("(?i)([(,]password=)[^,)]*", "$1****")
                 .replaceAll("(://[^:]*:)([^@]*)@", "$1****@");
-  }
+    }
 }
diff --git a/connectors/rocketmq-connect-jdbc/src/main/java/org/apache/rocketmq/connect/jdbc/dialect/provider/CachedConnectionProvider.java b/connectors/rocketmq-connect-jdbc/src/main/java/org/apache/rocketmq/connect/jdbc/dialect/provider/CachedConnectionProvider.java
index 4286a04..8607223 100644
--- a/connectors/rocketmq-connect-jdbc/src/main/java/org/apache/rocketmq/connect/jdbc/dialect/provider/CachedConnectionProvider.java
+++ b/connectors/rocketmq-connect-jdbc/src/main/java/org/apache/rocketmq/connect/jdbc/dialect/provider/CachedConnectionProvider.java
@@ -28,101 +28,101 @@ import java.sql.SQLException;
  */
 public class CachedConnectionProvider implements ConnectionProvider {
 
-  private static final Logger log = LoggerFactory.getLogger(CachedConnectionProvider.class);
+    private static final Logger log = LoggerFactory.getLogger(CachedConnectionProvider.class);
 
-  private static final int VALIDITY_CHECK_TIMEOUT_S = 5;
+    private static final int VALIDITY_CHECK_TIMEOUT_S = 5;
 
-  private final ConnectionProvider provider;
-  private final int maxConnectionAttempts;
-  private final long connectionRetryBackoff;
+    private final ConnectionProvider provider;
+    private final int maxConnectionAttempts;
+    private final long connectionRetryBackoff;
 
-  private int count = 0;
-  private Connection connection;
+    private int count = 0;
+    private Connection connection;
 
-  public CachedConnectionProvider(
-      ConnectionProvider provider,
-      int maxConnectionAttempts,
-      long connectionRetryBackoff
-  ) {
-    this.provider = provider;
-    this.maxConnectionAttempts = maxConnectionAttempts;
-    this.connectionRetryBackoff = connectionRetryBackoff;
-  }
+    public CachedConnectionProvider(
+            ConnectionProvider provider,
+            int maxConnectionAttempts,
+            long connectionRetryBackoff
+    ) {
+        this.provider = provider;
+        this.maxConnectionAttempts = maxConnectionAttempts;
+        this.connectionRetryBackoff = connectionRetryBackoff;
+    }
 
-  @Override
-  public synchronized Connection getConnection() {
-    try {
-      if (connection == null) {
-        newConnection();
-      } else if (!isConnectionValid(connection, VALIDITY_CHECK_TIMEOUT_S)) {
-        log.info("The database connection is invalid. Reconnecting...");
-        close();
-        newConnection();
-      }
-    } catch (SQLException sqle) {
-      throw new ConnectException(sqle);
+    @Override
+    public synchronized Connection getConnection() {
+        try {
+            if (connection == null) {
+                newConnection();
+            } else if (!isConnectionValid(connection, VALIDITY_CHECK_TIMEOUT_S)) {
+                log.info("The database connection is invalid. Reconnecting...");
+                close();
+                newConnection();
+            }
+        } catch (SQLException sqle) {
+            throw new ConnectException(sqle);
+        }
+        return connection;
     }
-    return connection;
-  }
 
-  @Override
-  public boolean isConnectionValid(Connection connection, int timeout) {
-    try {
-      return provider.isConnectionValid(connection, timeout);
-    } catch (SQLException sqle) {
-      log.debug("Unable to check if the underlying connection is valid", sqle);
-      return false;
+    @Override
+    public boolean isConnectionValid(Connection connection, int timeout) {
+        try {
+            return provider.isConnectionValid(connection, timeout);
+        } catch (SQLException sqle) {
+            log.debug("Unable to check if the underlying connection is valid", sqle);
+            return false;
+        }
     }
-  }
 
-  private void newConnection() throws SQLException {
-    int attempts = 0;
-    while (attempts < maxConnectionAttempts) {
-      try {
-        ++count;
-        log.info("Attempting to open connection #{} to {}", count, provider);
-        connection = provider.getConnection();
-        onConnect(connection);
-        return;
-      } catch (SQLException sqle) {
-        attempts++;
-        if (attempts < maxConnectionAttempts) {
-          log.info("Unable to connect to database on attempt {}/{}. Will retry in {} ms.", attempts,
-                   maxConnectionAttempts, connectionRetryBackoff, sqle
-          );
-          try {
-            Thread.sleep(connectionRetryBackoff);
-          } catch (InterruptedException e) {
-            // this is ok because just woke up early
-          }
-        } else {
-          throw sqle;
+    private void newConnection() throws SQLException {
+        int attempts = 0;
+        while (attempts < maxConnectionAttempts) {
+            try {
+                ++count;
+                log.info("Attempting to open connection #{} to {}", count, provider);
+                connection = provider.getConnection();
+                onConnect(connection);
+                return;
+            } catch (SQLException sqle) {
+                attempts++;
+                if (attempts < maxConnectionAttempts) {
+                    log.info("Unable to connect to database on attempt {}/{}. Will retry in {} ms.", attempts,
+                            maxConnectionAttempts, connectionRetryBackoff, sqle
+                    );
+                    try {
+                        Thread.sleep(connectionRetryBackoff);
+                    } catch (InterruptedException e) {
+                        // this is ok because just woke up early
+                    }
+                } else {
+                    throw sqle;
+                }
+            }
         }
-      }
     }
-  }
 
-  @Override
-  public synchronized void close() {
-    if (connection != null) {
-      try {
-        log.info("Closing connection #{} to {}", count, provider);
-        connection.close();
-      } catch (SQLException sqle) {
-        log.warn("Ignoring error closing connection", sqle);
-      } finally {
-        connection = null;
-        provider.close();
-      }
+    @Override
+    public synchronized void close() {
+        if (connection != null) {
+            try {
+                log.info("Closing connection #{} to {}", count, provider);
+                connection.close();
+            } catch (SQLException sqle) {
+                log.warn("Ignoring error closing connection", sqle);
+            } finally {
+                connection = null;
+                provider.close();
+            }
+        }
     }
-  }
 
-  @Override
-  public String identifier() {
-    return provider.identifier();
-  }
+    @Override
+    public String identifier() {
+        return provider.identifier();
+    }
 
-  protected void onConnect(Connection connection) throws SQLException {
-  }
+    protected void onConnect(Connection connection) throws SQLException {
+    }
 
 }
diff --git a/connectors/rocketmq-connect-jdbc/src/main/java/org/apache/rocketmq/connect/jdbc/dialect/provider/ConnectionProvider.java b/connectors/rocketmq-connect-jdbc/src/main/java/org/apache/rocketmq/connect/jdbc/dialect/provider/ConnectionProvider.java
index 3c5f1d3..6e6c7a7 100644
--- a/connectors/rocketmq-connect-jdbc/src/main/java/org/apache/rocketmq/connect/jdbc/dialect/provider/ConnectionProvider.java
+++ b/connectors/rocketmq-connect-jdbc/src/main/java/org/apache/rocketmq/connect/jdbc/dialect/provider/ConnectionProvider.java
@@ -24,42 +24,43 @@ import java.sql.SQLException;
  */
 public interface ConnectionProvider extends AutoCloseable {
 
-  /**
-   * Create a connection.
-   * @return the connection; never null
-   * @throws SQLException if there is a problem getting the connection
-   */
-  Connection getConnection() throws SQLException;
+    /**
+     * Create a connection.
+     *
+     * @return the connection; never null
+     * @throws SQLException if there is a problem getting the connection
+     */
+    Connection getConnection() throws SQLException;
 
-  /**
-   * Determine if the specified connection is valid.
-   *
-   * @param connection the database connection; may not be null
-   * @param timeout    The time in seconds to wait for the database operation used to validate
-   *                   the connection to complete.  If the timeout period expires before the
-   *                   operation completes, this method returns false.  A value of 0 indicates a
-   *                   timeout is not applied to the database operation.
-   * @return true if it is valid, or false otherwise
-   * @throws SQLException if there is an error with the database connection
-   */
-  boolean isConnectionValid(
-      Connection connection,
-      int timeout
-  ) throws SQLException;
+    /**
+     * Determine if the specified connection is valid.
+     *
+     * @param connection the database connection; may not be null
+     * @param timeout    The time in seconds to wait for the database operation used to validate
+     *                   the connection to complete.  If the timeout period expires before the
+     *                   operation completes, this method returns false.  A value of 0 indicates a
+     *                   timeout is not applied to the database operation.
+     * @return true if it is valid, or false otherwise
+     * @throws SQLException if there is an error with the database connection
+     */
+    boolean isConnectionValid(
+            Connection connection,
+            int timeout
+    ) throws SQLException;
 
-  /**
-   * Close this connection provider.
-   */
-  @Override
-  void close();
+    /**
+     * Close this connection provider.
+     */
+    @Override
+    void close();
 
-  /**
-   * Get the publicly viewable identifier for this connection provider and / or the database.
-   * The resulting value should not contain any secrets or passwords.
-   *
-   * @return the identifier; never null
-   */
-  default String identifier() {
-    return toString();
-  }
+    /**
+     * Get the publicly viewable identifier for this connection provider and / or the database.
+     * The resulting value should not contain any secrets or passwords.
+     *
+     * @return the identifier; never null
+     */
+    default String identifier() {
+        return toString();
+    }
 }
diff --git a/connectors/rocketmq-connect-jdbc/src/main/java/org/apache/rocketmq/connect/jdbc/dialect/provider/DatabaseDialectProvider.java b/connectors/rocketmq-connect-jdbc/src/main/java/org/apache/rocketmq/connect/jdbc/dialect/provider/DatabaseDialectProvider.java
index d72da5d..be91bad 100644
--- a/connectors/rocketmq-connect-jdbc/src/main/java/org/apache/rocketmq/connect/jdbc/dialect/provider/DatabaseDialectProvider.java
+++ b/connectors/rocketmq-connect-jdbc/src/main/java/org/apache/rocketmq/connect/jdbc/dialect/provider/DatabaseDialectProvider.java
@@ -26,33 +26,35 @@ import org.apache.rocketmq.connect.jdbc.dialect.DatabaseDialect;
 public abstract class DatabaseDialectProvider {
 
 
-  private final String name;
-  private final String protocol;
-
-  protected DatabaseDialectProvider(String name,String protocol) {
-    assert name != null;
-    this.name = name;
-    this.protocol = protocol;
-  }
-  /**
-   * create database dialect
-   * @param config
-   * @return
-   */
-  public abstract DatabaseDialect create(AbstractConfig config);
-
-  public String protocolName() {
-    return protocol;
-  }
-
-  public String dialectName() {
-    return name;
-  }
-
-  @Override
-  public String toString() {
-    return dialectName();
-  }
+    private final String name;
+    private final String protocol;
+
+    protected DatabaseDialectProvider(String name, String protocol) {
+        assert name != null;
+        this.name = name;
+        this.protocol = protocol;
+    }
+
+    /**
+     * create database dialect
+     *
+     * @param config
+     * @return
+     */
+    public abstract DatabaseDialect create(AbstractConfig config);
+
+    public String protocolName() {
+        return protocol;
+    }
+
+    public String dialectName() {
+        return name;
+    }
+
+    @Override
+    public String toString() {
+        return dialectName();
+    }
 
 
 }
diff --git a/connectors/rocketmq-connect-jdbc/src/main/java/org/apache/rocketmq/connect/jdbc/dialect/provider/JdbcUrlInfo.java b/connectors/rocketmq-connect-jdbc/src/main/java/org/apache/rocketmq/connect/jdbc/dialect/provider/JdbcUrlInfo.java
index 82d4136..4e182c7 100644
--- a/connectors/rocketmq-connect-jdbc/src/main/java/org/apache/rocketmq/connect/jdbc/dialect/provider/JdbcUrlInfo.java
+++ b/connectors/rocketmq-connect-jdbc/src/main/java/org/apache/rocketmq/connect/jdbc/dialect/provider/JdbcUrlInfo.java
@@ -39,4 +39,4 @@ public interface JdbcUrlInfo {
      * @return the URL.
      */
     String url();
-  }
+}
diff --git a/connectors/rocketmq-connect-jdbc/src/main/java/org/apache/rocketmq/connect/jdbc/exception/ConfigException.java b/connectors/rocketmq-connect-jdbc/src/main/java/org/apache/rocketmq/connect/jdbc/exception/ConfigException.java
index 1c181a4..4d4ebfd 100644
--- a/connectors/rocketmq-connect-jdbc/src/main/java/org/apache/rocketmq/connect/jdbc/exception/ConfigException.java
+++ b/connectors/rocketmq-connect-jdbc/src/main/java/org/apache/rocketmq/connect/jdbc/exception/ConfigException.java
@@ -28,7 +28,7 @@ public class ConfigException extends RuntimeException {
     }
 
     public ConfigException(String name, Object value) {
-        this(name, value, (String)null);
+        this(name, value, null);
     }
 
     public ConfigException(String name, Object value, String message) {
diff --git a/connectors/rocketmq-connect-jdbc/src/main/java/org/apache/rocketmq/connect/jdbc/exception/TableAlterOrCreateException.java b/connectors/rocketmq-connect-jdbc/src/main/java/org/apache/rocketmq/connect/jdbc/exception/TableAlterOrCreateException.java
index e32f26d..97c27d1 100644
--- a/connectors/rocketmq-connect-jdbc/src/main/java/org/apache/rocketmq/connect/jdbc/exception/TableAlterOrCreateException.java
+++ b/connectors/rocketmq-connect-jdbc/src/main/java/org/apache/rocketmq/connect/jdbc/exception/TableAlterOrCreateException.java
@@ -23,12 +23,12 @@ import io.openmessaging.connector.api.errors.ConnectException;
  */
 public class TableAlterOrCreateException extends ConnectException {
 
-  public TableAlterOrCreateException(String reason) {
-    super(reason);
-  }
+    public TableAlterOrCreateException(String reason) {
+        super(reason);
+    }
 
-  public TableAlterOrCreateException(String reason, Throwable throwable) {
-    super(reason, throwable);
-  }
+    public TableAlterOrCreateException(String reason, Throwable throwable) {
+        super(reason, throwable);
+    }
 }
 
diff --git a/connectors/rocketmq-connect-jdbc/src/main/java/org/apache/rocketmq/connect/jdbc/schema/column/ColumnDefAdjuster.java b/connectors/rocketmq-connect-jdbc/src/main/java/org/apache/rocketmq/connect/jdbc/schema/column/ColumnDefAdjuster.java
index 4f589c0..5e8b6c3 100644
--- a/connectors/rocketmq-connect-jdbc/src/main/java/org/apache/rocketmq/connect/jdbc/schema/column/ColumnDefAdjuster.java
+++ b/connectors/rocketmq-connect-jdbc/src/main/java/org/apache/rocketmq/connect/jdbc/schema/column/ColumnDefAdjuster.java
@@ -33,10 +33,10 @@ public class ColumnDefAdjuster {
                                            String catalogPattern,
                                            String schemaPattern,
                                            String tablePattern,
-                                           String columnPattern){
+                                           String columnPattern) {
         ColumnDefAdjuster adjuster = new ColumnDefAdjuster();
-        try(ResultSet rs = conn.getMetaData().getColumns(
-                catalogPattern, schemaPattern, tablePattern, columnPattern)){
+        try (ResultSet rs = conn.getMetaData().getColumns(
+                catalogPattern, schemaPattern, tablePattern, columnPattern)) {
             final int rsColumnCount = rs.getMetaData().getColumnCount();
             while (rs.next()) {
                 final String columnName = rs.getString(4);
@@ -56,15 +56,15 @@ public class ColumnDefAdjuster {
                 }
                 adjuster.nullable.put(columnName, nullability);
             }
-        }catch (SQLException e){
+        } catch (SQLException e) {
             //pass
         }
 
         return adjuster;
     }
 
-    public ColumnDefinition.Nullability nullable(String columnName){
-        if(nullable == null || !nullable.containsKey(columnName)){
+    public ColumnDefinition.Nullability nullable(String columnName) {
+        if (nullable == null || !nullable.containsKey(columnName)) {
             return null;
         }
         return nullable.get(columnName);
diff --git a/connectors/rocketmq-connect-jdbc/src/main/java/org/apache/rocketmq/connect/jdbc/schema/column/ColumnDefinition.java b/connectors/rocketmq-connect-jdbc/src/main/java/org/apache/rocketmq/connect/jdbc/schema/column/ColumnDefinition.java
index 25ff655..fceadef 100644
--- a/connectors/rocketmq-connect-jdbc/src/main/java/org/apache/rocketmq/connect/jdbc/schema/column/ColumnDefinition.java
+++ b/connectors/rocketmq-connect-jdbc/src/main/java/org/apache/rocketmq/connect/jdbc/schema/column/ColumnDefinition.java
@@ -26,314 +26,314 @@ import java.util.Objects;
  */
 public class ColumnDefinition {
 
-  /**
-   * The nullability of a column.
-   */
-  public enum Nullability {
-    NULL, NOT_NULL, UNKNOWN
-  }
-
-  /**
-   * The mutability of a column.
-   */
-  public enum Mutability {
-    READ_ONLY, MAYBE_WRITABLE, WRITABLE, UNKNOWN
-  }
+    /**
+     * The nullability of a column.
+     */
+    public enum Nullability {
+        NULL, NOT_NULL, UNKNOWN
+    }
 
-  private final ColumnId id;
-  private final String typeName;
-  private final int jdbcType;
-  private final int displaySize;
-  private final int precision;
-  private final int scale;
-  private final boolean autoIncremented;
-  private final boolean caseSensitive;
-  private final boolean searchable;
-  private final boolean currency;
-  private final boolean signedNumbers;
-  private final boolean isPrimaryKey;
-  private final Nullability nullability;
-  private final Mutability mutability;
-  private final String classNameForType;
+    /**
+     * The mutability of a column.
+     */
+    public enum Mutability {
+        READ_ONLY, MAYBE_WRITABLE, WRITABLE, UNKNOWN
+    }
 
-  public ColumnDefinition(
-      ColumnId id,
-      int jdbcType,
-      String typeName,
-      String classNameForType,
-      Nullability nullability,
-      Mutability mutability,
-      int precision,
-      int scale,
-      boolean signedNumbers,
-      int displaySize,
-      boolean autoIncremented,
-      boolean caseSensitive,
-      boolean searchable,
-      boolean currency,
-      boolean isPrimaryKey
-  ) {
-    this.id = id;
-    this.typeName = typeName;
-    this.jdbcType = jdbcType;
-    this.displaySize = displaySize;
-    this.precision = precision;
-    this.scale = scale;
-    this.autoIncremented = autoIncremented;
-    this.caseSensitive = caseSensitive;
-    this.searchable = searchable;
-    this.currency = currency;
-    this.signedNumbers = signedNumbers;
-    this.nullability = nullability != null ? nullability : Nullability.UNKNOWN;
-    this.mutability = mutability != null ? mutability : Mutability.MAYBE_WRITABLE;
-    this.classNameForType = classNameForType;
-    this.isPrimaryKey = isPrimaryKey;
-  }
+    private final ColumnId id;
+    private final String typeName;
+    private final int jdbcType;
+    private final int displaySize;
+    private final int precision;
+    private final int scale;
+    private final boolean autoIncremented;
+    private final boolean caseSensitive;
+    private final boolean searchable;
+    private final boolean currency;
+    private final boolean signedNumbers;
+    private final boolean isPrimaryKey;
+    private final Nullability nullability;
+    private final Mutability mutability;
+    private final String classNameForType;
 
+    public ColumnDefinition(
+            ColumnId id,
+            int jdbcType,
+            String typeName,
+            String classNameForType,
+            Nullability nullability,
+            Mutability mutability,
+            int precision,
+            int scale,
+            boolean signedNumbers,
+            int displaySize,
+            boolean autoIncremented,
+            boolean caseSensitive,
+            boolean searchable,
+            boolean currency,
+            boolean isPrimaryKey
+    ) {
+        this.id = id;
+        this.typeName = typeName;
+        this.jdbcType = jdbcType;
+        this.displaySize = displaySize;
+        this.precision = precision;
+        this.scale = scale;
+        this.autoIncremented = autoIncremented;
+        this.caseSensitive = caseSensitive;
+        this.searchable = searchable;
+        this.currency = currency;
+        this.signedNumbers = signedNumbers;
+        this.nullability = nullability != null ? nullability : Nullability.UNKNOWN;
+        this.mutability = mutability != null ? mutability : Mutability.MAYBE_WRITABLE;
+        this.classNameForType = classNameForType;
+        this.isPrimaryKey = isPrimaryKey;
+    }
 
-  /**
-   * Indicates whether the column is automatically numbered.
-   *
-   * @return <code>true</code> if so; <code>false</code> otherwise
-   */
-  public boolean isAutoIncrement() {
-    return autoIncremented;
-  }
 
-  /**
-   * Indicates whether the column's case matters.
-   *
-   * @return <code>true</code> if so; <code>false</code> otherwise
-   */
-  public boolean isCaseSensitive() {
-    return caseSensitive;
-  }
+    /**
+     * Indicates whether the column is automatically numbered.
+     *
+     * @return <code>true</code> if so; <code>false</code> otherwise
+     */
+    public boolean isAutoIncrement() {
+        return autoIncremented;
+    }
 
-  /**
-   * Indicates whether the column can be used in a where clause.
-   *
-   * @return <code>true</code> if so; <code>false</code> otherwise
-   */
-  public boolean isSearchable() {
-    return searchable;
-  }
+    /**
+     * Indicates whether the column's case matters.
+     *
+     * @return <code>true</code> if so; <code>false</code> otherwise
+     */
+    public boolean isCaseSensitive() {
+        return caseSensitive;
+    }
 
-  /**
-   * Indicates whether the column is a cash value.
-   *
-   * @return <code>true</code> if so; <code>false</code> otherwise
-   */
-  public boolean isCurrency() {
-    return currency;
-  }
+    /**
+     * Indicates whether the column can be used in a where clause.
+     *
+     * @return <code>true</code> if so; <code>false</code> otherwise
+     */
+    public boolean isSearchable() {
+        return searchable;
+    }
 
-  /**
-   * Indicates whether the column is part of the table's primary key.
-   *
-   * @return <code>true</code> if so; <code>false</code> otherwise
-   */
-  public boolean isPrimaryKey() {
-    return isPrimaryKey;
-  }
+    /**
+     * Indicates whether the column is a cash value.
+     *
+     * @return <code>true</code> if so; <code>false</code> otherwise
+     */
+    public boolean isCurrency() {
+        return currency;
+    }
 
-  /**
-   * Indicates the nullability of values in the column.
-   *
-   * @return the nullability status of the given column; never null
-   */
-  public Nullability nullability() {
-    return nullability;
-  }
+    /**
+     * Indicates whether the column is part of the table's primary key.
+     *
+     * @return <code>true</code> if so; <code>false</code> otherwise
+     */
+    public boolean isPrimaryKey() {
+        return isPrimaryKey;
+    }
 
-  /**
-   * Indicates whether values in the column are optional. This is equivalent to calling:
-   * <pre>
-   *   nullability() == Nullability.NULL || nullability() == Nullability.UNKNOWN
-   * </pre>
-   *
-   * @return <code>true</code> if so; <code>false</code> otherwise
-   */
-  public boolean isOptional() {
-    return nullability == Nullability.NULL || nullability == Nullability.UNKNOWN;
-  }
+    /**
+     * Indicates the nullability of values in the column.
+     *
+     * @return the nullability status of the given column; never null
+     */
+    public Nullability nullability() {
+        return nullability;
+    }
 
-  /**
-   * Indicates whether values in the column are signed numbers.
-   *
-   * @return <code>true</code> if so; <code>false</code> otherwise
-   */
-  public boolean isSignedNumber() {
-    return signedNumbers;
-  }
+    /**
+     * Indicates whether values in the column are optional. This is equivalent to calling:
+     * <pre>
+     *   nullability() == Nullability.NULL || nullability() == Nullability.UNKNOWN
+     * </pre>
+     *
+     * @return <code>true</code> if so; <code>false</code> otherwise
+     */
+    public boolean isOptional() {
+        return nullability == Nullability.NULL || nullability == Nullability.UNKNOWN;
+    }
 
-  /**
-   * Indicates the column's normal maximum width in characters.
-   *
-   * @return the normal maximum number of characters allowed as the width of the designated column
-   */
-  public int displaySize() {
-    return displaySize;
-  }
+    /**
+     * Indicates whether values in the column are signed numbers.
+     *
+     * @return <code>true</code> if so; <code>false</code> otherwise
+     */
+    public boolean isSignedNumber() {
+        return signedNumbers;
+    }
 
-  /**
-   * Get the column's identifier.
-   *
-   * @return column identifier; never null
-   */
-  public ColumnId id() {
-    return id;
-  }
+    /**
+     * Indicates the column's normal maximum width in characters.
+     *
+     * @return the normal maximum number of characters allowed as the width of the designated column
+     */
+    public int displaySize() {
+        return displaySize;
+    }
 
-  /**
-   * Get the column's table identifier.
-   *
-   * @return the table identifier; never null
-   */
-  public TableId tableId() {
-    return id.tableId();
-  }
+    /**
+     * Get the column's identifier.
+     *
+     * @return column identifier; never null
+     */
+    public ColumnId id() {
+        return id;
+    }
 
-  /**
-   * Get the column's specified column size. For numeric data, this is the maximum precision.  For
-   * character data, this is the length in characters. For datetime datatypes, this is the length in
-   * characters of the String representation (assuming the maximum allowed precision of the
-   * fractional seconds component). For binary data, this is the length in bytes. For the ROWID
-   * datatype, this is the length in bytes. 0 is returned for data types where the column size is
-   * not applicable.
-   *
-   * @return precision
-   */
-  public int precision() {
-    return precision;
-  }
+    /**
+     * Get the column's table identifier.
+     *
+     * @return the table identifier; never null
+     */
+    public TableId tableId() {
+        return id.tableId();
+    }
 
-  /**
-   * Gets the column's number of digits to right of the decimal point. 0 is returned for data types
-   * where the scale is not applicable.
-   *
-   * @return scale
-   */
-  public int scale() {
-    return scale;
-  }
+    /**
+     * Get the column's specified column size. For numeric data, this is the maximum precision.  For
+     * character data, this is the length in characters. For datetime datatypes, this is the length in
+     * characters of the String representation (assuming the maximum allowed precision of the
+     * fractional seconds component). For binary data, this is the length in bytes. For the ROWID
+     * datatype, this is the length in bytes. 0 is returned for data types where the column size is
+     * not applicable.
+     *
+     * @return precision
+     */
+    public int precision() {
+        return precision;
+    }
 
-  /**
-   * Retrieves the column's JDBC type.
-   *
-   * @return SQL type from java.sql.Types
-   * @see Types
-   */
-  public int type() {
-    return jdbcType;
-  }
+    /**
+     * Gets the column's number of digits to right of the decimal point. 0 is returned for data types
+     * where the scale is not applicable.
+     *
+     * @return scale
+     */
+    public int scale() {
+        return scale;
+    }
 
-  /**
-   * Retrieves the designated column's database-specific type name.
-   *
-   * @return type name used by the database. If the column type is a user-defined type, then a
-   *     fully-qualified type name is returned.
-   */
-  public String typeName() {
-    return typeName;
-  }
+    /**
+     * Retrieves the column's JDBC type.
+     *
+     * @return SQL type from java.sql.Types
+     * @see Types
+     */
+    public int type() {
+        return jdbcType;
+    }
 
-  /**
-   * Indicates whether the designated column is mutable.
-   *
-   * @return the mutability; never null
-   */
-  public Mutability mutability() {
-    return mutability;
-  }
+    /**
+     * Retrieves the designated column's database-specific type name.
+     *
+     * @return type name used by the database. If the column type is a user-defined type, then a
+     * fully-qualified type name is returned.
+     */
+    public String typeName() {
+        return typeName;
+    }
 
-  /**
-   * Returns the fully-qualified name of the Java class whose instances are manufactured if the
-   * method {@link java.sql.ResultSet#getObject(int)} is called to retrieve a value from the column.
-   * {@link java.sql.ResultSet#getObject(int)} may return a subclass of the class returned by this
-   * method.
-   *
-   * @return the fully-qualified name of the class in the Java programming language that would be
-   *     used by the method <code>ResultSet.getObject</code> to retrieve the value in the specified
-   *     column. This is the class name used for custom mapping.
-   */
-  public String classNameForType() {
-    return classNameForType;
-  }
+    /**
+     * Indicates whether the designated column is mutable.
+     *
+     * @return the mutability; never null
+     */
+    public Mutability mutability() {
+        return mutability;
+    }
 
+    /**
+     * Returns the fully-qualified name of the Java class whose instances are manufactured if the
+     * method {@link java.sql.ResultSet#getObject(int)} is called to retrieve a value from the column.
+     * {@link java.sql.ResultSet#getObject(int)} may return a subclass of the class returned by this
+     * method.
+     *
+     * @return the fully-qualified name of the class in the Java programming language that would be
+     * used by the method <code>ResultSet.getObject</code> to retrieve the value in the specified
+     * column. This is the class name used for custom mapping.
+     */
+    public String classNameForType() {
+        return classNameForType;
+    }
 
-  @Override
-  public int hashCode() {
-    return id.hashCode();
-  }
 
-  @Override
-  public boolean equals(Object obj) {
-    if (obj == this) {
-      return true;
+    @Override
+    public int hashCode() {
+        return id.hashCode();
     }
-    if (obj instanceof ColumnDefinition) {
-      ColumnDefinition that = (ColumnDefinition) obj;
-      return Objects.equals(this.id, that.id)
-             && this.jdbcType == that.jdbcType
-             && this.displaySize == that.displaySize
-             && this.precision == that.precision
-             && this.scale == that.scale
-             && this.autoIncremented == that.autoIncremented
-             && this.caseSensitive == that.caseSensitive
-             && this.searchable == that.searchable
-             && this.currency == that.currency
-             && this.signedNumbers == that.signedNumbers
-             && this.nullability == that.nullability
-             && Objects.equals(this.typeName, that.typeName)
-             && Objects.equals(this.classNameForType, that.classNameForType)
-             && Objects.equals(this.isPrimaryKey, that.isPrimaryKey);
+
+    @Override
+    public boolean equals(Object obj) {
+        if (obj == this) {
+            return true;
+        }
+        if (obj instanceof ColumnDefinition) {
+            ColumnDefinition that = (ColumnDefinition) obj;
+            return Objects.equals(this.id, that.id)
+                    && this.jdbcType == that.jdbcType
+                    && this.displaySize == that.displaySize
+                    && this.precision == that.precision
+                    && this.scale == that.scale
+                    && this.autoIncremented == that.autoIncremented
+                    && this.caseSensitive == that.caseSensitive
+                    && this.searchable == that.searchable
+                    && this.currency == that.currency
+                    && this.signedNumbers == that.signedNumbers
+                    && this.nullability == that.nullability
+                    && Objects.equals(this.typeName, that.typeName)
+                    && Objects.equals(this.classNameForType, that.classNameForType)
+                    && Objects.equals(this.isPrimaryKey, that.isPrimaryKey);
+        }
+        return false;
     }
-    return false;
-  }
 
-  @Override
-  public String toString() {
-    return "Column{'" + id.name() + '\'' + ", isPrimaryKey=" + isPrimaryKey + ", allowsNull="
-           + isOptional() + ", sqlType=" + typeName + '}';
-  }
+    @Override
+    public String toString() {
+        return "Column{'" + id.name() + '\'' + ", isPrimaryKey=" + isPrimaryKey + ", allowsNull="
+                + isOptional() + ", sqlType=" + typeName + '}';
+    }
 
-  /**
-   * Obtain a {@link ColumnDefinition} that has all the same characteristics as this column except
-   * that belongs to the specified table.
-   *
-   * @param tableId the new table ID; may not be null
-   * @return this definition if the specified table ID matches this definition's {@link #tableId()},
-   *     or a new definition that is a copy of this definition except with an {@link #id() ID} that
-   *     uses the specified table; never null
-   */
-  public ColumnDefinition forTable(TableId tableId) {
-    if (tableId().equals(tableId)) {
-      return this;
+    /**
+     * Obtain a {@link ColumnDefinition} that has all the same characteristics as this column except
+     * that belongs to the specified table.
+     *
+     * @param tableId the new table ID; may not be null
+     * @return this definition if the specified table ID matches this definition's {@link #tableId()},
+     * or a new definition that is a copy of this definition except with an {@link #id() ID} that
+     * uses the specified table; never null
+     */
+    public ColumnDefinition forTable(TableId tableId) {
+        if (tableId().equals(tableId)) {
+            return this;
+        }
+        ColumnId newId = new ColumnId(tableId, this.id().name());
+        return new ColumnDefinition(newId, jdbcType, typeName, classNameForType, nullability,
+                mutability, precision, scale, signedNumbers, displaySize,
+                autoIncremented, caseSensitive, searchable, currency, isPrimaryKey
+        );
     }
-    ColumnId newId = new ColumnId(tableId, this.id().name());
-    return new ColumnDefinition(newId, jdbcType, typeName, classNameForType, nullability,
-                                mutability, precision, scale, signedNumbers, displaySize,
-                                autoIncremented, caseSensitive, searchable, currency, isPrimaryKey
-    );
-  }
 
-  /**
-   * Obtain a {@link ColumnDefinition} that has all the same characteristics as this column except
-   * that it does or does not belong to the table's primary key
-   *
-   * @param isPrimaryKey true if the resulting column definition should be part of the table's
-   *                     primary key, or false otherwise
-   * @return a definition that is the same as this definition except it is or is not part of the
-   *     tables primary key, or may be this object if {@link #isPrimaryKey()} already matches the
-   *     supplied parameter; never null
-   */
-  public ColumnDefinition asPartOfPrimaryKey(boolean isPrimaryKey) {
-    if (isPrimaryKey == isPrimaryKey()) {
-      return this;
+    /**
+     * Obtain a {@link ColumnDefinition} that has all the same characteristics as this column except
+     * that it does or does not belong to the table's primary key
+     *
+     * @param isPrimaryKey true if the resulting column definition should be part of the table's
+     *                     primary key, or false otherwise
+     * @return a definition that is the same as this definition except it is or is not part of the
+     * tables primary key, or may be this object if {@link #isPrimaryKey()} already matches the
+     * supplied parameter; never null
+     */
+    public ColumnDefinition asPartOfPrimaryKey(boolean isPrimaryKey) {
+        if (isPrimaryKey == isPrimaryKey()) {
+            return this;
+        }
+        return new ColumnDefinition(id, jdbcType, typeName, classNameForType, nullability, mutability,
+                precision, scale, signedNumbers, displaySize, autoIncremented,
+                caseSensitive, searchable, currency, isPrimaryKey
+        );
     }
-    return new ColumnDefinition(id, jdbcType, typeName, classNameForType, nullability, mutability,
-                                precision, scale, signedNumbers, displaySize, autoIncremented,
-                                caseSensitive, searchable, currency, isPrimaryKey
-    );
-  }
 }
diff --git a/connectors/rocketmq-connect-jdbc/src/main/java/org/apache/rocketmq/connect/jdbc/schema/column/ColumnId.java b/connectors/rocketmq-connect-jdbc/src/main/java/org/apache/rocketmq/connect/jdbc/schema/column/ColumnId.java
index a237d70..8139958 100644
--- a/connectors/rocketmq-connect-jdbc/src/main/java/org/apache/rocketmq/connect/jdbc/schema/column/ColumnId.java
+++ b/connectors/rocketmq-connect-jdbc/src/main/java/org/apache/rocketmq/connect/jdbc/schema/column/ColumnId.java
@@ -27,79 +27,79 @@ import java.util.Objects;
  */
 public class ColumnId implements ExpressionBuilder.Expressable {
 
-  private final TableId tableId;
-  private final String name;
-  private final String alias;
-  private final int hash;
+    private final TableId tableId;
+    private final String name;
+    private final String alias;
+    private final int hash;
 
-  public ColumnId(
-      TableId tableId,
-      String columnName
-  ) {
-    this(tableId, columnName, null);
-  }
-
-  public ColumnId(
-      TableId tableId,
-      String columnName,
-      String alias
-  ) {
-    assert columnName != null;
-    this.tableId = tableId;
-    this.name = columnName;
-    this.alias = alias != null && !alias.trim().isEmpty() ? alias : name;
-    this.hash = Objects.hash(this.tableId, this.name);
-  }
+    public ColumnId(
+            TableId tableId,
+            String columnName
+    ) {
+        this(tableId, columnName, null);
+    }
 
-  public TableId tableId() {
-    return tableId;
-  }
+    public ColumnId(
+            TableId tableId,
+            String columnName,
+            String alias
+    ) {
+        assert columnName != null;
+        this.tableId = tableId;
+        this.name = columnName;
+        this.alias = alias != null && !alias.trim().isEmpty() ? alias : name;
+        this.hash = Objects.hash(this.tableId, this.name);
+    }
 
-  public String name() {
-    return name;
-  }
+    public TableId tableId() {
+        return tableId;
+    }
 
-  public String aliasOrName() {
-    return alias;
-  }
+    public String name() {
+        return name;
+    }
 
-  @Override
-  public void appendTo(ExpressionBuilder builder, boolean useQuotes) {
-    appendTo(builder, useQuotes ? QuoteMethod.ALWAYS : QuoteMethod.NEVER);
-  }
+    public String aliasOrName() {
+        return alias;
+    }
 
-  @Override
-  public void appendTo(
-      ExpressionBuilder builder,
-      QuoteMethod useQuotes
-  ) {
-    if (tableId != null) {
-      builder.append(tableId);
-      builder.appendIdentifierDelimiter();
+    @Override
+    public void appendTo(ExpressionBuilder builder, boolean useQuotes) {
+        appendTo(builder, useQuotes ? QuoteMethod.ALWAYS : QuoteMethod.NEVER);
     }
-    builder.appendColumnName(this.name, useQuotes);
-  }
 
-  @Override
-  public int hashCode() {
-    return hash;
-  }
+    @Override
+    public void appendTo(
+            ExpressionBuilder builder,
+            QuoteMethod useQuotes
+    ) {
+        if (tableId != null) {
+            builder.append(tableId);
+            builder.appendIdentifierDelimiter();
+        }
+        builder.appendColumnName(this.name, useQuotes);
+    }
 
-  @Override
-  public boolean equals(Object obj) {
-    if (obj == this) {
-      return true;
+    @Override
+    public int hashCode() {
+        return hash;
     }
-    if (obj instanceof ColumnId) {
-      ColumnId that = (ColumnId) obj;
-      return Objects.equals(this.name, that.name) && Objects.equals(this.alias, that.alias)
-             && Objects.equals(this.tableId, that.tableId);
+
+    @Override
+    public boolean equals(Object obj) {
+        if (obj == this) {
+            return true;
+        }
+        if (obj instanceof ColumnId) {
+            ColumnId that = (ColumnId) obj;
+            return Objects.equals(this.name, that.name) && Objects.equals(this.alias, that.alias)
+                    && Objects.equals(this.tableId, that.tableId);
+        }
+        return false;
     }
-    return false;
-  }
 
-  @Override
-  public String toString() {
-    return ExpressionBuilder.create().append(this).toString();
-  }
+    @Override
+    public String toString() {
+        return ExpressionBuilder.create().append(this).toString();
+    }
 }
diff --git a/connectors/rocketmq-connect-jdbc/src/main/java/org/apache/rocketmq/connect/jdbc/schema/column/parser/DateColumnParser.java b/connectors/rocketmq-connect-jdbc/src/main/java/org/apache/rocketmq/connect/jdbc/schema/column/parser/DateColumnParser.java
index 386fe42..005c9db 100644
--- a/connectors/rocketmq-connect-jdbc/src/main/java/org/apache/rocketmq/connect/jdbc/schema/column/parser/DateColumnParser.java
+++ b/connectors/rocketmq-connect-jdbc/src/main/java/org/apache/rocketmq/connect/jdbc/schema/column/parser/DateColumnParser.java
@@ -44,7 +44,7 @@ public class DateColumnParser {
             calendar.setTime(value);
             if (calendar.get(11) == 0 && calendar.get(12) == 0 && calendar.get(13) == 0 && calendar.get(14) == 0) {
                 long unixMillis = calendar.getTimeInMillis();
-                return (int)(unixMillis / 86400000L);
+                return (int) (unixMillis / 86400000L);
             } else {
                 throw new ConnectException("Kafka Connect Date type should not have any time fields set to non-zero values.");
             }
@@ -55,7 +55,7 @@ public class DateColumnParser {
         if (!LOGICAL_NAME.equals(schema.getName())) {
             throw new ConnectException("Requested conversion of Date object but the schema does not match.");
         } else {
-            return new java.util.Date((long)value * 86400000L);
+            return new java.util.Date((long) value * 86400000L);
         }
     }
 }
\ No newline at end of file
diff --git a/connectors/rocketmq-connect-jdbc/src/main/java/org/apache/rocketmq/connect/jdbc/schema/column/parser/TimeColumnParser.java b/connectors/rocketmq-connect-jdbc/src/main/java/org/apache/rocketmq/connect/jdbc/schema/column/parser/TimeColumnParser.java
index e5a4198..282b60d 100644
--- a/connectors/rocketmq-connect-jdbc/src/main/java/org/apache/rocketmq/connect/jdbc/schema/column/parser/TimeColumnParser.java
+++ b/connectors/rocketmq-connect-jdbc/src/main/java/org/apache/rocketmq/connect/jdbc/schema/column/parser/TimeColumnParser.java
@@ -25,7 +25,7 @@ import java.util.Calendar;
 import java.util.Date;
 import java.util.TimeZone;
 
-public class TimeColumnParser{
+public class TimeColumnParser {
     public static final String LOGICAL_NAME = "org.apache.rocketmq.connect.jdbc.schema.column.parser.TimeColumnParser";
     private static final long MILLIS_PER_DAY = 86400000L;
     private static final TimeZone UTC = TimeZone.getTimeZone("UTC");
@@ -43,7 +43,7 @@ public class TimeColumnParser{
             calendar.setTime(value);
             long unixMillis = calendar.getTimeInMillis();
             if (unixMillis >= 0L && unixMillis <= 86400000L) {
-                return (int)unixMillis;
+                return (int) unixMillis;
             } else {
                 throw new ConnectException("Kafka Connect Time type should not have any date fields set to non-zero values.");
             }
@@ -53,8 +53,8 @@ public class TimeColumnParser{
     public static Date toLogical(Schema schema, int value) {
         if (!LOGICAL_NAME.equals(schema.getName())) {
             throw new ConnectException("Requested conversion of Date object but the schema does not match.");
-        } else if (value >= 0 && (long)value <= 86400000L) {
-            return new Date((long)value);
+        } else if (value >= 0 && (long) value <= 86400000L) {
+            return new Date((long) value);
         } else {
             throw new ConnectException("Time values must use number of milliseconds greater than 0 and less than 86400000");
         }
diff --git a/connectors/rocketmq-connect-jdbc/src/main/java/org/apache/rocketmq/connect/jdbc/schema/db/DbStructure.java b/connectors/rocketmq-connect-jdbc/src/main/java/org/apache/rocketmq/connect/jdbc/schema/db/DbStructure.java
index e2cc7e7..158c3f3 100644
--- a/connectors/rocketmq-connect-jdbc/src/main/java/org/apache/rocketmq/connect/jdbc/schema/db/DbStructure.java
+++ b/connectors/rocketmq-connect-jdbc/src/main/java/org/apache/rocketmq/connect/jdbc/schema/db/DbStructure.java
@@ -25,7 +25,7 @@ import org.apache.rocketmq.connect.jdbc.schema.table.TableId;
 import org.apache.rocketmq.connect.jdbc.sink.metadata.FieldsMetadata;
 import org.apache.rocketmq.connect.jdbc.sink.metadata.SinkRecordField;
 import org.apache.rocketmq.connect.jdbc.exception.TableAlterOrCreateException;
-import org.apache.rocketmq.connect.jdbc.util.*;
+import org.apache.rocketmq.connect.jdbc.util.TableType;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
@@ -43,268 +43,269 @@ import java.util.concurrent.ConcurrentHashMap;
  *
  */
 public class DbStructure {
-  private static final Logger log = LoggerFactory.getLogger(DbStructure.class);
+    private static final Logger log = LoggerFactory.getLogger(DbStructure.class);
 
-  private final DatabaseDialect dbDialect;
-  private final TableDefinitions tableDefns;
+    private final DatabaseDialect dbDialect;
+    private final TableDefinitions tableDefns;
 
-  public DbStructure(DatabaseDialect dbDialect) {
-    this.dbDialect = dbDialect;
-    this.tableDefns = new TableDefinitions(dbDialect);
-  }
+    public DbStructure(DatabaseDialect dbDialect) {
+        this.dbDialect = dbDialect;
+        this.tableDefns = new TableDefinitions(dbDialect);
+    }
 
-  /**
-   * create 或者 alter 表
-   * Create or amend table.
-   *
-   * @param config the connector configuration
-   * @param connection the database connection handle
-   * @param tableId the table ID
-   * @param fieldsMetadata the fields metadata
-   * @return whether a DDL operation was performed
-   * @throws SQLException if a DDL operation was deemed necessary but failed
-   */
-  public boolean createOrAmendIfNecessary(
-      final JdbcSinkConfig config,
-      final Connection connection,
-      final TableId tableId,
-      final FieldsMetadata fieldsMetadata
-  ) throws SQLException {
-    if (tableDefns.get(connection, tableId) == null) {
-      // Table does not yet exist, so attempt to create it ...
-      try {
-        create(config, connection, tableId, fieldsMetadata);
-      } catch (SQLException sqle) {
-        log.warn("Create failed, will attempt amend if table already exists", sqle);
-        try {
-          TableDefinition newDefn = tableDefns.refresh(connection, tableId);
-          if (newDefn == null) {
-            throw sqle;
-          }
-        } catch (SQLException e) {
-          throw sqle;
+    /**
+     * Create or amend table.
+     *
+     * @param config         the connector configuration
+     * @param connection     the database connection handle
+     * @param tableId        the table ID
+     * @param fieldsMetadata the fields metadata
+     * @return whether a DDL operation was performed
+     * @throws SQLException if a DDL operation was deemed necessary but failed
+     */
+    public boolean createOrAmendIfNecessary(
+            final JdbcSinkConfig config,
+            final Connection connection,
+            final TableId tableId,
+            final FieldsMetadata fieldsMetadata
+    ) throws SQLException {
+        if (tableDefns.get(connection, tableId) == null) {
+            // Table does not yet exist, so attempt to create it ...
+            try {
+                create(config, connection, tableId, fieldsMetadata);
+            } catch (SQLException sqle) {
+                log.warn("Create failed, will attempt amend if table already exists", sqle);
+                try {
+                    TableDefinition newDefn = tableDefns.refresh(connection, tableId);
+                    if (newDefn == null) {
+                        throw sqle;
+                    }
+                } catch (SQLException e) {
+                    throw sqle;
+                }
+            }
         }
-      }
+        return amendIfNecessary(config, connection, tableId, fieldsMetadata, config.getMaxRetries());
     }
-    return amendIfNecessary(config, connection, tableId, fieldsMetadata, config.getMaxRetries());
-  }
 
-  public void applyDdl(String ddl) throws SQLException {
-    Connection connection = dbDialect.getConnection();
-    dbDialect.applyDdlStatements(connection, Collections.singletonList(ddl));
-  }
-  /**
-   * Get the definition for the table with the given ID. This returns a cached definition if
-   * there is one; otherwise, it reads the definition from the database
-   *
-   * @param connection the connection that may be used to fetch the table definition if not
-   *                   already known; may not be null
-   * @param tableId    the ID of the table; may not be null
-   * @return the table definition; or null if the table does not exist
-   * @throws SQLException if there is an error getting the definition from the database
-   */
-  public TableDefinition tableDefinition(
-      Connection connection,
-      TableId tableId
-  ) throws SQLException {
-    TableDefinition defn = tableDefns.get(connection, tableId);
-    if (defn != null) {
-      return defn;
+    public void applyDdl(String ddl) throws SQLException {
+        Connection connection = dbDialect.getConnection();
+        dbDialect.applyDdlStatements(connection, Collections.singletonList(ddl));
     }
-    return tableDefns.refresh(connection, tableId);
-  }
 
-  /**
-   * @throws SQLException if CREATE failed
-   */
-  void create(
-      final JdbcSinkConfig config,
-      final Connection connection,
-      final TableId tableId,
-      final FieldsMetadata fieldsMetadata
-  ) throws SQLException{
-    if (!config.isAutoCreate()) {
-      throw new TableAlterOrCreateException(
-          String.format("Table %s is missing and auto-creation is disabled", tableId)
-      );
+    /**
+     * Get the definition for the table with the given ID. This returns a cached definition if
+     * there is one; otherwise, it reads the definition from the database
+     *
+     * @param connection the connection that may be used to fetch the table definition if not
+     *                   already known; may not be null
+     * @param tableId    the ID of the table; may not be null
+     * @return the table definition; or null if the table does not exist
+     * @throws SQLException if there is an error getting the definition from the database
+     */
+    public TableDefinition tableDefinition(
+            Connection connection,
+            TableId tableId
+    ) throws SQLException {
+        TableDefinition defn = tableDefns.get(connection, tableId);
+        if (defn != null) {
+            return defn;
+        }
+        return tableDefns.refresh(connection, tableId);
     }
-    String sql = dbDialect.buildCreateTableStatement(tableId, fieldsMetadata.allFields.values());
-    log.info("Creating table with sql: {}", sql);
-    dbDialect.applyDdlStatements(connection, Collections.singletonList(sql));
-  }
-
-  /**
-   * @return whether an ALTER was successfully performed
-   * @throws SQLException if ALTER was deemed necessary but failed
-   */
-  boolean amendIfNecessary(
-      final JdbcSinkConfig config,
-      final Connection connection,
-      final TableId tableId,
-      final FieldsMetadata fieldsMetadata,
-      final int maxRetries
-  ) throws SQLException, TableAlterOrCreateException {
-    final TableDefinition tableDefn = tableDefns.get(connection, tableId);
-    final Set<SinkRecordField> missingFields = missingFields(
-        fieldsMetadata.allFields.values(),
-        tableDefn.columnNames()
-    );
 
-    if (missingFields.isEmpty()) {
-      return false;
+    /**
+     * @throws SQLException if CREATE failed
+     */
+    void create(
+            final JdbcSinkConfig config,
+            final Connection connection,
+            final TableId tableId,
+            final FieldsMetadata fieldsMetadata
+    ) throws SQLException {
+        if (!config.isAutoCreate()) {
+            throw new TableAlterOrCreateException(
+                    String.format("Table %s is missing and auto-creation is disabled", tableId)
+            );
+        }
+        String sql = dbDialect.buildCreateTableStatement(tableId, fieldsMetadata.allFields.values());
+        log.info("Creating table with sql: {}", sql);
+        dbDialect.applyDdlStatements(connection, Collections.singletonList(sql));
     }
-    // At this point there are missing fields
-    TableType type = tableDefn.type();
-    switch (type) {
-      case TABLE:
-        // Rather than embed the logic and change lots of lines, just break out
-        break;
-      case VIEW:
-      default:
-        throw new TableAlterOrCreateException(
-            String.format(
-                "%s %s is missing fields (%s) and ALTER %s is unsupported",
-                type.capitalized(),
-                tableId,
-                missingFields,
-                type.jdbcName()
-            )
+
+    /**
+     * @return whether an ALTER was successfully performed
+     * @throws SQLException if ALTER was deemed necessary but failed
+     */
+    boolean amendIfNecessary(
+            final JdbcSinkConfig config,
+            final Connection connection,
+            final TableId tableId,
+            final FieldsMetadata fieldsMetadata,
+            final int maxRetries
+    ) throws SQLException, TableAlterOrCreateException {
+        final TableDefinition tableDefn = tableDefns.get(connection, tableId);
+        final Set<SinkRecordField> missingFields = missingFields(
+                fieldsMetadata.allFields.values(),
+                tableDefn.columnNames()
         );
-    }
 
-    final Set<SinkRecordField> replacedMissingFields = new HashSet<>();
-    for (SinkRecordField missingField: missingFields) {
-      if (!missingField.isOptional() && missingField.defaultValue() == null) {
-        throw new TableAlterOrCreateException(String.format(
-            "Cannot ALTER %s %s to add missing field %s, as the field is not optional and does "
-            + "not have a default value",
-            type.jdbcName(),
-            tableId,
-            missingField
-        ));
-      }
-    }
+        if (missingFields.isEmpty()) {
+            return false;
+        }
+        // At this point there are missing fields
+        TableType type = tableDefn.type();
+        switch (type) {
+            case TABLE:
+                // Rather than embed the logic and change lots of lines, just break out
+                break;
+            case VIEW:
+            default:
+                throw new TableAlterOrCreateException(
+                        String.format(
+                                "%s %s is missing fields (%s) and ALTER %s is unsupported",
+                                type.capitalized(),
+                                tableId,
+                                missingFields,
+                                type.jdbcName()
+                        )
+                );
+        }
 
-    if (!config.isAutoCreate()) {
-      throw new TableAlterOrCreateException(String.format(
-          "%s %s is missing fields (%s) and auto-evolution is disabled",
-          type.capitalized(),
-          tableId,
-          replacedMissingFields
-      ));
-    }
+        final Set<SinkRecordField> replacedMissingFields = new HashSet<>();
+        for (SinkRecordField missingField : missingFields) {
+            if (!missingField.isOptional() && missingField.defaultValue() == null) {
+                throw new TableAlterOrCreateException(String.format(
+                        "Cannot ALTER %s %s to add missing field %s, as the field is not optional and does "
+                                + "not have a default value",
+                        type.jdbcName(),
+                        tableId,
+                        missingField
+                ));
+            }
+        }
 
-    final List<String> amendTableQueries = dbDialect.buildAlterTable(tableId, replacedMissingFields);
-    log.info(
-        "Amending {} to add missing fields:{} maxRetries:{} with SQL: {}",
-        type,
-        replacedMissingFields,
-        maxRetries,
-        amendTableQueries
-    );
-    try {
-      dbDialect.applyDdlStatements(connection, amendTableQueries);
-    } catch (SQLException sqle) {
-      if (maxRetries <= 0) {
-        throw new TableAlterOrCreateException(
-            String.format(
-                "Failed to amend %s '%s' to add missing fields: %s",
+        if (!config.isAutoCreate()) {
+            throw new TableAlterOrCreateException(String.format(
+                    "%s %s is missing fields (%s) and auto-evolution is disabled",
+                    type.capitalized(),
+                    tableId,
+                    replacedMissingFields
+            ));
+        }
+
+        final List<String> amendTableQueries = dbDialect.buildAlterTable(tableId, replacedMissingFields);
+        log.info(
+                "Amending {} to add missing fields:{} maxRetries:{} with SQL: {}",
                 type,
-                tableId,
-                replacedMissingFields
-            ),
-            sqle
+                replacedMissingFields,
+                maxRetries,
+                amendTableQueries
         );
-      }
-      log.warn("Amend failed, re-attempting", sqle);
-      tableDefns.refresh(connection, tableId);
-      // Perhaps there was a race with other tasks to add the columns
-      return amendIfNecessary(
-          config,
-          connection,
-          tableId,
-          fieldsMetadata,
-          maxRetries - 1
-      );
+        try {
+            dbDialect.applyDdlStatements(connection, amendTableQueries);
+        } catch (SQLException sqle) {
+            if (maxRetries <= 0) {
+                throw new TableAlterOrCreateException(
+                        String.format(
+                                "Failed to amend %s '%s' to add missing fields: %s",
+                                type,
+                                tableId,
+                                replacedMissingFields
+                        ),
+                        sqle
+                );
+            }
+            log.warn("Amend failed, re-attempting", sqle);
+            tableDefns.refresh(connection, tableId);
+            // Perhaps there was a race with other tasks to add the columns
+            return amendIfNecessary(
+                    config,
+                    connection,
+                    tableId,
+                    fieldsMetadata,
+                    maxRetries - 1
+            );
+        }
+
+        tableDefns.refresh(connection, tableId);
+        return true;
     }
 
-    tableDefns.refresh(connection, tableId);
-    return true;
-  }
+    Set<SinkRecordField> missingFields(
+            Collection<SinkRecordField> fields,
+            Set<String> dbColumnNames
+    ) {
+        final Set<SinkRecordField> missingFields = new HashSet<>();
+        for (SinkRecordField field : fields) {
+            if (!dbColumnNames.contains(field.name())) {
+                log.debug("Found missing field: {}", field);
+                missingFields.add(field);
+            }
+        }
 
-  Set<SinkRecordField> missingFields(
-      Collection<SinkRecordField> fields,
-      Set<String> dbColumnNames
-  ) {
-    final Set<SinkRecordField> missingFields = new HashSet<>();
-    for (SinkRecordField field : fields) {
-      if (!dbColumnNames.contains(field.name())) {
-        log.debug("Found missing field: {}", field);
-        missingFields.add(field);
-      }
-    }
+        if (missingFields.isEmpty()) {
+            return missingFields;
+        }
 
-    if (missingFields.isEmpty()) {
-      return missingFields;
-    }
+        // check if the missing fields can be located by ignoring case
+        Set<String> columnNamesLowerCase = new HashSet<>();
+        for (String columnName : dbColumnNames) {
+            columnNamesLowerCase.add(columnName.toLowerCase());
+        }
 
-    // check if the missing fields can be located by ignoring case
-    Set<String> columnNamesLowerCase = new HashSet<>();
-    for (String columnName: dbColumnNames) {
-      columnNamesLowerCase.add(columnName.toLowerCase());
-    }
+        if (columnNamesLowerCase.size() != dbColumnNames.size()) {
+            log.warn(
+                    "Table has column names that differ only by case. Original columns={}",
+                    dbColumnNames
+            );
+        }
 
-    if (columnNamesLowerCase.size() != dbColumnNames.size()) {
-      log.warn(
-          "Table has column names that differ only by case. Original columns={}",
-          dbColumnNames
-      );
-    }
+        final Set<SinkRecordField> missingFieldsIgnoreCase = new HashSet<>();
+        for (SinkRecordField missing : missingFields) {
+            if (!columnNamesLowerCase.contains(missing.name().toLowerCase())) {
+                missingFieldsIgnoreCase.add(missing);
+            }
+        }
 
-    final Set<SinkRecordField> missingFieldsIgnoreCase = new HashSet<>();
-    for (SinkRecordField missing: missingFields) {
-      if (!columnNamesLowerCase.contains(missing.name().toLowerCase())) {
-        missingFieldsIgnoreCase.add(missing);
-      }
-    }
+        if (missingFieldsIgnoreCase.size() > 0) {
+            log.info(
+                    "Unable to find fields {} among column names {}",
+                    missingFieldsIgnoreCase,
+                    dbColumnNames
+            );
+        }
 
-    if (missingFieldsIgnoreCase.size() > 0) {
-      log.info(
-          "Unable to find fields {} among column names {}",
-          missingFieldsIgnoreCase,
-          dbColumnNames
-      );
+        return missingFieldsIgnoreCase;
     }
 
-    return missingFieldsIgnoreCase;
-  }
 
+    /**
+     * delete field
+     *
+     * @param fields
+     * @param dbColumnNames
+     * @return
+     */
+    Set<ColumnDefinition> alterDeleteFields(
+            Collection<SinkRecordField> fields,
+            Map<String, ColumnDefinition> dbColumnNames
+    ) {
 
-  /**
-   * delete field
-   * @param fields
-   * @param dbColumnNames
-   * @return
-   */
-  Set<ColumnDefinition> alterDeleteFields(
-          Collection<SinkRecordField> fields,
-          Map<String, ColumnDefinition> dbColumnNames
-  ) {
-
-    final Map<String,SinkRecordField> recordFields = new ConcurrentHashMap<>();
-    for (SinkRecordField field : fields) {
-      recordFields.put(field.name(),field);
-    }
+        final Map<String, SinkRecordField> recordFields = new ConcurrentHashMap<>();
+        for (SinkRecordField field : fields) {
+            recordFields.put(field.name(), field);
+        }
 
-    Set<ColumnDefinition> deleteColumns=new HashSet<>();
-    // filter delete column name
-    for (String columnName : dbColumnNames.keySet()) {
-      if (!recordFields.containsKey(columnName)) {
-        log.debug("Found delete field: {}", columnName);
-        ColumnDefinition columnDefinition=dbColumnNames.get(columnName);
-        deleteColumns.add(dbColumnNames.get(columnName));
-      }
+        Set<ColumnDefinition> deleteColumns = new HashSet<>();
+        // filter delete column name
+        for (String columnName : dbColumnNames.keySet()) {
+            if (!recordFields.containsKey(columnName)) {
+                log.debug("Found delete field: {}", columnName);
+                ColumnDefinition columnDefinition = dbColumnNames.get(columnName);
+                deleteColumns.add(dbColumnNames.get(columnName));
+            }
+        }
+        return deleteColumns;
     }
-    return deleteColumns;
-  }
 }
diff --git a/connectors/rocketmq-connect-jdbc/src/main/java/org/apache/rocketmq/connect/jdbc/schema/table/TableDefinition.java b/connectors/rocketmq-connect-jdbc/src/main/java/org/apache/rocketmq/connect/jdbc/schema/table/TableDefinition.java
index 7027e9b..be64724 100644
--- a/connectors/rocketmq-connect-jdbc/src/main/java/org/apache/rocketmq/connect/jdbc/schema/table/TableDefinition.java
+++ b/connectors/rocketmq-connect-jdbc/src/main/java/org/apache/rocketmq/connect/jdbc/schema/table/TableDefinition.java
@@ -30,94 +30,94 @@ import java.util.Set;
  * A description of a table.
  */
 public class TableDefinition {
-  private final TableId id;
-  private final Map<String, ColumnDefinition> columnsByName = new LinkedHashMap<>();
-  private final Map<String, String> pkColumnNames = new LinkedHashMap<>();
-  private final TableType type;
-
-  public TableDefinition(
-      TableId id,
-      Iterable<ColumnDefinition> columns
-  ) {
-    this(id, columns, TableType.TABLE);
-  }
-
-  public TableDefinition(
-      TableId id,
-      Iterable<ColumnDefinition> columns,
-      TableType type
-  ) {
-    this.id = id;
-    this.type = Objects.requireNonNull(type);
-    for (ColumnDefinition defn : columns) {
-      String columnName = defn.id().name();
-      columnsByName.put(
-          columnName,
-          defn.forTable(this.id)
-      );
-      if (defn.isPrimaryKey()) {
-        this.pkColumnNames.put(
-            columnName,
-            columnName
-        );
-      }
+    private final TableId id;
+    private final Map<String, ColumnDefinition> columnsByName = new LinkedHashMap<>();
+    private final Map<String, String> pkColumnNames = new LinkedHashMap<>();
+    private final TableType type;
+
+    public TableDefinition(
+            TableId id,
+            Iterable<ColumnDefinition> columns
+    ) {
+        this(id, columns, TableType.TABLE);
     }
-  }
 
-  public TableId id() {
-    return id;
-  }
+    public TableDefinition(
+            TableId id,
+            Iterable<ColumnDefinition> columns,
+            TableType type
+    ) {
+        this.id = id;
+        this.type = Objects.requireNonNull(type);
+        for (ColumnDefinition defn : columns) {
+            String columnName = defn.id().name();
+            columnsByName.put(
+                    columnName,
+                    defn.forTable(this.id)
+            );
+            if (defn.isPrimaryKey()) {
+                this.pkColumnNames.put(
+                        columnName,
+                        columnName
+                );
+            }
+        }
+    }
 
-  public TableType type() {
-    return type;
-  }
+    public TableId id() {
+        return id;
+    }
 
-  public int columnCount() {
-    return columnsByName.size();
-  }
+    public TableType type() {
+        return type;
+    }
 
-  public ColumnDefinition definitionForColumn(String name) {
-    return columnsByName.get(name);
-  }
+    public int columnCount() {
+        return columnsByName.size();
+    }
 
-  public Collection<ColumnDefinition> definitionsForColumns() {
-    return columnsByName.values();
-  }
+    public ColumnDefinition definitionForColumn(String name) {
+        return columnsByName.get(name);
+    }
 
-  public Collection<String> primaryKeyColumnNames() {
-    return pkColumnNames.values();
-  }
+    public Collection<ColumnDefinition> definitionsForColumns() {
+        return columnsByName.values();
+    }
 
-  public Set<String> columnNames() {
-    return columnsByName.keySet();
-  }
+    public Collection<String> primaryKeyColumnNames() {
+        return pkColumnNames.values();
+    }
+
+    public Set<String> columnNames() {
+        return columnsByName.keySet();
+    }
 
-  @Override
-  public int hashCode() {
-    return id.hashCode();
-  }
+    @Override
+    public int hashCode() {
+        return id.hashCode();
+    }
 
-  @Override
-  public boolean equals(Object obj) {
-    if (obj == null) {
-      return true;
+    @Override
+    public boolean equals(Object obj) {
+        if (obj == null) {
+            return true;
+        }
+        if (obj instanceof TableDefinition) {
+            TableDefinition that = (TableDefinition) obj;
+            return Objects.equals(this.id(), that.id())
+                    && Objects.equals(this.type(), that.type())
+                    && Objects.equals(this.definitionsForColumns(), that.definitionsForColumns());
+        }
+        return false;
     }
-    if (obj instanceof TableDefinition) {
-      TableDefinition that = (TableDefinition) obj;
-      return Objects.equals(this.id(), that.id())
-             && Objects.equals(this.type(), that.type())
-             && Objects.equals(this.definitionsForColumns(), that.definitionsForColumns());
+
+    @Override
+    public String toString() {
+        return String.format(
+                "Table{name='%s', type=%s columns=%s}",
+                id,
+                type,
+                definitionsForColumns()
+        );
     }
-    return false;
-  }
-
-  @Override
-  public String toString() {
-    return String.format(
-        "Table{name='%s', type=%s columns=%s}",
-        id,
-        type,
-        definitionsForColumns()
-    );
-  }
 }
diff --git a/connectors/rocketmq-connect-jdbc/src/main/java/org/apache/rocketmq/connect/jdbc/schema/table/TableDefinitions.java b/connectors/rocketmq-connect-jdbc/src/main/java/org/apache/rocketmq/connect/jdbc/schema/table/TableDefinitions.java
index 5169490..719ee2e 100644
--- a/connectors/rocketmq-connect-jdbc/src/main/java/org/apache/rocketmq/connect/jdbc/schema/table/TableDefinitions.java
+++ b/connectors/rocketmq-connect-jdbc/src/main/java/org/apache/rocketmq/connect/jdbc/schema/table/TableDefinitions.java
@@ -30,64 +30,64 @@ import java.util.Map;
  */
 public class TableDefinitions {
 
-  private static final Logger log = LoggerFactory.getLogger(TableDefinitions.class);
+    private static final Logger log = LoggerFactory.getLogger(TableDefinitions.class);
 
-  private final Map<TableId, TableDefinition> cache = new HashMap<>();
-  private final DatabaseDialect dialect;
+    private final Map<TableId, TableDefinition> cache = new HashMap<>();
+    private final DatabaseDialect dialect;
 
-  /**
-   * Create an instance that uses the specified database dialect.
-   *
-   * @param dialect the database dialect; may not be null
-   */
-  public TableDefinitions(DatabaseDialect dialect) {
-    this.dialect = dialect;
-  }
+    /**
+     * Create an instance that uses the specified database dialect.
+     *
+     * @param dialect the database dialect; may not be null
+     */
+    public TableDefinitions(DatabaseDialect dialect) {
+        this.dialect = dialect;
+    }
 
-  /**
-   * Get the {@link TableDefinition} for the given table.
-   *
-   * @param connection the JDBC connection to use; may not be null
-   * @param tableId    the table identifier; may not be null
-   * @return the cached {@link TableDefinition}, or null if there is no such table
-   * @throws SQLException if there is any problem using the connection
-   */
-  public TableDefinition get(
-      Connection connection,
-      final TableId tableId
-  ) throws SQLException {
-    TableDefinition dbTable = cache.get(tableId);
-    if (dbTable == null) {
-      if (dialect.tableExists(connection, tableId)) {
-        dbTable = dialect.describeTable(connection, tableId);
-        if (dbTable != null) {
-          log.info("Setting metadata for table {} to {}", tableId, dbTable);
-          cache.put(tableId, dbTable);
+    /**
+     * Get the {@link TableDefinition} for the given table.
+     *
+     * @param connection the JDBC connection to use; may not be null
+     * @param tableId    the table identifier; may not be null
+     * @return the cached {@link TableDefinition}, or null if there is no such table
+     * @throws SQLException if there is any problem using the connection
+     */
+    public TableDefinition get(
+            Connection connection,
+            final TableId tableId
+    ) throws SQLException {
+        TableDefinition dbTable = cache.get(tableId);
+        if (dbTable == null) {
+            if (dialect.tableExists(connection, tableId)) {
+                dbTable = dialect.describeTable(connection, tableId);
+                if (dbTable != null) {
+                    log.info("Setting metadata for table {} to {}", tableId, dbTable);
+                    cache.put(tableId, dbTable);
+                }
+            }
         }
-      }
+        return dbTable;
     }
-    return dbTable;
-  }
 
-  /**
-   * Refresh the cached {@link TableDefinition} for the given table.
-   *
-   * @param connection the JDBC connection to use; may not be null
-   * @param tableId    the table identifier; may not be null
-   * @return the refreshed {@link TableDefinition}, or null if there is no such table
-   * @throws SQLException if there is any problem using the connection
-   */
-  public TableDefinition refresh(
-      Connection connection,
-      TableId tableId
-  ) throws SQLException {
-    TableDefinition dbTable = dialect.describeTable(connection, tableId);
-    if (dbTable != null) {
-      log.info("Refreshing metadata for table {} to {}", tableId, dbTable);
-      cache.put(dbTable.id(), dbTable);
-    } else {
-      log.warn("Failed to refresh metadata for table {}", tableId);
+    /**
+     * Refresh the cached {@link TableDefinition} for the given table.
+     *
+     * @param connection the JDBC connection to use; may not be null
+     * @param tableId    the table identifier; may not be null
+     * @return the refreshed {@link TableDefinition}, or null if there is no such table
+     * @throws SQLException if there is any problem using the connection
+     */
+    public TableDefinition refresh(
+            Connection connection,
+            TableId tableId
+    ) throws SQLException {
+        TableDefinition dbTable = dialect.describeTable(connection, tableId);
+        if (dbTable != null) {
+            log.info("Refreshing metadata for table {} to {}", tableId, dbTable);
+            cache.put(dbTable.id(), dbTable);
+        } else {
+            log.warn("Failed to refresh metadata for table {}", tableId);
+        }
+        return dbTable;
     }
-    return dbTable;
-  }
 }
diff --git a/connectors/rocketmq-connect-jdbc/src/main/java/org/apache/rocketmq/connect/jdbc/schema/table/TableId.java b/connectors/rocketmq-connect-jdbc/src/main/java/org/apache/rocketmq/connect/jdbc/schema/table/TableId.java
index e004fd2..776f786 100644
--- a/connectors/rocketmq-connect-jdbc/src/main/java/org/apache/rocketmq/connect/jdbc/schema/table/TableId.java
+++ b/connectors/rocketmq-connect-jdbc/src/main/java/org/apache/rocketmq/connect/jdbc/schema/table/TableId.java
@@ -24,121 +24,121 @@ import java.util.Objects;
 
 public class TableId implements Comparable<TableId>, ExpressionBuilder.Expressable {
 
-  private final String catalogName;
-  private final String schemaName;
-  private final String tableName;
-  private final int hash;
+    private final String catalogName;
+    private final String schemaName;
+    private final String tableName;
+    private final int hash;
 
-  public TableId(
-      String catalogName,
-      String schemaName,
-      String tableName
-  ) {
-    this.catalogName = catalogName == null || catalogName.isEmpty() ? null : catalogName;
-    this.schemaName = schemaName == null || schemaName.isEmpty() ? null : schemaName;
-    this.tableName = tableName;
-    this.hash = Objects.hash(catalogName, schemaName, tableName);
-  }
-
-  public String catalogName() {
-    return catalogName;
-  }
-
-  public String schemaName() {
-    return schemaName;
-  }
-
-  public String dbName(){
-    if(StringUtils.isNotBlank(catalogName)){
-      return catalogName;
+    public TableId(
+            String catalogName,
+            String schemaName,
+            String tableName
+    ) {
+        this.catalogName = catalogName == null || catalogName.isEmpty() ? null : catalogName;
+        this.schemaName = schemaName == null || schemaName.isEmpty() ? null : schemaName;
+        this.tableName = tableName;
+        this.hash = Objects.hash(catalogName, schemaName, tableName);
     }
-    return schemaName;
-  }
 
-  public String tableName() {
-    return tableName;
-  }
-
-  @Override
-  public void appendTo(ExpressionBuilder builder, boolean useQuotes) {
-    appendTo(builder, useQuotes ? QuoteMethod.ALWAYS : QuoteMethod.NEVER);
-  }
-
-  @Override
-  public void appendTo(
-      ExpressionBuilder builder,
-      QuoteMethod useQuotes
-  ) {
-    if (catalogName != null) {
-      builder.appendIdentifier(catalogName, useQuotes);
-      builder.appendIdentifierDelimiter();
+    public String catalogName() {
+        return catalogName;
     }
-    if (schemaName != null) {
-      builder.appendIdentifier(schemaName, useQuotes);
-      builder.appendIdentifierDelimiter();
+
+    public String schemaName() {
+        return schemaName;
     }
-    builder.appendTableName(tableName, useQuotes);
-  }
 
-  @Override
-  public int hashCode() {
-    return hash;
-  }
+    public String dbName() {
+        if (StringUtils.isNotBlank(catalogName)) {
+            return catalogName;
+        }
+        return schemaName;
+    }
 
-  @Override
-  public boolean equals(Object obj) {
-    if (obj == this) {
-      return true;
+    public String tableName() {
+        return tableName;
     }
-    if (obj instanceof TableId) {
-      TableId that = (TableId) obj;
-      return Objects.equals(this.catalogName, that.catalogName)
-             && Objects.equals(this.schemaName, that.schemaName)
-             && Objects.equals(this.tableName, that.tableName);
+
+    @Override
+    public void appendTo(ExpressionBuilder builder, boolean useQuotes) {
+        appendTo(builder, useQuotes ? QuoteMethod.ALWAYS : QuoteMethod.NEVER);
     }
-    return false;
-  }
 
-  @Override
-  public int compareTo(TableId that) {
-    if (that == this) {
-      return 0;
+    @Override
+    public void appendTo(
+            ExpressionBuilder builder,
+            QuoteMethod useQuotes
+    ) {
+        if (catalogName != null) {
+            builder.appendIdentifier(catalogName, useQuotes);
+            builder.appendIdentifierDelimiter();
+        }
+        if (schemaName != null) {
+            builder.appendIdentifier(schemaName, useQuotes);
+            builder.appendIdentifierDelimiter();
+        }
+        builder.appendTableName(tableName, useQuotes);
     }
-    int diff = this.tableName.compareTo(that.tableName);
-    if (diff != 0) {
-      return diff;
+
+    @Override
+    public int hashCode() {
+        return hash;
     }
-    if (this.schemaName == null) {
-      if (that.schemaName != null) {
-        return -1;
-      }
-    } else {
-      if (that.schemaName == null) {
-        return 1;
-      }
-      diff = this.schemaName.compareTo(that.schemaName);
-      if (diff != 0) {
-        return diff;
-      }
+
+    @Override
+    public boolean equals(Object obj) {
+        if (obj == this) {
+            return true;
+        }
+        if (obj instanceof TableId) {
+            TableId that = (TableId) obj;
+            return Objects.equals(this.catalogName, that.catalogName)
+                    && Objects.equals(this.schemaName, that.schemaName)
+                    && Objects.equals(this.tableName, that.tableName);
+        }
+        return false;
     }
-    if (this.catalogName == null) {
-      if (that.catalogName != null) {
-        return -1;
-      }
-    } else {
-      if (that.catalogName == null) {
-        return 1;
-      }
-      diff = this.catalogName.compareTo(that.catalogName);
-      if (diff != 0) {
-        return diff;
-      }
+
+    @Override
+    public int compareTo(TableId that) {
+        if (that == this) {
+            return 0;
+        }
+        int diff = this.tableName.compareTo(that.tableName);
+        if (diff != 0) {
+            return diff;
+        }
+        if (this.schemaName == null) {
+            if (that.schemaName != null) {
+                return -1;
+            }
+        } else {
+            if (that.schemaName == null) {
+                return 1;
+            }
+            diff = this.schemaName.compareTo(that.schemaName);
+            if (diff != 0) {
+                return diff;
+            }
+        }
+        if (this.catalogName == null) {
+            if (that.catalogName != null) {
+                return -1;
+            }
+        } else {
+            if (that.catalogName == null) {
+                return 1;
+            }
+            diff = this.catalogName.compareTo(that.catalogName);
+            if (diff != 0) {
+                return diff;
+            }
+        }
+        return 0;
     }
-    return 0;
-  }
 
-  @Override
-  public String toString() {
-    return ExpressionBuilder.create().append(this).toString();
-  }
+    @Override
+    public String toString() {
+        return ExpressionBuilder.create().append(this).toString();
+    }
 }
diff --git a/connectors/rocketmq-connect-jdbc/src/main/java/org/apache/rocketmq/connect/jdbc/sink/BufferedRecords.java b/connectors/rocketmq-connect-jdbc/src/main/java/org/apache/rocketmq/connect/jdbc/sink/BufferedRecords.java
index f20927c..87773fb 100644
--- a/connectors/rocketmq-connect-jdbc/src/main/java/org/apache/rocketmq/connect/jdbc/sink/BufferedRecords.java
+++ b/connectors/rocketmq-connect-jdbc/src/main/java/org/apache/rocketmq/connect/jdbc/sink/BufferedRecords.java
@@ -21,6 +21,8 @@ import io.openmessaging.connector.api.data.Schema;
 import io.openmessaging.connector.api.errors.ConnectException;
 import org.apache.rocketmq.connect.jdbc.connector.JdbcSinkConfig;
 import org.apache.rocketmq.connect.jdbc.dialect.DatabaseDialect;
+import org.apache.rocketmq.connect.jdbc.dialect.impl.GenericDatabaseDialect;
+import org.apache.rocketmq.connect.jdbc.dialect.impl.OpenMLDBDatabaseDialect;
 import org.apache.rocketmq.connect.jdbc.schema.db.DbStructure;
 import org.apache.rocketmq.connect.jdbc.sink.metadata.FieldsMetadata;
 import org.apache.rocketmq.connect.jdbc.sink.metadata.SchemaPair;
@@ -39,6 +41,7 @@ import java.util.List;
 import java.util.Objects;
 import java.util.Optional;
 import java.util.stream.Collectors;
+
 import static java.util.Objects.isNull;
 import static java.util.Objects.nonNull;
 
@@ -46,285 +49,303 @@ import static java.util.Objects.nonNull;
  * buffered records
  */
 public class BufferedRecords {
-  private static final Logger log = LoggerFactory.getLogger(BufferedRecords.class);
-
-
-  private final TableId tableId;
-  private final JdbcSinkConfig config;
-  private final DatabaseDialect dbDialect;
-  private final DbStructure dbStructure;
-  private final Connection connection;
+    private static final Logger log = LoggerFactory.getLogger(BufferedRecords.class);
 
-  private List<ConnectRecord> records = new ArrayList<>();
-  private Schema schema;
-  private FieldsMetadata fieldsMetadata;
-  private PreparedStatement updatePreparedStatement;
-  private PreparedStatement deletePreparedStatement;
-  private DatabaseDialect.StatementBinder updateStatementBinder;
-  private DatabaseDialect.StatementBinder deleteStatementBinder;
+    private final TableId tableId;
+    private final JdbcSinkConfig config;
+    private final DatabaseDialect dbDialect;
+    private final DbStructure dbStructure;
+    private final Connection connection;
 
-  public BufferedRecords(
-      JdbcSinkConfig config,
-      TableId tableId,
-      DatabaseDialect dbDialect,
-      DbStructure dbStructure,
-      Connection connection
-  ) {
-    this.tableId = tableId;
-    this.config = config;
-    this.dbDialect = dbDialect;
-    this.dbStructure = dbStructure;
-    this.connection = connection;
-  }
+    private List<ConnectRecord> records = new ArrayList<>();
+    private Schema schema;
+    private FieldsMetadata fieldsMetadata;
+    private PreparedStatement updatePreparedStatement;
+    private PreparedStatement deletePreparedStatement;
+    private DatabaseDialect.StatementBinder updateStatementBinder;
+    private DatabaseDialect.StatementBinder deleteStatementBinder;
 
-  /**
-   * add record
-   * @param record
-   * @return
-   * @throws SQLException
-   */
-  public List<ConnectRecord> add(ConnectRecord record) throws SQLException {
-    final List<ConnectRecord> flushed = new ArrayList<>();
-    boolean schemaChanged = false;
-    // check and update schema
-    if (!Objects.equals(schema, record.getSchema())) {
-      schema = record.getSchema();
-      schemaChanged = true;
+    public BufferedRecords(
+            JdbcSinkConfig config,
+            TableId tableId,
+            DatabaseDialect dbDialect,
+            DbStructure dbStructure,
+            Connection connection
+    ) {
+        this.tableId = tableId;
+        this.config = config;
+        this.dbDialect = dbDialect;
+        this.dbStructure = dbStructure;
+        this.connection = connection;
     }
 
-    if (schemaChanged || updateStatementBinder == null) {
-      // Each batch needs to have the same schemas, so get the buffered records out
-      flushed.addAll(flush());
-      // re-initialize everything that depends on the record schema
-      final SchemaPair schemaPair = new SchemaPair(
-          record.getSchema(),
-          record.getExtensions()
-      );
-      // 抽取字段
-      fieldsMetadata = FieldsMetadata.extract(
-          tableId.tableName(),
-          config.pkMode,
-          config.getPkFields(),
-          config.getFieldsWhitelist(),
-          schemaPair
-      );
-      // create or alter table
-      dbStructure.createOrAmendIfNecessary(
-          config,
-          connection,
-          tableId,
-          fieldsMetadata
-      );
-      final String insertSql = getInsertSql();
-      final String deleteSql = getDeleteSql();
-      log.debug(
-          "{} sql: {} deleteSql: {} meta: {}",
-          config.getInsertMode(),
-          insertSql,
-          deleteSql,
-          fieldsMetadata
-      );
-      close();
-      updatePreparedStatement = dbDialect.createPreparedStatement(connection, insertSql);
-      updateStatementBinder = dbDialect.statementBinder(
-          updatePreparedStatement,
-          config.pkMode,
-          schemaPair,
-          fieldsMetadata,
-          dbStructure.tableDefinition(connection, tableId),
-          config.getInsertMode()
-      );
-      if (config.isDeleteEnabled() && nonNull(deleteSql)) {
-        deletePreparedStatement = dbDialect.createPreparedStatement(connection, deleteSql);
-        deleteStatementBinder = dbDialect.statementBinder(
-            deletePreparedStatement,
-            config.pkMode,
-            schemaPair,
-            fieldsMetadata,
-            dbStructure.tableDefinition(connection, tableId),
-            config.getInsertMode()
-        );
-      }
-    }
+    /**
+     * add record
+     *
+     * @param record
+     * @return
+     * @throws SQLException
+     */
+    public List<ConnectRecord> add(ConnectRecord record) throws SQLException {
+        final List<ConnectRecord> flushed = new ArrayList<>();
+        boolean schemaChanged = false;
+        // check and update schema
+        if (!Objects.equals(schema, record.getSchema())) {
+            schema = record.getSchema();
+            schemaChanged = true;
+        }
 
-    records.add(record);
-    if (records.size() >= config.getBatchSize()) {
-      flushed.addAll(flush());
-    }
-    return flushed;
-  }
+        if (schemaChanged || updateStatementBinder == null) {
+            // Each batch needs to have the same schemas, so get the buffered records out
+            flushed.addAll(flush());
+            // re-initialize everything that depends on the record schema
+            final SchemaPair schemaPair = new SchemaPair(
+                    record.getSchema(),
+                    record.getExtensions()
+            );
+            // extract field
+            fieldsMetadata = FieldsMetadata.extract(
+                    tableId.tableName(),
+                    config.pkMode,
+                    config.getPkFields(),
+                    config.getFieldsWhitelist(),
+                    schemaPair
+            );
+            // create or alter table
+            dbStructure.createOrAmendIfNecessary(
+                    config,
+                    connection,
+                    tableId,
+                    fieldsMetadata
+            );
+            final String insertSql = getInsertSql();
+            final String deleteSql = getDeleteSql();
+            log.debug(
+                    "{} sql: {} deleteSql: {} meta: {}",
+                    config.getInsertMode(),
+                    insertSql,
+                    deleteSql,
+                    fieldsMetadata
+            );
+            close();
+            updatePreparedStatement = dbDialect.createPreparedStatement(connection, insertSql);
+            updateStatementBinder = dbDialect.statementBinder(
+                    updatePreparedStatement,
+                    config.pkMode,
+                    schemaPair,
+                    fieldsMetadata,
+                    dbStructure.tableDefinition(connection, tableId),
+                    config.getInsertMode()
+            );
+            if (config.isDeleteEnabled() && nonNull(deleteSql)) {
+                deletePreparedStatement = dbDialect.createPreparedStatement(connection, deleteSql);
+                deleteStatementBinder = dbDialect.statementBinder(
+                        deletePreparedStatement,
+                        config.pkMode,
+                        schemaPair,
+                        fieldsMetadata,
+                        dbStructure.tableDefinition(connection, tableId),
+                        config.getInsertMode()
+                );
+            }
+        }
 
-  public List<ConnectRecord> flush() throws SQLException {
-    if (records.isEmpty()) {
-      log.debug("Records is empty");
-      return new ArrayList<>();
-    }
-    log.debug("Flushing {} buffered records", records.size());
-    for (ConnectRecord record : records) {
-      if (isNull(record.getData()) && nonNull(deleteStatementBinder)) {
-        deleteStatementBinder.bindRecord(record);
-      } else {
-        updateStatementBinder.bindRecord(record);
-      }
+        records.add(record);
+        if (records.size() >= config.getBatchSize()) {
+            flushed.addAll(flush());
+        }
+        return flushed;
     }
-    Optional<Long> totalUpdateCount = executeUpdates();
-    long totalDeleteCount = executeDeletes();
 
-    final long expectedCount = updateRecordCount();
-    log.trace("{} records:{} resulting in totalUpdateCount:{} totalDeleteCount:{}",
-        config.getInsertMode(), records.size(), totalUpdateCount, totalDeleteCount
-    );
-    if (totalUpdateCount.filter(total -> total != expectedCount).isPresent()
-        && config.getInsertMode() == JdbcSinkConfig.InsertMode.INSERT) {
-      throw new ConnectException(String.format(
-          "Update count (%d) did not sum up to total number of records inserted (%d)",
-          totalUpdateCount.get(),
-          expectedCount
-      ));
-    }
-    if (!totalUpdateCount.isPresent()) {
-      log.info(
-          "{} records:{} , but no count of the number of rows it affected is available",
-          config.getInsertMode(),
-          records.size()
-      );
-    }
+    public List<ConnectRecord> flush() throws SQLException {
+        if (records.isEmpty()) {
+            log.debug("Records is empty");
+            return new ArrayList<>();
+        }
+        log.debug("Flushing {} buffered records", records.size());
+        for (ConnectRecord record : records) {
+            if (isNull(record.getData()) && nonNull(deleteStatementBinder)) {
+                deleteStatementBinder.bindRecord(record);
+            } else {
+                updateStatementBinder.bindRecord(record);
+            }
+        }
+        Optional<Long> totalUpdateCount = executeUpdates();
+        long totalDeleteCount = executeDeletes();
+        final long expectedCount = updateRecordCount();
+        log.trace("{} records:{} resulting in totalUpdateCount:{} totalDeleteCount:{}",
+                config.getInsertMode(),
+                records.size(),
+                totalUpdateCount,
+                totalDeleteCount
+        );
+        if (totalUpdateCount.filter(total -> total != expectedCount).isPresent()
+                && config.getInsertMode() == JdbcSinkConfig.InsertMode.INSERT) {
+            if (dbDialect.name().equals(GenericDatabaseDialect.DialectName.generateDialectName(OpenMLDBDatabaseDialect.class)) && totalUpdateCount.get() == 0) {
+                // openMLDB execute success result 0; do nothing
+            } else {
+                throw new ConnectException(
+                        String.format(
+                                "Update count (%d) did not sum up to total number of records inserted (%d)",
+                                totalUpdateCount.get(),
+                                expectedCount
+                        )
+                );
+            }
+        }
+        if (!totalUpdateCount.isPresent()) {
+            log.info(
+                    "{} records:{} , but no count of the number of rows it affected is available",
+                    config.getInsertMode(),
+                    records.size()
+            );
+        }
 
-    final List<ConnectRecord> flushedRecords = records;
-    records = new ArrayList<>();
-    return flushedRecords;
-  }
+        final List<ConnectRecord> flushedRecords = records;
+        records = new ArrayList<>();
+        return flushedRecords;
+    }
 
-  /**
-   * @return an optional count of all updated rows or an empty optional if no info is available
-   */
-  private Optional<Long> executeUpdates() throws SQLException {
-    Optional<Long> count = Optional.empty();
-    if (nonNull(updatePreparedStatement)){
-      try{
-        for (int updateCount : updatePreparedStatement.executeBatch()) {
-          if (updateCount != Statement.SUCCESS_NO_INFO) {
-            count = count.isPresent()
-                    ? count.map(total -> total + updateCount)
-                    : Optional.of((long) updateCount);
-          }
+    /**
+     * @return an optional count of all updated rows or an empty optional if no info is available
+     */
+    private Optional<Long> executeUpdates() throws SQLException {
+        Optional<Long> count = Optional.empty();
+        if (nonNull(updatePreparedStatement)) {
+            try {
+                for (int updateCount : updatePreparedStatement.executeBatch()) {
+                    if (updateCount != Statement.SUCCESS_NO_INFO) {
+                        count = count.isPresent()
+                                ? count.map(total -> total + updateCount)
+                                : Optional.of((long) updateCount);
+                    }
+                }
+            } catch (SQLException e) {
+                log.error("updatePreparedStatement.executeBatch failed, " +
+                                "errCode={}, " +
+                                "sqlState={}, " +
+                                "error msg={}, " +
+                                "cause={}, " +
+                                "sql={}",
+                        e.getErrorCode(),
+                        e.getSQLState(),
+                        e.getMessage(),
+                        e.getCause(),
+                        updatePreparedStatement
+                );
+                throw e;
+            }
         }
-      }catch(SQLException e){
-        log.error("updatePreparedStatement.executeBatch failed, errCode={}, sqlState={}, error msg={}, cause={}, sql={}",
-                e.getErrorCode(), e.getSQLState(), e.getMessage(), e.getCause(), updatePreparedStatement);
-        throw e;
-      }
+        return count;
     }
-    return count;
-  }
 
-  private long executeDeletes() throws SQLException {
-    long totalDeleteCount = 0;
-    if (nonNull(deletePreparedStatement)) {
-      for (int updateCount : deletePreparedStatement.executeBatch()) {
-        if (updateCount != Statement.SUCCESS_NO_INFO) {
-          totalDeleteCount += updateCount;
+    private long executeDeletes() throws SQLException {
+        long totalDeleteCount = 0;
+        if (nonNull(deletePreparedStatement)) {
+            for (int updateCount : deletePreparedStatement.executeBatch()) {
+                if (updateCount != Statement.SUCCESS_NO_INFO) {
+                    totalDeleteCount += updateCount;
+                }
+            }
         }
-      }
+        return totalDeleteCount;
     }
-    return totalDeleteCount;
-  }
-
-  private long updateRecordCount() {
-    return records
-        .stream()
-        // ignore deletes
-        .filter(record -> nonNull(record.getData()) || !config.isDeleteEnabled())
-        .count();
-  }
 
-  public void close() throws SQLException {
-    log.debug(
-        "Closing BufferedRecords with updatePreparedStatement: {} deletePreparedStatement: {}",
-        updatePreparedStatement,
-        deletePreparedStatement
-    );
-    if (nonNull(updatePreparedStatement)) {
-      updatePreparedStatement.close();
-      updatePreparedStatement = null;
+    private long updateRecordCount() {
+        return records
+                .stream()
+                // ignore deletes
+                .filter(record -> nonNull(record.getData()) || !config.isDeleteEnabled())
+                .count();
     }
-    if (nonNull(deletePreparedStatement)) {
-      deletePreparedStatement.close();
-      deletePreparedStatement = null;
-    }
-  }
 
-  private String getInsertSql() throws SQLException {
-    switch (config.getInsertMode()) {
-      case INSERT:
-        return dbDialect.buildInsertStatement(
-            tableId,
-            asColumns(fieldsMetadata.keyFieldNames),
-            asColumns(fieldsMetadata.nonKeyFieldNames)
+    public void close() throws SQLException {
+        log.debug(
+                "Closing BufferedRecords with updatePreparedStatement: {} deletePreparedStatement: {}",
+                updatePreparedStatement,
+                deletePreparedStatement
         );
-      case UPSERT:
-        if (fieldsMetadata.keyFieldNames.isEmpty()) {
-          throw new ConnectException(String.format(
-              "Write to table '%s' in UPSERT mode requires key field names to be known, check the"
-                  + " primary key configuration",
-              tableId
-          ));
+        if (nonNull(updatePreparedStatement)) {
+            updatePreparedStatement.close();
+            updatePreparedStatement = null;
         }
-        try {
-          return dbDialect.buildUpsertQueryStatement(
-              tableId,
-              asColumns(fieldsMetadata.keyFieldNames),
-              asColumns(fieldsMetadata.nonKeyFieldNames)
-          );
-        } catch (UnsupportedOperationException e) {
-          throw new ConnectException(String.format(
-              "Write to table '%s' in UPSERT mode is not supported with the %s dialect.",
-              tableId,
-              dbDialect.name()
-          ));
+        if (nonNull(deletePreparedStatement)) {
+            deletePreparedStatement.close();
+            deletePreparedStatement = null;
         }
-      case UPDATE:
-        return dbDialect.buildUpdateStatement(
-            tableId,
-            asColumns(fieldsMetadata.keyFieldNames),
-            asColumns(fieldsMetadata.nonKeyFieldNames)
-        );
-      default:
-        throw new ConnectException("Invalid insert mode");
     }
-  }
 
-  private String getDeleteSql() {
-    String sql = null;
-    if (config.isDeleteEnabled()) {
-      switch (config.pkMode) {
-        case RECORD_KEY:
-          if (fieldsMetadata.keyFieldNames.isEmpty()) {
-            throw new ConnectException("Require primary keys to support delete");
-          }
-          try {
-            sql = dbDialect.buildDeleteStatement(
-                tableId,
-                asColumns(fieldsMetadata.keyFieldNames)
-            );
-          } catch (UnsupportedOperationException e) {
-            throw new ConnectException(String.format(
-                "Deletes to table '%s' are not supported with the %s dialect.",
-                tableId,
-                dbDialect.name()
-            ));
-          }
-          break;
-        default:
-          throw new ConnectException("Deletes are only supported for pk.mode record_key");
-      }
+    private String getInsertSql() {
+        switch (config.getInsertMode()) {
+            case INSERT:
+                return dbDialect.buildInsertStatement(
+                        tableId,
+                        asColumns(fieldsMetadata.keyFieldNames),
+                        asColumns(fieldsMetadata.nonKeyFieldNames)
+                );
+            case UPSERT:
+                if (fieldsMetadata.keyFieldNames.isEmpty()) {
+                    throw new ConnectException(String.format(
+                            "Write to table '%s' in UPSERT mode requires key field names to be known, check the"
+                                    + " primary key configuration",
+                            tableId
+                    ));
+                }
+                try {
+                    return dbDialect.buildUpsertQueryStatement(
+                            tableId,
+                            asColumns(fieldsMetadata.keyFieldNames),
+                            asColumns(fieldsMetadata.nonKeyFieldNames)
+                    );
+                } catch (UnsupportedOperationException e) {
+                    throw new ConnectException(String.format(
+                            "Write to table '%s' in UPSERT mode is not supported with the %s dialect.",
+                            tableId,
+                            dbDialect.name()
+                    ));
+                }
+            case UPDATE:
+                return dbDialect.buildUpdateStatement(
+                        tableId,
+                        asColumns(fieldsMetadata.keyFieldNames),
+                        asColumns(fieldsMetadata.nonKeyFieldNames)
+                );
+            default:
+                throw new ConnectException("Invalid insert mode");
+        }
     }
-    return sql;
-  }
 
-  private Collection<ColumnId> asColumns(Collection<String> names) {
-    return names.stream()
-        .map(name -> new ColumnId(tableId, name))
-        .collect(Collectors.toList());
-  }
+    private String getDeleteSql() {
+        String sql = null;
+        if (config.isDeleteEnabled()) {
+            switch (config.pkMode) {
+                case RECORD_KEY:
+                    if (fieldsMetadata.keyFieldNames.isEmpty()) {
+                        throw new ConnectException("Require primary keys to support delete");
+                    }
+                    try {
+                        sql = dbDialect.buildDeleteStatement(
+                                tableId,
+                                asColumns(fieldsMetadata.keyFieldNames)
+                        );
+                    } catch (UnsupportedOperationException e) {
+                        throw new ConnectException(String.format(
+                                "Deletes to table '%s' are not supported with the %s dialect.",
+                                tableId,
+                                dbDialect.name()
+                        ));
+                    }
+                    break;
+                default:
+                    throw new ConnectException("Deletes are only supported for pk.mode record_key");
+            }
+        }
+        return sql;
+    }
+
+    private Collection<ColumnId> asColumns(Collection<String> names) {
+        return names.stream()
+                .map(name -> new ColumnId(tableId, name))
+                .collect(Collectors.toList());
+    }
 }
diff --git a/connectors/rocketmq-connect-jdbc/src/main/java/org/apache/rocketmq/connect/jdbc/sink/Updater.java b/connectors/rocketmq-connect-jdbc/src/main/java/org/apache/rocketmq/connect/jdbc/sink/Updater.java
index 8f146dc..dcd22f9 100644
--- a/connectors/rocketmq-connect-jdbc/src/main/java/org/apache/rocketmq/connect/jdbc/sink/Updater.java
+++ b/connectors/rocketmq-connect-jdbc/src/main/java/org/apache/rocketmq/connect/jdbc/sink/Updater.java
@@ -37,69 +37,69 @@ import java.util.Map;
  */
 public class Updater {
 
-  private static final Logger log = LoggerFactory.getLogger(Updater.class);
-  private final JdbcSinkConfig config;
-  private final DatabaseDialect dbDialect;
-  private final DbStructure dbStructure;
-  final CachedConnectionProvider cachedConnectionProvider;
+    private static final Logger log = LoggerFactory.getLogger(Updater.class);
+    private final JdbcSinkConfig config;
+    private final DatabaseDialect dbDialect;
+    private final DbStructure dbStructure;
+    final CachedConnectionProvider cachedConnectionProvider;
 
-  public Updater(final JdbcSinkConfig config, DatabaseDialect dbDialect, DbStructure dbStructure) {
-    this.config = config;
-    this.dbDialect = dbDialect;
-    this.dbStructure = dbStructure;
+    public Updater(final JdbcSinkConfig config, DatabaseDialect dbDialect, DbStructure dbStructure) {
+        this.config = config;
+        this.dbDialect = dbDialect;
+        this.dbStructure = dbStructure;
 
-    this.cachedConnectionProvider = connectionProvider(
-        config.getAttempts(),
-        config.getRetryBackoffMs()
-    );
-  }
+        this.cachedConnectionProvider = connectionProvider(
+                config.getAttempts(),
+                config.getRetryBackoffMs()
+        );
+    }
 
-  protected CachedConnectionProvider connectionProvider(int maxConnAttempts, long retryBackoff) {
-    return new CachedConnectionProvider(this.dbDialect, maxConnAttempts, retryBackoff) {
-      @Override
-      protected void onConnect(final Connection connection) throws SQLException {
-        connection.setAutoCommit(false);
-      }
-    };
-  }
+    protected CachedConnectionProvider connectionProvider(int maxConnAttempts, long retryBackoff) {
+        return new CachedConnectionProvider(this.dbDialect, maxConnAttempts, retryBackoff) {
+            @Override
+            protected void onConnect(final Connection connection) throws SQLException {
+                connection.setAutoCommit(false);
+            }
+        };
+    }
 
-  public void write(final Collection<ConnectRecord> records)
-      throws SQLException, TableAlterOrCreateException {
-    final Connection connection = cachedConnectionProvider.getConnection();
-    try {
-      final Map<TableId, BufferedRecords> bufferByTable = new HashMap<>();
-      for (ConnectRecord record : records) {
-        // destination table
-        final TableId tableId = destinationTable(record);
-        if (!config.filterWhiteTable(dbDialect,tableId)){
-          continue;
-        }
-        BufferedRecords buffer = bufferByTable.get(tableId);
-        if (buffer == null) {
-          buffer = new BufferedRecords(config, tableId, dbDialect, dbStructure, connection);
-          bufferByTable.put(tableId, buffer);
+    public void write(final Collection<ConnectRecord> records)
+            throws SQLException, TableAlterOrCreateException {
+        final Connection connection = cachedConnectionProvider.getConnection();
+        try {
+            final Map<TableId, BufferedRecords> bufferByTable = new HashMap<>();
+            for (ConnectRecord record : records) {
+                // destination table
+                final TableId tableId = destinationTable(record);
+                if (!config.filterWhiteTable(dbDialect, tableId)) {
+                    continue;
+                }
+                BufferedRecords buffer = bufferByTable.get(tableId);
+                if (buffer == null) {
+                    buffer = new BufferedRecords(config, tableId, dbDialect, dbStructure, connection);
+                    bufferByTable.put(tableId, buffer);
+                }
+                buffer.add(record);
+            }
+            for (Map.Entry<TableId, BufferedRecords> entry : bufferByTable.entrySet()) {
+                TableId tableId = entry.getKey();
+                BufferedRecords buffer = entry.getValue();
+                log.debug("Flushing records in JDBC Writer for table ID: {}", tableId);
+                buffer.flush();
+                buffer.close();
+            }
+            connection.commit();
+        } catch (SQLException | TableAlterOrCreateException e) {
+            connection.rollback();
         }
-        buffer.add(record);
-      }
-      for (Map.Entry<TableId, BufferedRecords> entry : bufferByTable.entrySet()) {
-        TableId tableId = entry.getKey();
-        BufferedRecords buffer = entry.getValue();
-        log.debug("Flushing records in JDBC Writer for table ID: {}", tableId);
-        buffer.flush();
-        buffer.close();
-      }
-      connection.commit();
-    } catch (SQLException | TableAlterOrCreateException e) {
-      connection.rollback();
     }
-  }
 
-  public void closeQuietly() {
-    cachedConnectionProvider.close();
-  }
+    public void closeQuietly() {
+        cachedConnectionProvider.close();
+    }
 
-  TableId destinationTable(ConnectRecord record) {
-    // todo table from header
-    return dbDialect.parseToTableId(record.getSchema().getName());
-  }
+    TableId destinationTable(ConnectRecord record) {
+        // todo table from header
+        return dbDialect.parseToTableId(record.getSchema().getName());
+    }
 }
diff --git a/connectors/rocketmq-connect-jdbc/src/main/java/org/apache/rocketmq/connect/jdbc/sink/metadata/FieldsMetadata.java b/connectors/rocketmq-connect-jdbc/src/main/java/org/apache/rocketmq/connect/jdbc/sink/metadata/FieldsMetadata.java
index d6f224d..d185218 100644
--- a/connectors/rocketmq-connect-jdbc/src/main/java/org/apache/rocketmq/connect/jdbc/sink/metadata/FieldsMetadata.java
+++ b/connectors/rocketmq-connect-jdbc/src/main/java/org/apache/rocketmq/connect/jdbc/sink/metadata/FieldsMetadata.java
@@ -38,202 +38,204 @@ import java.util.Set;
  */
 public class FieldsMetadata {
 
-  public final Set<String> keyFieldNames;
-  public final Set<String> nonKeyFieldNames;
-  public final Map<String, SinkRecordField> allFields;
-
-  // visible for testing
-  public FieldsMetadata(
-      Set<String> keyFieldNames,
-      Set<String> nonKeyFieldNames,
-      Map<String, SinkRecordField> allFields
-  ) {
-    boolean fieldCountsMatch = (keyFieldNames.size() + nonKeyFieldNames.size() == allFields.size());
-    boolean allFieldsContained = (allFields.keySet().containsAll(keyFieldNames)
-                   && allFields.keySet().containsAll(nonKeyFieldNames));
-    if (!fieldCountsMatch || !allFieldsContained) {
-      throw new IllegalArgumentException(String.format(
-          "Validation fail -- keyFieldNames:%s nonKeyFieldNames:%s allFields:%s",
-          keyFieldNames, nonKeyFieldNames, allFields
-      ));
-    }
-    this.keyFieldNames = keyFieldNames;
-    this.nonKeyFieldNames = nonKeyFieldNames;
-    this.allFields = allFields;
-  }
-
-  public static FieldsMetadata extract(
-      final String tableName,
-      final JdbcSinkConfig.PrimaryKeyMode pkMode,
-      final List<String> configuredPkFields,
-      final Set<String> fieldsWhitelist,
-      final SchemaPair schemaPair
-  ) {
-    return extract(
-        tableName,
-        pkMode,
-        configuredPkFields,
-        fieldsWhitelist,
-        schemaPair.schema,
-        schemaPair.extensions
-    );
-  }
-
-  /**
-   * extract metadata info
-   * @param tableName
-   * @param pkMode
-   * @param configuredPkFields
-   * @param fieldsWhitelist
-   * @param schema
-   * @param headers
-   * @return
-   */
-  public static FieldsMetadata extract(
-      final String tableName,
-      final JdbcSinkConfig.PrimaryKeyMode pkMode,
-      final List<String> configuredPkFields,
-      final Set<String> fieldsWhitelist,
-      final Schema schema,
-      final KeyValue headers
-  ) {
-    if (schema != null && schema.getFieldType() != FieldType.STRUCT) {
-      throw new ConnectException("Value schema must be of type Struct");
+    public final Set<String> keyFieldNames;
+    public final Set<String> nonKeyFieldNames;
+    public final Map<String, SinkRecordField> allFields;
+
+    // visible for testing
+    public FieldsMetadata(
+            Set<String> keyFieldNames,
+            Set<String> nonKeyFieldNames,
+            Map<String, SinkRecordField> allFields
+    ) {
+        boolean fieldCountsMatch = (keyFieldNames.size() + nonKeyFieldNames.size()) == allFields.size();
+        boolean allFieldsContained = allFields.keySet().containsAll(keyFieldNames)
+                && allFields.keySet().containsAll(nonKeyFieldNames);
+        if (!fieldCountsMatch || !allFieldsContained) {
+            throw new IllegalArgumentException(String.format(
+                    "Validation fail -- keyFieldNames:%s nonKeyFieldNames:%s allFields:%s",
+                    keyFieldNames, nonKeyFieldNames, allFields
+            ));
+        }
+        this.keyFieldNames = keyFieldNames;
+        this.nonKeyFieldNames = nonKeyFieldNames;
+        this.allFields = allFields;
     }
-    final Map<String, SinkRecordField> allFields = new HashMap<>();
-    final Set<String> keyFieldNames = new LinkedHashSet<>();
-    switch (pkMode) {
-      case NONE:
-        break;
-      case RECORD_VALUE:
-        extractRecordValuePk(tableName, configuredPkFields, schema, headers, allFields, keyFieldNames);
-        break;
-      default:
-        throw new ConnectException("Unknown primary key mode: " + pkMode);
+
+    public static FieldsMetadata extract(
+            final String tableName,
+            final JdbcSinkConfig.PrimaryKeyMode pkMode,
+            final List<String> configuredPkFields,
+            final Set<String> fieldsWhitelist,
+            final SchemaPair schemaPair
+    ) {
+        return extract(
+                tableName,
+                pkMode,
+                configuredPkFields,
+                fieldsWhitelist,
+                schemaPair.schema,
+                schemaPair.extensions
+        );
     }
-    final Set<String> nonKeyFieldNames = new LinkedHashSet<>();
-    if (schema != null) {
-      for (Field field : schema.getFields()) {
-        if (keyFieldNames.contains(field.getName())) {
-          continue;
+
+    /**
+     * extract metadata info
+     *
+     * @param tableName
+     * @param pkMode
+     * @param configuredPkFields
+     * @param fieldsWhitelist
+     * @param schema
+     * @param headers
+     * @return
+     */
+    public static FieldsMetadata extract(
+            final String tableName,
+            final JdbcSinkConfig.PrimaryKeyMode pkMode,
+            final List<String> configuredPkFields,
+            final Set<String> fieldsWhitelist,
+            final Schema schema,
+            final KeyValue headers
+    ) {
+        if (schema != null && schema.getFieldType() != FieldType.STRUCT) {
+            throw new ConnectException("Value schema must be of type Struct");
         }
-        if (!fieldsWhitelist.isEmpty() && !fieldsWhitelist.contains(field.getName())) {
-          continue;
+        final Map<String, SinkRecordField> allFields = new HashMap<>();
+        final Set<String> keyFieldNames = new LinkedHashSet<>();
+        switch (pkMode) {
+            case NONE:
+                break;
+            case RECORD_VALUE:
+                extractRecordValuePk(tableName, configuredPkFields, schema, headers, allFields, keyFieldNames);
+                break;
+            default:
+                throw new ConnectException("Unknown primary key mode: " + pkMode);
+        }
+        final Set<String> nonKeyFieldNames = new LinkedHashSet<>();
+        if (schema != null) {
+            for (Field field : schema.getFields()) {
+                if (keyFieldNames.contains(field.getName())) {
+                    continue;
+                }
+                if (!fieldsWhitelist.isEmpty() && !fieldsWhitelist.contains(field.getName())) {
+                    continue;
+                }
+                nonKeyFieldNames.add(field.getName());
+                final Schema fieldSchema = field.getSchema();
+                allFields.put(field.getName(), new SinkRecordField(fieldSchema, field.getName(), false));
+            }
         }
-        nonKeyFieldNames.add(field.getName());
-        final Schema fieldSchema = field.getSchema();
-        allFields.put(field.getName(), new SinkRecordField(fieldSchema, field.getName(), false));
-      }
-    }
 
-    if (allFields.isEmpty()) {
-      throw new ConnectException(
-          "No fields found using key and value schemas for table: " + tableName
-      );
-    }
+        if (allFields.isEmpty()) {
+            throw new ConnectException(
+                    "No fields found using key and value schemas for table: " + tableName
+            );
+        }
 
-    final Map<String, SinkRecordField> allFieldsOrdered = new LinkedHashMap<>();
+        final Map<String, SinkRecordField> allFieldsOrdered = new LinkedHashMap<>();
 
-    if (schema != null) {
-      for (Field field : schema.getFields()) {
-        String fieldName = field.getName();
-        if (allFields.containsKey(fieldName)) {
-          allFieldsOrdered.put(fieldName, allFields.get(fieldName));
+        if (schema != null) {
+            for (Field field : schema.getFields()) {
+                String fieldName = field.getName();
+                if (allFields.containsKey(fieldName)) {
+                    allFieldsOrdered.put(fieldName, allFields.get(fieldName));
+                }
+            }
         }
-      }
-    }
 
-    if (allFieldsOrdered.size() < allFields.size()) {
-      ArrayList<String> fieldKeys = new ArrayList<>(allFields.keySet());
-      Collections.sort(fieldKeys);
-      for (String fieldName : fieldKeys) {
-        if (!allFieldsOrdered.containsKey(fieldName)) {
-          allFieldsOrdered.put(fieldName, allFields.get(fieldName));
+        if (allFieldsOrdered.size() < allFields.size()) {
+            ArrayList<String> fieldKeys = new ArrayList<>(allFields.keySet());
+            Collections.sort(fieldKeys);
+            for (String fieldName : fieldKeys) {
+                if (!allFieldsOrdered.containsKey(fieldName)) {
+                    allFieldsOrdered.put(fieldName, allFields.get(fieldName));
+                }
+            }
         }
-      }
-    }
 
-    return new FieldsMetadata(keyFieldNames, nonKeyFieldNames, allFieldsOrdered);
-  }
-
-
-  /**
-   * record value
-   * @param tableName
-   * @param configuredPkFields
-   * @param valueSchema
-   * @param headers
-   * @param allFields
-   * @param keyFieldNames
-   */
-  private static void extractRecordValuePk(
-      final String tableName,
-      final List<String> configuredPkFields,
-      final Schema valueSchema,
-      final KeyValue headers,
-      final Map<String, SinkRecordField> allFields,
-      final Set<String> keyFieldNames
-  ) {
-    if (valueSchema == null) {
-      throw new ConnectException(String.format(
-          "PK mode for table '%s' is %s, but record value schema is missing",
-          tableName,
-          JdbcSinkConfig.PrimaryKeyMode.RECORD_VALUE)
-      );
+        return new FieldsMetadata(keyFieldNames, nonKeyFieldNames, allFieldsOrdered);
     }
-    List<String> pkFields = new ArrayList<>(configuredPkFields);
-    if (pkFields.isEmpty()) {
-      for (Field keyField : valueSchema.getFields()) {
-        keyFieldNames.add(keyField.getName());
-      }
-    } else {
-      for (Field keyField : valueSchema.getFields()) {
-        keyFieldNames.add(keyField.getName());
-      }
-      for (String fieldName : pkFields) {
-        if (!keyFieldNames.contains(fieldName)) {
-          throw new ConnectException(String.format(
-              "PK mode for table '%s' is %s with configured PK fields %s, but record value "
-              + "schema does not contain field: %s",
-                  tableName,
-                  JdbcSinkConfig.PrimaryKeyMode.RECORD_VALUE,
-                  pkFields,
-                  fieldName
-          ));
+
+
+    /**
+     * record value
+     *
+     * @param tableName
+     * @param configuredPkFields
+     * @param valueSchema
+     * @param headers
+     * @param allFields
+     * @param keyFieldNames
+     */
+    private static void extractRecordValuePk(
+            final String tableName,
+            final List<String> configuredPkFields,
+            final Schema valueSchema,
+            final KeyValue headers,
+            final Map<String, SinkRecordField> allFields,
+            final Set<String> keyFieldNames
+    ) {
+        if (valueSchema == null) {
+            throw new ConnectException(String.format(
+                    "PK mode for table '%s' is %s, but record value schema is missing",
+                    tableName,
+                    JdbcSinkConfig.PrimaryKeyMode.RECORD_VALUE)
+            );
+        }
+        List<String> pkFields = new ArrayList<>(configuredPkFields);
+        if (pkFields.isEmpty()) {
+            for (Field keyField : valueSchema.getFields()) {
+                keyFieldNames.add(keyField.getName());
+            }
+        } else {
+            for (Field keyField : valueSchema.getFields()) {
+                keyFieldNames.add(keyField.getName());
+            }
+            for (String fieldName : pkFields) {
+                if (!keyFieldNames.contains(fieldName)) {
+                    throw new ConnectException(String.format(
+                            "PK mode for table '%s' is %s with configured PK fields %s, but record value "
+                                    + "schema does not contain field: %s",
+                            tableName,
+                            JdbcSinkConfig.PrimaryKeyMode.RECORD_VALUE,
+                            pkFields,
+                            fieldName
+                    ));
+                }
+            }
+            keyFieldNames.addAll(pkFields);
         }
-      }
-      keyFieldNames.addAll(pkFields);
+        for (String fieldName : keyFieldNames) {
+            final Schema fieldSchema = valueSchema.getField(fieldName).getSchema();
+            allFields.put(fieldName, new SinkRecordField(fieldSchema, fieldName, true));
+        }
+
     }
-    for (String fieldName : keyFieldNames) {
-      final Schema fieldSchema = valueSchema.getField(fieldName).getSchema();
-      allFields.put(fieldName, new SinkRecordField(fieldSchema, fieldName, true));
+
+    public static boolean isPrimitive(FieldType type) {
+        switch (type) {
+            case INT8:
+            case INT32:
+            case INT64:
+            case FLOAT32:
+            case FLOAT64:
+            case BOOLEAN:
+            case STRING:
+            case BYTES:
+                return true;
+            default:
+                return false;
+        }
     }
 
-  }
-
-  public static boolean isPrimitive(FieldType type) {
-    switch(type) {
-      case INT8:
-      case INT32:
-      case INT64:
-      case FLOAT32:
-      case FLOAT64:
-      case BOOLEAN:
-      case STRING:
-      case BYTES:
-        return true;
-      default:
-        return false;
+    @Override
+    public String toString() {
+        return "FieldsMetadata{"
+                + "keyFieldNames=" + keyFieldNames
+                + ", nonKeyFieldNames=" + nonKeyFieldNames
+                + ", allFields=" + allFields
+                + '}';
     }
-  }
-
-  @Override
-  public String toString() {
-    return "FieldsMetadata{"
-            + "keyFieldNames=" + keyFieldNames
-            + ", nonKeyFieldNames=" + nonKeyFieldNames
-            + ", allFields=" + allFields
-            + '}';
-  }
 
 }
diff --git a/connectors/rocketmq-connect-jdbc/src/main/java/org/apache/rocketmq/connect/jdbc/sink/metadata/SchemaPair.java b/connectors/rocketmq-connect-jdbc/src/main/java/org/apache/rocketmq/connect/jdbc/sink/metadata/SchemaPair.java
index e499520..77955d7 100644
--- a/connectors/rocketmq-connect-jdbc/src/main/java/org/apache/rocketmq/connect/jdbc/sink/metadata/SchemaPair.java
+++ b/connectors/rocketmq-connect-jdbc/src/main/java/org/apache/rocketmq/connect/jdbc/sink/metadata/SchemaPair.java
@@ -25,38 +25,38 @@ import java.util.Objects;
  * schema pair
  */
 public class SchemaPair {
-  public final Schema schema;
-  public final KeyValue extensions;
-
-  public SchemaPair(Schema schema) {
-    this.schema = schema;
-    this.extensions = null;
-  }
-
-  public SchemaPair(Schema schema, KeyValue extensions) {
-    this.schema = schema;
-    this.extensions = extensions;
-  }
-
-  @Override
-  public boolean equals(Object o) {
-    if (this == o) {
-      return true;
+    public final Schema schema;
+    public final KeyValue extensions;
+
+    public SchemaPair(Schema schema) {
+        this.schema = schema;
+        this.extensions = null;
+    }
+
+    public SchemaPair(Schema schema, KeyValue extensions) {
+        this.schema = schema;
+        this.extensions = extensions;
+    }
+
+    @Override
+    public boolean equals(Object o) {
+        if (this == o) {
+            return true;
+        }
+        if (o == null || getClass() != o.getClass()) {
+            return false;
+        }
+        SchemaPair that = (SchemaPair) o;
+        return Objects.equals(schema, that.schema);
+    }
+
+    @Override
+    public int hashCode() {
+        return Objects.hash(schema);
     }
-    if (o == null || getClass() != o.getClass()) {
-      return false;
+
+    @Override
+    public String toString() {
+        return String.format("<SchemaPair: %s>", schema);
     }
-    SchemaPair that = (SchemaPair) o;
-    return Objects.equals(schema, that.schema);
-  }
-
-  @Override
-  public int hashCode() {
-    return Objects.hash(schema);
-  }
-
-  @Override
-  public String toString() {
-    return String.format("<SchemaPair: %s>", schema);
-  }
 }
diff --git a/connectors/rocketmq-connect-jdbc/src/main/java/org/apache/rocketmq/connect/jdbc/sink/metadata/SinkRecordField.java b/connectors/rocketmq-connect-jdbc/src/main/java/org/apache/rocketmq/connect/jdbc/sink/metadata/SinkRecordField.java
index f06eabb..fef7bff 100644
--- a/connectors/rocketmq-connect-jdbc/src/main/java/org/apache/rocketmq/connect/jdbc/sink/metadata/SinkRecordField.java
+++ b/connectors/rocketmq-connect-jdbc/src/main/java/org/apache/rocketmq/connect/jdbc/sink/metadata/SinkRecordField.java
@@ -19,76 +19,74 @@ package org.apache.rocketmq.connect.jdbc.sink.metadata;
 import io.openmessaging.connector.api.data.FieldType;
 import io.openmessaging.connector.api.data.Schema;
 
-/**
- * @author xiaoyi
- */
+
 public class SinkRecordField {
 
-  private final Schema schema;
-  private final String name;
-  private final boolean isPrimaryKey;
+    private final Schema schema;
+    private final String name;
+    private final boolean isPrimaryKey;
 
-  public SinkRecordField(Schema schema, String name, boolean isPrimaryKey) {
-    this.schema = schema;
-    this.name = name;
-    this.isPrimaryKey = isPrimaryKey;
-  }
+    public SinkRecordField(Schema schema, String name, boolean isPrimaryKey) {
+        this.schema = schema;
+        this.name = name;
+        this.isPrimaryKey = isPrimaryKey;
+    }
 
-  public Schema schema() {
-    return schema;
-  }
+    public Schema schema() {
+        return schema;
+    }
 
-  public String schemaName() {
-    return schema.getName();
-  }
+    public String schemaName() {
+        return schema.getName();
+    }
 
-  public FieldType schemaType() {
-    return schema.getFieldType();
-  }
+    public FieldType schemaType() {
+        return schema.getFieldType();
+    }
 
-  public String name() {
-    return name;
-  }
+    public String name() {
+        return name;
+    }
 
-  public boolean isOptional() {
-    return !isPrimaryKey;
-  }
+    public boolean isOptional() {
+        return !isPrimaryKey;
+    }
 
-  public Object defaultValue() {
+    public Object defaultValue() {
 //    return schema.defaultValue();
-    return null;
-  }
-
-  public boolean isPrimaryKey() {
-    return isPrimaryKey;
-  }
-
-
-  public Object getDefaultValue(FieldType type){
-    switch (type){
-      case BOOLEAN:
-      case INT8:
-        return (byte)0;
-      case INT32:
-        return 0;
-      case INT64:
-        return 0L;
-      case FLOAT32:
-        return 0.0f;
-      case FLOAT64:
-        return 0.0d;
-      default:
         return null;
     }
 
-  }
+    public boolean isPrimaryKey() {
+        return isPrimaryKey;
+    }
+
+
+    public Object getDefaultValue(FieldType type) {
+        switch (type) {
+            case BOOLEAN:
+            case INT8:
+                return (byte) 0;
+            case INT32:
+                return 0;
+            case INT64:
+                return 0L;
+            case FLOAT32:
+                return 0.0f;
+            case FLOAT64:
+                return 0.0d;
+            default:
+                return null;
+        }
 
-  @Override
-  public String toString() {
-    return "SinkRecordField{"
-           + "schema=" + schema
-           + ", name='" + name + '\''
-           + ", isPrimaryKey=" + isPrimaryKey
-           + '}';
-  }
+    }
+
+    @Override
+    public String toString() {
+        return "SinkRecordField{"
+                + "schema=" + schema
+                + ", name='" + name + '\''
+                + ", isPrimaryKey=" + isPrimaryKey
+                + '}';
+    }
 }
diff --git a/connectors/rocketmq-connect-jdbc/src/main/java/org/apache/rocketmq/connect/jdbc/source/TimestampIncrementingCriteria.java b/connectors/rocketmq-connect-jdbc/src/main/java/org/apache/rocketmq/connect/jdbc/source/TimestampIncrementingCriteria.java
index 24057ce..fcd612b 100644
--- a/connectors/rocketmq-connect-jdbc/src/main/java/org/apache/rocketmq/connect/jdbc/source/TimestampIncrementingCriteria.java
+++ b/connectors/rocketmq-connect-jdbc/src/main/java/org/apache/rocketmq/connect/jdbc/source/TimestampIncrementingCriteria.java
@@ -37,310 +37,310 @@ import java.util.stream.Collectors;
 
 public class TimestampIncrementingCriteria {
 
-  /**
-   * The values that can be used in a statement's WHERE clause.
-   */
-  public interface CriteriaValues {
-
-    /**
-     * Get the beginning of the time period.
-     *
-     * @return the beginning timestamp; may be null
-     * @throws SQLException if there is a problem accessing the value
-     */
-    Timestamp beginTimestampValue() throws SQLException;
-
-    /**
-     * Get the end of the time period.
-     *
-     * @return the ending timestamp; never null
-     * @throws SQLException if there is a problem accessing the value
-     */
-    Timestamp endTimestampValue(Timestamp beginTime) throws SQLException;
-
     /**
-     * Get the last incremented value seen.
-     *
-     * @return the last incremented value from one of the rows
-     * @throws SQLException if there is a problem accessing the value
+     * The values that can be used in a statement's WHERE clause.
      */
-    Long lastIncrementedValue() throws SQLException;
-  }
+    public interface CriteriaValues {
 
-  protected static final BigDecimal LONG_MAX_VALUE_AS_BIGDEC = new BigDecimal(Long.MAX_VALUE);
+        /**
+         * Get the beginning of the time period.
+         *
+         * @return the beginning timestamp; may be null
+         * @throws SQLException if there is a problem accessing the value
+         */
+        Timestamp beginTimestampValue() throws SQLException;
 
-  protected final Logger log = LoggerFactory.getLogger(getClass());
-  protected final List<ColumnId> timestampColumns;
-  protected final ColumnId incrementingColumn;
-  protected final TimeZone timeZone;
+        /**
+         * Get the end of the time period.
+         *
+         * @return the ending timestamp; never null
+         * @throws SQLException if there is a problem accessing the value
+         */
+        Timestamp endTimestampValue(Timestamp beginTime) throws SQLException;
 
-  public TimestampIncrementingCriteria(
-      ColumnId incrementingColumn,
-      List<ColumnId> timestampColumns,
-      TimeZone timeZone
-  ) {
-    this.timestampColumns =
-        timestampColumns != null ? timestampColumns : Collections.<ColumnId>emptyList();
-    this.incrementingColumn = incrementingColumn;
-    this.timeZone = timeZone;
-  }
+        /**
+         * Get the last incremented value seen.
+         *
+         * @return the last incremented value from one of the rows
+         * @throws SQLException if there is a problem accessing the value
+         */
+        Long lastIncrementedValue() throws SQLException;
+    }
 
-  protected boolean hasTimestampColumns() {
-    return !timestampColumns.isEmpty();
-  }
+    protected static final BigDecimal LONG_MAX_VALUE_AS_BIGDEC = new BigDecimal(Long.MAX_VALUE);
 
-  protected boolean hasIncrementedColumn() {
-    return incrementingColumn != null;
-  }
+    protected final Logger log = LoggerFactory.getLogger(getClass());
+    protected final List<ColumnId> timestampColumns;
+    protected final ColumnId incrementingColumn;
+    protected final TimeZone timeZone;
 
-  /**
-   * Build the WHERE clause for the columns used in this criteria.
-   *
-   * @param builder the string builder to which the WHERE clause should be appended; never null
-   */
-  public void whereClause(ExpressionBuilder builder) {
-    if (hasTimestampColumns() && hasIncrementedColumn()) {
-      timestampIncrementingWhereClause(builder);
-    } else if (hasTimestampColumns()) {
-      timestampWhereClause(builder);
-    } else if (hasIncrementedColumn()) {
-      incrementingWhereClause(builder);
+    public TimestampIncrementingCriteria(
+            ColumnId incrementingColumn,
+            List<ColumnId> timestampColumns,
+            TimeZone timeZone
+    ) {
+        this.timestampColumns =
+                timestampColumns != null ? timestampColumns : Collections.<ColumnId>emptyList();
+        this.incrementingColumn = incrementingColumn;
+        this.timeZone = timeZone;
     }
-  }
 
-  /**
-   * Set the query parameters on the prepared statement whose WHERE clause was generated with the
-   * previous call to {@link #whereClause(ExpressionBuilder)}.
-   *
-   * @param stmt   the prepared statement; never null
-   * @param values the values that can be used in the criteria parameters; never null
-   * @throws SQLException if there is a problem using the prepared statement
-   */
-  public void setQueryParameters(
-      PreparedStatement stmt,
-      CriteriaValues values
-  ) throws SQLException {
-    if (hasTimestampColumns() && hasIncrementedColumn()) {
-      setQueryParametersTimestampIncrementing(stmt, values);
-    } else if (hasTimestampColumns()) {
-      setQueryParametersTimestamp(stmt, values);
-    } else if (hasIncrementedColumn()) {
-      setQueryParametersIncrementing(stmt, values);
+    protected boolean hasTimestampColumns() {
+        return !timestampColumns.isEmpty();
     }
-  }
 
-  protected void setQueryParametersTimestampIncrementing(
-      PreparedStatement stmt,
-      CriteriaValues values
-  ) throws SQLException{
-    Timestamp beginTime = values.beginTimestampValue();
-    Timestamp endTime = values.endTimestampValue(beginTime);
-    Long incOffset = values.lastIncrementedValue();
-    stmt.setTimestamp(1, endTime, DateTimeUtils.getTimeZoneCalendar(timeZone));
-    stmt.setTimestamp(2, beginTime, DateTimeUtils.getTimeZoneCalendar(timeZone));
-    stmt.setLong(3, incOffset);
-    stmt.setTimestamp(4, beginTime, DateTimeUtils.getTimeZoneCalendar(timeZone));
-    log.debug(
-        "Executing prepared statement with start time value = {} end time = {} and incrementing"
-        + " value = {}", DateTimeUtils.formatTimestamp(beginTime, timeZone),
-        DateTimeUtils.formatTimestamp(endTime, timeZone), incOffset
-    );
-  }
+    protected boolean hasIncrementedColumn() {
+        return incrementingColumn != null;
+    }
 
-  protected void setQueryParametersIncrementing(
-      PreparedStatement stmt,
-      CriteriaValues values
-  ) throws SQLException {
-    Long incOffset = values.lastIncrementedValue();
-    stmt.setLong(1, incOffset);
-    log.debug("Executing prepared statement with incrementing value = {}", incOffset);
-  }
+    /**
+     * Build the WHERE clause for the columns used in this criteria.
+     *
+     * @param builder the string builder to which the WHERE clause should be appended; never null
+     */
+    public void whereClause(ExpressionBuilder builder) {
+        if (hasTimestampColumns() && hasIncrementedColumn()) {
+            timestampIncrementingWhereClause(builder);
+        } else if (hasTimestampColumns()) {
+            timestampWhereClause(builder);
+        } else if (hasIncrementedColumn()) {
+            incrementingWhereClause(builder);
+        }
+    }
 
-  protected void setQueryParametersTimestamp(
-      PreparedStatement stmt,
-      CriteriaValues values
-  ) throws SQLException {
-    Timestamp beginTime = values.beginTimestampValue();
-    Timestamp endTime = values.endTimestampValue(beginTime);
-    stmt.setTimestamp(1, beginTime, DateTimeUtils.getTimeZoneCalendar(timeZone));
-    stmt.setTimestamp(2, endTime, DateTimeUtils.getTimeZoneCalendar(timeZone));
-    log.debug("Executing prepared statement with timestamp value = {} end time = {}",
-        DateTimeUtils.formatTimestamp(beginTime, timeZone),
-        DateTimeUtils.formatTimestamp(endTime, timeZone)
-    );
-  }
+    /**
+     * Set the query parameters on the prepared statement whose WHERE clause was generated with the
+     * previous call to {@link #whereClause(ExpressionBuilder)}.
+     *
+     * @param stmt   the prepared statement; never null
+     * @param values the values that can be used in the criteria parameters; never null
+     * @throws SQLException if there is a problem using the prepared statement
+     */
+    public void setQueryParameters(
+            PreparedStatement stmt,
+            CriteriaValues values
+    ) throws SQLException {
+        if (hasTimestampColumns() && hasIncrementedColumn()) {
+            setQueryParametersTimestampIncrementing(stmt, values);
+        } else if (hasTimestampColumns()) {
+            setQueryParametersTimestamp(stmt, values);
+        } else if (hasIncrementedColumn()) {
+            setQueryParametersIncrementing(stmt, values);
+        }
+    }
 
-  /**
-   * Extract the offset values from the row.
-   *
-   * @param schema the record's schema; never null
-   * @param record the record's struct; never null
-   * @param previousOffset a previous timestamp offset if the table has timestamp columns
-   * @return the timestamp for this row; may not be null
-   */
-  public TimestampIncrementingOffset extractValues(
-      Schema schema,
-      Object[] record,
-      TimestampIncrementingOffset previousOffset
-  ) {
-    Timestamp extractedTimestamp = null;
-    if (hasTimestampColumns()) {
-      extractedTimestamp = extractOffsetTimestamp(schema, record);
-      assert previousOffset == null || (previousOffset.getTimestampOffset() != null
-                                        && previousOffset.getTimestampOffset().compareTo(
-          extractedTimestamp) <= 0
-      );
+    protected void setQueryParametersTimestampIncrementing(
+            PreparedStatement stmt,
+            CriteriaValues values
+    ) throws SQLException {
+        Timestamp beginTime = values.beginTimestampValue();
+        Timestamp endTime = values.endTimestampValue(beginTime);
+        Long incOffset = values.lastIncrementedValue();
+        stmt.setTimestamp(1, endTime, DateTimeUtils.getTimeZoneCalendar(timeZone));
+        stmt.setTimestamp(2, beginTime, DateTimeUtils.getTimeZoneCalendar(timeZone));
+        stmt.setLong(3, incOffset);
+        stmt.setTimestamp(4, beginTime, DateTimeUtils.getTimeZoneCalendar(timeZone));
+        log.debug(
+                "Executing prepared statement with start time value = {} end time = {} and incrementing"
+                        + " value = {}", DateTimeUtils.formatTimestamp(beginTime, timeZone),
+                DateTimeUtils.formatTimestamp(endTime, timeZone), incOffset
+        );
     }
-    Long extractedId = null;
-    if (hasIncrementedColumn()) {
-      extractedId = extractOffsetIncrementedId(schema, record);
-      // If we are only using an incrementing column, then this must be incrementing.
-      // If we are also using a timestamp, then we may see updates to older rows.
-      assert previousOffset == null || previousOffset.getIncrementingOffset() == -1L
-             || extractedId > previousOffset.getIncrementingOffset() || hasTimestampColumns();
+
+    protected void setQueryParametersIncrementing(
+            PreparedStatement stmt,
+            CriteriaValues values
+    ) throws SQLException {
+        Long incOffset = values.lastIncrementedValue();
+        stmt.setLong(1, incOffset);
+        log.debug("Executing prepared statement with incrementing value = {}", incOffset);
     }
-    return new TimestampIncrementingOffset(extractedTimestamp, extractedId);
-  }
 
-  /**
-   * Extract the timestamp from the row.
-   *
-   * @param schema the record's schema; never null
-   * @param record the record's struct; never null
-   * @return the timestamp for this row; may not be null
-   */
-  protected Timestamp extractOffsetTimestamp(
-          Schema schema,
-          Object[] record
-  ) {
-    for (ColumnId timestampColumn : timestampColumns) {
-      Field field=schema.getField(timestampColumn.name());
-      Timestamp ts = (Timestamp) record[field.getIndex()];
-      if (ts != null) {
-        return ts;
-      }
+    protected void setQueryParametersTimestamp(
+            PreparedStatement stmt,
+            CriteriaValues values
+    ) throws SQLException {
+        Timestamp beginTime = values.beginTimestampValue();
+        Timestamp endTime = values.endTimestampValue(beginTime);
+        stmt.setTimestamp(1, beginTime, DateTimeUtils.getTimeZoneCalendar(timeZone));
+        stmt.setTimestamp(2, endTime, DateTimeUtils.getTimeZoneCalendar(timeZone));
+        log.debug("Executing prepared statement with timestamp value = {} end time = {}",
+                DateTimeUtils.formatTimestamp(beginTime, timeZone),
+                DateTimeUtils.formatTimestamp(endTime, timeZone)
... 4121 lines suppressed ...