diff --git a/connectors/rocketmq-connect-jdbc/README.md b/connectors/rocketmq-connect-jdbc/README.md
index 003c91e63..1ed4cd29f 100644
--- a/connectors/rocketmq-connect-jdbc/README.md
+++ b/connectors/rocketmq-connect-jdbc/README.md
@@ -1,20 +1,24 @@
# rocketmq-connect-jdbc
-为方便扩展,rocketmq-connect-jdbc目前采用模块化插件的形式进行组织,内部的connector需要用户手动编译成jar包来进行使用。目前支持Mysql、OpenMLDB等数据库
+为方便扩展,rocketmq-connect-jdbc目前采用Spi插件的形式进行扩展,核心扩展api主要有:
+ ```SPI api
+ org.apache.rocketmq.connect.jdbc.dialect.DatabaseDialectFactory
+ org.apache.rocketmq.connect.jdbc.dialect.DatabaseDialect
+ ```
+目前支持Mysql、OpenMLDB数据库, pg、oracle、sqlserver、db2 等关系型数据库还在持续扩展中
## rocketmq-connect-jdbc使用方法
-1. 进入想要使用的connector
- 目录下(以rocketmq-connect-jdbc-mysql目录为例),使用以下指令将此connector打包为jar文件
-
+1. 进入想要使用的connectors目录下(以rocketmq-connect-jdbc目录为例),使用以下指令将插件进行打包
```shell
mvn clean package -Dmaven.test.skip=true
```
-2. 打包好的jar文件将出现在`rocketmq-connect-jdbc-mysql/target/`目录下
+2. 打包好的插件以tar.gz的模式出现在`rocketmq-connect-jdbc/target/`目录下
+
3. 在`distribution/conf`目录下找的对应的配置文件进行更新,对于standalone的启动方式,更新`connect-standalone.conf`文件中的`pluginPaths`变量
```lombok.config
- pluginPaths=rocketmq-connect-sample/target/rocketmq-connect-jdbc-mysql-0.0.1-SNAPSHOT-jar-with-dependencies.jar
+ pluginPaths=(you plugin path)
```
相应的,使用distributed启动方式,则更新`connect-distributed.conf`中的变量
@@ -40,7 +44,7 @@ mvn clean package -Dmaven.test.skip=true
```
POST http://${runtime-ip}:${runtime-port}/connectors/${rocketmq-jdbc-source-connector-name}
{
- "connector.class":"org.apache.rocketmq.connect.jdbc.mysql.source.BaseSourceConnector",
+ "connector.class":"org.apache.rocketmq.connect.jdbc.mysql.source.JdbcSourceConnector",
"max.tasks":"2",
"connection.url":"jdbc:mysql://XXXXXXXXX:3306",
"connection.user":"*****",
@@ -92,7 +96,7 @@ http://${runtime-ip}:${runtime-port}/connectors/${rocketmq-jdbc-connector-name}/
* **jdbc-source-connector 参数说明**
| KEY | TYPE | Must be filled | Description | Example |
-| ------------------------ | ------- | -------------- | ---------------- | --------------------------------------------------------- |
+|--------------------------| ------- | -------------- |------------------| --------------------------------------------------------- |
| connection.url | String | YES | source端 jdbc连接 | jdbc:mysql://XXXXXXXXX:3306 |
| connection.user | String | YES | source端 DB 用户名 | root |
| connection.password | String | YES | source端 DB 密码 | root |
@@ -104,8 +108,9 @@ http://${runtime-ip}:${runtime-port}/connectors/${rocketmq-jdbc-connector-name}/
| incrementing.column.name | Integer | NO | 增量字段,常用ID | id |
| timestamp.column.name | String | YES | 时间增量字段 | modified_time |
| table.whitelist | String | YES | 需要扫描的表 | db.table,db.table01 |
-| max-task | Integer | YES | 任务数量,最大不能大于表的数量 | 2 |
-| source-record-converter | Integer | YES | data转换器 | org.apache.rocketmq.connect.doris.converter.JsonConverter |
+| max.tasks | Integer | YES | 任务数量,最大不能大于表的数量 | 2 |
+| key.converter | Integer | YES | key转换器 | org.apache.rocketmq.connect.doris.converter.JsonConverter |
+| value.converter | Integer | YES | data转换器 | org.apache.rocketmq.connect.doris.converter.JsonConverter |
```
注:1.source拉取的数据写入到以表名自动创建的topic中,如果需要写入特定的topic中则需要指定"connect-topicname" 参数
@@ -114,14 +119,15 @@ http://${runtime-ip}:${runtime-port}/connectors/${rocketmq-jdbc-connector-name}/
* **jdbc-sink-connector 参数说明**
-| KEY | TYPE | Must be filled | Description | Example |
-| ----------------------- | ------- | -------------- | --------------- | --------------------------------------------------------- |
-| connection.url | String | YES | sink端 jdbc连接 | jdbc:mysql://XXXXXXXXX:3306 |
-| connection.user | String | YES | sink端 DB 用户名 | root |
-| connection.password | String | YES | sink端 DB 密码 | root |
-| host | String | YES | doris host | 192.168.0.1 |
-| port | String | YES | doris http port | 8030 |
-| user | String | YES | 监听的topic | root |
-| passwd | String | YES | 监听的topic | passwd |
-| max-task | Integer | NO | 任务数量 | 2 |
-| source-record-converter | Integer | YES | data转换器 | org.apache.rocketmq.connect.doris.converter.JsonConverter |
+| KEY | TYPE | Must be filled | Description | Example |
+|---------------------| ------- | -------------- |-----------------| --------------------------------------------------------- |
+| connection.url | String | YES | sink端 jdbc连接 | jdbc:mysql://XXXXXXXXX:3306 |
+| connection.user | String | YES | sink端 DB 用户名 | root |
+| connection.password | String | YES | sink端 DB 密码 | root |
+| host | String | YES | doris host | 192.168.0.1 |
+| port | String | YES | doris http port | 8030 |
+| user | String | YES | 监听的topic | root |
+| passwd | String | YES | 监听的topic | passwd |
+| max.tasks | Integer | NO | 任务数量 | 2 |
+| key.converter | Integer | YES | key转换器 | org.apache.rocketmq.connect.doris.converter.JsonConverter |
+| value.converter | Integer | YES | data转换器 | org.apache.rocketmq.connect.doris.converter.JsonConverter |
diff --git a/connectors/rocketmq-connect-jdbc/pom.xml b/connectors/rocketmq-connect-jdbc/pom.xml
index de4470878..4d88a902c 100644
--- a/connectors/rocketmq-connect-jdbc/pom.xml
+++ b/connectors/rocketmq-connect-jdbc/pom.xml
@@ -16,18 +16,12 @@
4.0.0
org.apache.rocketmq
rocketmq-connect-jdbc
- pom
+ jar
0.0.1-SNAPSHOT
-
- rocketmq-connect-jdbc-core
- rocketmq-connect-jdbc-mysql
- rocketmq-connect-jdbc-openmldb
-
rocketmq-connect-jdbc
https://github.com/apache/incubator-rocketmq-externals/tree/master/rocketmq-connect-jdbc
-
The Apache Software License, Version 2.0
@@ -73,56 +67,35 @@
-
- org.apache.rocketmq
- rocketmq-connect-jdbc-core
- ${project.version}
-
-
io.openmessaging
openmessaging-connector
${openmessaging-connector.version}
+ provided
io.openmessaging
openmessaging-api
${openmessaging-api.version}
+ provided
org.apache.commons
commons-lang3
${commons-lang3.version}
+ provided
commons-codec
commons-codec
${commons-codec.version}
+ provided
commons-cli
commons-cli
${commons-cli.version}
-
-
-
-
- junit
- junit
- ${junit.version}
- test
-
-
- org.assertj
- assertj-core
- ${assertj.version}
- test
-
-
- org.mockito
- mockito-core
- ${mockito.version}
- test
+ provided
@@ -130,39 +103,175 @@
com.alibaba
fastjson
${fastjson.version}
+ provided
org.slf4j
slf4j-api
${slf4j-api.version}
+ provided
ch.qos.logback
logback-classic
${logback-classic.version}
+ provided
ch.qos.logback
logback-core
${logback-core.version}
+ provided
io.debezium
debezium-core
${debezium.version}
+ provided
+
+
- org.projectlombok
- lombok
- 1.18.2
+ junit
+ junit
+ ${junit.version}
+ test
+
+
+ org.assertj
+ assertj-core
+ ${assertj.version}
+ test
+
+ org.mockito
+ mockito-core
+ ${mockito.version}
+ test
+
+
+
+
+
+ mysql
+ mysql-connector-java
+ 8.0.30
+ provided
+
+
+
+
+ com.4paradigm.openmldb
+ openmldb-native
+ 0.5.0-macos
+ provided
+
+
+ com.4paradigm.openmldb
+ openmldb-jdbc
+ 0.5.0
+ provided
+
+
+ com.4paradigm.openmldb
+ openmldb-native
+
+
+ org.projectlombok
+ lombok
+
+
+
+
+
+ io.openmessaging
+ openmessaging-connector
+
+
+ io.openmessaging
+ openmessaging-api
+
+
+ org.apache.commons
+ commons-lang3
+
+
+ commons-codec
+ commons-codec
+
+
+ commons-cli
+ commons-cli
+
+
+
+
+ com.alibaba
+ fastjson
+
+
+
+ org.slf4j
+ slf4j-api
+
+
+ ch.qos.logback
+ logback-classic
+
+
+ ch.qos.logback
+ logback-core
+
+
+
+ io.debezium
+ debezium-core
+
+
+
+
+
+ junit
+ junit
+ test
+
+
+ org.assertj
+ assertj-core
+ test
+
+
+ org.mockito
+ mockito-core
+ test
+
+
+
+
+
+
+ mysql
+ mysql-connector-java
+
+
+
+
+ com.4paradigm.openmldb
+ openmldb-native
+
+
+ com.4paradigm.openmldb
+ openmldb-jdbc
+
+
+
@@ -263,6 +372,24 @@
findbugs-maven-plugin
3.0.4
+
+ maven-assembly-plugin
+ 3.0.0
+
+
+ src/assembly/assembly.xml
+
+
+
+
+ make-assembly
+ package
+
+ single
+
+
+
+
maven-checkstyle-plugin
2.17
@@ -271,7 +398,7 @@
verify
verify
- ../../style/rmq_checkstyle.xml
+ ../../../style/rmq_checkstyle.xml
UTF-8
true
true
@@ -286,4 +413,5 @@
-
+
+
\ No newline at end of file
diff --git a/connectors/rocketmq-connect-jdbc/rocketmq-connect-jdbc-core/pom.xml b/connectors/rocketmq-connect-jdbc/rocketmq-connect-jdbc-core/pom.xml
deleted file mode 100644
index 1bb310cf6..000000000
--- a/connectors/rocketmq-connect-jdbc/rocketmq-connect-jdbc-core/pom.xml
+++ /dev/null
@@ -1,95 +0,0 @@
-
-
-
- rocketmq-connect-jdbc
- org.apache.rocketmq
- 0.0.1-SNAPSHOT
-
- 4.0.0
-
- rocketmq-connect-jdbc-core
-
-
- 8
- 8
- UTF-8
-
-
-
-
-
-
- io.openmessaging
- openmessaging-connector
-
-
- io.openmessaging
- openmessaging-api
-
-
-
- org.apache.commons
- commons-lang3
-
-
-
-
-
- junit
- junit
- test
-
-
- org.assertj
- assertj-core
- test
-
-
- org.mockito
- mockito-core
- test
-
-
-
-
- com.alibaba
- fastjson
-
-
-
- org.slf4j
- slf4j-api
-
-
- ch.qos.logback
- logback-classic
-
-
- ch.qos.logback
- logback-core
-
-
-
- commons-codec
- commons-codec
-
-
- commons-cli
- commons-cli
-
-
-
- io.debezium
- debezium-core
-
-
-
- org.projectlombok
- lombok
- provided
-
-
-
-
\ No newline at end of file
diff --git a/connectors/rocketmq-connect-jdbc/rocketmq-connect-jdbc-core/src/main/java/org/apache/rocketmq/connect/jdbc/config/ConfigDef.java b/connectors/rocketmq-connect-jdbc/rocketmq-connect-jdbc-core/src/main/java/org/apache/rocketmq/connect/jdbc/config/ConfigDef.java
deleted file mode 100644
index 8bd7ca59f..000000000
--- a/connectors/rocketmq-connect-jdbc/rocketmq-connect-jdbc-core/src/main/java/org/apache/rocketmq/connect/jdbc/config/ConfigDef.java
+++ /dev/null
@@ -1,582 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements. See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF licenses this file to You under the Apache License, Version 2.0
- * (the "License"); you may not use this file except in compliance with
- * the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package org.apache.rocketmq.connect.jdbc.config;
-
-import org.apache.commons.lang3.StringUtils;
-import org.apache.rocketmq.connect.jdbc.exception.ConfigException;
-
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.Collections;
-import java.util.HashMap;
-import java.util.LinkedHashMap;
-import java.util.List;
-import java.util.Locale;
-import java.util.Map;
-import java.util.Set;
-import java.util.function.BiConsumer;
-import java.util.function.Supplier;
-import java.util.regex.Pattern;
-import java.util.stream.Collectors;
-
-/**
- * config def
- */
-public class ConfigDef {
-
- private static final Pattern COMMA_WITH_WHITESPACE = Pattern.compile("\\s*,\\s*");
- /**
- * A unique Java object which represents the lack of a default value.
- */
- public static final Object NO_DEFAULT_VALUE = new Object();
-
- private final Map configKeys;
-
- public ConfigDef() {
- configKeys = new LinkedHashMap<>();
- }
-
-
- public ConfigDef define(ConfigKey key) {
- if (configKeys.containsKey(key.name)) {
- throw new ConfigException("Configuration " + key.name + " is defined twice.");
- }
- configKeys.put(key.name, key);
- return this;
- }
-
- /**
- * no validate
- *
- * @param name
- * @param type
- * @param defaultValue
- * @param validator
- * @param documentation
- * @return
- */
- 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
- * @param documentation
- * @return
- */
- public ConfigDef define(String name, Type type, Object defaultValue, String documentation) {
- return define(new ConfigKey(name, type, defaultValue, null, documentation));
- }
-
-
- /**
- * Get the configuration keys
- *
- * @return a map containing all configuration keys
- */
- public Map configKeys() {
- return configKeys;
- }
-
- /**
- * parse props
- *
- * @param props
- * @return
- */
- public Map parse(Map, ?> props) {
- // parse all known keys
- Map values = new HashMap<>();
- for (ConfigKey key : configKeys.values()) {
- values.put(key.name, parseValue(key, props.get(key.name), props.containsKey(key.name)));
- }
- return values;
- }
-
- /**
- * convert + validate
- *
- * @param key
- * @param value
- * @param isSet
- * @return
- */
- Object parseValue(ConfigKey key, Object value, boolean isSet) {
- Object parsedValue;
- if (isSet) {
- parsedValue = parseType(key.name, value, key.type);
- // props map doesn't contain setting, the key is required because no default value specified - its an error
- } else if (NO_DEFAULT_VALUE.equals(key.defaultValue)) {
- throw new ConfigException("Missing required configuration \"" + key.name + "\" which has no default value.");
- } else {
- parsedValue = key.defaultValue;
- }
- if (key.validator != null) {
- key.validator.ensureValid(key.name, parsedValue);
- }
- return parsedValue;
- }
-
-
- /**
- * Parse a value according to its expected type.
- *
- * @param name The config name
- * @param value The config value
- * @param type The expected type
- * @return The parsed object
- */
- public static Object parseType(String name, Object value, Type type) {
- try {
- if (value == null) {
- return null;
- }
- String trimmed = null;
- if (value instanceof String) {
- trimmed = ((String) value).trim();
- }
- switch (type) {
- case BOOLEAN:
- if (value instanceof String) {
- if (trimmed.equalsIgnoreCase("true")) {
- return true;
- } else if (trimmed.equalsIgnoreCase("false")) {
- return false;
- } else {
- throw new ConfigException(name, value, "Expected value to be either true or false");
- }
- } else if (value instanceof Boolean) {
- return value;
- } else {
- throw new ConfigException(name, value, "Expected value to be either true or false");
- }
- case STRING:
- if (value instanceof String) {
- return trimmed;
- } else {
- throw new ConfigException(name, value, "Expected value to be a string, but it was a " + value.getClass().getName());
- }
- case INT:
- if (value instanceof Integer) {
- return value;
- } else if (value instanceof String) {
- return Integer.parseInt(trimmed);
- } else {
- throw new ConfigException(name, value, "Expected value to be a 32-bit integer, but it was a " + value.getClass().getName());
- }
- case SHORT:
- if (value instanceof Short) {
- return value;
- } else if (value instanceof String) {
- return Short.parseShort(trimmed);
- } else {
- throw new ConfigException(name, value, "Expected value to be a 16-bit integer (short), but it was a " + value.getClass().getName());
- }
- case LONG:
- if (value instanceof Integer) {
- return ((Integer) value).longValue();
- }
- if (value instanceof Long) {
- return value;
- } else if (value instanceof String) {
- return Long.parseLong(trimmed);
- } else {
- throw new ConfigException(name, value, "Expected value to be a 64-bit integer (long), but it was a " + value.getClass().getName());
- }
- case DOUBLE:
- if (value instanceof Number) {
- return ((Number) value).doubleValue();
- } else if (value instanceof String) {
- return Double.parseDouble(trimmed);
- } else {
- throw new ConfigException(name, value, "Expected value to be a double, but it was a " + value.getClass().getName());
- }
- case LIST:
- if (value instanceof List) {
- return value;
- } else if (value instanceof String) {
- if (trimmed.isEmpty()) {
- return Collections.emptyList();
- } else {
- return Arrays.asList(COMMA_WITH_WHITESPACE.split(trimmed, -1));
- }
- } else {
- throw new ConfigException(name, value, "Expected a comma separated list.");
- }
- default:
- throw new IllegalStateException("Unknown type.");
- }
- } catch (NumberFormatException e) {
- throw new ConfigException(name, value, "Not a number of type " + type);
- }
- }
-
- public static String convertToString(Object parsedValue, Type type) {
- if (parsedValue == null) {
- return null;
- }
-
- if (type == null) {
- return parsedValue.toString();
- }
-
- switch (type) {
- case BOOLEAN:
- case SHORT:
- case INT:
- case LONG:
- case DOUBLE:
- case STRING:
- case PASSWORD:
- return parsedValue.toString();
- case LIST:
- List> valueList = (List>) parsedValue;
- return StringUtils.join(valueList, ",");
- case CLASS:
- Class> clazz = (Class>) parsedValue;
- return clazz.getName();
- default:
- throw new IllegalStateException("Unknown type.");
- }
- }
-
- /**
- * The config types
- */
- public enum Type {
- BOOLEAN, STRING, INT, SHORT, LONG, DOUBLE, LIST, CLASS, PASSWORD
- }
-
-
- /**
- * 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 value The value of the configuration
- * @throws ConfigException if the value is invalid.
- */
- void ensureValid(String name, Object value);
- }
-
- /**
- * Validation logic for numeric ranges
- */
- 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
- */
- 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) {
- return new Range(min, null);
- }
-
- /**
- * A numeric range that checks both the upper (inclusive) and lower bound
- */
- public static Range between(Number min, Number max) {
- return new Range(min, max);
- }
-
- @Override
- public void ensureValid(String name, Object o) {
- if (o == null) {
- throw new ConfigException(name, null, "Value must be non-null");
- }
- Number n = (Number) o;
- if (min != null && n.doubleValue() < min.doubleValue()) {
- throw new ConfigException(name, o, "Value must be at least " + min);
- }
- if (max != null && n.doubleValue() > max.doubleValue()) {
- throw new ConfigException(name, o, "Value must be no more than " + max);
- }
- }
-
- @Override
- public String toString() {
- if (min == null && max == null) {
- return "[...]";
- } else if (min == null) {
- return "[...," + max + "]";
- } else if (max == null) {
- return "[" + min + ",...]";
- } else {
- return "[" + min + ",...," + max + "]";
- }
- }
- }
-
- public static class ValidList implements Validator {
-
- final ValidString validString;
-
- private ValidList(List validStrings) {
- this.validString = new ValidString(validStrings);
- }
-
- public static ValidList in(String... validStrings) {
- return new ValidList(Arrays.asList(validStrings));
- }
-
- @Override
- public void ensureValid(final String name, final Object value) {
- @SuppressWarnings("unchecked")
- List values = (List) value;
- for (String string : values) {
- validString.ensureValid(name, string);
- }
- }
-
- @Override
- public String toString() {
- return validString.toString();
- }
- }
-
- public static class ValidString implements Validator {
- final List validStrings;
-
- private ValidString(List validStrings) {
- this.validStrings = validStrings;
- }
-
- public static ValidString in(String... validStrings) {
- return new ValidString(Arrays.asList(validStrings));
- }
-
- @Override
- public void ensureValid(String name, Object o) {
- String s = (String) o;
- if (!validStrings.contains(s)) {
- throw new ConfigException(name, o, "String must be one of: " + StringUtils.join(validStrings, ", "));
- }
-
- }
-
- @Override
- public String toString() {
- return "[" + StringUtils.join(validStrings, ", ") + "]";
- }
- }
-
- public static class CaseInsensitiveValidString implements Validator {
-
- final Set validStrings;
-
- private CaseInsensitiveValidString(List validStrings) {
- this.validStrings = validStrings.stream()
- .map(s -> s.toUpperCase(Locale.ROOT))
- .collect(Collectors.toSet());
- }
-
- public static CaseInsensitiveValidString in(String... validStrings) {
- return new CaseInsensitiveValidString(Arrays.asList(validStrings));
- }
-
- @Override
- public void ensureValid(String name, Object o) {
- String s = (String) o;
- if (s == null || !validStrings.contains(s.toUpperCase(Locale.ROOT))) {
- throw new ConfigException(name, o, "String must be one of (case insensitive): " + StringUtils.join(validStrings, ", "));
- }
- }
-
- @Override
- public String toString() {
- return "(case insensitive) [" + StringUtils.join(validStrings, ", ") + "]";
- }
- }
-
- public static class NonNullValidator implements Validator {
- @Override
- public void ensureValid(String name, Object value) {
- if (value == null) {
- // Pass in the string null to avoid the spotbugs warning
- throw new ConfigException(name, "null", "entry must be non null");
- }
- }
-
- @Override
- public String toString() {
- return "non-null string";
- }
- }
-
- public static class LambdaValidator implements Validator {
- BiConsumer ensureValid;
- Supplier toStringFunction;
-
- private LambdaValidator(BiConsumer ensureValid,
- Supplier toStringFunction) {
- this.ensureValid = ensureValid;
- this.toStringFunction = toStringFunction;
- }
-
- public static LambdaValidator with(BiConsumer ensureValid,
- Supplier toStringFunction) {
- return new LambdaValidator(ensureValid, toStringFunction);
- }
-
- @Override
- public void ensureValid(String name, Object value) {
- ensureValid.accept(name, value);
- }
-
- @Override
- public String toString() {
- return toStringFunction.get();
- }
- }
-
- public static class CompositeValidator implements Validator {
- private final List validators;
-
- private CompositeValidator(List validators) {
- this.validators = Collections.unmodifiableList(validators);
- }
-
- public static CompositeValidator of(Validator... validators) {
- return new CompositeValidator(Arrays.asList(validators));
- }
-
- @Override
- public void ensureValid(String name, Object value) {
- for (Validator validator : validators) {
- validator.ensureValid(name, value);
- }
- }
-
- @Override
- public String toString() {
- if (validators == null) {
- return "";
- }
- StringBuilder desc = new StringBuilder();
- for (Validator v : validators) {
- if (desc.length() > 0) {
- desc.append(',').append(' ');
- }
- desc.append(String.valueOf(v));
- }
- return desc.toString();
- }
- }
-
- public static class NonEmptyString implements Validator {
-
- @Override
- public void ensureValid(String name, Object o) {
- String s = (String) o;
- if (s != null && s.isEmpty()) {
- throw new ConfigException(name, o, "String must be non-empty");
- }
- }
-
- @Override
- public String toString() {
- return "non-empty string";
- }
- }
-
- public static class NonEmptyStringWithoutControlChars implements Validator {
-
- public static NonEmptyStringWithoutControlChars nonEmptyStringWithoutControlChars() {
- return new NonEmptyStringWithoutControlChars();
- }
-
- @Override
- public void ensureValid(String name, Object value) {
- String s = (String) value;
-
- if (s == null) {
- // This can happen during creation of the config object due to no default value being defined for the
- // name configuration - a missing name parameter is caught when checking for mandatory parameters,
- // thus we can ok a null value here
- return;
- } else if (s.isEmpty()) {
- throw new ConfigException(name, value, "String may not be empty");
- }
-
- // Check name string for illegal characters
- ArrayList foundIllegalCharacters = new ArrayList<>();
-
- for (int i = 0; i < s.length(); i++) {
- if (Character.isISOControl(s.codePointAt(i))) {
- foundIllegalCharacters.add(s.codePointAt(i));
- }
- }
- if (!foundIllegalCharacters.isEmpty()) {
- throw new ConfigException(name, value, "String may not contain control sequences but had the following ASCII chars: " + StringUtils.join(foundIllegalCharacters, ","));
- }
- }
-
- @Override
- public String toString() {
- return "non-empty string without ISO control characters";
- }
- }
-
- public static class ConfigKey {
- public final String name;
- public final Type type;
- public final String documentation;
- public final Object defaultValue;
- public final Validator validator;
-
- public ConfigKey(String name, Type type, Object defaultValue, Validator validator, String documentation) {
- this.name = name;
- this.type = type;
- this.defaultValue = NO_DEFAULT_VALUE.equals(defaultValue) ? NO_DEFAULT_VALUE : parseType(name, defaultValue, type);
- this.validator = validator;
- if (this.validator != null && hasDefault()) {
- this.validator.ensureValid(name, this.defaultValue);
- }
- this.documentation = documentation;
- }
-
- public boolean hasDefault() {
- return !NO_DEFAULT_VALUE.equals(this.defaultValue);
- }
-
- public Type type() {
- return type;
- }
- }
-
-}
diff --git a/connectors/rocketmq-connect-jdbc/rocketmq-connect-jdbc-core/src/main/java/org/apache/rocketmq/connect/jdbc/dialect/DatabaseDialect.java b/connectors/rocketmq-connect-jdbc/rocketmq-connect-jdbc-core/src/main/java/org/apache/rocketmq/connect/jdbc/dialect/DatabaseDialect.java
deleted file mode 100644
index c2152cbbb..000000000
--- a/connectors/rocketmq-connect-jdbc/rocketmq-connect-jdbc-core/src/main/java/org/apache/rocketmq/connect/jdbc/dialect/DatabaseDialect.java
+++ /dev/null
@@ -1,380 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements. See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF licenses this file to You under the Apache License, Version 2.0
- * (the "License"); you may not use this file except in compliance with
- * the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package org.apache.rocketmq.connect.jdbc.dialect;
-
-import io.openmessaging.connector.api.data.ConnectRecord;
-import io.openmessaging.connector.api.data.Schema;
-import io.openmessaging.connector.api.data.SchemaBuilder;
-import org.apache.rocketmq.connect.jdbc.dialect.provider.ConnectionProvider;
-import org.apache.rocketmq.connect.jdbc.schema.column.ColumnDefinition;
-import org.apache.rocketmq.connect.jdbc.schema.column.ColumnId;
-import org.apache.rocketmq.connect.jdbc.schema.table.TableDefinition;
-import org.apache.rocketmq.connect.jdbc.schema.table.TableId;
-import org.apache.rocketmq.connect.jdbc.sink.JdbcSinkConfig;
-import org.apache.rocketmq.connect.jdbc.sink.metadata.FieldsMetadata;
-import org.apache.rocketmq.connect.jdbc.sink.metadata.SchemaPair;
-import org.apache.rocketmq.connect.jdbc.sink.metadata.SinkRecordField;
-import org.apache.rocketmq.connect.jdbc.source.TimestampIncrementingCriteria;
-import org.apache.rocketmq.connect.jdbc.source.metadata.ColumnMapping;
-import org.apache.rocketmq.connect.jdbc.util.ExpressionBuilder;
-import org.apache.rocketmq.connect.jdbc.util.IdentifierRules;
-
-import java.io.IOException;
-import java.sql.Connection;
-import java.sql.PreparedStatement;
-import java.sql.ResultSet;
-import java.sql.ResultSetMetaData;
-import java.sql.SQLException;
-import java.sql.Timestamp;
-import java.util.Calendar;
-import java.util.Collection;
-import java.util.List;
-import java.util.Map;
-import java.util.Optional;
-
-/**
- * database dialect
- */
-public interface DatabaseDialect extends ConnectionProvider {
-
- /**
- * dialect name
- *
- * @return
- */
- String name();
-
- /**
- * get dialect class
- *
- * @return
- */
- default Class getDialectClass() {
- return this.getClass();
- }
-
- /**
- * 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 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 describeColumns(Connection connection, String tablePattern, String columnPattern) throws SQLException;
-
- /**
- * Create the definition for the columns described by the database metadata.
- */
- Map describeColumns(Connection connection, String catalogPattern, String schemaPattern, String tablePattern, String columnPattern) throws SQLException;
-
- /**
- * Create the definition for the columns in the result set.
- */
- Map 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 describeColumnsByQuerying(Connection connection, TableId tableId) throws SQLException;
-
- /**
- * add field to schema
- *
- * @param column
- * @return
- */
- String addFieldToSchema(ColumnDefinition column, SchemaBuilder schemaBuilder);
-
- /**
- * 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 statements) throws SQLException;
-
- /**
- * build dml statement
- *
- * @param table
- * @param keyColumns
- * @param nonKeyColumns
- * @return
- */
- String buildInsertStatement(TableId table, Collection keyColumns, Collection nonKeyColumns);
-
- /**
- * update statement
- *
- * @param table
- * @param keyColumns
- * @param nonKeyColumns
- * @return
- */
- String buildUpdateStatement(TableId table, Collection keyColumns, Collection nonKeyColumns);
-
- /**
- * upsert statment
- *
- * @param table
- * @param keyColumns
- * @param nonKeyColumns
- * @return
- */
- String buildUpsertQueryStatement(TableId table, Collection keyColumns, Collection nonKeyColumns);
-
- /**
- * delete statement
- *
- * @param table
- * @param keyColumns
- * @return
- */
- default String buildDeleteStatement(TableId table, Collection keyColumns) {
- throw new UnsupportedOperationException();
- }
-
- /**
- * Get insert sql
- *
- * @return
- */
- String getInsertSql(JdbcSinkConfig config, FieldsMetadata fieldsMetadata, TableId tableId);
-
- /**
- * Get insert sql
- *
- * @return
- */
- String getDeleteSql(JdbcSinkConfig config, FieldsMetadata fieldsMetadata, TableId tableId);
-
-
- /**
- * 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 fields);
-
- /**
- * build alter table
- *
- * @param table
- * @param fields
- * @return
- */
- List buildAlterTable(TableId table, Collection 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 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 timestampColumns
- );
-
- /**
- * get min timestamp value
- *
- * @param con
- * @param tableOrQuery
- * @param timestampColumns
- * @return
- * @throws SQLException
- */
- Long getMinTimestampValue(Connection con, String tableOrQuery, List 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;
-
-
- default Optional executeUpdates(PreparedStatement preparedStatement) throws SQLException {
- // no-op
- return Optional.empty();
- }
-
- default long executeDeletes(PreparedStatement preparedStatement) throws SQLException {
- // no-op
- return 0;
- }
- }
-
-
- /**
- * 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/rocketmq-connect-jdbc-core/src/main/java/org/apache/rocketmq/connect/jdbc/dialect/DropOptions.java b/connectors/rocketmq-connect-jdbc/rocketmq-connect-jdbc-core/src/main/java/org/apache/rocketmq/connect/jdbc/dialect/DropOptions.java
deleted file mode 100644
index 11d66747f..000000000
--- a/connectors/rocketmq-connect-jdbc/rocketmq-connect-jdbc-core/src/main/java/org/apache/rocketmq/connect/jdbc/dialect/DropOptions.java
+++ /dev/null
@@ -1,100 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements. See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF licenses this file to You under the Apache License, Version 2.0
- * (the "License"); you may not use this file except in compliance with
- * the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package org.apache.rocketmq.connect.jdbc.dialect;
-
-import java.util.Objects;
-
-public class DropOptions {
-
- 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;
- }
-
- /**
- * 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;
- }
-
- /**
- * 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 '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);
- }
-
- /**
- * 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 String toString() {
- return "DropOptions{ifExists=" + ifExists + ", cascade=" + cascade + "}";
- }
-
- @Override
- public int hashCode() {
- return Objects.hash(ifExists, 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;
- }
-}
diff --git a/connectors/rocketmq-connect-jdbc/rocketmq-connect-jdbc-core/src/main/java/org/apache/rocketmq/connect/jdbc/dialect/provider/ConnectionProvider.java b/connectors/rocketmq-connect-jdbc/rocketmq-connect-jdbc-core/src/main/java/org/apache/rocketmq/connect/jdbc/dialect/provider/ConnectionProvider.java
deleted file mode 100644
index 6e6c7a7e7..000000000
--- a/connectors/rocketmq-connect-jdbc/rocketmq-connect-jdbc-core/src/main/java/org/apache/rocketmq/connect/jdbc/dialect/provider/ConnectionProvider.java
+++ /dev/null
@@ -1,66 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements. See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF licenses this file to You under the Apache License, Version 2.0
- * (the "License"); you may not use this file except in compliance with
- * the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package org.apache.rocketmq.connect.jdbc.dialect.provider;
-
-import java.sql.Connection;
-import java.sql.SQLException;
-
-/**
- * A provider of JDBC {@link Connection} instances.
- */
-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;
-
- /**
- * 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();
-
- /**
- * 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/rocketmq-connect-jdbc-core/src/main/java/org/apache/rocketmq/connect/jdbc/source/metadata/SchemaMapping.java b/connectors/rocketmq-connect-jdbc/rocketmq-connect-jdbc-core/src/main/java/org/apache/rocketmq/connect/jdbc/source/metadata/SchemaMapping.java
deleted file mode 100644
index 9affae6e2..000000000
--- a/connectors/rocketmq-connect-jdbc/rocketmq-connect-jdbc-core/src/main/java/org/apache/rocketmq/connect/jdbc/source/metadata/SchemaMapping.java
+++ /dev/null
@@ -1,162 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements. See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF licenses this file to You under the Apache License, Version 2.0
- * (the "License"); you may not use this file except in compliance with
- * the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package org.apache.rocketmq.connect.jdbc.source.metadata;
-
-import io.openmessaging.connector.api.data.Field;
-import io.openmessaging.connector.api.data.Schema;
-import io.openmessaging.connector.api.data.SchemaBuilder;
-import io.openmessaging.connector.api.data.Struct;
-import org.apache.rocketmq.connect.jdbc.dialect.DatabaseDialect;
-import org.apache.rocketmq.connect.jdbc.schema.column.ColumnDefinition;
-import org.apache.rocketmq.connect.jdbc.schema.column.ColumnId;
-import org.apache.rocketmq.connect.jdbc.schema.table.TableId;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-import java.io.IOException;
-import java.sql.Connection;
-import java.sql.ResultSet;
-import java.sql.ResultSetMetaData;
-import java.sql.SQLException;
-import java.util.ArrayList;
-import java.util.Collections;
-import java.util.LinkedHashMap;
-import java.util.List;
-import java.util.Map;
-
-/**
- * schema mapping
- */
-public final class SchemaMapping {
- private static final Logger log = LoggerFactory.getLogger(SchemaMapping.class);
-
- public static SchemaMapping create(
- Connection conn,
- TableId tableId,
- ResultSetMetaData metadata,
- DatabaseDialect dialect
- ) throws SQLException {
- // backwards compatible
- String schemaName = tableId != null ? tableId.tableName() : null;
- // describe columns
- Map colDefins = dialect.describeColumns(conn, tableId, metadata);
- Map colConvertersByFieldName = new LinkedHashMap<>();
- SchemaBuilder builder = SchemaBuilder.struct().name(schemaName);
-
- int columnNumber = 0;
- for (ColumnDefinition colDefn : colDefins.values()) {
- ++columnNumber;
- String fieldName = dialect.addFieldToSchema(colDefn, builder);
- if (fieldName == null) {
- continue;
- }
- Field field = builder.field(fieldName);
- ColumnMapping mapping = new ColumnMapping(colDefn, columnNumber, field);
- DatabaseDialect.ColumnConverter converter = dialect.createColumnConverter(mapping);
- colConvertersByFieldName.put(fieldName, converter);
- }
- return new SchemaMapping(builder.build(), colConvertersByFieldName);
- }
-
- private final Schema schema;
- private final List fieldSetters;
-
- private SchemaMapping(
- Schema schema,
- Map convertersByFieldName
- ) {
- assert schema != null;
- assert convertersByFieldName != null;
- assert !convertersByFieldName.isEmpty();
- this.schema = schema;
- List fieldSetters = new ArrayList<>(convertersByFieldName.size());
- for (Map.Entry entry : convertersByFieldName.entrySet()) {
- DatabaseDialect.ColumnConverter converter = entry.getValue();
- Field field = schema.getField(entry.getKey());
- assert field != null;
- fieldSetters.add(new FieldSetter(converter, field));
- }
- this.fieldSetters = Collections.unmodifiableList(fieldSetters);
- }
-
- /**
- * schema
- *
- * @return
- */
- public Schema schema() {
- return schema;
- }
-
- /**
- * field setters
- *
- * @return
- */
- public List fieldSetters() {
- return fieldSetters;
- }
-
- @Override
- public String toString() {
- return "Mapping for " + schema.getName();
- }
-
- public static final class FieldSetter {
-
- private final DatabaseDialect.ColumnConverter converter;
- private final Field field;
-
- private FieldSetter(
- DatabaseDialect.ColumnConverter converter,
- Field field
- ) {
- this.converter = converter;
- this.field = field;
- }
-
- /**
- * Get the {@link Field} that this setter function sets.
- *
- * @return the field; never null
- */
- public Field field() {
- return field;
- }
-
- /**
- * set field
- *
- * @param payload
- * @param resultSet
- * @throws SQLException
- * @throws IOException
- */
- public void setField(
- Struct payload,
- ResultSet resultSet
- ) throws SQLException, IOException {
- Object value = this.converter.convert(resultSet);
- payload.put(field, value);
- }
-
- @Override
- public String toString() {
- return field.getName();
- }
- }
-}
diff --git a/connectors/rocketmq-connect-jdbc/rocketmq-connect-jdbc-core/src/main/java/org/apache/rocketmq/connect/jdbc/util/ExpressionBuilder.java b/connectors/rocketmq-connect-jdbc/rocketmq-connect-jdbc-core/src/main/java/org/apache/rocketmq/connect/jdbc/util/ExpressionBuilder.java
deleted file mode 100644
index cc90f1009..000000000
--- a/connectors/rocketmq-connect-jdbc/rocketmq-connect-jdbc-core/src/main/java/org/apache/rocketmq/connect/jdbc/util/ExpressionBuilder.java
+++ /dev/null
@@ -1,597 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements. See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF licenses this file to You under the Apache License, Version 2.0
- * (the "License"); you may not use this file except in compliance with
- * the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package org.apache.rocketmq.connect.jdbc.util;
-
-import org.apache.rocketmq.connect.jdbc.schema.column.ColumnId;
-
-public class ExpressionBuilder {
-
- /**
- * A functional interface for anything that can be appended to an expression builder.
- * This makes use of double-dispatch to allow implementations to customize the behavior,
- * yet have callers not care about the differences in behavior.
- */
- @FunctionalInterface
- public interface Expressable {
-
- /**
- * Append this object to the specified builder.
- *
- * @param builder the builder to use; may not be null
- * @param useQuotes whether quotes should be used for this object
- */
- void appendTo(
- ExpressionBuilder builder,
- boolean useQuotes
- );
-
- /**
- * Append this object to the specified builder.
- *
- * @param builder the builder to use; may not be null
- * @param useQuotes whether quotes should be used for this object
- */
- default void appendTo(
- ExpressionBuilder builder,
- QuoteMethod useQuotes
- ) {
- switch (useQuotes) {
- case ALWAYS:
- appendTo(builder, true);
- break;
- case NEVER:
- default:
- // do nothing
- break;
- }
- }
- }
-
- /**
- * A functional interface for a transformation that an expression builder might use when
- * appending one or more other objects.
- *
- * @param the type of object to transform before appending.
- */
- @FunctionalInterface
- public interface Transform {
- void apply(
- ExpressionBuilder builder,
- T input
- );
- }
-
- /**
- * A fluent API interface returned by the {@link ExpressionBuilder#appendList()} method that
- * allows a caller to easily define a custom delimiter to be used between items in the list,
- * an optional transformation that should be applied to each item in the list, and the
- * items in the list. This is very handle when the number of items is not known a priori.
- *
- * @param the type of object to be appended to the expression builder
- */
- public interface ListBuilder {
-
- /**
- * Define the delimiter to appear between items in the list. If not specified, a comma
- * is used as the default delimiter.
- *
- * @param delimiter the delimiter; may not be null
- * @return this builder to enable methods to be chained; never null
- */
- ListBuilder delimitedBy(String delimiter);
-
- /**
- * Define a {@link Transform} that should be applied to every item in the list as it is
- * appended.
- *
- * @param transform the transform; may not be null
- * @param the type of item to be transformed
- * @return this builder to enable methods to be chained; never null
- */
- ListBuilder transformedBy(Transform transform);
-
- /**
- * Append to this list all of the items in the specified {@link Iterable}.
- *
- * @param objects the objects to be appended to the list
- * @return this builder to enable methods to be chained; never null
- */
- ExpressionBuilder of(Iterable extends T> objects);
-
- /**
- * Append to this list all of the items in the specified {@link Iterable} objects.
- *
- * @param objects1 the first collection of objects to be added to the list
- * @param objects2 a second collection of objects to be added to the list
- * @return this builder to enable methods to be chained; never null
- */
- default ExpressionBuilder of(Iterable extends T> objects1, Iterable extends T> objects2) {
- of(objects1);
- return of(objects2);
- }
-
- /**
- * Append to this list all of the items in the specified {@link Iterable} objects.
- *
- * @param objects1 the first collection of objects to be added to the list
- * @param objects2 a second collection of objects to be added to the list
- * @param objects3 a third collection of objects to be added to the list
- * @return this builder to enable methods to be chained; never null
- */
- default ExpressionBuilder of(
- Iterable extends T> objects1,
- Iterable extends T> objects2,
- Iterable extends T> objects3
- ) {
- of(objects1);
- of(objects2);
- return of(objects3);
- }
- }
-
- /**
- * Get a {@link Transform} that will surround the inputs with quotes.
- *
- * @return the transform; never null
- */
- public static Transform quote() {
- return (builder, input) -> builder.appendColumnName(input);
- }
-
- /**
- * Get a {@link Transform} that will quote just the column names.
- *
- * @return the transform; never null
- */
- public static Transform columnNames() {
- return (builder, input) -> builder.appendColumnName(input.name());
- }
-
- /**
- * Get a {@link Transform} that will quote just the column names and append the given string.
- *
- * @param appended the string to append after the quoted column names
- * @return the transform; never null
- */
- public static Transform columnNamesWith(final String appended) {
- return (builder, input) -> {
- builder.appendColumnName(input.name());
- builder.append(appended);
- };
- }
-
- /**
- * Get a {@link Transform} that will append a placeholder rather than each of the column names.
- *
- * @param str the string to output instead the each column name
- * @return the transform; never null
- */
- public static Transform placeholderInsteadOfColumnNames(final String str) {
- return (builder, input) -> builder.append(str);
- }
-
- /**
- * Get a {@link Transform} that will append the prefix and then the quoted column name.
- *
- * @param prefix the string to output before the quoted column names
- * @return the transform; never null
- */
- public static Transform columnNamesWithPrefix(final String prefix) {
- return (builder, input) -> {
- builder.append(prefix);
- builder.appendColumnName(input.name());
- };
- }
-
- /**
- * Create a new ExpressionBuilder using the default {@link IdentifierRules}.
- *
- * @return the expression builder
- */
- public static ExpressionBuilder create() {
- return new ExpressionBuilder();
- }
-
- protected static final QuoteMethod DEFAULT_QUOTE_METHOD = QuoteMethod.ALWAYS;
-
- private final IdentifierRules rules;
- private final StringBuilder sb = new StringBuilder();
- private QuoteMethod quoteSqlIdentifiers = DEFAULT_QUOTE_METHOD;
-
- /**
- * Create a new expression builder with the default {@link IdentifierRules}.
- */
- public ExpressionBuilder() {
- this(null);
- }
-
- /**
- * Create a new expression builder that uses the specified {@link IdentifierRules}.
- *
- * @param rules the rules; may be null if the default rules are to be used
- */
- public ExpressionBuilder(IdentifierRules rules) {
- this.rules = rules != null ? rules : IdentifierRules.DEFAULT;
- }
-
- /**
- * Set when this expression builder should quote identifiers, such as table and column names.
- *
- * @param method the quoting method; may be null if the default method
- * ({@link QuoteMethod#ALWAYS always}) should be used
- * @return this expression builder; never null
- */
- public ExpressionBuilder setQuoteIdentifiers(QuoteMethod method) {
- this.quoteSqlIdentifiers = method != null ? method : DEFAULT_QUOTE_METHOD;
- return this;
- }
-
- /**
- * Return a new ExpressionBuilder that escapes quotes with the specified prefix.
- * This builder remains unaffected.
- *
- * @param prefix the prefix
- * @return the new ExpressionBuilder, or this builder if the prefix is null or empty
- */
- public ExpressionBuilder escapeQuotesWith(String prefix) {
- if (prefix == null || prefix.isEmpty()) {
- return this;
- }
- return new ExpressionBuilder(this.rules.escapeQuotesWith(prefix));
- }
-
- /**
- * Append to this builder's expression the delimiter defined by this builder's
- * {@link IdentifierRules}.
- *
- * @return this builder to enable methods to be chained; never null
- */
- public ExpressionBuilder appendIdentifierDelimiter() {
- sb.append(rules.identifierDelimiter());
- return this;
- }
-
- /**
- * Always append to this builder's expression the leading quote character(s) defined by this
- * builder's {@link IdentifierRules}.
- *
- * @return this builder to enable methods to be chained; never null
- */
- public ExpressionBuilder appendLeadingQuote() {
- return appendLeadingQuote(QuoteMethod.ALWAYS);
- }
-
-
- protected ExpressionBuilder appendLeadingQuote(QuoteMethod method) {
- switch (method) {
- case ALWAYS:
- sb.append(rules.leadingQuoteString());
- break;
- case NEVER:
- default:
- break;
- }
- return this;
- }
-
- /**
- * Always append to this builder's expression the trailing quote character(s) defined by this
- * builder's {@link IdentifierRules}.
- *
- * @return this builder to enable methods to be chained; never null
- */
- public ExpressionBuilder appendTrailingQuote() {
- return appendTrailingQuote(QuoteMethod.ALWAYS);
- }
-
- protected ExpressionBuilder appendTrailingQuote(QuoteMethod method) {
- switch (method) {
- case ALWAYS:
- sb.append(rules.trailingQuoteString());
- break;
- case NEVER:
- default:
- break;
- }
- return this;
- }
-
- /**
- * Append to this builder's expression the string quote character ({@code '}).
- *
- * @return this builder to enable methods to be chained; never null
- */
- public ExpressionBuilder appendStringQuote() {
- sb.append("'");
- return this;
- }
-
- /**
- * Append to this builder's expression a string surrounded by single quote characters ({@code '}).
- * Use {@link #appendIdentifier(String, QuoteMethod)} for identifiers,
- * {@link #appendColumnName(String, QuoteMethod)} for column names, or
- * {@link #appendTableName(String, QuoteMethod)} for table names.
- *
- * @param name the object whose string representation is to be appended
- * @return this builder to enable methods to be chained; never null
- */
- public ExpressionBuilder appendStringQuoted(Object name) {
- appendStringQuote();
- sb.append(name);
- appendStringQuote();
- return this;
- }
-
- /**
- * Append to this builder's expression the identifier.
- *
- * @param name the name to be appended
- * @param quoted true if the name should be quoted, or false otherwise
- * @return this builder to enable methods to be chained; never null
- * @deprecated use {@link #appendIdentifier(String, QuoteMethod)} instead
- */
- @Deprecated
- public ExpressionBuilder appendIdentifier(
- String name,
- boolean quoted
- ) {
- return appendIdentifier(name, quoted ? QuoteMethod.ALWAYS : QuoteMethod.NEVER);
- }
-
- /**
- * Append to this builder's expression the identifier.
- *
- * @param name the name to be appended
- * @param quoted true if the name should be quoted, or false otherwise
- * @return this builder to enable methods to be chained; never null
- */
- public ExpressionBuilder appendIdentifier(
- String name,
- QuoteMethod quoted
- ) {
- appendLeadingQuote(quoted);
- sb.append(name);
- appendTrailingQuote(quoted);
- return this;
- }
-
- /**
- * Append to this builder's expression the specified Column identifier, possibly surrounded by
- * the leading and trailing quotes based upon {@link #setQuoteIdentifiers(QuoteMethod)}.
- *
- * @param name the name to be appended
- * @return this builder to enable methods to be chained; never null
- */
- public ExpressionBuilder appendTableName(String name) {
- return appendTableName(name, quoteSqlIdentifiers);
- }
-
- /**
- * Append to this builder's expression the specified Column identifier, possibly surrounded by
- * the leading and trailing quotes based upon {@link #setQuoteIdentifiers(QuoteMethod)}.
- *
- * @param name the name to be appended
- * @param quote the quote method to be used
- * @return this builder to enable methods to be chained; never null
- */
- public ExpressionBuilder appendTableName(String name, QuoteMethod quote) {
- appendLeadingQuote(quote);
- sb.append(name);
- appendTrailingQuote(quote);
- return this;
- }
-
- /**
- * Append to this builder's expression the specified Column identifier, possibly surrounded by
- * the leading and trailing quotes based upon {@link #setQuoteIdentifiers(QuoteMethod)}.
- *
- * @param name the name to be appended
- * @return this builder to enable methods to be chained; never null
- */
- public ExpressionBuilder appendColumnName(String name) {
- return appendColumnName(name, quoteSqlIdentifiers);
- }
-
- /**
- * Append to this builder's expression the specified Column identifier, possibly surrounded by
- * the leading and trailing quotes based upon {@link #setQuoteIdentifiers(QuoteMethod)}.
- *
- * @param name the name to be appended
- * @param quote whether to quote the column name; may not be null
- * @return this builder to enable methods to be chained; never null
- */
- public ExpressionBuilder appendColumnName(String name, QuoteMethod quote) {
- appendLeadingQuote(quote);
- sb.append(name);
- appendTrailingQuote(quote);
- return this;
- }
-
- /**
- * Append to this builder's expression the specified identifier, surrounded by the leading and
- * trailing quotes.
- *
- * @param name the name to be appended
- * @return this builder to enable methods to be chained; never null
- */
- public ExpressionBuilder appendIdentifierQuoted(String name) {
- appendLeadingQuote();
- sb.append(name);
- appendTrailingQuote();
- return this;
- }
-
- /**
- * Append to this builder's expression the binary value as a hex string, prefixed and
- * suffixed by a single quote character.
- *
- * @param value the value to be appended
- * @return this builder to enable methods to be chained; never null
- */
- public ExpressionBuilder appendBinaryLiteral(byte[] value) {
- return append("x'").append(BytesUtil.toHex(value)).append("'");
- }
-
- /**
- * Append to this builder's expression a new line.
- *
- * @return this builder to enable methods to be chained; never null
- */
- public ExpressionBuilder appendNewLine() {
- sb.append(System.lineSeparator());
- return this;
- }
-
- /**
- * Append to this builder's expression the specified object. If the object is {@link Expressable},
- * then this builder delegates to the object's
- * {@link Expressable#appendTo(ExpressionBuilder, boolean)} method. Otherwise, the string
- * representation of the object is appended to the expression.
- *
- * @param obj the object to be appended
- * @param useQuotes true if the object should be surrounded by quotes, or false otherwise
- * @return this builder to enable methods to be chained; never null
- * @deprecated use {@link #append(Object, QuoteMethod)} instead
- */
- @Deprecated
- public ExpressionBuilder append(
- Object obj,
- boolean useQuotes
- ) {
- return append(obj, useQuotes ? QuoteMethod.ALWAYS : QuoteMethod.NEVER);
- }
-
- /**
- * Append to this builder's expression the specified object. If the object is {@link Expressable},
- * then this builder delegates to the object's
- * {@link Expressable#appendTo(ExpressionBuilder, boolean)} method. Otherwise, the string
- * representation of the object is appended to the expression.
- *
- * @param obj the object to be appended
- * @param useQuotes true if the object should be surrounded by quotes, or false otherwise
- * @return this builder to enable methods to be chained; never null
- */
- public ExpressionBuilder append(
- Object obj,
- QuoteMethod useQuotes
- ) {
- if (obj instanceof Expressable) {
- ((Expressable) obj).appendTo(this, useQuotes);
- } else if (obj != null) {
- sb.append(obj);
- }
- return this;
- }
-
- /**
- * Append to this builder's expression the specified object surrounded by quotes. If the object
- * is {@link Expressable}, then this builder delegates to the object's
- * {@link Expressable#appendTo(ExpressionBuilder, boolean)} method. Otherwise, the string
- * representation of the object is appended to the expression.
- *
- * @param obj the object to be appended
- * @return this builder to enable methods to be chained; never null
- */
- public ExpressionBuilder append(Object obj) {
- return append(obj, quoteSqlIdentifiers);
- }
-
- /**
- * Append to this builder's expression the specified object surrounded by quotes. If the object
- * is {@link Expressable}, then this builder delegates to the object's
- * {@link Expressable#appendTo(ExpressionBuilder, boolean)} method. Otherwise, the string
- * representation of the object is appended to the expression.
- *
- * @param obj the object to be appended
- * @param transform the transform that should be used on the supplied object to obtain the
- * representation that is appended to the expression; may be null
- * @param the type of object to transform before appending.
- * @return this builder to enable methods to be chained; never null
- */
- public ExpressionBuilder append(
- T obj,
- Transform transform
- ) {
- if (transform != null) {
- transform.apply(this, obj);
- } else {
- append(obj);
- }
- return this;
- }
-
- protected class BasicListBuilder implements ListBuilder {
- private final String delimiter;
- private final Transform transform;
- private boolean first = true;
-
- BasicListBuilder() {
- this(", ", null);
- }
-
- BasicListBuilder(String delimiter, Transform transform) {
- this.delimiter = delimiter;
- this.transform = transform != null ? transform : ExpressionBuilder::append;
- }
-
- @Override
- public ListBuilder delimitedBy(String delimiter) {
- return new BasicListBuilder(delimiter, transform);
- }
-
- @Override
- public ListBuilder transformedBy(Transform transform) {
- return new BasicListBuilder<>(delimiter, transform);
- }
-
- @Override
- public ExpressionBuilder of(Iterable extends T> objects) {
- for (T obj : objects) {
- if (first) {
- first = false;
- } else {
- append(delimiter);
- }
- append(obj, transform);
- }
- return ExpressionBuilder.this;
- }
- }
-
- public ListBuilder appendList() {
- return new BasicListBuilder<>();
- }
-
- public ExpressionBuilder appendMultiple(
- String delimiter,
- String expression,
- int times
- ) {
- for (int i = 0; i < times; i++) {
- if (i > 0) {
- append(delimiter);
- }
- append(expression);
- }
- return this;
- }
-
- @Override
- public String toString() {
- return sb.toString();
- }
-}
diff --git a/connectors/rocketmq-connect-jdbc/rocketmq-connect-jdbc-core/src/main/resources/META-INF/services/org.apache.rocketmq.connect.jdbc.dialect.provider.DatabaseDialectProvider b/connectors/rocketmq-connect-jdbc/rocketmq-connect-jdbc-core/src/main/resources/META-INF/services/org.apache.rocketmq.connect.jdbc.dialect.provider.DatabaseDialectProvider
deleted file mode 100644
index 4b79c5373..000000000
--- a/connectors/rocketmq-connect-jdbc/rocketmq-connect-jdbc-core/src/main/resources/META-INF/services/org.apache.rocketmq.connect.jdbc.dialect.provider.DatabaseDialectProvider
+++ /dev/null
@@ -1,3 +0,0 @@
-org.apache.rocketmq.connect.jdbc.dialect.GenericDatabaseDialect$Provider
-org.apache.rocketmq.connect.jdbc.dialect.impl.MySqlDatabaseDialect$Provider
-org.apache.rocketmq.connect.jdbc.dialect.impl.OpenMLDBDatabaseDialect$Provider
\ No newline at end of file
diff --git a/connectors/rocketmq-connect-jdbc/rocketmq-connect-jdbc-mysql/pom.xml b/connectors/rocketmq-connect-jdbc/rocketmq-connect-jdbc-mysql/pom.xml
deleted file mode 100644
index 72f48347a..000000000
--- a/connectors/rocketmq-connect-jdbc/rocketmq-connect-jdbc-mysql/pom.xml
+++ /dev/null
@@ -1,169 +0,0 @@
-
-
-
- rocketmq-connect-jdbc
- org.apache.rocketmq
- 0.0.1-SNAPSHOT
-
- 4.0.0
-
- rocketmq-connect-jdbc-mysql
-
-
- 8
- 8
- UTF-8
-
-
-
-
-
- mysql
- mysql-connector-java
- 8.0.30
-
-
- org.apache.rocketmq
- rocketmq-connect-jdbc-core
- compile
-
-
-
-
-
-
-
- org.codehaus.mojo
- versions-maven-plugin
- 2.3
-
-
- org.codehaus.mojo
- clirr-maven-plugin
- 2.7
-
-
- maven-dependency-plugin
-
- ${project.build.directory}/lib
- false
- true
-
-
-
- maven-compiler-plugin
- 3.6.1
-
- ${maven.compiler.source}
- ${maven.compiler.target}
- ${maven.compiler.source}
- true
- true
-
-
-
- maven-surefire-plugin
- 2.19.1
-
- -Xms512m -Xmx1024m
- always
-
- **/*Test.java
-
-
-
-
- maven-site-plugin
- 3.6
-
- en_US
- UTF-8
- UTF-8
-
-
-
- maven-source-plugin
- 3.0.1
-
-
- attach-sources
-
- jar
-
-
-
-
-
- maven-javadoc-plugin
- 2.10.4
-
- UTF-8
- en_US
- io.openmessaging.internal
-
-
-
- aggregate
-
- aggregate
-
- site
-
-
-
-
- maven-resources-plugin
- 3.0.2
-
- ${project.build.sourceEncoding}
-
-
-
- org.codehaus.mojo
- findbugs-maven-plugin
- 3.0.4
-
-
- maven-assembly-plugin
- 3.0.0
-
-
- jar-with-dependencies
-
-
-
-
- make-assembly
- package
-
- single
-
-
-
-
-
- maven-checkstyle-plugin
- 2.17
-
-
- verify
- verify
-
- ../../../style/rmq_checkstyle.xml
- UTF-8
- true
- true
- false
- false
-
-
- check
-
-
-
-
-
-
-
\ No newline at end of file
diff --git a/connectors/rocketmq-connect-jdbc/rocketmq-connect-jdbc-openmldb/pom.xml b/connectors/rocketmq-connect-jdbc/rocketmq-connect-jdbc-openmldb/pom.xml
deleted file mode 100644
index 01af1627b..000000000
--- a/connectors/rocketmq-connect-jdbc/rocketmq-connect-jdbc-openmldb/pom.xml
+++ /dev/null
@@ -1,185 +0,0 @@
-
-
-
- rocketmq-connect-jdbc
- org.apache.rocketmq
- 0.0.1-SNAPSHOT
-
- 4.0.0
-
- rocketmq-connect-jdbc-openmldb
-
-
- 8
- 8
- UTF-8
-
-
-
-
- org.apache.rocketmq
- rocketmq-connect-jdbc-core
- compile
-
-
-
- com.4paradigm.openmldb
- openmldb-native
- 0.5.0-macos
-
-
- com.4paradigm.openmldb
- openmldb-jdbc
- 0.5.0
-
-
- com.4paradigm.openmldb
- openmldb-native
-
-
- org.projectlombok
- lombok
-
-
-
-
-
-
-
-
-
- org.codehaus.mojo
- versions-maven-plugin
- 2.3
-
-
- org.codehaus.mojo
- clirr-maven-plugin
- 2.7
-
-
- maven-dependency-plugin
-
- ${project.build.directory}/lib
- false
- true
-
-
-
- maven-compiler-plugin
- 3.6.1
-
- ${maven.compiler.source}
- ${maven.compiler.target}
- ${maven.compiler.source}
- true
- true
-
-
-
- maven-surefire-plugin
- 2.19.1
-
- -Xms512m -Xmx1024m
- always
-
- **/*Test.java
-
-
-
-
- maven-site-plugin
- 3.6
-
- en_US
- UTF-8
- UTF-8
-
-
-
- maven-source-plugin
- 3.0.1
-
-
- attach-sources
-
- jar
-
-
-
-
-
- maven-javadoc-plugin
- 2.10.4
-
- UTF-8
- en_US
- io.openmessaging.internal
-
-
-
- aggregate
-
- aggregate
-
- site
-
-
-
-
- maven-resources-plugin
- 3.0.2
-
- ${project.build.sourceEncoding}
-
-
-
- org.codehaus.mojo
- findbugs-maven-plugin
- 3.0.4
-
-
- maven-assembly-plugin
- 3.0.0
-
-
- jar-with-dependencies
-
-
-
-
- make-assembly
- package
-
- single
-
-
-
-
-
- maven-checkstyle-plugin
- 2.17
-
-
- verify
- verify
-
- ../../../style/rmq_checkstyle.xml
- UTF-8
- true
- true
- false
- false
-
-
- check
-
-
-
-
-
-
-
-
\ No newline at end of file
diff --git a/connectors/rocketmq-connect-jdbc/rocketmq-connect-jdbc-openmldb/src/main/resources/openmldb-jdbc-sink.conf b/connectors/rocketmq-connect-jdbc/rocketmq-connect-jdbc-openmldb/src/main/resources/openmldb-jdbc-sink.conf
deleted file mode 100644
index e69de29bb..000000000
diff --git a/connectors/rocketmq-connect-jdbc/src/assembly/assembly.xml b/connectors/rocketmq-connect-jdbc/src/assembly/assembly.xml
new file mode 100644
index 000000000..430fbb866
--- /dev/null
+++ b/connectors/rocketmq-connect-jdbc/src/assembly/assembly.xml
@@ -0,0 +1,52 @@
+
+
+
+ package
+ false
+
+ dir
+ tar.gz
+
+
+
+
+ false
+ share/java/rocketmq-connect-jdbc/
+ true
+ true
+ system
+
+
+
+ false
+ share/java/rocketmq-connect-jdbc/
+ true
+ true
+ runtime
+
+
+
+ false
+ share/java/rocketmq-connect-jdbc/
+ true
+ true
+ provided
+
+
+
+
diff --git a/connectors/rocketmq-connect-jdbc/rocketmq-connect-jdbc-core/src/main/java/org/apache/rocketmq/connect/jdbc/dialect/PreparedStatementBinder.java b/connectors/rocketmq-connect-jdbc/src/main/java/org/apache/rocketmq/connect/jdbc/binder/AbstractJdbcRecordBinder.java
similarity index 55%
rename from connectors/rocketmq-connect-jdbc/rocketmq-connect-jdbc-core/src/main/java/org/apache/rocketmq/connect/jdbc/dialect/PreparedStatementBinder.java
rename to connectors/rocketmq-connect-jdbc/src/main/java/org/apache/rocketmq/connect/jdbc/binder/AbstractJdbcRecordBinder.java
index 6bf2cd082..f5410e1d3 100644
--- a/connectors/rocketmq-connect-jdbc/rocketmq-connect-jdbc-core/src/main/java/org/apache/rocketmq/connect/jdbc/dialect/PreparedStatementBinder.java
+++ b/connectors/rocketmq-connect-jdbc/src/main/java/org/apache/rocketmq/connect/jdbc/binder/AbstractJdbcRecordBinder.java
@@ -14,14 +14,16 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package org.apache.rocketmq.connect.jdbc.dialect;
+package org.apache.rocketmq.connect.jdbc.binder;
import io.openmessaging.connector.api.data.ConnectRecord;
import io.openmessaging.connector.api.data.Field;
import io.openmessaging.connector.api.data.Schema;
import io.openmessaging.connector.api.data.Struct;
import io.openmessaging.connector.api.errors.ConnectException;
-import org.apache.rocketmq.connect.jdbc.schema.column.ColumnDefinition;
+import java.sql.PreparedStatement;
+import java.sql.SQLException;
+import java.util.Objects;
import org.apache.rocketmq.connect.jdbc.schema.table.TableDefinition;
import org.apache.rocketmq.connect.jdbc.sink.JdbcSinkConfig;
import org.apache.rocketmq.connect.jdbc.sink.metadata.FieldsMetadata;
@@ -29,45 +31,32 @@
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
-import java.sql.PreparedStatement;
-import java.sql.SQLException;
-import java.sql.Statement;
-import java.util.Objects;
-import java.util.Optional;
-
-import static java.util.Objects.nonNull;
-
/**
* prepared statement binder
*/
-public class PreparedStatementBinder implements DatabaseDialect.StatementBinder {
-
- private final static Logger log = LoggerFactory.getLogger(PreparedStatementBinder.class);
-
+public abstract class AbstractJdbcRecordBinder implements JdbcRecordBinder {
+ private final static Logger log = LoggerFactory.getLogger(AbstractJdbcRecordBinder.class);
private final JdbcSinkConfig.PrimaryKeyMode pkMode;
- private final PreparedStatement statement;
+ private final JdbcSinkConfig.InsertMode insertMode;
+ private final PreparedStatement preparedStatement;
private final SchemaPair schemaPair;
private final FieldsMetadata fieldsMetadata;
- private final JdbcSinkConfig.InsertMode insertMode;
- private final DatabaseDialect dialect;
- private final TableDefinition tabDef;
+ private final TableDefinition tableDefinition;
- public PreparedStatementBinder(
- DatabaseDialect dialect,
- PreparedStatement statement,
- JdbcSinkConfig.PrimaryKeyMode pkMode,
- SchemaPair schemaPair,
- FieldsMetadata fieldsMetadata,
- TableDefinition tabDef,
- JdbcSinkConfig.InsertMode insertMode
+ public AbstractJdbcRecordBinder(
+ PreparedStatement statement,
+ TableDefinition tableDefinition,
+ FieldsMetadata fieldsMetadata,
+ SchemaPair schemaPair,
+ JdbcSinkConfig.PrimaryKeyMode pkMode,
+ JdbcSinkConfig.InsertMode insertMode
) {
- this.dialect = dialect;
this.pkMode = pkMode;
- this.statement = statement;
+ this.preparedStatement = statement;
+ this.tableDefinition = tableDefinition;
this.schemaPair = schemaPair;
this.fieldsMetadata = fieldsMetadata;
this.insertMode = insertMode;
- this.tabDef = tabDef;
}
@Override
@@ -91,50 +80,9 @@ public void bindRecord(ConnectRecord record) throws SQLException {
throw new AssertionError();
}
}
- statement.addBatch();
+ preparedStatement.addBatch();
}
- public long executeDeletes(PreparedStatement deletePreparedStatement) throws SQLException {
- long totalDeleteCount = 0;
- if (nonNull(deletePreparedStatement)) {
- try {
- for (int updateCount : deletePreparedStatement.executeBatch()) {
- if (updateCount != Statement.SUCCESS_NO_INFO) {
- totalDeleteCount += updateCount;
- }
- }
- } catch (SQLException e) {
- log.error("deletePreparedStatement.executeBatch failed, errCode={}, sqlState={}, error msg={}, cause={}, sql={}",
- e.getErrorCode(), e.getSQLState(), e.getMessage(), e.getCause(), deletePreparedStatement);
- throw new SQLException(e);
- }
- }
- return totalDeleteCount;
- }
-
- @Override
- public Optional executeUpdates(PreparedStatement updatePreparedStatement) throws SQLException {
- Optional 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 new SQLException(e);
- }
- }
- return count;
- }
-
-
protected int bindKeyFields(ConnectRecord record, int index) throws SQLException {
switch (pkMode) {
case NONE:
@@ -142,27 +90,25 @@ protected int bindKeyFields(ConnectRecord record, int index) throws SQLException
throw new AssertionError();
}
break;
- case RECORD_KEY: {
+ case RECORD_KEY:
if (schemaPair.keySchema.getFieldType().isPrimitive()) {
assert fieldsMetadata.keyFieldNames.size() == 1;
bindField(index++, schemaPair.keySchema, record.getKey(),
- fieldsMetadata.keyFieldNames.iterator().next());
+ fieldsMetadata.keyFieldNames.iterator().next());
} else {
for (String fieldName : fieldsMetadata.keyFieldNames) {
final Field field = schemaPair.keySchema.getField(fieldName);
bindField(index++, field.getSchema(), ((Struct) record.getKey()).get(field), fieldName);
}
}
- }
- break;
- case RECORD_VALUE: {
+ break;
+ case RECORD_VALUE:
Struct struct = (Struct) record.getData();
for (String fieldName : fieldsMetadata.keyFieldNames) {
- final Field field = schemaPair.schema.getField(fieldName);
+ final Field field = schemaPair.valueSchema.getField(fieldName);
bindField(index++, field.getSchema(), struct.get(fieldName), fieldName);
}
- }
- break;
+ break;
default:
throw new ConnectException("Unknown primary key mode: " + pkMode);
}
@@ -171,7 +117,7 @@ protected int bindKeyFields(ConnectRecord record, int index) throws SQLException
protected int bindNonKeyFields(
ConnectRecord record,
- int index
+ int index
) throws SQLException {
Struct struct = (Struct) record.getData();
for (final String fieldName : fieldsMetadata.nonKeyFieldNames) {
@@ -181,9 +127,14 @@ protected int bindNonKeyFields(
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 PreparedStatement getPreparedStatement() {
+ return preparedStatement;
+ }
+
+ protected TableDefinition getTableDefinition() {
+ return tableDefinition;
}
+
+ // Bind field
+ protected abstract void bindField(int index, Schema schema, Object value, String fieldName) throws SQLException;
}
diff --git a/connectors/rocketmq-connect-jdbc/src/main/java/org/apache/rocketmq/connect/jdbc/binder/DefaultJdbcRecordBinder.java b/connectors/rocketmq-connect-jdbc/src/main/java/org/apache/rocketmq/connect/jdbc/binder/DefaultJdbcRecordBinder.java
new file mode 100644
index 000000000..058a71b4c
--- /dev/null
+++ b/connectors/rocketmq-connect-jdbc/src/main/java/org/apache/rocketmq/connect/jdbc/binder/DefaultJdbcRecordBinder.java
@@ -0,0 +1,205 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.rocketmq.connect.jdbc.binder;
+
+import io.openmessaging.connector.api.data.Schema;
+import io.openmessaging.connector.api.data.logical.Date;
+import io.openmessaging.connector.api.data.logical.Decimal;
+import io.openmessaging.connector.api.data.logical.Time;
+import java.math.BigDecimal;
+import java.nio.ByteBuffer;
+import java.sql.PreparedStatement;
+import java.sql.SQLException;
+import java.sql.Timestamp;
+import java.time.ZoneId;
+import java.util.Objects;
+import java.util.TimeZone;
+import org.apache.rocketmq.connect.jdbc.common.DebeziumTimeTypes;
+import org.apache.rocketmq.connect.jdbc.schema.table.TableDefinition;
+import org.apache.rocketmq.connect.jdbc.sink.JdbcSinkConfig;
+import org.apache.rocketmq.connect.jdbc.sink.metadata.FieldsMetadata;
+import org.apache.rocketmq.connect.jdbc.sink.metadata.SchemaPair;
+import org.apache.rocketmq.connect.jdbc.util.DateTimeUtils;
+
+/**
+ * Jdbc record binder
+ */
+public class DefaultJdbcRecordBinder extends AbstractJdbcRecordBinder {
+
+ private TimeZone timeZone = TimeZone.getTimeZone(ZoneId.of(JdbcSinkConfig.DB_TIMEZONE_DEFAULT));
+
+ public DefaultJdbcRecordBinder(PreparedStatement statement, TableDefinition tableDefinition,
+ FieldsMetadata fieldsMetadata,
+ SchemaPair schemaPair, JdbcSinkConfig.PrimaryKeyMode pkMode, JdbcSinkConfig.InsertMode insertMode,
+ TimeZone timeZone) {
+ super(statement, tableDefinition, fieldsMetadata, schemaPair, pkMode, insertMode);
+ if (Objects.nonNull(this.timeZone)) {
+ this.timeZone = timeZone;
+ }
+ }
+
+ @Override
+ public void bindField(int index, Schema schema, Object value, String fieldName) throws SQLException {
+ PreparedStatement statement = getPreparedStatement();
+ if (Objects.isNull(value)) {
+ Integer type = getSqlTypeForSchema(schema);
+ if (type != null) {
+ statement.setNull(index, type);
+ } else {
+ statement.setObject(index, null);
+ }
+ } else {
+ boolean bound = maybeBindLogical(statement, index, schema, value);
+ if (!bound) {
+ bound = maybeBindDebeziumLogical(statement, index, schema, value);
+ }
+ 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) throws SQLException {
+ if (schema.getName() != null) {
+ switch (schema.getName()) {
+ case Decimal.LOGICAL_NAME:
+ statement.setBigDecimal(index, (BigDecimal) value);
+ return true;
+ case Date.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 Time.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 io.openmessaging.connector.api.data.logical.Timestamp.LOGICAL_NAME:
+ Timestamp timestamp;
+ if (value instanceof java.util.Date) {
+ timestamp = new Timestamp(((java.util.Date) value).getTime());
+ } else {
+ timestamp = new Timestamp((long) value);
+ }
+ statement.setTimestamp(
+ index, timestamp,
+ DateTimeUtils.getTimeZoneCalendar(timeZone)
+ );
+ return true;
+ default:
+ return false;
+ }
+ }
+ return false;
+ }
+
+ /**
+ * Dialects not supporting `setObject(index, null)` can override this method
+ * to provide a specific sqlType, as per the JDBC documentation
+ *
+ * @param schema the schema
+ * @return the SQL type
+ */
+ protected Integer getSqlTypeForSchema(Schema schema) {
+ return null;
+ }
+
+ protected boolean maybeBindDebeziumLogical(
+ PreparedStatement statement,
+ int index,
+ Schema schema,
+ Object value
+ ) throws SQLException {
+ return DebeziumTimeTypes.maybeBindDebeziumLogical(statement, index, schema, value, timeZone);
+ }
+
+ protected boolean maybeBindPrimitive(
+ PreparedStatement statement,
+ int index,
+ Schema schema,
+ Object value
+ ) throws SQLException {
+ switch (schema.getFieldType()) {
+ case INT8:
+ statement.setByte(index, Byte.parseByte(value.toString()));
+ break;
+ case INT32:
+ statement.setInt(index, Integer.parseInt(value.toString()));
+ break;
+ case INT64:
+ statement.setLong(index, Long.parseLong(value.toString()));
+ break;
+ case FLOAT32:
+ statement.setFloat(index, Float.parseFloat(value.toString()));
+ break;
+ case FLOAT64:
+ statement.setDouble(index, Double.parseDouble(value.toString()));
+ break;
+ case BOOLEAN:
+ statement.setBoolean(index, Boolean.parseBoolean(value.toString()));
+ 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;
+ }
+}
diff --git a/connectors/rocketmq-connect-jdbc/rocketmq-connect-jdbc-mysql/src/main/java/org/apache/rocketmq/connect/jdbc/mysql/sink/MysqlJdbcSinkConnector.java b/connectors/rocketmq-connect-jdbc/src/main/java/org/apache/rocketmq/connect/jdbc/binder/JdbcRecordBinder.java
similarity index 67%
rename from connectors/rocketmq-connect-jdbc/rocketmq-connect-jdbc-mysql/src/main/java/org/apache/rocketmq/connect/jdbc/mysql/sink/MysqlJdbcSinkConnector.java
rename to connectors/rocketmq-connect-jdbc/src/main/java/org/apache/rocketmq/connect/jdbc/binder/JdbcRecordBinder.java
index efe131153..248eb1b70 100644
--- a/connectors/rocketmq-connect-jdbc/rocketmq-connect-jdbc-mysql/src/main/java/org/apache/rocketmq/connect/jdbc/mysql/sink/MysqlJdbcSinkConnector.java
+++ b/connectors/rocketmq-connect-jdbc/src/main/java/org/apache/rocketmq/connect/jdbc/binder/JdbcRecordBinder.java
@@ -14,17 +14,18 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package org.apache.rocketmq.connect.jdbc.mysql.sink;
-import io.openmessaging.connector.api.component.task.Task;
-import org.apache.rocketmq.connect.jdbc.sink.BaseSinkConnector;
+package org.apache.rocketmq.connect.jdbc.binder;
-/**
- * mysql jdbc sink connector
- */
-public class MysqlJdbcSinkConnector extends BaseSinkConnector {
- @Override
- public Class extends Task> taskClass() {
- return MysqlJdbcSinkTask.class;
- }
-}
+import io.openmessaging.connector.api.data.ConnectRecord;
+import java.sql.SQLException;
+
+public interface JdbcRecordBinder {
+ /**
+ * bind record
+ *
+ * @param record
+ * @throws SQLException
+ */
+ void bindRecord(ConnectRecord record) throws SQLException;
+}
\ No newline at end of file
diff --git a/connectors/rocketmq-connect-jdbc/rocketmq-connect-jdbc-core/src/main/java/org/apache/rocketmq/connect/jdbc/common/DebeziumTimeTypes.java b/connectors/rocketmq-connect-jdbc/src/main/java/org/apache/rocketmq/connect/jdbc/common/DebeziumTimeTypes.java
similarity index 99%
rename from connectors/rocketmq-connect-jdbc/rocketmq-connect-jdbc-core/src/main/java/org/apache/rocketmq/connect/jdbc/common/DebeziumTimeTypes.java
rename to connectors/rocketmq-connect-jdbc/src/main/java/org/apache/rocketmq/connect/jdbc/common/DebeziumTimeTypes.java
index ffd4cf959..b685b9cd5 100644
--- a/connectors/rocketmq-connect-jdbc/rocketmq-connect-jdbc-core/src/main/java/org/apache/rocketmq/connect/jdbc/common/DebeziumTimeTypes.java
+++ b/connectors/rocketmq-connect-jdbc/src/main/java/org/apache/rocketmq/connect/jdbc/common/DebeziumTimeTypes.java
@@ -20,8 +20,6 @@
import io.debezium.time.Date;
import io.debezium.time.ZonedTimestamp;
import io.openmessaging.connector.api.data.Schema;
-import org.apache.rocketmq.connect.jdbc.util.DateTimeUtils;
-
import java.sql.PreparedStatement;
import java.sql.SQLException;
import java.time.LocalDate;
@@ -29,6 +27,7 @@
import java.time.ZoneOffset;
import java.time.format.DateTimeFormatter;
import java.util.TimeZone;
+import org.apache.rocketmq.connect.jdbc.util.DateTimeUtils;
/**
diff --git a/connectors/rocketmq-connect-jdbc/rocketmq-connect-jdbc-core/src/main/java/org/apache/rocketmq/connect/jdbc/common/HeaderField.java b/connectors/rocketmq-connect-jdbc/src/main/java/org/apache/rocketmq/connect/jdbc/common/HeaderField.java
similarity index 92%
rename from connectors/rocketmq-connect-jdbc/rocketmq-connect-jdbc-core/src/main/java/org/apache/rocketmq/connect/jdbc/common/HeaderField.java
rename to connectors/rocketmq-connect-jdbc/src/main/java/org/apache/rocketmq/connect/jdbc/common/HeaderField.java
index f79c688da..f4829e931 100644
--- a/connectors/rocketmq-connect-jdbc/rocketmq-connect-jdbc-core/src/main/java/org/apache/rocketmq/connect/jdbc/common/HeaderField.java
+++ b/connectors/rocketmq-connect-jdbc/src/main/java/org/apache/rocketmq/connect/jdbc/common/HeaderField.java
@@ -21,9 +21,6 @@
* header field
*/
public interface HeaderField {
-
String SOURCE_TABLE_KEY = "__source_table";
String SOURCE_DB_KEY = "__source_db";
- String SINK_TABLE_KEY = "__sink_table";
- String SINK_DB_KEY = "__sink_db";
}
diff --git a/connectors/rocketmq-connect-jdbc/rocketmq-connect-jdbc-core/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
similarity index 100%
rename from connectors/rocketmq-connect-jdbc/rocketmq-connect-jdbc-core/src/main/java/org/apache/rocketmq/connect/jdbc/common/JdbcSourceConfigConstants.java
rename to connectors/rocketmq-connect-jdbc/src/main/java/org/apache/rocketmq/connect/jdbc/common/JdbcSourceConfigConstants.java
diff --git a/connectors/rocketmq-connect-jdbc/rocketmq-connect-jdbc-core/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
similarity index 86%
rename from connectors/rocketmq-connect-jdbc/rocketmq-connect-jdbc-core/src/main/java/org/apache/rocketmq/connect/jdbc/config/AbstractConfig.java
rename to connectors/rocketmq-connect-jdbc/src/main/java/org/apache/rocketmq/connect/jdbc/config/AbstractConfig.java
index 3d7c52daf..2f0c04036 100644
--- a/connectors/rocketmq-connect-jdbc/rocketmq-connect-jdbc-core/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
@@ -18,45 +18,37 @@
import com.google.common.collect.Lists;
import io.openmessaging.KeyValue;
-import org.apache.rocketmq.connect.jdbc.util.QuoteMethod;
-
import java.util.Arrays;
import java.util.List;
import java.util.Objects;
import java.util.regex.Pattern;
+import org.apache.rocketmq.connect.jdbc.util.QuoteMethod;
/**
* abstract config
*/
public abstract class AbstractConfig {
-
private static final Pattern COMMA_WITH_WHITESPACE = Pattern.compile("\\s*,\\s*");
-
// connection url
public static final String CONNECTION_PREFIX = "connection.";
public static final String CONNECTION_URL_CONFIG = CONNECTION_PREFIX + "url";
// connection user
public static final String CONNECTION_USER_CONFIG = CONNECTION_PREFIX + "user";
- private static final String CONNECTION_USER_DOC = "JDBC connection user.";
// connection password
public static final String CONNECTION_PASSWORD_CONFIG = CONNECTION_PREFIX + "password";
- private static final String CONNECTION_PASSWORD_DOC = "JDBC connection password.";
// connection attempts
public static final String CONNECTION_ATTEMPTS_CONFIG = CONNECTION_PREFIX + "attempts";
- public static final String CONNECTION_ATTEMPTS_DOC = "Maximum number of attempts to retrieve a valid JDBC connection.Must be a positive integer.";
+
public static final int CONNECTION_ATTEMPTS_DEFAULT = 3;
// backoff ms
public static final String CONNECTION_BACKOFF_CONFIG = CONNECTION_PREFIX + "backoff.ms";
- public static final String CONNECTION_BACKOFF_DOC = "Backoff time in milliseconds between connection attempts.";
+
public static final long CONNECTION_BACKOFF_DEFAULT = 10000L;
/**
* quote.sql.identifiers
*/
public static final String QUOTE_SQL_IDENTIFIERS_CONFIG = "quote.sql.identifiers";
public static final String QUOTE_SQL_IDENTIFIERS_DEFAULT = QuoteMethod.ALWAYS.name().toString();
- public static final String QUOTE_SQL_IDENTIFIERS_DOC =
- "When to quote table names, column names, and other identifiers in SQL statements. "
- + "For backward compatibility, the default is ``always``.";
private String connectionDbUrl;
diff --git a/connectors/rocketmq-connect-jdbc/rocketmq-connect-jdbc-core/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/connection/CachedConnectionProvider.java
similarity index 96%
rename from connectors/rocketmq-connect-jdbc/rocketmq-connect-jdbc-core/src/main/java/org/apache/rocketmq/connect/jdbc/dialect/provider/CachedConnectionProvider.java
rename to connectors/rocketmq-connect-jdbc/src/main/java/org/apache/rocketmq/connect/jdbc/connection/CachedConnectionProvider.java
index de6716d4e..0b1f1ce28 100644
--- a/connectors/rocketmq-connect-jdbc/rocketmq-connect-jdbc-core/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/connection/CachedConnectionProvider.java
@@ -14,14 +14,13 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package org.apache.rocketmq.connect.jdbc.dialect.provider;
+package org.apache.rocketmq.connect.jdbc.connection;
import io.openmessaging.connector.api.errors.ConnectException;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
import java.sql.Connection;
import java.sql.SQLException;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
/**
* cached connection provider
@@ -115,11 +114,6 @@ public synchronized void close() {
}
}
- @Override
- public String identifier() {
- return provider.identifier();
- }
-
protected void onConnect(Connection connection) throws SQLException {
}
diff --git a/connectors/rocketmq-connect-jdbc/rocketmq-connect-jdbc-mysql/src/main/java/org/apache/rocketmq/connect/jdbc/mysql/sink/MysqlJdbcSinkTask.java b/connectors/rocketmq-connect-jdbc/src/main/java/org/apache/rocketmq/connect/jdbc/connection/ConnectionProvider.java
similarity index 61%
rename from connectors/rocketmq-connect-jdbc/rocketmq-connect-jdbc-mysql/src/main/java/org/apache/rocketmq/connect/jdbc/mysql/sink/MysqlJdbcSinkTask.java
rename to connectors/rocketmq-connect-jdbc/src/main/java/org/apache/rocketmq/connect/jdbc/connection/ConnectionProvider.java
index 1bd1f3233..3197a4137 100644
--- a/connectors/rocketmq-connect-jdbc/rocketmq-connect-jdbc-mysql/src/main/java/org/apache/rocketmq/connect/jdbc/mysql/sink/MysqlJdbcSinkTask.java
+++ b/connectors/rocketmq-connect-jdbc/src/main/java/org/apache/rocketmq/connect/jdbc/connection/ConnectionProvider.java
@@ -14,19 +14,29 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package org.apache.rocketmq.connect.jdbc.mysql.sink;
+package org.apache.rocketmq.connect.jdbc.connection;
-import org.apache.rocketmq.connect.jdbc.dialect.DatabaseDialect;
-import org.apache.rocketmq.connect.jdbc.mysql.dialect.MySqlDatabaseDialect;
-import org.apache.rocketmq.connect.jdbc.sink.BaseSinkTask;
-import org.apache.rocketmq.connect.jdbc.sink.JdbcSinkConfig;
+import java.sql.Connection;
+import java.sql.SQLException;
/**
- * mysql jdbc sink task
+ * A provider of JDBC {@link Connection} instances.
*/
-public class MysqlJdbcSinkTask extends BaseSinkTask {
+public interface ConnectionProvider extends AutoCloseable {
+
+ /**
+ * Create connection
+ *
+ * @return
+ * @throws SQLException
+ */
+ Connection getConnection() throws SQLException;
+
+ boolean isConnectionValid(Connection connection, int timeout) throws SQLException;
+
+ /**
+ * Close this connection provider.
+ */
@Override
- protected DatabaseDialect newDialect(JdbcSinkConfig config) {
- return new MySqlDatabaseDialect(config);
- }
+ void close();
}
diff --git a/connectors/rocketmq-connect-jdbc/src/main/java/org/apache/rocketmq/connect/jdbc/converter/DefaultColumnConverter.java b/connectors/rocketmq-connect-jdbc/src/main/java/org/apache/rocketmq/connect/jdbc/converter/DefaultColumnConverter.java
new file mode 100644
index 000000000..a16233fb4
--- /dev/null
+++ b/connectors/rocketmq-connect-jdbc/src/main/java/org/apache/rocketmq/connect/jdbc/converter/DefaultColumnConverter.java
@@ -0,0 +1,451 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.rocketmq.connect.jdbc.converter;
+
+import io.openmessaging.connector.api.data.Schema;
+import io.openmessaging.connector.api.data.SchemaBuilder;
+import io.openmessaging.connector.api.data.logical.Date;
+import io.openmessaging.connector.api.data.logical.Decimal;
+import io.openmessaging.connector.api.data.logical.Time;
+import java.io.IOException;
+import java.net.URL;
+import java.sql.Blob;
+import java.sql.Clob;
+import java.sql.ResultSet;
+import java.sql.SQLException;
+import java.sql.SQLXML;
+import java.sql.Types;
+import java.time.ZoneOffset;
+import java.util.TimeZone;
+import org.apache.rocketmq.connect.jdbc.schema.column.ColumnDefinition;
+import org.apache.rocketmq.connect.jdbc.util.DateTimeUtils;
+import org.apache.rocketmq.connect.jdbc.util.NumericMapping;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+public class DefaultColumnConverter implements JdbcColumnConverter {
+
+ 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 Logger log = LoggerFactory.getLogger(DefaultColumnConverter.class);
+ // The maximum precision that can be achieved in a signed 64-bit integer is 2^63 ~= 9.223372e+18
+ private static final int MAX_INTEGER_TYPE_PRECISION = 18;
+
+ private final NumericMapping numericMapping;
+ private final boolean isJdbc4;
+ private final TimeZone timeZone;
+
+ public DefaultColumnConverter(NumericMapping numericMapping, boolean isJdbc4, TimeZone timeZone) {
+ this.numericMapping = numericMapping;
+ this.isJdbc4 = isJdbc4;
+ this.timeZone = timeZone;
+ }
+
+ @Override
+ public Object convertToConnectFieldValue(ResultSet rs, ColumnDefinition columnDefinition, int columnNumber) throws SQLException, IOException {
+ switch (columnDefinition.type()) {
+ case Types.BOOLEAN:
+ return rs.getBoolean(columnNumber);
+
+ case Types.BIT:
+ return rs.getByte(columnNumber);
+
+ // 8 bits int
+ case Types.TINYINT:
+ if (columnDefinition.isSignedNumber()) {
+ return rs.getByte(columnNumber);
+ } else {
+ return rs.getShort(columnNumber);
+ }
+
+ // 16 bits int
+ case Types.SMALLINT:
+ if (columnDefinition.isSignedNumber()) {
+ return rs.getShort(columnNumber);
+ } else {
+ return rs.getInt(columnNumber);
+ }
+
+ // 32 bits int
+ case Types.INTEGER:
+ if (columnDefinition.isSignedNumber()) {
+ return rs.getInt(columnNumber);
+ } else {
+ return rs.getLong(columnNumber);
+ }
+
+ // 64 bits int
+ case Types.BIGINT:
+ return rs.getLong(columnNumber);
+
+ // REAL is a single precision floating point value, i.e. a Java float
+ case Types.REAL:
+ return rs.getFloat(columnNumber);
+
+ // FLOAT is, confusingly, double precision and effectively the same as DOUBLE. See REAL
+ // for single precision
+ case Types.FLOAT:
+ case Types.DOUBLE:
+ return rs.getDouble(columnNumber);
+
+ case Types.NUMERIC:
+ if (numericMapping == NumericMapping.PRECISION_ONLY) {
+ int precision = columnDefinition.precision();
+ int scale = columnDefinition.scale();
+ log.trace("NUMERIC with precision: '{}' and scale: '{}'", precision, scale);
+ if (scale == 0 && precision <= MAX_INTEGER_TYPE_PRECISION) { // integer
+ if (precision > 9) {
+ return rs.getLong(columnNumber);
+ } else if (precision > 4) {
+ return rs.getInt(columnNumber);
+ } else if (precision > 2) {
+ return rs.getShort(columnNumber);
+ } else {
+ return rs.getByte(columnNumber);
+ }
+ }
+ } else if (numericMapping == NumericMapping.BEST_FIT) {
+ int precision = columnDefinition.precision();
+ int scale = columnDefinition.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.getLong(columnNumber);
+ } else if (precision > 4) {
+ return rs.getInt(columnNumber);
+ } else if (precision > 2) {
+ return rs.getShort(columnNumber);
+ } else {
+ return rs.getByte(columnNumber);
+ }
+ } else if (scale > 0) { // floating point - use double in all cases
+ return rs.getDouble(columnNumber);
+ }
+ }
+ } else if (numericMapping == NumericMapping.BEST_FIT_EAGER_DOUBLE) {
+ int precision = columnDefinition.precision();
+ int scale = columnDefinition.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.getLong(columnNumber);
+ } else if (precision > 4) {
+ return rs.getInt(columnNumber);
+ } else if (precision > 2) {
+ return rs.getShort(columnNumber);
+ } else {
+ return rs.getByte(columnNumber);
+ }
+ }
+ } else if (scale > 0) { // floating point - use double in all cases
+ return rs.getDouble(columnNumber);
+ }
+ }
+ // fallthrough
+
+ case Types.DECIMAL:
+ final int precision = columnDefinition.precision();
+ log.debug("DECIMAL with precision: '{}' and scale: '{}'", precision, columnDefinition.scale());
+ final int scale = decimalScale(columnDefinition);
+ return rs.getBigDecimal(columnNumber, scale);
+
+ case Types.CHAR:
+ case Types.VARCHAR:
+ case Types.LONGVARCHAR:
+ return rs.getString(columnNumber);
+
+ case Types.NCHAR:
+ case Types.NVARCHAR:
+ case Types.LONGNVARCHAR:
+ return rs.getNString(columnNumber);
+
+ // Binary == fixed, VARBINARY and LONGVARBINARY == bytes
+ case Types.BINARY:
+ case Types.VARBINARY:
+ case Types.LONGVARBINARY:
+ return rs.getBytes(columnNumber);
+
+ // Date is day + month + year
+ case Types.DATE:
+ return rs.getDate(columnNumber,
+ DateTimeUtils.getTimeZoneCalendar(TimeZone.getTimeZone(ZoneOffset.UTC)));
+
+ // Time is a time of day -- hour, minute, seconds, nanoseconds
+ case Types.TIME:
+ return rs.getTime(columnNumber, DateTimeUtils.getTimeZoneCalendar(timeZone));
+
+ // Timestamp is a date + time
+ case Types.TIMESTAMP:
+ return rs.getTimestamp(columnNumber, DateTimeUtils.getTimeZoneCalendar(timeZone));
+
+ // Datalink is basically a URL -> string
+ case Types.DATALINK:
+ URL url = rs.getURL(columnNumber);
+ return (url != null) ? url.toString() : null;
+
+ // BLOB == fixed
+ case Types.BLOB:
+ Blob blob = rs.getBlob(columnNumber);
+ 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:
+ Clob clob = rs.getClob(columnNumber);
+ 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:
+ Clob nClob = rs.getNClob(columnNumber);
+ if (nClob == null) {
+ return null;
+ } else {
+ try {
+ if (nClob.length() > Integer.MAX_VALUE) {
+ throw new IOException("Can't process NCLOBs longer than " + Integer.MAX_VALUE);
+ }
+ return nClob.getSubString(1, (int) nClob.length());
+ } finally {
+ if (isJdbc4) {
+ free(nClob);
+ }
+ }
+ }
+
+ // XML -> string
+ case Types.SQLXML:
+ SQLXML xml = rs.getSQLXML(columnNumber);
+ 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
+ log.warn("JDBC type {} ({}) not supported", columnDefinition.type(), columnDefinition.typeName());
+ break;
+ }
+ return null;
+ }
+
+ protected void free(Blob blob) throws SQLException {
+ blob.free();
+ }
+
+ protected void free(Clob clob) throws SQLException {
+ clob.free();
+ }
+
+ protected int decimalScale(ColumnDefinition defn) {
+ return defn.scale() == NUMERIC_TYPE_SCALE_UNSET ? NUMERIC_TYPE_SCALE_HIGH : defn.scale();
+ }
+
+ @Override
+ public String convertToConnectFieldSchema(ColumnDefinition columnDefinition, SchemaBuilder builder) {
+ int precision = columnDefinition.precision();
+ int scale = columnDefinition.scale();
+ int sqlType = columnDefinition.type();
+ boolean optional = columnDefinition.isOptional();
+ String fieldName = fieldNameFor(columnDefinition);
+ switch (sqlType) {
+ case Types.NULL:
+ log.debug("JDBC type 'NULL' not currently supported for column '{}'", fieldName);
+ return null;
+
+ case Types.BOOLEAN:
+ builder.field(fieldName, SchemaBuilder.bool().build());
+ break;
+
+ case Types.BIT:
+ builder.field(fieldName, SchemaBuilder.int8().build());
+ break;
+
+ case Types.TINYINT:
+ if (columnDefinition.isSignedNumber()) {
+ builder.field(fieldName, SchemaBuilder.int8().build());
+ } else {
+ builder.field(fieldName, SchemaBuilder.int32().build());
+ }
+ break;
+
+ case Types.SMALLINT:
+ builder.field(fieldName, SchemaBuilder.int32().build());
+ break;
+
+ case Types.INTEGER:
+ if (columnDefinition.isSignedNumber()) {
+ builder.field(fieldName, SchemaBuilder.int32().build());
+ } else {
+ builder.field(fieldName, SchemaBuilder.int64().build());
+ }
+ break;
+
+ case Types.BIGINT:
+ builder.field(fieldName, SchemaBuilder.int64().build());
+ break;
+
+ case Types.REAL:
+ builder.field(fieldName, SchemaBuilder.float32().build());
+ break;
+
+ case Types.FLOAT:
+ case Types.DOUBLE:
+ builder.field(fieldName, SchemaBuilder.float64().build());
+ break;
+ case Types.DECIMAL:
+ scale = decimalScale(columnDefinition);
+ SchemaBuilder fieldBuilder = Decimal.builder(scale);
+ if (optional) {
+ fieldBuilder.optional();
+ }
+ builder.field(fieldName, fieldBuilder.build());
+ break;
+
+ case Types.NUMERIC:
+ if (numericMapping == NumericMapping.PRECISION_ONLY) {
+ log.debug("NUMERIC with precision: '{}' and scale: '{}'", precision, scale);
+ if (scale == 0 && precision <= MAX_INTEGER_TYPE_PRECISION) { // integer
+ builder.field(fieldName, integerSchema(optional, precision));
+ break;
+ }
+ } else if (numericMapping == NumericMapping.BEST_FIT) {
+ log.debug("NUMERIC with precision: '{}' and scale: '{}'", precision, scale);
+ if (precision <= MAX_INTEGER_TYPE_PRECISION) {
+ if (scale < 1 && scale >= NUMERIC_TYPE_SCALE_LOW) {
+ builder.field(fieldName, integerSchema(optional, precision));
+ break;
+ } else if (scale > 0) {
+ builder.field(fieldName, SchemaBuilder.float64().build());
+ break;
+ }
+ }
+ } else if (numericMapping == 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.field(fieldName, integerSchema(optional, precision));
+ break;
+ }
+ } else if (scale > 0) {
+ builder.field(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:
+ builder.field(fieldName, SchemaBuilder.string().build());
+ break;
+
+ case Types.BINARY:
+ case Types.BLOB:
+ case Types.VARBINARY:
+ case Types.LONGVARBINARY:
+ builder.field(fieldName, SchemaBuilder.bytes().build());
+ break;
+
+ case Types.DATE:
+ SchemaBuilder dateSchemaBuilder = Date.builder();
+ builder.field(fieldName, dateSchemaBuilder.build());
+ break;
+
+ case Types.TIME:
+ SchemaBuilder timeSchemaBuilder = Time.builder();
+ builder.field(fieldName, timeSchemaBuilder.build());
+ break;
+
+ case Types.TIMESTAMP:
+ SchemaBuilder tsSchemaBuilder = io.openmessaging.connector.api.data.logical.Timestamp.builder();
+ builder.field(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 supported", sqlType, columnDefinition.typeName());
+ return null;
+ }
+ return fieldName;
+ }
+
+ /**
+ * Determine the name of the field. By default this is the column alias or name.
+ */
+ protected String fieldNameFor(ColumnDefinition columnDefinition) {
+ return columnDefinition.id().aliasOrName();
+ }
+
+ 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();
+ }
+ if (optional) {
+ schema.setOptional(true);
+ }
+ return schema;
+ }
+
+}
\ No newline at end of file
diff --git a/connectors/rocketmq-connect-jdbc/src/main/java/org/apache/rocketmq/connect/jdbc/converter/JdbcColumnConverter.java b/connectors/rocketmq-connect-jdbc/src/main/java/org/apache/rocketmq/connect/jdbc/converter/JdbcColumnConverter.java
new file mode 100644
index 000000000..eeb81ad41
--- /dev/null
+++ b/connectors/rocketmq-connect-jdbc/src/main/java/org/apache/rocketmq/connect/jdbc/converter/JdbcColumnConverter.java
@@ -0,0 +1,47 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.rocketmq.connect.jdbc.converter;
+
+import io.openmessaging.connector.api.data.SchemaBuilder;
+import java.io.IOException;
+import java.sql.ResultSet;
+import java.sql.SQLException;
+import org.apache.rocketmq.connect.jdbc.schema.column.ColumnDefinition;
+
+public interface JdbcColumnConverter {
+
+ /**
+ * Convert column to connect schema
+ *
+ * @param columnDefinition
+ * @param schemaBuilder
+ * @return
+ */
+ String convertToConnectFieldSchema(ColumnDefinition columnDefinition, SchemaBuilder schemaBuilder);
+
+ /**
+ * Convert column data to connect value
+ * @param rs
+ * @param columnDefinition
+ * @param columnNumber
+ * @return
+ * @throws SQLException
+ * @throws IOException
+ */
+ Object convertToConnectFieldValue(ResultSet rs, ColumnDefinition columnDefinition, int columnNumber) 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/DatabaseDialect.java b/connectors/rocketmq-connect-jdbc/src/main/java/org/apache/rocketmq/connect/jdbc/dialect/DatabaseDialect.java
new file mode 100644
index 000000000..ecd791b21
--- /dev/null
+++ b/connectors/rocketmq-connect-jdbc/src/main/java/org/apache/rocketmq/connect/jdbc/dialect/DatabaseDialect.java
@@ -0,0 +1,307 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.rocketmq.connect.jdbc.dialect;
+
+import java.sql.Connection;
+import java.sql.PreparedStatement;
+import java.sql.ResultSet;
+import java.sql.ResultSetMetaData;
+import java.sql.SQLException;
+import java.sql.Statement;
+import java.sql.Timestamp;
+import java.util.Calendar;
+import java.util.Collection;
+import java.util.List;
+import java.util.Map;
+import java.util.Optional;
+import org.apache.rocketmq.connect.jdbc.binder.JdbcRecordBinder;
+import org.apache.rocketmq.connect.jdbc.converter.JdbcColumnConverter;
+import org.apache.rocketmq.connect.jdbc.connection.ConnectionProvider;
+import org.apache.rocketmq.connect.jdbc.schema.column.ColumnDefinition;
+import org.apache.rocketmq.connect.jdbc.schema.column.ColumnId;
+import org.apache.rocketmq.connect.jdbc.schema.table.TableDefinition;
+import org.apache.rocketmq.connect.jdbc.schema.table.TableId;
+import org.apache.rocketmq.connect.jdbc.sink.JdbcSinkConfig;
+import org.apache.rocketmq.connect.jdbc.sink.metadata.FieldsMetadata;
+import org.apache.rocketmq.connect.jdbc.sink.metadata.SchemaPair;
+import org.apache.rocketmq.connect.jdbc.sink.metadata.SinkRecordField;
+import org.apache.rocketmq.connect.jdbc.source.TimestampIncrementingCriteria;
+import org.apache.rocketmq.connect.jdbc.util.ExpressionBuilder;
+import org.apache.rocketmq.connect.jdbc.util.IdentifierRules;
+
+import static java.util.Objects.nonNull;
+
+/**
+ * database dialect
+ */
+public interface DatabaseDialect extends ConnectionProvider {
+ /**
+ * dialect name
+ *
+ * @return
+ */
+ String name();
+
+ JdbcColumnConverter createJdbcColumnConverter();
+
+ JdbcRecordBinder getJdbcRecordBinder(PreparedStatement statement, JdbcSinkConfig.PrimaryKeyMode pkMode,
+ SchemaPair schemaPair, FieldsMetadata fieldsMetadata, TableDefinition tableDefinition,
+ JdbcSinkConfig.InsertMode insertMode);
+
+ default PreparedStatement createPreparedStatement(Connection db, String query,
+ int batchMaxRows) throws SQLException {
+ PreparedStatement stmt = db.prepareStatement(query);
+ if (batchMaxRows > 0) {
+ stmt.setFetchSize(batchMaxRows);
+ }
+ return stmt;
+ }
+
+ default PreparedStatement createPreparedStatement(Connection db, String query) throws SQLException {
+ return createPreparedStatement(db, query, 0);
+ }
+
+ default Optional executeUpdates(PreparedStatement updatePreparedStatement) throws SQLException {
+ Optional 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) {
+ throw new SQLException(e);
+ }
+ }
+ return count;
+ }
+
+ default Optional executeDeletes(PreparedStatement deletePreparedStatement) throws SQLException {
+ Optional totalDeleteCount = Optional.empty();
+ if (nonNull(deletePreparedStatement)) {
+ try {
+ for (int deleteCount : deletePreparedStatement.executeBatch()) {
+ if (deleteCount != Statement.SUCCESS_NO_INFO) {
+ totalDeleteCount = totalDeleteCount.isPresent()
+ ? totalDeleteCount.map(total -> total + deleteCount)
+ : Optional.of((long) deleteCount);
+ }
+ }
+ } catch (SQLException e) {
+ throw new SQLException(e);
+ }
+ }
+ return totalDeleteCount;
+ }
+
+ /**
+ * parse to Table Id
+ *
+ * @param fqn
+ * @return
+ */
+ default TableId parseTableNameToTableId(String fqn) {
+ List 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));
+ }
+ if (useCatalog()) {
+ return new TableId(parts.get(0), null, parts.get(1));
+ }
+ return new TableId(null, parts.get(0), parts.get(1));
+ }
+
+ default boolean useCatalog() {
+ return true;
+ }
+
+ /**
+ * Get the identifier rules for this database.
+ *
+ * @return the identifier rules
+ */
+ IdentifierRules identifierRules();
+
+ ExpressionBuilder expressionBuilder();
+
+ List listTableIds(Connection connection) throws SQLException;
+
+ boolean tableExists(Connection connection, TableId tableId) throws SQLException;
+
+ TableDefinition describeTable(Connection connection, TableId tableId) throws SQLException;
+
+ Map describeColumns(Connection connection, String tablePattern,
+ String columnPattern) throws SQLException;
+
+ Map describeColumns(Connection connection, String catalogPattern, String schemaPattern,
+ String tablePattern, String columnPattern) throws SQLException;
+
+ Map describeColumns(Connection conn, TableId tableId,
+ ResultSetMetaData rsMetadata) throws SQLException;
+
+ Map describeColumnsByQuerying(Connection connection,
+ TableId tableId) throws SQLException;
+
+ default void executeSchemaChangeStatements(Connection connection, List statements) throws SQLException {
+ try (Statement statement = connection.createStatement()) {
+ for (String ddl : statements) {
+ statement.executeUpdate(ddl);
+ }
+ }
+ }
+
+ // Insert statement
+ String getInsertSql(JdbcSinkConfig config, FieldsMetadata fieldsMetadata, TableId tableId);
+
+ default String buildInsertStatement(TableId table, Collection keyColumns,
+ Collection 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();
+ }
+
+ default String buildUpdateStatement(TableId table, Collection keyColumns,
+ Collection 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();
+ }
+
+ default String buildUpsertQueryStatement(TableId table, Collection keyColumns,
+ Collection nonKeyColumns) {
+ throw new UnsupportedOperationException();
+ }
+
+ // build delete statement
+ String getDeleteSql(JdbcSinkConfig config, FieldsMetadata fieldsMetadata, TableId tableId);
+
+ default String buildDeleteStatement(TableId table, Collection 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();
+ }
+
+ // drop table
+ default String buildDropTableStatement(TableId table, boolean ifExists, boolean cascade) {
+ ExpressionBuilder builder = expressionBuilder();
+ builder.append("DROP TABLE ");
+ builder.append(table);
+ if (ifExists) {
+ builder.append(" IF EXISTS");
+ }
+ if (cascade) {
+ builder.append(" CASCADE");
+ }
+ return builder.toString();
+ }
+
+ // create table
+ String buildCreateTableStatement(TableId table, Collection fields);
+
+ // alter table
+ List buildAlterTable(TableId table, Collection fields);
+
+ default void validateColumnTypes(ResultSetMetaData rsMetadata,
+ List columns) throws io.openmessaging.connector.api.errors.ConnectException {
+ // do nothing
+ }
+
+ default String buildSelectTableMode() {
+ return "SELECT * FROM ";
+ }
+
+ default void buildSelectTable(ExpressionBuilder builder, TableId tableId) {
+ String mode = buildSelectTableMode();
+ builder.append(mode).append(tableId);
+ }
+
+ TimestampIncrementingCriteria criteriaFor(ColumnId incrementingColumn, List timestampColumns);
+
+ default Long getMinTimestampValue(Connection con, String tableOrQuery,
+ List 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;
+ }
+
+ Timestamp currentTimeOnDB(Connection connection, Calendar cal) throws SQLException;
+}
\ No newline at end of file
diff --git a/connectors/rocketmq-connect-jdbc/rocketmq-connect-jdbc-openmldb/src/main/java/org/apache/rocketmq/connect/jdbc/openmldb/sink/OpenMLDBJdbcSinkConnector.java b/connectors/rocketmq-connect-jdbc/src/main/java/org/apache/rocketmq/connect/jdbc/dialect/DatabaseDialectFactory.java
similarity index 66%
rename from connectors/rocketmq-connect-jdbc/rocketmq-connect-jdbc-openmldb/src/main/java/org/apache/rocketmq/connect/jdbc/openmldb/sink/OpenMLDBJdbcSinkConnector.java
rename to connectors/rocketmq-connect-jdbc/src/main/java/org/apache/rocketmq/connect/jdbc/dialect/DatabaseDialectFactory.java
index 612b5ae8c..9fd0bb3e1 100644
--- a/connectors/rocketmq-connect-jdbc/rocketmq-connect-jdbc-openmldb/src/main/java/org/apache/rocketmq/connect/jdbc/openmldb/sink/OpenMLDBJdbcSinkConnector.java
+++ b/connectors/rocketmq-connect-jdbc/src/main/java/org/apache/rocketmq/connect/jdbc/dialect/DatabaseDialectFactory.java
@@ -14,17 +14,14 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package org.apache.rocketmq.connect.jdbc.openmldb.sink;
+package org.apache.rocketmq.connect.jdbc.dialect;
-import io.openmessaging.connector.api.component.task.Task;
-import org.apache.rocketmq.connect.jdbc.sink.BaseSinkConnector;
+import java.util.Set;
+import org.apache.rocketmq.connect.jdbc.config.AbstractConfig;
-/**
- * OpenMLDB jdbc sink connector
- */
-public class OpenMLDBJdbcSinkConnector extends BaseSinkConnector {
- @Override
- public Class extends Task> taskClass() {
- return OpenMLDBJdbcSinkTask.class;
- }
-}
+public interface DatabaseDialectFactory {
+
+ Set subProtocols();
+
+ DatabaseDialect create(AbstractConfig config);
+}
\ No newline at end of file
diff --git a/connectors/rocketmq-connect-jdbc/src/main/java/org/apache/rocketmq/connect/jdbc/dialect/DatabaseDialectLoader.java b/connectors/rocketmq-connect-jdbc/src/main/java/org/apache/rocketmq/connect/jdbc/dialect/DatabaseDialectLoader.java
new file mode 100644
index 000000000..db9362747
--- /dev/null
+++ b/connectors/rocketmq-connect-jdbc/src/main/java/org/apache/rocketmq/connect/jdbc/dialect/DatabaseDialectLoader.java
@@ -0,0 +1,128 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.rocketmq.connect.jdbc.dialect;
+
+import io.openmessaging.connector.api.errors.ConnectException;
+import java.security.AccessController;
+import java.security.PrivilegedAction;
+import java.util.HashSet;
+import java.util.List;
+import java.util.ServiceConfigurationError;
+import java.util.ServiceLoader;
+import java.util.Set;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+import java.util.stream.Collectors;
+import org.apache.rocketmq.connect.jdbc.config.AbstractConfig;
+import org.apache.rocketmq.connect.jdbc.util.JdbcUrlInfo;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+public final class DatabaseDialectLoader {
+
+ private static final Logger LOG = LoggerFactory.getLogger(DatabaseDialectLoader.class);
+ private static final Pattern PROTOCOL_PATTERN = Pattern.compile("jdbc:([^:]+):(.*)");
+ private static final Set DATABASE_DIALECT_FACTORY = new HashSet<>();
+
+ static {
+ DATABASE_DIALECT_FACTORY.addAll(loadDatabaseDialectFactories());
+ }
+
+ /**
+ * Get database dialect factory
+ *
+ * @param config
+ * @return
+ */
+ public static DatabaseDialect getDatabaseDialect(AbstractConfig config) {
+ String url = config.getConnectionDbUrl();
+ assert url != null;
+ JdbcUrlInfo jdbcUrlInfo = extractJdbcUrlInfo(url);
+ final List matchingFactories =
+ DATABASE_DIALECT_FACTORY.stream().filter(f -> f.subProtocols().contains(jdbcUrlInfo.subprotocol())).collect(Collectors.toList());
+ if (matchingFactories.isEmpty()) {
+ throw new ConnectException(String.format("Cannot get database dialect by url [%s]", url));
+ }
+ return matchingFactories.get(0).create(config);
+ }
+
+ private static Set loadDatabaseDialectFactories() {
+
+ try {
+ ClassLoader cl = DatabaseDialectFactory.class.getClassLoader();
+ return AccessController.doPrivileged(new PrivilegedAction>() {
+ public Set run() {
+ final Set result = new HashSet<>();
+ ServiceLoader databaseDialectFactories = ServiceLoader.load(DatabaseDialectFactory.class, cl);
+ databaseDialectFactories.iterator().forEachRemaining(result::add);
+ return result;
+ }
+ });
+ } catch (ServiceConfigurationError e) {
+ LOG.error("Could not load service provider for jdbc dialects factory.", e);
+ throw new ConnectException("Could not load service provider for jdbc dialects factory.", e);
+ }
+ }
+
+ static JdbcUrlInfo extractJdbcUrlInfo(final String url) {
+ LOG.info("Validating JDBC URL.");
+ Matcher matcher = PROTOCOL_PATTERN.matcher(url);
+ if (matcher.matches()) {
+ LOG.info("Validated JDBC URL.");
+ return new JdbcUrlDetails(matcher.group(1), matcher.group(2), url);
+ }
+ LOG.error("Not a valid JDBC URL: " + url);
+ throw new ConnectException("Not a valid JDBC URL: " + url);
+ }
+
+ /**
+ * Jdbc url details
+ */
+ 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;
+ }
+
+ @Override
+ public String subprotocol() {
+ return subprotocol;
+ }
+
+ @Override
+ public String subname() {
+ return subname;
+ }
+
+ @Override
+ public String url() {
+ return url;
+ }
+
+ @Override
+ public String toString() {
+ return "JDBC subprotocol '" + subprotocol + "' and source '" + url + "'";
+ }
+ }
+
+}
diff --git a/connectors/rocketmq-connect-jdbc/rocketmq-connect-jdbc-core/src/main/java/org/apache/rocketmq/connect/jdbc/dialect/GenericDatabaseDialect.java b/connectors/rocketmq-connect-jdbc/src/main/java/org/apache/rocketmq/connect/jdbc/dialect/GenericDatabaseDialect.java
similarity index 50%
rename from connectors/rocketmq-connect-jdbc/rocketmq-connect-jdbc-core/src/main/java/org/apache/rocketmq/connect/jdbc/dialect/GenericDatabaseDialect.java
rename to connectors/rocketmq-connect-jdbc/src/main/java/org/apache/rocketmq/connect/jdbc/dialect/GenericDatabaseDialect.java
index 3c84ffc0c..0923a441d 100644
--- a/connectors/rocketmq-connect-jdbc/rocketmq-connect-jdbc-core/src/main/java/org/apache/rocketmq/connect/jdbc/dialect/GenericDatabaseDialect.java
+++ b/connectors/rocketmq-connect-jdbc/src/main/java/org/apache/rocketmq/connect/jdbc/dialect/GenericDatabaseDialect.java
@@ -17,42 +17,8 @@
package org.apache.rocketmq.connect.jdbc.dialect;
import io.openmessaging.connector.api.data.FieldType;
-import io.openmessaging.connector.api.data.Schema;
-import io.openmessaging.connector.api.data.SchemaBuilder;
-import io.openmessaging.connector.api.data.logical.Date;
-import io.openmessaging.connector.api.data.logical.Decimal;
-import io.openmessaging.connector.api.data.logical.Time;
import io.openmessaging.connector.api.errors.ConnectException;
-import org.apache.rocketmq.connect.jdbc.common.DebeziumTimeTypes;
-import org.apache.rocketmq.connect.jdbc.config.AbstractConfig;
-import org.apache.rocketmq.connect.jdbc.schema.column.ColumnDefAdjuster;
-import org.apache.rocketmq.connect.jdbc.schema.column.ColumnDefinition;
-import org.apache.rocketmq.connect.jdbc.schema.column.ColumnId;
-import org.apache.rocketmq.connect.jdbc.schema.table.TableDefinition;
-import org.apache.rocketmq.connect.jdbc.schema.table.TableId;
-import org.apache.rocketmq.connect.jdbc.sink.JdbcSinkConfig;
-import org.apache.rocketmq.connect.jdbc.sink.metadata.FieldsMetadata;
-import org.apache.rocketmq.connect.jdbc.sink.metadata.SchemaPair;
-import org.apache.rocketmq.connect.jdbc.sink.metadata.SinkRecordField;
-import org.apache.rocketmq.connect.jdbc.source.JdbcSourceConfig;
-import org.apache.rocketmq.connect.jdbc.source.TimestampIncrementingCriteria;
-import org.apache.rocketmq.connect.jdbc.source.metadata.ColumnMapping;
-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;
-
-import java.io.IOException;
-import java.math.BigDecimal;
-import java.net.URL;
import java.nio.ByteBuffer;
-import java.sql.Blob;
-import java.sql.Clob;
import java.sql.Connection;
import java.sql.DatabaseMetaData;
import java.sql.DriverManager;
@@ -60,11 +26,8 @@
import java.sql.ResultSet;
import java.sql.ResultSetMetaData;
import java.sql.SQLException;
-import java.sql.SQLXML;
import java.sql.Statement;
import java.sql.Timestamp;
-import java.sql.Types;
-import java.time.ZoneOffset;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Calendar;
@@ -76,6 +39,7 @@
import java.util.List;
import java.util.Locale;
import java.util.Map;
+import java.util.Objects;
import java.util.Properties;
import java.util.Queue;
import java.util.Set;
@@ -83,28 +47,37 @@
import java.util.concurrent.ConcurrentLinkedQueue;
import java.util.concurrent.atomic.AtomicReference;
import java.util.stream.Collectors;
+import org.apache.rocketmq.connect.jdbc.binder.DefaultJdbcRecordBinder;
+import org.apache.rocketmq.connect.jdbc.binder.JdbcRecordBinder;
+import org.apache.rocketmq.connect.jdbc.config.AbstractConfig;
+import org.apache.rocketmq.connect.jdbc.converter.DefaultColumnConverter;
+import org.apache.rocketmq.connect.jdbc.converter.JdbcColumnConverter;
+import org.apache.rocketmq.connect.jdbc.util.ColumnDefAdjuster;
+import org.apache.rocketmq.connect.jdbc.schema.column.ColumnDefinition;
+import org.apache.rocketmq.connect.jdbc.schema.column.ColumnId;
+import org.apache.rocketmq.connect.jdbc.schema.table.TableDefinition;
+import org.apache.rocketmq.connect.jdbc.schema.table.TableId;
+import org.apache.rocketmq.connect.jdbc.sink.JdbcSinkConfig;
+import org.apache.rocketmq.connect.jdbc.sink.metadata.FieldsMetadata;
+import org.apache.rocketmq.connect.jdbc.sink.metadata.SchemaPair;
+import org.apache.rocketmq.connect.jdbc.sink.metadata.SinkRecordField;
+import org.apache.rocketmq.connect.jdbc.source.JdbcSourceConfig;
+import org.apache.rocketmq.connect.jdbc.source.TimestampIncrementingCriteria;
+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;
/**
* generic database dialect
*/
-public class GenericDatabaseDialect implements DatabaseDialect {
+public abstract class GenericDatabaseDialect implements DatabaseDialect {
private static final Logger log = LoggerFactory.getLogger(GenericDatabaseDialect.class);
- 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;
-
- /**
- * The provider for GenericDatabaseDialect
- */
- public static class DialectName {
- public static String generateDialectName(Class clazz) {
- return clazz.getSimpleName().replace("DatabaseDialect", "");
- }
- }
-
protected AbstractConfig config;
/**
* Whether to map {@code NUMERIC} JDBC types by precision.
@@ -114,12 +87,12 @@ public static String generateDialectName(Class clazz) {
protected String schemaPattern;
protected Set tableTypes;
protected String jdbcUrl;
- private QuoteMethod quoteSqlIdentifiers = QuoteMethod.ALWAYS;
+ private final QuoteMethod quoteSqlIdentifiers;
private IdentifierRules defaultIdentifierRules;
private AtomicReference identifierRules = new AtomicReference<>();
private Queue connections = new ConcurrentLinkedQueue<>();
private volatile JdbcDriverInfo jdbcDriverInfo;
- private int batchMaxRows;
+
private TimeZone timeZone;
public GenericDatabaseDialect(AbstractConfig config) {
@@ -137,7 +110,6 @@ protected GenericDatabaseDialect(AbstractConfig config, IdentifierRules defaultI
tableTypes = sinkConfig.tableTypeNames();
quoteSqlIdentifiers = QuoteMethod.get(config.getQuoteSqlIdentifiers());
mapNumerics = NumericMapping.NONE;
- batchMaxRows = 0;
timeZone = sinkConfig.getTimeZone();
} else {
JdbcSourceConfig sourceConfig = (JdbcSourceConfig) config;
@@ -146,16 +118,10 @@ protected GenericDatabaseDialect(AbstractConfig config, IdentifierRules defaultI
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());
- }
-
/**
* init jdbc connection
*
@@ -176,8 +142,9 @@ public Connection getConnection() throws SQLException {
properties = addConnectionProperties(properties);
DriverManager.setLoginTimeout(40);
Connection connection = DriverManager.getConnection(jdbcUrl, properties);
- if (jdbcDriverInfo == null) {
- jdbcDriverInfo = createJdbcDriverInfo(connection);
+ // init jdbc driver info
+ if (Objects.isNull(jdbcDriverInfo)){
+ jdbcDriverInfo = createdJdbcDriverInfo(connection);
}
connections.add(connection);
return connection;
@@ -207,7 +174,7 @@ public void close() {
@Override
public boolean isConnectionValid(Connection connection, int timeout) throws SQLException {
- if (jdbcDriverInfo().jdbcMajorVersion() >= 4) {
+ if (jdbcDriverInfo.jdbcMajorVersion() >= 4) {
return connection.isValid(timeout);
}
String query = checkConnectionQuery();
@@ -239,76 +206,18 @@ protected String checkConnectionQuery() {
/**
* Get jdbc driver info
- *
* @return
*/
- 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 {
+ protected JdbcDriverInfo createdJdbcDriverInfo(Connection connection) throws SQLException {
DatabaseMetaData metadata = connection.getMetaData();
- return new JdbcDriverInfo(
- metadata.getJDBCMajorVersion(),
- metadata.getJDBCMinorVersion(),
- metadata.getDriverName(),
- metadata.getDatabaseProductName(),
- metadata.getDatabaseProductVersion()
+ this.jdbcDriverInfo = new JdbcDriverInfo(
+ metadata.getJDBCMajorVersion(),
+ metadata.getJDBCMinorVersion(),
+ metadata.getDriverName(),
+ metadata.getDatabaseProductName(),
+ metadata.getDatabaseProductVersion()
);
- }
-
- @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 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;
+ return jdbcDriverInfo;
}
/**
@@ -318,7 +227,6 @@ protected boolean useCatalog() {
protected String catalogPattern() {
return catalogPattern;
}
-
/**
* schema config
* @return
@@ -328,7 +236,7 @@ protected String schemaPattern() {
}
@Override
- public List tableIds(Connection conn) throws SQLException {
+ public List listTableIds(Connection conn) throws SQLException {
DatabaseMetaData metadata = conn.getMetaData();
String[] tableTypes = tableTypes(metadata, this.tableTypes);
String tableTypeDisplay = displayableTableTypes(tableTypes, ", ");
@@ -422,7 +330,7 @@ public IdentifierRules identifierRules() {
@Override
public ExpressionBuilder expressionBuilder() {
return identifierRules().expressionBuilder()
- .setQuoteIdentifiers(quoteSqlIdentifiers);
+ .setQuoteIdentifiers(quoteSqlIdentifiers);
}
/**
@@ -504,7 +412,7 @@ public Map describeColumns(
String columnPattern
) throws SQLException {
//if the table pattern is fqn, then just use the actual table name
- TableId tableId = parseToTableId(tablePattern);
+ TableId tableId = parseTableNameToTableId(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);
@@ -833,608 +741,18 @@ protected ColumnDefinition columnDefinition(
);
}
- /**
- * 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,
- SchemaBuilder schemaBuilder
- ) {
- return addFieldToSchema(columnDefn, schemaBuilder, fieldNameFor(columnDefn), columnDefn.type(),
- columnDefn.isOptional()
- );
+ public JdbcColumnConverter createJdbcColumnConverter() {
+ return new DefaultColumnConverter(mapNumerics, jdbcDriverInfo.jdbcVersionAtLeast(4, 0), timeZone);
}
- /**
- * add field to schema
- *
- * @param columnDefn
- * @param builder
- * @param fieldName
- * @param sqlType
- * @param optional
- * @return
- */
- @SuppressWarnings("fallthrough")
- protected String addFieldToSchema(
- final ColumnDefinition columnDefn,
- final SchemaBuilder builder,
- final String fieldName,
- 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.field(fieldName, SchemaBuilder.bool().build());
- break;
- }
-
- // ints <= 8 bits
- case Types.BIT: {
- builder.field(fieldName, SchemaBuilder.int8().build());
- break;
- }
-
- case Types.TINYINT: {
- if (columnDefn.isSignedNumber()) {
- builder.field(fieldName, SchemaBuilder.int8().build());
- } else {
- builder.field(fieldName, SchemaBuilder.int32().build());
- }
- break;
- }
-
- // 16 bit ints
- case Types.SMALLINT: {
- builder.field(fieldName, SchemaBuilder.int32().build());
- break;
- }
-
- // 32 bit ints
- case Types.INTEGER: {
- if (columnDefn.isSignedNumber()) {
- builder.field(fieldName, SchemaBuilder.int32().build());
- } else {
- builder.field(fieldName, SchemaBuilder.int64().build());
- }
- break;
- }
-
- // 64 bit ints
- case Types.BIGINT: {
- builder.field(fieldName, SchemaBuilder.int64().build());
- break;
- }
-
- // REAL is a single precision floating point value, i.e. a Java float
- case Types.REAL: {
- builder.field(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:
- builder.field(fieldName, SchemaBuilder.float64().build());
- break;
- case Types.DECIMAL:
- scale = decimalScale(columnDefn);
- SchemaBuilder fieldBuilder = Decimal.builder(scale);
- if (optional) {
- fieldBuilder.optional();
- }
- builder.field(fieldName, fieldBuilder.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.field(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.field(fieldName, integerSchema(optional, precision));
- break;
- } else if (scale > 0) { // floating point - use double in all cases
- builder.field(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.field(fieldName, integerSchema(optional, precision));
- break;
- }
- } else if (scale > 0) { // floating point - use double in all cases
- builder.field(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.field(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.field(fieldName, SchemaBuilder.bytes().build());
- break;
- }
-
- // Date is day + moth + year
- case Types.DATE: {
- SchemaBuilder dateSchemaBuilder = Date.builder();
- builder.field(fieldName, dateSchemaBuilder.build());
- break;
- }
-
- // Time is a time of day -- hour, minute, seconds, nanoseconds
- case Types.TIME: {
- SchemaBuilder timeSchemaBuilder = Time.builder();
- builder.field(fieldName, timeSchemaBuilder.build());
- break;
- }
-
- // Timestamp is a date + time
- case Types.TIMESTAMP: {
- SchemaBuilder tsSchemaBuilder = io.openmessaging.connector.api.data.logical.Timestamp.builder();
- builder.field(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;
- }
- }
- 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 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)
- );
+ public JdbcRecordBinder getJdbcRecordBinder(PreparedStatement statement, JdbcSinkConfig.PrimaryKeyMode pkMode,
+ SchemaPair schemaPair, FieldsMetadata fieldsMetadata, TableDefinition tableDefinition,
+ JdbcSinkConfig.InsertMode insertMode) {
+ return new DefaultJdbcRecordBinder(statement, tableDefinition, fieldsMetadata, schemaPair, pkMode, insertMode, timeZone);
}
- /**
- * column converter
- *
- * @param mapping
- * @param defn
- * @param col
- * @param isJdbc4
- * @return
- */
- 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;
- }
-
- 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 keyColumns,
- Collection 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 keyColumns,
- Collection 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
- public String buildUpsertQueryStatement(
- TableId table,
- Collection keyColumns,
- Collection nonKeyColumns
- ) {
- throw new UnsupportedOperationException();
- }
-
- @Override
- public String buildDeleteStatement(
- TableId table,
- Collection 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();
- }
@Override
public String getInsertSql(JdbcSinkConfig config, FieldsMetadata fieldsMetadata, TableId tableId) {
@@ -1463,7 +781,7 @@ public String getInsertSql(JdbcSinkConfig config, FieldsMetadata fieldsMetadata,
throw new ConnectException(String.format(
"Write to table '%s' in UPSERT mode is not supported with the %s dialect.",
tableId,
- name()
+ name()
));
}
case UPDATE:
@@ -1495,7 +813,7 @@ public String getDeleteSql(JdbcSinkConfig config, FieldsMetadata fieldsMetadata,
throw new ConnectException(String.format(
"Deletes to table '%s' are not supported with the %s dialect.",
tableId,
- name()
+ name()
));
}
break;
@@ -1506,242 +824,6 @@ public String getDeleteSql(JdbcSinkConfig config, FieldsMetadata fieldsMetadata,
return sql;
}
- /**
- * table mode
- *
- * @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);
- if (!bound) {
- bound = maybeBindDebeziumLogical(statement, index, schema, value);
- }
- 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
- ) throws SQLException {
- if (schema.getName() != null) {
- switch (schema.getName()) {
- case Decimal.LOGICAL_NAME:
- statement.setBigDecimal(index, (BigDecimal) value);
- return true;
- case Date.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 Time.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 io.openmessaging.connector.api.data.logical.Timestamp.LOGICAL_NAME:
- Timestamp timestamp;
- if (value instanceof java.util.Date) {
- timestamp = new Timestamp(((java.util.Date) value).getTime());
- } else {
- timestamp = new Timestamp((long) value);
- }
- statement.setTimestamp(
- index, timestamp,
- DateTimeUtils.getTimeZoneCalendar(timeZone)
- );
- return true;
- default:
- return false;
- }
- }
- return false;
- }
-
-
- @Override
- public TimestampIncrementingCriteria criteriaFor(ColumnId incrementingColumn, List timestampColumns) {
- return new TimestampIncrementingCriteria(
- incrementingColumn, timestampColumns, timeZone);
- }
-
- @Override
- public Long getMinTimestampValue(Connection con, String tableOrQuery, List 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 maybeBindDebeziumLogical(
- PreparedStatement statement,
- int index,
- Schema schema,
- Object value
- ) throws SQLException {
- return DebeziumTimeTypes.maybeBindDebeziumLogical(statement, index, schema, value, timeZone);
- }
-
- protected boolean maybeBindPrimitive(
- PreparedStatement statement,
- int index,
- Schema schema,
- Object value
- ) throws SQLException {
- switch (schema.getFieldType()) {
- case INT8:
- statement.setByte(index, Byte.parseByte(value.toString()));
- break;
- case INT32:
- statement.setInt(index, Integer.parseInt(value.toString()));
- break;
- case INT64:
- statement.setLong(index, Long.parseLong(value.toString()));
- break;
- case FLOAT32:
- statement.setFloat(index, Float.parseFloat(value.toString()));
- break;
- case FLOAT64:
- statement.setDouble(index, Double.parseDouble(value.toString()));
- break;
- case BOOLEAN:
- statement.setBoolean(index, Boolean.parseBoolean(value.toString()));
- 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;
- }
-
/**
* create table statement
*
@@ -1773,33 +855,8 @@ public String buildCreateTableStatement(
return builder.toString();
}
- /**
- * Drop table statement
- *
- * @param table
- * @param options
- * @return
- */
- @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();
- }
-
/**
* alter table statement
- *
* @param table
* @param fields
* @return
@@ -1829,11 +886,9 @@ public List buildAlterTable(
}
@Override
- public void validateColumnTypes(
- ResultSetMetaData rsMetadata,
- List columns
- ) throws io.openmessaging.connector.api.errors.ConnectException {
- // No-op
+ public TimestampIncrementingCriteria criteriaFor(ColumnId incrementingColumn, List timestampColumns) {
+ return new TimestampIncrementingCriteria(
+ incrementingColumn, timestampColumns, timeZone);
}
protected List extractPrimaryKeyFieldNames(Collection fields) {
@@ -1922,34 +977,19 @@ protected void formatColumnValue(
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()
+ "%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) {
- return url.replaceAll("(?i)([?&]([^=&]*)password([^=&]*)=)[^&]*", "$1****");
- }
-
- @Override
- public String identifier() {
- return name() + " database " + sanitizedUrl(jdbcUrl);
+ private Collection asColumns(Collection names, TableId tableId) {
+ return names.stream()
+ .map(name -> new ColumnId(tableId, name))
+ .collect(Collectors.toList());
}
@Override
public String toString() {
return name();
}
-
-
- private Collection asColumns(Collection names, TableId tableId) {
- return names.stream()
- .map(name -> new ColumnId(tableId, name))
- .collect(Collectors.toList());
- }
}
diff --git a/connectors/rocketmq-connect-jdbc/rocketmq-connect-jdbc-mysql/src/main/java/org/apache/rocketmq/connect/jdbc/mysql/dialect/MySqlDatabaseDialect.java b/connectors/rocketmq-connect-jdbc/src/main/java/org/apache/rocketmq/connect/jdbc/dialect/mysql/MySqlDatabaseDialect.java
similarity index 77%
rename from connectors/rocketmq-connect-jdbc/rocketmq-connect-jdbc-mysql/src/main/java/org/apache/rocketmq/connect/jdbc/mysql/dialect/MySqlDatabaseDialect.java
rename to connectors/rocketmq-connect-jdbc/src/main/java/org/apache/rocketmq/connect/jdbc/dialect/mysql/MySqlDatabaseDialect.java
index da03bb4be..7ebac1276 100644
--- a/connectors/rocketmq-connect-jdbc/rocketmq-connect-jdbc-mysql/src/main/java/org/apache/rocketmq/connect/jdbc/mysql/dialect/MySqlDatabaseDialect.java
+++ b/connectors/rocketmq-connect-jdbc/src/main/java/org/apache/rocketmq/connect/jdbc/dialect/mysql/MySqlDatabaseDialect.java
@@ -14,8 +14,13 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package org.apache.rocketmq.connect.jdbc.mysql.dialect;
+package org.apache.rocketmq.connect.jdbc.dialect.mysql;
+import java.sql.Connection;
+import java.sql.PreparedStatement;
+import java.sql.ResultSet;
+import java.sql.SQLException;
+import java.util.Collection;
import org.apache.rocketmq.connect.jdbc.config.AbstractConfig;
import org.apache.rocketmq.connect.jdbc.dialect.GenericDatabaseDialect;
import org.apache.rocketmq.connect.jdbc.schema.column.ColumnId;
@@ -23,41 +28,33 @@
import org.apache.rocketmq.connect.jdbc.sink.metadata.SinkRecordField;
import org.apache.rocketmq.connect.jdbc.util.ExpressionBuilder;
import org.apache.rocketmq.connect.jdbc.util.IdentifierRules;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-import java.sql.PreparedStatement;
-import java.sql.ResultSet;
-import java.sql.SQLException;
-import java.util.Collection;
/**
* mysql database dialect
*/
public class MySqlDatabaseDialect extends GenericDatabaseDialect {
-
- private final static Logger log = LoggerFactory.getLogger(MySqlDatabaseDialect.class);
-
- /**
- * 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);
+ public String name() {
+ return "mysql";
+ }
+
+ @Override public PreparedStatement createPreparedStatement(Connection db, String query) throws SQLException {
+ return createPreparedStatement(db, query, Integer.MIN_VALUE);
+ }
+
+ @Override
+ public PreparedStatement createPreparedStatement(Connection db, String query,
+ int batchMaxRows) throws SQLException {
+ PreparedStatement stmt = db.prepareStatement(query);
+ if (batchMaxRows > 0 || batchMaxRows == Integer.MIN_VALUE) {
+ stmt.setFetchSize(batchMaxRows);
+ }
stmt.setFetchDirection(ResultSet.FETCH_FORWARD);
+ return stmt;
}
/**
@@ -121,13 +118,4 @@ public String buildUpsertQueryStatement(
.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)
- .replaceAll("(?i)([(,]password=)[^,)]*", "$1****")
- .replaceAll("(://[^:]*:)([^@]*)@", "$1****@");
- }
}
diff --git a/connectors/rocketmq-connect-jdbc/rocketmq-connect-jdbc-mysql/src/main/java/org/apache/rocketmq/connect/jdbc/mysql/source/MysqlJdbcSourceTask.java b/connectors/rocketmq-connect-jdbc/src/main/java/org/apache/rocketmq/connect/jdbc/dialect/mysql/MysqlDatabaseDialectFactory.java
similarity index 65%
rename from connectors/rocketmq-connect-jdbc/rocketmq-connect-jdbc-mysql/src/main/java/org/apache/rocketmq/connect/jdbc/mysql/source/MysqlJdbcSourceTask.java
rename to connectors/rocketmq-connect-jdbc/src/main/java/org/apache/rocketmq/connect/jdbc/dialect/mysql/MysqlDatabaseDialectFactory.java
index 402936851..5c93254f2 100644
--- a/connectors/rocketmq-connect-jdbc/rocketmq-connect-jdbc-mysql/src/main/java/org/apache/rocketmq/connect/jdbc/mysql/source/MysqlJdbcSourceTask.java
+++ b/connectors/rocketmq-connect-jdbc/src/main/java/org/apache/rocketmq/connect/jdbc/dialect/mysql/MysqlDatabaseDialectFactory.java
@@ -14,19 +14,21 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package org.apache.rocketmq.connect.jdbc.mysql.source;
+package org.apache.rocketmq.connect.jdbc.dialect.mysql;
+import com.google.common.collect.Sets;
+import java.util.Set;
+import org.apache.rocketmq.connect.jdbc.config.AbstractConfig;
import org.apache.rocketmq.connect.jdbc.dialect.DatabaseDialect;
-import org.apache.rocketmq.connect.jdbc.mysql.dialect.MySqlDatabaseDialect;
-import org.apache.rocketmq.connect.jdbc.source.BaseSourceTask;
-import org.apache.rocketmq.connect.jdbc.source.JdbcSourceTaskConfig;
+import org.apache.rocketmq.connect.jdbc.dialect.DatabaseDialectFactory;
-/**
- * mysql jdbc source task
- */
-public class MysqlJdbcSourceTask extends BaseSourceTask{
+public class MysqlDatabaseDialectFactory implements DatabaseDialectFactory {
@Override
- protected DatabaseDialect newDialect(JdbcSourceTaskConfig config) {
+ public Set subProtocols() {
+ return Sets.newHashSet("mariadb", "mysql");
+ }
+
+ @Override public DatabaseDialect create(AbstractConfig config) {
return new MySqlDatabaseDialect(config);
}
-}
+}
\ No newline at end of file
diff --git a/connectors/rocketmq-connect-jdbc/rocketmq-connect-jdbc-openmldb/src/main/java/org/apache/rocketmq/connect/jdbc/openmldb/dialect/OpenMLDBDatabaseDialect.java b/connectors/rocketmq-connect-jdbc/src/main/java/org/apache/rocketmq/connect/jdbc/dialect/openmldb/OpenMLDBDatabaseDialect.java
similarity index 84%
rename from connectors/rocketmq-connect-jdbc/rocketmq-connect-jdbc-openmldb/src/main/java/org/apache/rocketmq/connect/jdbc/openmldb/dialect/OpenMLDBDatabaseDialect.java
rename to connectors/rocketmq-connect-jdbc/src/main/java/org/apache/rocketmq/connect/jdbc/dialect/openmldb/OpenMLDBDatabaseDialect.java
index 00d115fc6..9b810bc59 100644
--- a/connectors/rocketmq-connect-jdbc/rocketmq-connect-jdbc-openmldb/src/main/java/org/apache/rocketmq/connect/jdbc/openmldb/dialect/OpenMLDBDatabaseDialect.java
+++ b/connectors/rocketmq-connect-jdbc/src/main/java/org/apache/rocketmq/connect/jdbc/dialect/openmldb/OpenMLDBDatabaseDialect.java
@@ -14,25 +14,20 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package org.apache.rocketmq.connect.jdbc.openmldb.dialect;
+package org.apache.rocketmq.connect.jdbc.dialect.openmldb;
-import io.openmessaging.connector.api.data.Schema;
import io.openmessaging.connector.api.data.logical.Date;
import io.openmessaging.connector.api.data.logical.Time;
import io.openmessaging.connector.api.data.logical.Timestamp;
+import java.util.Collection;
+import java.util.List;
import org.apache.rocketmq.connect.jdbc.config.AbstractConfig;
-import org.apache.rocketmq.connect.jdbc.dialect.DropOptions;
import org.apache.rocketmq.connect.jdbc.dialect.GenericDatabaseDialect;
import org.apache.rocketmq.connect.jdbc.schema.column.ColumnId;
import org.apache.rocketmq.connect.jdbc.schema.table.TableId;
import org.apache.rocketmq.connect.jdbc.sink.metadata.SinkRecordField;
import org.apache.rocketmq.connect.jdbc.util.ExpressionBuilder;
import org.apache.rocketmq.connect.jdbc.util.IdentifierRules;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-import java.util.Collection;
-import java.util.List;
/**
@@ -40,8 +35,6 @@
*/
public class OpenMLDBDatabaseDialect extends GenericDatabaseDialect {
- private final Logger log = LoggerFactory.getLogger(OpenMLDBDatabaseDialect.class);
-
/**
* create openMLDB database dialect
*
@@ -51,6 +44,10 @@ public OpenMLDBDatabaseDialect(AbstractConfig config) {
super(config, new IdentifierRules(".", "`", "`"));
}
+ @Override
+ public String name() {
+ return "OpenMLDB";
+ }
@Override
protected String currentTimestampDatabaseQuery() {
@@ -111,11 +108,10 @@ protected void writeColumnSpec(ExpressionBuilder builder, SinkRecordField f) {
} else if (!this.isColumnOptional(f)) {
builder.append(" NOT NULL");
}
-
}
@Override
- public String buildDropTableStatement(TableId table, DropOptions options) {
+ public String buildDropTableStatement(TableId table, boolean ifExists, boolean cascade) {
ExpressionBuilder builder = this.expressionBuilder();
builder.append("DROP TABLE ");
builder.append(table);
@@ -137,18 +133,4 @@ public String buildDeleteStatement(TableId table, Collection keyColumn
throw new UnsupportedOperationException("delete is unsupported");
}
- @Override
- protected Integer getSqlTypeForSchema(Schema schema) {
- return 0;
- }
-
-
- @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/rocketmq-connect-jdbc-openmldb/src/main/java/org/apache/rocketmq/connect/jdbc/openmldb/sink/OpenMLDBJdbcSinkTask.java b/connectors/rocketmq-connect-jdbc/src/main/java/org/apache/rocketmq/connect/jdbc/dialect/openmldb/OpenMLDBDatabaseDialectFactory.java
similarity index 65%
rename from connectors/rocketmq-connect-jdbc/rocketmq-connect-jdbc-openmldb/src/main/java/org/apache/rocketmq/connect/jdbc/openmldb/sink/OpenMLDBJdbcSinkTask.java
rename to connectors/rocketmq-connect-jdbc/src/main/java/org/apache/rocketmq/connect/jdbc/dialect/openmldb/OpenMLDBDatabaseDialectFactory.java
index d237e8da0..54c2fba98 100644
--- a/connectors/rocketmq-connect-jdbc/rocketmq-connect-jdbc-openmldb/src/main/java/org/apache/rocketmq/connect/jdbc/openmldb/sink/OpenMLDBJdbcSinkTask.java
+++ b/connectors/rocketmq-connect-jdbc/src/main/java/org/apache/rocketmq/connect/jdbc/dialect/openmldb/OpenMLDBDatabaseDialectFactory.java
@@ -14,19 +14,21 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package org.apache.rocketmq.connect.jdbc.openmldb.sink;
+package org.apache.rocketmq.connect.jdbc.dialect.openmldb;
+import com.google.common.collect.Sets;
+import java.util.Set;
+import org.apache.rocketmq.connect.jdbc.config.AbstractConfig;
import org.apache.rocketmq.connect.jdbc.dialect.DatabaseDialect;
-import org.apache.rocketmq.connect.jdbc.openmldb.dialect.OpenMLDBDatabaseDialect;
-import org.apache.rocketmq.connect.jdbc.source.BaseSourceTask;
-import org.apache.rocketmq.connect.jdbc.source.JdbcSourceTaskConfig;
+import org.apache.rocketmq.connect.jdbc.dialect.DatabaseDialectFactory;
-/**
- * OpenMLDB jdbc source task
- */
-public class OpenMLDBJdbcSinkTask extends BaseSourceTask {
+public class OpenMLDBDatabaseDialectFactory implements DatabaseDialectFactory {
@Override
- protected DatabaseDialect newDialect(JdbcSourceTaskConfig config) {
+ public Set subProtocols() {
+ return Sets.newHashSet("openmldb");
+ }
+
+ @Override public DatabaseDialect create(AbstractConfig config) {
return new OpenMLDBDatabaseDialect(config);
}
-}
+}
\ No newline at end of file
diff --git a/connectors/rocketmq-connect-jdbc/rocketmq-connect-jdbc-core/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
similarity index 100%
rename from connectors/rocketmq-connect-jdbc/rocketmq-connect-jdbc-core/src/main/java/org/apache/rocketmq/connect/jdbc/exception/ConfigException.java
rename to connectors/rocketmq-connect-jdbc/src/main/java/org/apache/rocketmq/connect/jdbc/exception/ConfigException.java
diff --git a/connectors/rocketmq-connect-jdbc/rocketmq-connect-jdbc-core/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
similarity index 100%
rename from connectors/rocketmq-connect-jdbc/rocketmq-connect-jdbc-core/src/main/java/org/apache/rocketmq/connect/jdbc/exception/TableAlterOrCreateException.java
rename to connectors/rocketmq-connect-jdbc/src/main/java/org/apache/rocketmq/connect/jdbc/exception/TableAlterOrCreateException.java
diff --git a/connectors/rocketmq-connect-jdbc/rocketmq-connect-jdbc-core/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
similarity index 99%
rename from connectors/rocketmq-connect-jdbc/rocketmq-connect-jdbc-core/src/main/java/org/apache/rocketmq/connect/jdbc/schema/column/ColumnDefinition.java
rename to connectors/rocketmq-connect-jdbc/src/main/java/org/apache/rocketmq/connect/jdbc/schema/column/ColumnDefinition.java
index fceadefb8..364d52706 100644
--- a/connectors/rocketmq-connect-jdbc/rocketmq-connect-jdbc-core/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
@@ -16,10 +16,9 @@
*/
package org.apache.rocketmq.connect.jdbc.schema.column;
-import org.apache.rocketmq.connect.jdbc.schema.table.TableId;
-
import java.sql.Types;
import java.util.Objects;
+import org.apache.rocketmq.connect.jdbc.schema.table.TableId;
/**
* column definition
diff --git a/connectors/rocketmq-connect-jdbc/rocketmq-connect-jdbc-core/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
similarity index 99%
rename from connectors/rocketmq-connect-jdbc/rocketmq-connect-jdbc-core/src/main/java/org/apache/rocketmq/connect/jdbc/schema/column/ColumnId.java
rename to connectors/rocketmq-connect-jdbc/src/main/java/org/apache/rocketmq/connect/jdbc/schema/column/ColumnId.java
index a6b280322..e02c48083 100644
--- a/connectors/rocketmq-connect-jdbc/rocketmq-connect-jdbc-core/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
@@ -16,12 +16,11 @@
*/
package org.apache.rocketmq.connect.jdbc.schema.column;
+import java.util.Objects;
import org.apache.rocketmq.connect.jdbc.schema.table.TableId;
import org.apache.rocketmq.connect.jdbc.util.ExpressionBuilder;
import org.apache.rocketmq.connect.jdbc.util.QuoteMethod;
-import java.util.Objects;
-
/**
* column id
*/
diff --git a/connectors/rocketmq-connect-jdbc/rocketmq-connect-jdbc-core/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
similarity index 99%
rename from connectors/rocketmq-connect-jdbc/rocketmq-connect-jdbc-core/src/main/java/org/apache/rocketmq/connect/jdbc/schema/table/TableDefinition.java
rename to connectors/rocketmq-connect-jdbc/src/main/java/org/apache/rocketmq/connect/jdbc/schema/table/TableDefinition.java
index be6472450..925cf7a0f 100644
--- a/connectors/rocketmq-connect-jdbc/rocketmq-connect-jdbc-core/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
@@ -16,14 +16,13 @@
*/
package org.apache.rocketmq.connect.jdbc.schema.table;
-import org.apache.rocketmq.connect.jdbc.schema.column.ColumnDefinition;
-import org.apache.rocketmq.connect.jdbc.util.TableType;
-
import java.util.Collection;
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.Objects;
import java.util.Set;
+import org.apache.rocketmq.connect.jdbc.schema.column.ColumnDefinition;
+import org.apache.rocketmq.connect.jdbc.util.TableType;
/**
diff --git a/connectors/rocketmq-connect-jdbc/rocketmq-connect-jdbc-core/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
similarity index 99%
rename from connectors/rocketmq-connect-jdbc/rocketmq-connect-jdbc-core/src/main/java/org/apache/rocketmq/connect/jdbc/schema/table/TableDefinitions.java
rename to connectors/rocketmq-connect-jdbc/src/main/java/org/apache/rocketmq/connect/jdbc/schema/table/TableDefinitions.java
index 719ee2e43..6bd980709 100644
--- a/connectors/rocketmq-connect-jdbc/rocketmq-connect-jdbc-core/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
@@ -16,14 +16,13 @@
*/
package org.apache.rocketmq.connect.jdbc.schema.table;
-import org.apache.rocketmq.connect.jdbc.dialect.DatabaseDialect;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
import java.sql.Connection;
import java.sql.SQLException;
import java.util.HashMap;
import java.util.Map;
+import org.apache.rocketmq.connect.jdbc.dialect.DatabaseDialect;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
/**
* A simple cache of {@link TableDefinition} keyed.
diff --git a/connectors/rocketmq-connect-jdbc/rocketmq-connect-jdbc-core/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
similarity index 99%
rename from connectors/rocketmq-connect-jdbc/rocketmq-connect-jdbc-core/src/main/java/org/apache/rocketmq/connect/jdbc/schema/table/TableId.java
rename to connectors/rocketmq-connect-jdbc/src/main/java/org/apache/rocketmq/connect/jdbc/schema/table/TableId.java
index 776f78629..79fbd9a5c 100644
--- a/connectors/rocketmq-connect-jdbc/rocketmq-connect-jdbc-core/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
@@ -16,12 +16,11 @@
*/
package org.apache.rocketmq.connect.jdbc.schema.table;
+import java.util.Objects;
import org.apache.commons.lang3.StringUtils;
import org.apache.rocketmq.connect.jdbc.util.ExpressionBuilder;
import org.apache.rocketmq.connect.jdbc.util.QuoteMethod;
-import java.util.Objects;
-
public class TableId implements Comparable, ExpressionBuilder.Expressable {
private final String catalogName;
diff --git a/connectors/rocketmq-connect-jdbc/rocketmq-connect-jdbc-core/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
similarity index 57%
rename from connectors/rocketmq-connect-jdbc/rocketmq-connect-jdbc-core/src/main/java/org/apache/rocketmq/connect/jdbc/sink/BufferedRecords.java
rename to connectors/rocketmq-connect-jdbc/src/main/java/org/apache/rocketmq/connect/jdbc/sink/BufferedRecords.java
index b0418184e..2ce03e6eb 100644
--- a/connectors/rocketmq-connect-jdbc/rocketmq-connect-jdbc-core/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
@@ -18,16 +18,6 @@
import io.openmessaging.connector.api.data.ConnectRecord;
import io.openmessaging.connector.api.data.Schema;
-import io.openmessaging.connector.api.errors.ConnectException;
-import org.apache.rocketmq.connect.jdbc.dialect.DatabaseDialect;
-import org.apache.rocketmq.connect.jdbc.dialect.GenericDatabaseDialect;
-import org.apache.rocketmq.connect.jdbc.schema.db.DbStructure;
-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.SchemaPair;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.SQLException;
@@ -35,6 +25,13 @@
import java.util.List;
import java.util.Objects;
import java.util.Optional;
+import org.apache.rocketmq.connect.jdbc.binder.JdbcRecordBinder;
+import org.apache.rocketmq.connect.jdbc.dialect.DatabaseDialect;
+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.SchemaPair;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
import static java.util.Objects.isNull;
import static java.util.Objects.nonNull;
@@ -57,11 +54,12 @@ public class BufferedRecords {
private RecordValidator recordValidator;
private PreparedStatement updatePreparedStatement;
private PreparedStatement deletePreparedStatement;
- private DatabaseDialect.StatementBinder updateStatementBinder;
- private DatabaseDialect.StatementBinder deleteStatementBinder;
+ private JdbcRecordBinder updateRecordBinder;
+ private JdbcRecordBinder deleteRecordBinder;
private boolean deletesInBatch = false;
- public BufferedRecords(JdbcSinkConfig config, TableId tableId, DatabaseDialect dbDialect, DbStructure dbStructure, Connection connection) {
+ public BufferedRecords(JdbcSinkConfig config, TableId tableId, DatabaseDialect dbDialect, DbStructure dbStructure,
+ Connection connection) {
this.tableId = tableId;
this.config = config;
this.dbDialect = dbDialect;
@@ -80,32 +78,11 @@ public BufferedRecords(JdbcSinkConfig config, TableId tableId, DatabaseDialect d
public List add(ConnectRecord record) throws SQLException {
recordValidator.validate(record);
final List flushed = new ArrayList<>();
- // check schema changed
- boolean schemaChanged = isSchemaChanged(record);
- //Judge and flush delete data
- flushDeletedRecord(record, flushed);
+ boolean schemaChanged = schemaChangedOrFlushedDeleteRecordIfNeed(record, 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.getKeySchema(), 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);
- // build insert sql and delete sql
- final String insertSql = dbDialect.getInsertSql(config, fieldsMetadata, tableId);
- final String deleteSql = dbDialect.getDeleteSql(config, fieldsMetadata, tableId);
-
- 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());
- }
+ // First record add or schema changed
+ if (schemaChanged || updateRecordBinder == null) {
+ flushedAndReinitializeStatement(record, flushed);
}
// set deletesInBatch if schema value is not null
if (isNull(record.getData()) && config.isDeleteEnabled()) {
@@ -118,6 +95,52 @@ public List add(ConnectRecord record) throws SQLException {
return flushed;
}
+ private void flushedAndReinitializeStatement(ConnectRecord record,
+ List flushed) throws SQLException {
+ // Each batch needs to have the same schemas, so get the buffered records out
+ flushed.addAll(flush());
+ // First close statement
+ close();
+ // re-initialize everything that depends on the record schema
+ final SchemaPair schemaPair = new SchemaPair(record.getKeySchema(), 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);
+ // build insert sql and delete sql
+ final String insertSql = dbDialect.getInsertSql(config, fieldsMetadata, tableId);
+ final String deleteSql = dbDialect.getDeleteSql(config, fieldsMetadata, tableId);
+ log.info("{} sql: {} deleteSql: {} meta: {}", config.getInsertMode(), insertSql, deleteSql, fieldsMetadata);
+ updatePreparedStatement = dbDialect.createPreparedStatement(connection, insertSql);
+ updateRecordBinder = dbDialect.getJdbcRecordBinder(updatePreparedStatement, config.pkMode, schemaPair, fieldsMetadata, dbStructure.tableDefinition(connection, tableId), config.getInsertMode());
+ if (config.isDeleteEnabled() && nonNull(deleteSql)) {
+ deletePreparedStatement = dbDialect.createPreparedStatement(connection, deleteSql);
+ deleteRecordBinder = dbDialect.getJdbcRecordBinder(deletePreparedStatement, config.pkMode, schemaPair, fieldsMetadata, dbStructure.tableDefinition(connection, tableId), config.getInsertMode());
+ }
+ }
+
+ private boolean schemaChangedOrFlushedDeleteRecordIfNeed(ConnectRecord record,
+ List flushed) throws SQLException {
+ boolean schemaChanged = false;
+ if (!Objects.equals(keySchema, record.getKeySchema())) {
+ keySchema = record.getKeySchema();
+ schemaChanged = true;
+ }
+ if (isNull(record.getSchema())) {
+ if (config.isDeleteEnabled()) {
+ deletesInBatch = true;
+ }
+ } else if (Objects.equals(valueSchema, record.getSchema())) {
+ if (config.isDeleteEnabled() && deletesInBatch) {
+ flushed.addAll(flush());
+ }
+ } else {
+ valueSchema = record.getSchema();
+ schemaChanged = true;
+ }
+ return schemaChanged;
+ }
+
public List flush() throws SQLException {
if (records.isEmpty()) {
log.debug("Records is empty");
@@ -125,41 +148,26 @@ public List flush() throws SQLException {
}
log.debug("Flushing {} buffered records", records.size());
for (ConnectRecord record : records) {
- if (isNull(record.getData()) && nonNull(deleteStatementBinder)) {
- deleteStatementBinder.bindRecord(record);
+ if (isNull(record.getData()) && nonNull(deleteRecordBinder)) {
+ deleteRecordBinder.bindRecord(record);
} else {
- updateStatementBinder.bindRecord(record);
+ updateRecordBinder.bindRecord(record);
}
}
- Optional totalUpdateCount =
- updateStatementBinder != null ? updateStatementBinder.executeUpdates(updatePreparedStatement) :
- Optional.empty();
- long totalDeleteCount = deleteStatementBinder != null ?
- deleteStatementBinder.executeDeletes(deletePreparedStatement) : 0;
+ Optional totalUpdateCount = dbDialect.executeUpdates(updatePreparedStatement);
+ Optional totalDeleteCount = dbDialect.executeDeletes(deletePreparedStatement);
final long expectedCount = updateRecordCount();
- log.trace("{} records:{} resulting in totalUpdateCount:{} totalDeleteCount:{}",
- config.getInsertMode(),
- records.size(),
- totalUpdateCount,
- totalDeleteCount
- );
+ 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(dbDialect.getDialectClass())) && 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
- )
- );
- }
+ log.warn(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 flushedRecords = records;
records = new ArrayList<>();
return flushedRecords;
@@ -173,33 +181,6 @@ private long updateRecordCount() {
.count();
}
- private boolean isSchemaChanged(ConnectRecord record) {
- boolean schemaChanged = false;
- if (!Objects.equals(keySchema, record.getKeySchema())) {
- keySchema = record.getKeySchema();
- schemaChanged = true;
- }
- if (!isNull(record.getSchema()) && !Objects.equals(valueSchema, record.getSchema())) {
- // value schema is not null and has changed. This is a real schema change.
- valueSchema = record.getSchema();
- schemaChanged = true;
- }
- return schemaChanged;
- }
-
- private void flushDeletedRecord(ConnectRecord record, List flushed) throws SQLException {
- if (isNull(record.getSchema())) {
- if (config.isDeleteEnabled()) {
- deletesInBatch = true;
- }
- } else if (Objects.equals(valueSchema, record.getSchema())) {
- if (config.isDeleteEnabled() && deletesInBatch) {
- // flush so an insert after a delete of same record isn't lost
- flushed.addAll(flush());
- }
- }
- }
-
public void close() throws SQLException {
log.debug(
"Closing BufferedRecords with updatePreparedStatement: {} deletePreparedStatement: {}",
diff --git a/connectors/rocketmq-connect-jdbc/rocketmq-connect-jdbc-core/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/sink/DbStructure.java
similarity index 80%
rename from connectors/rocketmq-connect-jdbc/rocketmq-connect-jdbc-core/src/main/java/org/apache/rocketmq/connect/jdbc/schema/db/DbStructure.java
rename to connectors/rocketmq-connect-jdbc/src/main/java/org/apache/rocketmq/connect/jdbc/sink/DbStructure.java
index f1e7cfe71..7f1f3f510 100644
--- a/connectors/rocketmq-connect-jdbc/rocketmq-connect-jdbc-core/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/sink/DbStructure.java
@@ -14,68 +14,56 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package org.apache.rocketmq.connect.jdbc.schema.db;
+package org.apache.rocketmq.connect.jdbc.sink;
+import java.sql.Connection;
+import java.sql.SQLException;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.concurrent.ConcurrentHashMap;
import org.apache.rocketmq.connect.jdbc.dialect.DatabaseDialect;
import org.apache.rocketmq.connect.jdbc.exception.TableAlterOrCreateException;
import org.apache.rocketmq.connect.jdbc.schema.column.ColumnDefinition;
import org.apache.rocketmq.connect.jdbc.schema.table.TableDefinition;
import org.apache.rocketmq.connect.jdbc.schema.table.TableDefinitions;
import org.apache.rocketmq.connect.jdbc.schema.table.TableId;
-import org.apache.rocketmq.connect.jdbc.sink.JdbcSinkConfig;
import org.apache.rocketmq.connect.jdbc.sink.metadata.FieldsMetadata;
import org.apache.rocketmq.connect.jdbc.sink.metadata.SinkRecordField;
import org.apache.rocketmq.connect.jdbc.util.TableType;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
-import java.sql.Connection;
-import java.sql.SQLException;
-import java.util.Collection;
-import java.util.Collections;
-import java.util.HashSet;
-import java.util.List;
-import java.util.Map;
-import java.util.Set;
-import java.util.concurrent.ConcurrentHashMap;
-
/**
*
*/
public class DbStructure {
private static final Logger log = LoggerFactory.getLogger(DbStructure.class);
private final DatabaseDialect dbDialect;
- private final TableDefinitions tableDefns;
+ private final TableDefinitions tableDefinitions;
public DbStructure(DatabaseDialect dbDialect) {
this.dbDialect = dbDialect;
- this.tableDefns = new TableDefinitions(dbDialect);
+ this.tableDefinitions = new TableDefinitions(dbDialect);
}
- /**
- * 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 ...
+ if (tableDefinitions.get(connection, tableId) == null) {
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);
+ TableDefinition newDefn = tableDefinitions.refresh(connection, tableId);
if (newDefn == null) {
throw sqle;
}
@@ -87,30 +75,18 @@ public boolean createOrAmendIfNecessary(
return amendIfNecessary(config, connection, tableId, fieldsMetadata, config.getMaxRetries());
}
- /**
- * 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;
+ TableDefinition tableDefinition = tableDefinitions.get(connection, tableId);
+ if (tableDefinition != null) {
+ return tableDefinition;
}
- return tableDefns.refresh(connection, tableId);
+ return tableDefinitions.refresh(connection, tableId);
}
- /**
- * @throws SQLException if CREATE failed
- */
void create(
final JdbcSinkConfig config,
final Connection connection,
@@ -124,13 +100,9 @@ void create(
}
String sql = dbDialect.buildCreateTableStatement(tableId, fieldsMetadata.allFields.values());
log.info("Creating table with sql: {}", sql);
- dbDialect.applyDdlStatements(connection, Collections.singletonList(sql));
+ dbDialect.executeSchemaChangeStatements(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,
@@ -138,16 +110,15 @@ boolean amendIfNecessary(
final FieldsMetadata fieldsMetadata,
final int maxRetries
) throws SQLException, TableAlterOrCreateException {
- final TableDefinition tableDefn = tableDefns.get(connection, tableId);
+ final TableDefinition tableDefn = tableDefinitions.get(connection, tableId);
final Set missingFields = missingFields(
- fieldsMetadata.allFields.values(),
- tableDefn.columnNames()
+ fieldsMetadata.allFields.values(),
+ tableDefn.columnNames()
);
if (missingFields.isEmpty()) {
return false;
}
- // At this point there are missing fields
TableType type = tableDefn.type();
switch (type) {
case TABLE:
@@ -197,7 +168,7 @@ boolean amendIfNecessary(
amendTableQueries
);
try {
- dbDialect.applyDdlStatements(connection, amendTableQueries);
+ dbDialect.executeSchemaChangeStatements(connection, amendTableQueries);
} catch (SQLException sqle) {
if (maxRetries <= 0) {
throw new TableAlterOrCreateException(
@@ -211,7 +182,7 @@ boolean amendIfNecessary(
);
}
log.warn("Amend failed, re-attempting", sqle);
- tableDefns.refresh(connection, tableId);
+ tableDefinitions.refresh(connection, tableId);
// Perhaps there was a race with other tasks to add the columns
return amendIfNecessary(
config,
@@ -222,7 +193,7 @@ boolean amendIfNecessary(
);
}
- tableDefns.refresh(connection, tableId);
+ tableDefinitions.refresh(connection, tableId);
return true;
}
diff --git a/connectors/rocketmq-connect-jdbc/rocketmq-connect-jdbc-core/src/main/java/org/apache/rocketmq/connect/jdbc/sink/JdbcSinkConfig.java b/connectors/rocketmq-connect-jdbc/src/main/java/org/apache/rocketmq/connect/jdbc/sink/JdbcSinkConfig.java
similarity index 70%
rename from connectors/rocketmq-connect-jdbc/rocketmq-connect-jdbc-core/src/main/java/org/apache/rocketmq/connect/jdbc/sink/JdbcSinkConfig.java
rename to connectors/rocketmq-connect-jdbc/src/main/java/org/apache/rocketmq/connect/jdbc/sink/JdbcSinkConfig.java
index 398537a42..b8655e888 100644
--- a/connectors/rocketmq-connect-jdbc/rocketmq-connect-jdbc-core/src/main/java/org/apache/rocketmq/connect/jdbc/sink/JdbcSinkConfig.java
+++ b/connectors/rocketmq-connect-jdbc/src/main/java/org/apache/rocketmq/connect/jdbc/sink/JdbcSinkConfig.java
@@ -17,12 +17,6 @@
package org.apache.rocketmq.connect.jdbc.sink;
import io.openmessaging.KeyValue;
-import org.apache.rocketmq.connect.jdbc.config.AbstractConfig;
-import org.apache.rocketmq.connect.jdbc.dialect.DatabaseDialect;
-import org.apache.rocketmq.connect.jdbc.exception.ConfigException;
-import org.apache.rocketmq.connect.jdbc.schema.table.TableId;
-import org.apache.rocketmq.connect.jdbc.util.TableType;
-
import java.time.ZoneId;
import java.util.EnumSet;
import java.util.HashSet;
@@ -31,6 +25,11 @@
import java.util.Set;
import java.util.TimeZone;
import java.util.stream.Collectors;
+import org.apache.rocketmq.connect.jdbc.config.AbstractConfig;
+import org.apache.rocketmq.connect.jdbc.dialect.DatabaseDialect;
+import org.apache.rocketmq.connect.jdbc.exception.ConfigException;
+import org.apache.rocketmq.connect.jdbc.schema.table.TableId;
+import org.apache.rocketmq.connect.jdbc.util.TableType;
/**
* jdbc sink config
@@ -52,94 +51,31 @@ public enum PrimaryKeyMode {
public static final String TABLE_NAME_FORMAT = "table.name.format";
public static final String TABLE_NAME_FORMAT_DEFAULT = "${topic}";
- private static final String TABLE_NAME_FORMAT_DISPLAY = "Table Name Format";
-
/**
* table name from header
*/
public static final String TABLE_NAME_FROM_HEADER = "table.name.from.header";
- private static final boolean TABLE_NAME_FROM_HEADER_DEFAULT = Boolean.FALSE;
- private static final String TABLE_NAME_FROM_HEADER_DISPLAY = "Table from header";
-
/**
* max retries
*/
public static final String MAX_RETRIES = "max.retries";
private static final int MAX_RETRIES_DEFAULT = 10;
- private static final String MAX_RETRIES_DOC =
- "The maximum number of times to retry on errors before failing the task.";
-
public static final String RETRY_BACKOFF_MS = "retry.backoff.ms";
private static final int RETRY_BACKOFF_MS_DEFAULT = 3000;
-
public static final String BATCH_SIZE = "batch.size";
private static final int BATCH_SIZE_DEFAULT = 100;
-
-
public static final String DELETE_ENABLED = "delete.enabled";
private static final boolean DELETE_ENABLED_DEFAULT = false;
-
-
public static final String AUTO_CREATE = "auto.create";
private static final boolean AUTO_CREATE_DEFAULT = false;
-
public static final String AUTO_EVOLVE = "auto.evolve";
private static final boolean AUTO_EVOLVE_DEFAULT = false;
- private static final String AUTO_EVOLVE_DOC =
- "Whether to automatically add columns in the table schema when found to be missing relative "
- + "to the record schema by issuing ``ALTER``.";
-
public static final String INSERT_MODE = "insert.mode";
private static final String INSERT_MODE_DEFAULT = "insert";
- private static final String INSERT_MODE_DOC =
- "The insertion mode to use. Supported modes are:\n"
- + "``insert``\n"
- + " Use standard SQL ``INSERT`` statements.\n"
- + "``upsert``\n"
- + " Use the appropriate upsert semantics for the target database if it is supported by "
- + "the connector, e.g. ``INSERT OR IGNORE``.\n"
- + "``update``\n"
- + " Use the appropriate update semantics for the target database if it is supported by "
- + "the connector, e.g. ``UPDATE``.";
- private static final String INSERT_MODE_DISPLAY = "Insert Mode";
-
-
public static final String PK_FIELDS = "pk.fields";
- private static final String PK_FIELDS_DEFAULT = "";
- private static final String PK_FIELDS_DOC =
- "List of comma-separated primary key field names. The runtime interpretation of this config"
- + " depends on the ``pk.mode``:\n"
- + "``none``\n"
- + " Ignored as no fields are used as primary key in this mode.\n"
- + "``record_key``\n"
- + " If empty, all fields from the key struct will be used, otherwise used to extract the"
- + " desired fields - for primitive key only a single field name must be configured.\n"
- + "``record_value``\n"
- + " If empty, all fields from the value struct will be used, otherwise used to extract "
- + "the desired fields.";
- private static final String PK_FIELDS_DISPLAY = "Primary Key Fields";
-
public static final String PK_MODE = "pk.mode";
private static final String PK_MODE_DEFAULT = "none";
- private static final String PK_MODE_DOC =
- "The primary key mode, also refer to ``" + PK_FIELDS + "`` documentation for interplay. "
- + "Supported modes are:\n"
- + "``none``\n"
- + " No keys utilized.\n"
- + "``record_value``\n"
- + " Field(s) from the record value are used, which must be a struct.";
- private static final String PK_MODE_DISPLAY = "Primary Key Mode";
-
public static final String FIELDS_WHITELIST = "fields.whitelist";
- private static final String FIELDS_WHITELIST_DEFAULT = "";
- private static final String FIELDS_WHITELIST_DOC =
- "List of comma-separated record value field names. If empty, all fields from the record "
- + "value are utilized, otherwise used to filter to the desired fields.\n"
- + "Note that ``" + PK_FIELDS + "`` is applied independently in the context of which field"
- + "(s) form the primary key columns in the destination database,"
- + " while this configuration is applicable for the other columns.";
- private static final String FIELDS_WHITELIST_DISPLAY = "Fields Whitelist";
-
public static final String DB_TIMEZONE_CONFIG = "db.timezone";
public static final String DB_TIMEZONE_DEFAULT = "UTC";
@@ -281,7 +217,7 @@ public boolean filterWhiteTable(DatabaseDialect dbDialect, TableId tableId) {
return true;
}
for (String tableName : tableWhitelist) {
- TableId table = dbDialect.parseToTableId(tableName);
+ TableId table = dbDialect.parseTableNameToTableId(tableName);
if (table.catalogName() != null && table.catalogName().equals(tableId.catalogName())) {
return true;
}
diff --git a/connectors/rocketmq-connect-jdbc/rocketmq-connect-jdbc-core/src/main/java/org/apache/rocketmq/connect/jdbc/sink/BaseSinkConnector.java b/connectors/rocketmq-connect-jdbc/src/main/java/org/apache/rocketmq/connect/jdbc/sink/JdbcSinkConnector.java
similarity index 87%
rename from connectors/rocketmq-connect-jdbc/rocketmq-connect-jdbc-core/src/main/java/org/apache/rocketmq/connect/jdbc/sink/BaseSinkConnector.java
rename to connectors/rocketmq-connect-jdbc/src/main/java/org/apache/rocketmq/connect/jdbc/sink/JdbcSinkConnector.java
index a7f4f75a9..7da550212 100644
--- a/connectors/rocketmq-connect-jdbc/rocketmq-connect-jdbc-core/src/main/java/org/apache/rocketmq/connect/jdbc/sink/BaseSinkConnector.java
+++ b/connectors/rocketmq-connect-jdbc/src/main/java/org/apache/rocketmq/connect/jdbc/sink/JdbcSinkConnector.java
@@ -18,19 +18,19 @@
package org.apache.rocketmq.connect.jdbc.sink;
import io.openmessaging.KeyValue;
+import io.openmessaging.connector.api.component.task.Task;
import io.openmessaging.connector.api.component.task.sink.SinkConnector;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
import java.util.ArrayList;
import java.util.List;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
/**
* jdbc sink connector
*/
-public abstract class BaseSinkConnector extends SinkConnector {
- private static final Logger log = LoggerFactory.getLogger(BaseSinkConnector.class);
+public class JdbcSinkConnector extends SinkConnector {
+ private static final Logger log = LoggerFactory.getLogger(JdbcSinkConnector.class);
private KeyValue connectConfig;
@Override
@@ -70,4 +70,9 @@ public List taskConfigs(int maxTasks) {
}
return configs;
}
+
+ @Override
+ public Class extends Task> taskClass() {
+ return JdbcSinkTask.class;
+ }
}
diff --git a/connectors/rocketmq-connect-jdbc/rocketmq-connect-jdbc-core/src/main/java/org/apache/rocketmq/connect/jdbc/sink/BaseSinkTask.java b/connectors/rocketmq-connect-jdbc/src/main/java/org/apache/rocketmq/connect/jdbc/sink/JdbcSinkTask.java
similarity index 88%
rename from connectors/rocketmq-connect-jdbc/rocketmq-connect-jdbc-core/src/main/java/org/apache/rocketmq/connect/jdbc/sink/BaseSinkTask.java
rename to connectors/rocketmq-connect-jdbc/src/main/java/org/apache/rocketmq/connect/jdbc/sink/JdbcSinkTask.java
index 2c2a5f550..4d88597c3 100644
--- a/connectors/rocketmq-connect-jdbc/rocketmq-connect-jdbc-core/src/main/java/org/apache/rocketmq/connect/jdbc/sink/BaseSinkTask.java
+++ b/connectors/rocketmq-connect-jdbc/src/main/java/org/apache/rocketmq/connect/jdbc/sink/JdbcSinkTask.java
@@ -17,27 +17,24 @@
package org.apache.rocketmq.connect.jdbc.sink;
-
import io.openmessaging.KeyValue;
import io.openmessaging.connector.api.component.task.sink.SinkTask;
import io.openmessaging.connector.api.data.ConnectRecord;
import io.openmessaging.connector.api.errors.ConnectException;
import io.openmessaging.connector.api.errors.RetriableException;
+import java.sql.SQLException;
+import java.util.List;
import org.apache.rocketmq.connect.jdbc.dialect.DatabaseDialect;
+import org.apache.rocketmq.connect.jdbc.dialect.DatabaseDialectLoader;
import org.apache.rocketmq.connect.jdbc.exception.TableAlterOrCreateException;
-import org.apache.rocketmq.connect.jdbc.schema.db.DbStructure;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
-import java.sql.SQLException;
-import java.util.List;
-
/**
* jdbc sink task
*/
-public abstract class BaseSinkTask extends SinkTask {
-
- private static final Logger log = LoggerFactory.getLogger(BaseSinkTask.class);
+public class JdbcSinkTask extends SinkTask {
+ private static final Logger log = LoggerFactory.getLogger(JdbcSinkTask.class);
private KeyValue originalConfig;
private JdbcSinkConfig config;
private DatabaseDialect dialect;
@@ -46,7 +43,6 @@ public abstract class BaseSinkTask extends SinkTask {
/**
* Start the component
- *
* @param keyValue
*/
@Override
@@ -54,10 +50,9 @@ public void start(KeyValue keyValue) {
originalConfig = keyValue;
config = new JdbcSinkConfig(keyValue);
remainingRetries = config.getMaxRetries();
- this.dialect = newDialect(config);
- final DbStructure dbStructure = new DbStructure(dialect);
+ this.dialect = DatabaseDialectLoader.getDatabaseDialect(config);
log.info("Initializing writer using SQL dialect: {}", dialect.getClass().getSimpleName());
- this.jdbcWriter = new JdbcWriter(config, dialect, dbStructure);
+ this.jdbcWriter = new JdbcWriter(config, dialect);
}
/**
@@ -117,6 +112,4 @@ public void stop() {
}
}
- protected abstract DatabaseDialect newDialect(JdbcSinkConfig config);
-
}
diff --git a/connectors/rocketmq-connect-jdbc/rocketmq-connect-jdbc-core/src/main/java/org/apache/rocketmq/connect/jdbc/sink/JdbcWriter.java b/connectors/rocketmq-connect-jdbc/src/main/java/org/apache/rocketmq/connect/jdbc/sink/JdbcWriter.java
similarity index 85%
rename from connectors/rocketmq-connect-jdbc/rocketmq-connect-jdbc-core/src/main/java/org/apache/rocketmq/connect/jdbc/sink/JdbcWriter.java
rename to connectors/rocketmq-connect-jdbc/src/main/java/org/apache/rocketmq/connect/jdbc/sink/JdbcWriter.java
index d82e44127..5fc130076 100644
--- a/connectors/rocketmq-connect-jdbc/rocketmq-connect-jdbc-core/src/main/java/org/apache/rocketmq/connect/jdbc/sink/JdbcWriter.java
+++ b/connectors/rocketmq-connect-jdbc/src/main/java/org/apache/rocketmq/connect/jdbc/sink/JdbcWriter.java
@@ -17,21 +17,19 @@
package org.apache.rocketmq.connect.jdbc.sink;
import io.openmessaging.connector.api.data.ConnectRecord;
+import java.sql.Connection;
+import java.sql.SQLException;
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.Map;
import org.apache.rocketmq.connect.jdbc.common.HeaderField;
import org.apache.rocketmq.connect.jdbc.dialect.DatabaseDialect;
-import org.apache.rocketmq.connect.jdbc.dialect.provider.CachedConnectionProvider;
+import org.apache.rocketmq.connect.jdbc.connection.CachedConnectionProvider;
import org.apache.rocketmq.connect.jdbc.exception.TableAlterOrCreateException;
-import org.apache.rocketmq.connect.jdbc.schema.db.DbStructure;
import org.apache.rocketmq.connect.jdbc.schema.table.TableId;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
-import java.sql.Connection;
-import java.sql.SQLException;
-import java.util.Collection;
-import java.util.HashMap;
-import java.util.Map;
-
/**
* jdbc db updater
*/
@@ -43,10 +41,10 @@ public class JdbcWriter {
private final DbStructure dbStructure;
final CachedConnectionProvider cachedConnectionProvider;
- public JdbcWriter(final JdbcSinkConfig config, DatabaseDialect dbDialect, DbStructure dbStructure) {
+ public JdbcWriter(final JdbcSinkConfig config, DatabaseDialect dbDialect) {
this.config = config;
this.dbDialect = dbDialect;
- this.dbStructure = dbStructure;
+ this.dbStructure = new DbStructure(dbDialect);
this.cachedConnectionProvider = connectionProvider(
config.getAttempts(),
@@ -63,8 +61,7 @@ protected void onConnect(final Connection connection) throws SQLException {
};
}
- public void write(final Collection records)
- throws SQLException, TableAlterOrCreateException {
+ public void write(final Collection records) throws SQLException, TableAlterOrCreateException {
final Connection connection = cachedConnectionProvider.getConnection();
try {
final Map bufferByTable = new HashMap<>();
@@ -91,7 +88,12 @@ public void write(final Collection records)
connection.commit();
} catch (SQLException | TableAlterOrCreateException e) {
log.error("Jdbc writer error {}", e);
- connection.rollback();
+ try {
+ connection.rollback();
+ } catch (Exception ex) {
+ // ignore exception
+ log.warn(ex.getMessage());
+ }
}
}
@@ -102,8 +104,8 @@ public void closeQuietly() {
TableId destinationTable(ConnectRecord record) {
// todo table from header
if (config.isTableFromHeader()) {
- return dbDialect.parseToTableId(record.getExtensions().getString(HeaderField.SOURCE_TABLE_KEY));
+ return dbDialect.parseTableNameToTableId(record.getExtensions().getString(HeaderField.SOURCE_TABLE_KEY));
}
- return dbDialect.parseToTableId(record.getSchema().getName());
+ return dbDialect.parseTableNameToTableId(record.getSchema().getName());
}
}
diff --git a/connectors/rocketmq-connect-jdbc/rocketmq-connect-jdbc-core/src/main/java/org/apache/rocketmq/connect/jdbc/sink/RecordValidator.java b/connectors/rocketmq-connect-jdbc/src/main/java/org/apache/rocketmq/connect/jdbc/sink/RecordValidator.java
similarity index 98%
rename from connectors/rocketmq-connect-jdbc/rocketmq-connect-jdbc-core/src/main/java/org/apache/rocketmq/connect/jdbc/sink/RecordValidator.java
rename to connectors/rocketmq-connect-jdbc/src/main/java/org/apache/rocketmq/connect/jdbc/sink/RecordValidator.java
index 805d92fbc..4c2fd5061 100644
--- a/connectors/rocketmq-connect-jdbc/rocketmq-connect-jdbc-core/src/main/java/org/apache/rocketmq/connect/jdbc/sink/RecordValidator.java
+++ b/connectors/rocketmq-connect-jdbc/src/main/java/org/apache/rocketmq/connect/jdbc/sink/RecordValidator.java
@@ -24,8 +24,7 @@
@FunctionalInterface
public interface RecordValidator {
- RecordValidator NO_OP = (record) -> {
- };
+ RecordValidator NO_OP = (record) -> { };
static RecordValidator create(JdbcSinkConfig config) {
RecordValidator requiresKey = requiresKey(config);
diff --git a/connectors/rocketmq-connect-jdbc/rocketmq-connect-jdbc-core/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
similarity index 98%
rename from connectors/rocketmq-connect-jdbc/rocketmq-connect-jdbc-core/src/main/java/org/apache/rocketmq/connect/jdbc/sink/metadata/FieldsMetadata.java
rename to connectors/rocketmq-connect-jdbc/src/main/java/org/apache/rocketmq/connect/jdbc/sink/metadata/FieldsMetadata.java
index c12280d2b..4148c2e67 100644
--- a/connectors/rocketmq-connect-jdbc/rocketmq-connect-jdbc-core/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
@@ -21,8 +21,6 @@
import io.openmessaging.connector.api.data.FieldType;
import io.openmessaging.connector.api.data.Schema;
import io.openmessaging.connector.api.errors.ConnectException;
-import org.apache.rocketmq.connect.jdbc.sink.JdbcSinkConfig;
-
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
@@ -31,6 +29,7 @@
import java.util.List;
import java.util.Map;
import java.util.Set;
+import org.apache.rocketmq.connect.jdbc.sink.JdbcSinkConfig;
/**
@@ -70,13 +69,13 @@ public static FieldsMetadata extract(
final SchemaPair schemaPair
) {
return extract(
- tableName,
- pkMode,
- configuredPkFields,
- fieldsWhitelist,
- schemaPair.keySchema,
- schemaPair.schema,
- schemaPair.extensions
+ tableName,
+ pkMode,
+ configuredPkFields,
+ fieldsWhitelist,
+ schemaPair.keySchema,
+ schemaPair.valueSchema,
+ schemaPair.extensions
);
}
diff --git a/connectors/rocketmq-connect-jdbc/rocketmq-connect-jdbc-core/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
similarity index 86%
rename from connectors/rocketmq-connect-jdbc/rocketmq-connect-jdbc-core/src/main/java/org/apache/rocketmq/connect/jdbc/sink/metadata/SchemaPair.java
rename to connectors/rocketmq-connect-jdbc/src/main/java/org/apache/rocketmq/connect/jdbc/sink/metadata/SchemaPair.java
index f2196f413..b87072ed2 100644
--- a/connectors/rocketmq-connect-jdbc/rocketmq-connect-jdbc-core/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
@@ -18,7 +18,6 @@
import io.openmessaging.KeyValue;
import io.openmessaging.connector.api.data.Schema;
-
import java.util.Objects;
/**
@@ -26,18 +25,18 @@
*/
public class SchemaPair {
public final Schema keySchema;
- public final Schema schema;
+ public final Schema valueSchema;
public final KeyValue extensions;
public SchemaPair(Schema keySchema, Schema schema) {
this.keySchema = keySchema;
- this.schema = schema;
+ this.valueSchema = schema;
this.extensions = null;
}
public SchemaPair(Schema keySchema, Schema schema, KeyValue extensions) {
this.keySchema = keySchema;
- this.schema = schema;
+ this.valueSchema = schema;
this.extensions = extensions;
}
@@ -50,16 +49,16 @@ public boolean equals(Object o) {
return false;
}
SchemaPair that = (SchemaPair) o;
- return Objects.equals(schema, that.schema);
+ return Objects.equals(valueSchema, that.valueSchema);
}
@Override
public int hashCode() {
- return Objects.hash(schema);
+ return Objects.hash(valueSchema);
}
@Override
public String toString() {
- return String.format("", schema);
+ return String.format("", valueSchema);
}
}
diff --git a/connectors/rocketmq-connect-jdbc/rocketmq-connect-jdbc-core/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
similarity index 99%
rename from connectors/rocketmq-connect-jdbc/rocketmq-connect-jdbc-core/src/main/java/org/apache/rocketmq/connect/jdbc/sink/metadata/SinkRecordField.java
rename to connectors/rocketmq-connect-jdbc/src/main/java/org/apache/rocketmq/connect/jdbc/sink/metadata/SinkRecordField.java
index 633ef366e..d11233fdf 100644
--- a/connectors/rocketmq-connect-jdbc/rocketmq-connect-jdbc-core/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
@@ -21,7 +21,6 @@
public class SinkRecordField {
-
private final Schema schema;
private final String name;
private final boolean isPrimaryKey;
diff --git a/connectors/rocketmq-connect-jdbc/rocketmq-connect-jdbc-core/src/main/java/org/apache/rocketmq/connect/jdbc/source/JdbcSourceConfig.java b/connectors/rocketmq-connect-jdbc/src/main/java/org/apache/rocketmq/connect/jdbc/source/JdbcSourceConfig.java
similarity index 70%
rename from connectors/rocketmq-connect-jdbc/rocketmq-connect-jdbc-core/src/main/java/org/apache/rocketmq/connect/jdbc/source/JdbcSourceConfig.java
rename to connectors/rocketmq-connect-jdbc/src/main/java/org/apache/rocketmq/connect/jdbc/source/JdbcSourceConfig.java
index 4becb900d..9682cf388 100644
--- a/connectors/rocketmq-connect-jdbc/rocketmq-connect-jdbc-core/src/main/java/org/apache/rocketmq/connect/jdbc/source/JdbcSourceConfig.java
+++ b/connectors/rocketmq-connect-jdbc/src/main/java/org/apache/rocketmq/connect/jdbc/source/JdbcSourceConfig.java
@@ -17,59 +17,27 @@
package org.apache.rocketmq.connect.jdbc.source;
import io.openmessaging.KeyValue;
-import org.apache.rocketmq.connect.jdbc.config.AbstractConfig;
-import org.apache.rocketmq.connect.jdbc.util.NumericMapping;
-import org.apache.rocketmq.connect.jdbc.util.TableType;
-
import java.time.ZoneId;
import java.util.EnumSet;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.TimeZone;
+import org.apache.rocketmq.connect.jdbc.config.AbstractConfig;
+import org.apache.rocketmq.connect.jdbc.util.NumericMapping;
+import org.apache.rocketmq.connect.jdbc.util.TableType;
/**
* jdbc source config
*/
public class JdbcSourceConfig extends AbstractConfig {
- /**
- * table load mode
- */
- public enum TableLoadMode {
- MODE_BULK("bulk"),
- MODE_TIMESTAMP("timestamp"),
- MODE_INCREMENTING("incrementing"),
- MODE_TIMESTAMP_INCREMENTING("timestamp+incrementing");
- private String name;
-
- TableLoadMode(String name) {
- this.name = name;
- }
-
- public String getName() {
- return name;
- }
-
- public void setName(String name) {
- this.name = 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);
- }
- }
//source poll interval ms
public static final String POLL_INTERVAL_MS_CONFIG = "poll.interval.ms";
- private static final String POLL_INTERVAL_MS_DOC = "Frequency in ms to poll for new data in each table.";
public static final int POLL_INTERVAL_MS_DEFAULT = 5000;
- // batch max rows
+ // batch max rows
public static final String BATCH_MAX_ROWS_CONFIG = "batch.max.rows";
public static final int BATCH_MAX_ROWS_DEFAULT = 100;
@@ -82,7 +50,6 @@ public static TableLoadMode findTableLoadModeByName(String name) {
public static final String NUMERIC_MAPPING_DEFAULT = null;
// dialect name
-
public static final String DIALECT_NAME_CONFIG = "dialect.name";
public static final String DIALECT_NAME_DEFAULT = "";
@@ -91,85 +58,44 @@ public static TableLoadMode findTableLoadModeByName(String name) {
// incrementing column name
public static final String INCREMENTING_COLUMN_NAME_CONFIG = "incrementing.column.name";
- public static final String INCREMENTING_COLUMN_NAME_DEFAULT = "";
// timestamp column name
public static final String TIMESTAMP_COLUMN_NAME_CONFIG = "timestamp.column.name";
- public static final String TIMESTAMP_COLUMN_NAME_DEFAULT = "";
-
// timestamp initial
public static final String TIMESTAMP_INITIAL_CONFIG = "timestamp.initial";
public static final Long TIMESTAMP_INITIAL_DEFAULT = null;
public static final long TIMESTAMP_INITIAL_CURRENT = Long.valueOf(-1);
- // Metadata Change Monitoring Interval (ms)
- public static final String TABLE_POLL_INTERVAL_MS_CONFIG = "table.poll.interval.ms";
- public static final long TABLE_POLL_INTERVAL_MS_DEFAULT = 60 * 1000;
-
// table white list
public static final String TABLE_WHITELIST_CONFIG = "table.whitelist";
- public static final String TABLE_WHITELIST_DEFAULT = "";
// table black list
public static final String TABLE_BLACKLIST_CONFIG = "table.blacklist";
public static final String TABLE_BLACKLIST_DEFAULT = "";
public static final String SCHEMA_PATTERN_CONFIG = "schema.pattern";
- private static final String SCHEMA_PATTERN_DOC =
- "Schema pattern to fetch table metadata from the database.\n"
- + " * ``\"\"`` retrieves those without a schema.\n"
- + " * null (default) indicates that the schema name is not used to narrow the search and "
- + "that all table metadata is fetched, regardless of the schema.";
- private static final String SCHEMA_PATTERN_DISPLAY = "Schema pattern";
+
public static final String SCHEMA_PATTERN_DEFAULT = null;
public static final String CATALOG_PATTERN_CONFIG = "catalog.pattern";
- private static final String CATALOG_PATTERN_DOC =
- "Catalog pattern to fetch table metadata from the database.\n"
- + " * ``\"\"`` retrieves those without a catalog \n"
- + " * null (default) indicates that the schema name is not used to narrow the search and "
- + "that all table metadata is fetched, regardless of the catalog.";
- private static final String CATALOG_PATTERN_DISPLAY = "Schema pattern";
+
public static final String CATALOG_PATTERN_DEFAULT = null;
public static final String QUERY_CONFIG = "query";
- public static final String QUERY_DEFAULT = "";
public static final String TOPIC_PREFIX_CONFIG = "topic.prefix";
- private static final String TOPIC_PREFIX_DOC =
- "Prefix to prepend to table names to generate the name of the Kafka topic to publish data "
- + "to, or in the case of a custom query, the full name of the topic to publish to.";
- private static final String TOPIC_PREFIX_DISPLAY = "Topic Prefix";
-
/**
* validate non null
*/
public static final String VALIDATE_NON_NULL_CONFIG = "validate.non.null";
- private static final String VALIDATE_NON_NULL_DOC =
- "By default, the JDBC connector will validate that all incrementing and timestamp tables "
- + "have NOT NULL set for the columns being used as their ID/timestamp. If the tables don't,"
- + " JDBC connector will fail to start. Setting this to false will disable these checks.";
+
public static final boolean VALIDATE_NON_NULL_DEFAULT = true;
- private static final String VALIDATE_NON_NULL_DISPLAY = "Validate Non Null";
public static final String TIMESTAMP_DELAY_INTERVAL_MS_CONFIG = "timestamp.delay.interval.ms";
- private static final String TIMESTAMP_DELAY_INTERVAL_MS_DOC =
- "How long to wait after a row with certain timestamp appears before we include it in the "
- + "result. You may choose to add some delay to allow transactions with earlier timestamp to"
- + " complete. The first execution will fetch all available records (i.e. starting at "
- + "timestamp 0) until current time minus the delay. Every following execution will get data"
- + " from the last time we fetched until current time minus the delay.";
- public static final long TIMESTAMP_DELAY_INTERVAL_MS_DEFAULT = 0;
- private static final String TIMESTAMP_DELAY_INTERVAL_MS_DISPLAY = "Delay Interval (ms)";
-
public static final String DB_TIMEZONE_CONFIG = "db.timezone";
public static final String DB_TIMEZONE_DEFAULT = "UTC";
- private static final String DB_TIMEZONE_CONFIG_DOC =
- "Name of the JDBC timezone used in the connector when "
- + "querying with time-based criteria. Defaults to UTC.";
- private static final String DB_TIMEZONE_CONFIG_DISPLAY = "DB time zone";
public static final String TABLE_TYPE_DEFAULT = "TABLE";
public static final String TABLE_TYPE_CONFIG = "table.types";
diff --git a/connectors/rocketmq-connect-jdbc/rocketmq-connect-jdbc-core/src/main/java/org/apache/rocketmq/connect/jdbc/source/BaseSourceConnector.java b/connectors/rocketmq-connect-jdbc/src/main/java/org/apache/rocketmq/connect/jdbc/source/JdbcSourceConnector.java
similarity index 91%
rename from connectors/rocketmq-connect-jdbc/rocketmq-connect-jdbc-core/src/main/java/org/apache/rocketmq/connect/jdbc/source/BaseSourceConnector.java
rename to connectors/rocketmq-connect-jdbc/src/main/java/org/apache/rocketmq/connect/jdbc/source/JdbcSourceConnector.java
index ac3eb98ad..a98b9826e 100644
--- a/connectors/rocketmq-connect-jdbc/rocketmq-connect-jdbc-core/src/main/java/org/apache/rocketmq/connect/jdbc/source/BaseSourceConnector.java
+++ b/connectors/rocketmq-connect-jdbc/src/main/java/org/apache/rocketmq/connect/jdbc/source/JdbcSourceConnector.java
@@ -19,24 +19,23 @@
import com.google.common.collect.Lists;
import io.openmessaging.KeyValue;
+import io.openmessaging.connector.api.component.task.Task;
import io.openmessaging.connector.api.component.task.source.SourceConnector;
import io.openmessaging.internal.DefaultKeyValue;
+import java.util.List;
import org.apache.commons.lang3.StringUtils;
import org.apache.rocketmq.connect.jdbc.util.ConnectorGroupUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
-import java.util.List;
-
/**
* jdbc source connector
*/
-public abstract class BaseSourceConnector extends SourceConnector {
- private static final Logger log = LoggerFactory.getLogger(BaseSourceConnector.class);
+public class JdbcSourceConnector extends SourceConnector {
+ private static final Logger log = LoggerFactory.getLogger(JdbcSourceConnector.class);
private JdbcSourceConfig jdbcSourceConfig;
private KeyValue originalConfig;
-
/**
* Should invoke before start the connector.
*
@@ -95,4 +94,9 @@ public List taskConfigs(int maxTasks) {
}
return keyValues;
}
+
+ @Override
+ public Class extends Task> taskClass() {
+ return JdbcSourceTask.class;
+ }
}
diff --git a/connectors/rocketmq-connect-jdbc/rocketmq-connect-jdbc-core/src/main/java/org/apache/rocketmq/connect/jdbc/source/BaseSourceTask.java b/connectors/rocketmq-connect-jdbc/src/main/java/org/apache/rocketmq/connect/jdbc/source/JdbcSourceTask.java
similarity index 63%
rename from connectors/rocketmq-connect-jdbc/rocketmq-connect-jdbc-core/src/main/java/org/apache/rocketmq/connect/jdbc/source/BaseSourceTask.java
rename to connectors/rocketmq-connect-jdbc/src/main/java/org/apache/rocketmq/connect/jdbc/source/JdbcSourceTask.java
index 437fda095..6c79e15cb 100644
--- a/connectors/rocketmq-connect-jdbc/rocketmq-connect-jdbc-core/src/main/java/org/apache/rocketmq/connect/jdbc/source/BaseSourceTask.java
+++ b/connectors/rocketmq-connect-jdbc/src/main/java/org/apache/rocketmq/connect/jdbc/source/JdbcSourceTask.java
@@ -20,16 +20,6 @@
import io.openmessaging.KeyValue;
import io.openmessaging.connector.api.component.task.source.SourceTask;
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.provider.CachedConnectionProvider;
-import org.apache.rocketmq.connect.jdbc.source.offset.SourceOffsetCompute;
-import org.apache.rocketmq.connect.jdbc.source.querier.BulkQuerier;
-import org.apache.rocketmq.connect.jdbc.source.querier.Querier;
-import org.apache.rocketmq.connect.jdbc.source.querier.TimestampIncrementingQuerier;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
import java.sql.Connection;
import java.sql.SQLException;
import java.util.ArrayList;
@@ -42,18 +32,30 @@
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.function.Function;
import java.util.stream.Collectors;
+import org.apache.commons.lang3.StringUtils;
+import org.apache.rocketmq.connect.jdbc.connection.CachedConnectionProvider;
+import org.apache.rocketmq.connect.jdbc.dialect.DatabaseDialect;
+import org.apache.rocketmq.connect.jdbc.dialect.DatabaseDialectLoader;
+import org.apache.rocketmq.connect.jdbc.source.common.IncrementContext;
+import org.apache.rocketmq.connect.jdbc.source.common.QueryContext;
+import org.apache.rocketmq.connect.jdbc.source.common.QueryMode;
+import org.apache.rocketmq.connect.jdbc.source.common.TableLoadMode;
+import org.apache.rocketmq.connect.jdbc.source.offset.SourceOffsetCompute;
+import org.apache.rocketmq.connect.jdbc.source.querier.BulkQuerier;
+import org.apache.rocketmq.connect.jdbc.source.querier.Querier;
+import org.apache.rocketmq.connect.jdbc.source.querier.TimestampIncrementingQuerier;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
/**
* jdbc source task
*/
-public abstract class BaseSourceTask extends SourceTask {
-
- private static final Logger log = LoggerFactory.getLogger(BaseSourceTask.class);
+public class JdbcSourceTask extends SourceTask {
+ private static final Logger log = LoggerFactory.getLogger(JdbcSourceTask.class);
private static final int CONSECUTIVE_EMPTY_RESULTS_BEFORE_RETURN = 3;
private JdbcSourceTaskConfig config;
private DatabaseDialect dialect;
private CachedConnectionProvider cachedConnectionProvider;
-
BlockingQueue tableQueue = new LinkedBlockingQueue();
private final AtomicBoolean running = new AtomicBoolean(false);
@@ -63,37 +65,21 @@ public List poll() {
Map consecutiveEmptyResults = tableQueue.stream().collect(Collectors.toMap(Function.identity(), (q) -> 0));
while (running.get()) {
final Querier querier = tableQueue.peek();
- if (!querier.querying()) {
- // If not in the middle of an update, wait for next update time
- final long nextUpdate = querier.getLastUpdate() + config.getPollIntervalMs();
- final long now = System.currentTimeMillis();
- final long sleepMs = Math.min(nextUpdate - now, 100);
- if (sleepMs > 0) {
- log.trace("Waiting {} ms to poll {} next", nextUpdate - now, querier.toString());
- try {
- Thread.sleep(sleepMs);
- } catch (InterruptedException e) {
- Thread.currentThread().interrupt();
- }
- continue;
- }
- }
+ if (sleepIfNeed(querier))
+ continue;
// poll data
final List results = new ArrayList<>();
try {
log.debug("Checking for next block of results from {}", querier);
querier.maybeStartQuery(cachedConnectionProvider);
- int batchMaxRows = config.getBatchMaxRows();
boolean hasNext = true;
- while (results.size() < batchMaxRows && (hasNext = querier.hasNext())) {
+ while (results.size() < config.getBatchMaxRows() && (hasNext = querier.hasNext())) {
results.add(querier.extractRecord());
}
if (!hasNext) {
- // the querier to the tail of the queue
resetAndRequeueHead(querier);
}
-
if (results.isEmpty()) {
consecutiveEmptyResults.compute(querier, (k, v) -> v + 1);
log.trace("No updates for {}", querier);
@@ -106,7 +92,6 @@ public List poll() {
} else {
consecutiveEmptyResults.put(querier, 0);
}
-
log.debug("Returning {} records for {}", results.size(), querier.toString());
return results;
} catch (SQLException sqle) {
@@ -120,7 +105,7 @@ public List poll() {
throw t;
}
}
- // Only in case of shutdown
+
final Querier querier = tableQueue.peek();
if (querier != null) {
resetAndRequeueHead(querier);
@@ -129,6 +114,30 @@ public List poll() {
return null;
}
+ /**
+ * Sleep if need
+ *
+ * @param querier
+ * @return
+ */
+ private boolean sleepIfNeed(Querier querier) {
+ if (!querier.querying()) {
+ final long nextUpdate = querier.getLastUpdate() + config.getPollIntervalMs();
+ final long now = System.currentTimeMillis();
+ final long sleepMs = Math.min(nextUpdate - now, 100);
+ if (sleepMs > 0) {
+ log.trace("Waiting {} ms to poll {} next", nextUpdate - now, querier);
+ try {
+ Thread.sleep(sleepMs);
+ } catch (InterruptedException e) {
+ Thread.currentThread().interrupt();
+ }
+ return true;
+ }
+ }
+ return false;
+ }
+
private void resetAndRequeueHead(Querier querier) {
log.debug("Resetting querier {}", querier.toString());
tableQueue.poll();
@@ -158,23 +167,20 @@ public void validate(KeyValue config) {
public void start(KeyValue props) {
// init config
config = new JdbcSourceTaskConfig(props);
- this.dialect = newDialect(config);
- final int maxConnAttempts = config.getAttempts();
- final long retryBackoff = config.getBackoffMs();
- cachedConnectionProvider = connectionProvider(maxConnAttempts, retryBackoff);
+ this.dialect = DatabaseDialectLoader.getDatabaseDialect(config);
+ cachedConnectionProvider = connectionProvider(config.getAttempts(), config.getBackoffMs());
log.info("Using JDBC dialect {}", dialect.name());
-
// compute table offset
Map> offsetValues = SourceOffsetCompute.initOffset(config, sourceTaskContext, dialect, cachedConnectionProvider);
for (String tableOrQuery : offsetValues.keySet()) {
this.buildAndAddQuerier(
- JdbcSourceConfig.TableLoadMode.findTableLoadModeByName(this.config.getMode()),
- this.config.getQuerySuffix(),
- this.config.getIncrementingColumnName(),
- this.config.getTimestampColumnNames(),
- this.config.getTimestampDelayIntervalMs(),
- this.config.getTimeZone(), tableOrQuery,
- offsetValues.get(tableOrQuery)
+ TableLoadMode.findTableLoadModeByName(this.config.getMode()),
+ this.config.getQuerySuffix(),
+ this.config.getIncrementingColumnName(),
+ this.config.getTimestampColumnNames(),
+ this.config.getTimestampDelayIntervalMs(),
+ this.config.getTimeZone(), tableOrQuery,
+ offsetValues.get(tableOrQuery)
);
}
running.set(true);
@@ -193,73 +199,87 @@ public void start(KeyValue props) {
* @param tableOrQuery
* @param offset
*/
- private void buildAndAddQuerier(JdbcSourceConfig.TableLoadMode loadMode, String querySuffix, String incrementingColumn, List timestampColumns, Long timestampDelayInterval, TimeZone timeZone, String tableOrQuery, Map offset) {
+ private void buildAndAddQuerier(TableLoadMode loadMode, String querySuffix, String incrementingColumn,
+ List timestampColumns, Long timestampDelayInterval, TimeZone timeZone, String tableOrQuery,
+ Map offset) {
String topicPrefix = config.getTopicPrefix();
- Querier.QueryMode queryMode = !StringUtils.isEmpty(config.getQuery()) ? Querier.QueryMode.QUERY : Querier.QueryMode.TABLE;
- Querier querier = null;
+ QueryMode queryMode = !StringUtils.isEmpty(config.getQuery()) ? QueryMode.QUERY : QueryMode.TABLE;
+ Querier querier;
switch (loadMode) {
case MODE_BULK:
querier = new BulkQuerier(
- dialect,
- queryMode,
- tableOrQuery,
- topicPrefix,
- querySuffix,
- this.config.getOffsetSuffix()
+ dialect,
+ getContext(querySuffix, tableOrQuery, topicPrefix, queryMode)
);
tableQueue.add(querier);
break;
case MODE_INCREMENTING:
querier = new TimestampIncrementingQuerier(
- dialect,
- queryMode,
- tableOrQuery,
- topicPrefix,
- null,
- incrementingColumn,
- offset,
- timestampDelayInterval,
- timeZone,
- querySuffix,
- this.config.getOffsetSuffix()
+ dialect,
+ this.getIncrementContext(querySuffix, tableOrQuery, topicPrefix, queryMode, null, incrementingColumn, offset, timestampDelayInterval, timeZone)
);
tableQueue.add(querier);
break;
case MODE_TIMESTAMP:
querier = new TimestampIncrementingQuerier(
- dialect,
- queryMode,
- tableOrQuery,
- topicPrefix,
- timestampColumns,
- null,
- offset,
- timestampDelayInterval,
- timeZone,
- querySuffix,
- this.config.getOffsetSuffix()
+ dialect,
+ this.getIncrementContext(querySuffix, tableOrQuery, topicPrefix, queryMode, timestampColumns, null, offset, timestampDelayInterval, timeZone)
);
tableQueue.add(querier);
break;
case MODE_TIMESTAMP_INCREMENTING:
querier = new TimestampIncrementingQuerier(
- dialect,
- queryMode,
- tableOrQuery,
- topicPrefix,
- timestampColumns,
- incrementingColumn,
- offset,
- timestampDelayInterval,
- timeZone,
- querySuffix,
- this.config.getOffsetSuffix()
+ dialect,
+ this.getIncrementContext(querySuffix, tableOrQuery, topicPrefix, queryMode, timestampColumns, incrementingColumn, offset, timestampDelayInterval, timeZone)
);
tableQueue.add(querier);
break;
}
}
+ // Common context
+ private QueryContext getContext(String querySuffix, String tableOrQuery, String topicPrefix, QueryMode queryMode) {
+ QueryContext context = new QueryContext(
+ queryMode,
+ queryMode == QueryMode.TABLE ? dialect.parseTableNameToTableId(tableOrQuery) : null,
+ queryMode == QueryMode.QUERY ? tableOrQuery : null,
+ topicPrefix,
+ this.config.getOffsetSuffix(),
+ querySuffix,
+ config.getBatchMaxRows()
+ );
+ return context;
+ }
+
+ // Increment context
+ private IncrementContext getIncrementContext(
+ String querySuffix,
+ String tableOrQuery,
+ String topicPrefix,
+ QueryMode queryMode,
+ List timestampColumnNames,
+ String incrementingColumnName,
+ Map offsetMap,
+ Long timestampDelay,
+ TimeZone timeZone
+
+ ) {
+ IncrementContext context = new IncrementContext(
+ queryMode,
+ queryMode == QueryMode.TABLE ? dialect.parseTableNameToTableId(tableOrQuery) : null,
+ queryMode == QueryMode.QUERY ? tableOrQuery : null,
+ topicPrefix,
+ this.config.getOffsetSuffix(),
+ querySuffix,
+ config.getBatchMaxRows(),
+ timestampColumnNames != null ? timestampColumnNames : Collections.emptyList(),
+ incrementingColumnName,
+ offsetMap,
+ timestampDelay,
+ timeZone
+ );
+ return context;
+ }
protected CachedConnectionProvider connectionProvider(int maxConnAttempts, long retryBackoff) {
return new CachedConnectionProvider(dialect, maxConnAttempts, retryBackoff) {
@@ -299,6 +319,4 @@ protected void closeResources() {
}
}
}
-
- protected abstract DatabaseDialect newDialect(JdbcSourceTaskConfig config);
}
diff --git a/connectors/rocketmq-connect-jdbc/rocketmq-connect-jdbc-core/src/main/java/org/apache/rocketmq/connect/jdbc/source/JdbcSourceTaskConfig.java b/connectors/rocketmq-connect-jdbc/src/main/java/org/apache/rocketmq/connect/jdbc/source/JdbcSourceTaskConfig.java
similarity index 99%
rename from connectors/rocketmq-connect-jdbc/rocketmq-connect-jdbc-core/src/main/java/org/apache/rocketmq/connect/jdbc/source/JdbcSourceTaskConfig.java
rename to connectors/rocketmq-connect-jdbc/src/main/java/org/apache/rocketmq/connect/jdbc/source/JdbcSourceTaskConfig.java
index ea779dfc5..5e6c49bf1 100644
--- a/connectors/rocketmq-connect-jdbc/rocketmq-connect-jdbc-core/src/main/java/org/apache/rocketmq/connect/jdbc/source/JdbcSourceTaskConfig.java
+++ b/connectors/rocketmq-connect-jdbc/src/main/java/org/apache/rocketmq/connect/jdbc/source/JdbcSourceTaskConfig.java
@@ -17,7 +17,6 @@
package org.apache.rocketmq.connect.jdbc.source;
import io.openmessaging.KeyValue;
-
import java.util.List;
diff --git a/connectors/rocketmq-connect-jdbc/rocketmq-connect-jdbc-core/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
similarity index 89%
rename from connectors/rocketmq-connect-jdbc/rocketmq-connect-jdbc-core/src/main/java/org/apache/rocketmq/connect/jdbc/source/TimestampIncrementingCriteria.java
rename to connectors/rocketmq-connect-jdbc/src/main/java/org/apache/rocketmq/connect/jdbc/source/TimestampIncrementingCriteria.java
index 22aeb4cbf..210b03acb 100644
--- a/connectors/rocketmq-connect-jdbc/rocketmq-connect-jdbc-core/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
@@ -20,13 +20,6 @@
import io.openmessaging.connector.api.data.Schema;
import io.openmessaging.connector.api.data.Struct;
import io.openmessaging.connector.api.errors.ConnectException;
-import org.apache.rocketmq.connect.jdbc.schema.column.ColumnId;
-import org.apache.rocketmq.connect.jdbc.source.offset.TimestampIncrementingOffset;
-import org.apache.rocketmq.connect.jdbc.util.DateTimeUtils;
-import org.apache.rocketmq.connect.jdbc.util.ExpressionBuilder;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
import java.math.BigDecimal;
import java.sql.PreparedStatement;
import java.sql.SQLException;
@@ -35,6 +28,12 @@
import java.util.List;
import java.util.TimeZone;
import java.util.stream.Collectors;
+import org.apache.rocketmq.connect.jdbc.schema.column.ColumnId;
+import org.apache.rocketmq.connect.jdbc.source.offset.TimestampIncrementingOffset;
+import org.apache.rocketmq.connect.jdbc.util.DateTimeUtils;
+import org.apache.rocketmq.connect.jdbc.util.ExpressionBuilder;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
public class TimestampIncrementingCriteria {
@@ -42,29 +41,8 @@ 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
- */
Long lastIncrementedValue() throws SQLException;
}
@@ -94,11 +72,6 @@ protected boolean hasIncrementedColumn() {
return incrementingColumn != null;
}
- /**
- * 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);
@@ -109,14 +82,6 @@ public void whereClause(ExpressionBuilder builder) {
}
}
- /**
- * 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
@@ -306,8 +271,6 @@ protected void timestampIncrementingWhereClause(ExpressionBuilder builder) {
builder.append(" ASC");
}
- // where id > ? order by id asc
- // where id > ? and id < ? order by id asc
protected void incrementingWhereClause(ExpressionBuilder builder) {
builder.append(" WHERE ");
builder.append(incrementingColumn);
diff --git a/connectors/rocketmq-connect-jdbc/rocketmq-connect-jdbc-core/src/main/java/org/apache/rocketmq/connect/jdbc/source/metadata/ColumnMapping.java b/connectors/rocketmq-connect-jdbc/src/main/java/org/apache/rocketmq/connect/jdbc/source/common/ColumnMapping.java
similarity index 66%
rename from connectors/rocketmq-connect-jdbc/rocketmq-connect-jdbc-core/src/main/java/org/apache/rocketmq/connect/jdbc/source/metadata/ColumnMapping.java
rename to connectors/rocketmq-connect-jdbc/src/main/java/org/apache/rocketmq/connect/jdbc/source/common/ColumnMapping.java
index 6c1804065..0a528a4b4 100644
--- a/connectors/rocketmq-connect-jdbc/rocketmq-connect-jdbc-core/src/main/java/org/apache/rocketmq/connect/jdbc/source/metadata/ColumnMapping.java
+++ b/connectors/rocketmq-connect-jdbc/src/main/java/org/apache/rocketmq/connect/jdbc/source/common/ColumnMapping.java
@@ -14,63 +14,44 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package org.apache.rocketmq.connect.jdbc.source.metadata;
+package org.apache.rocketmq.connect.jdbc.source.common;
import io.openmessaging.connector.api.data.Field;
-import org.apache.rocketmq.connect.jdbc.schema.column.ColumnDefinition;
-
-import java.sql.ResultSet;
import java.util.Objects;
+import org.apache.rocketmq.connect.jdbc.schema.column.ColumnDefinition;
/**
* column mapping
*/
public class ColumnMapping {
-
private final Field field;
- private final ColumnDefinition columnDefn;
+ private final ColumnDefinition columnDefinition;
private final int columnNumber;
private final int hash;
public ColumnMapping(
- ColumnDefinition columnDefn,
- int columnNumber,
- Field field
+ ColumnDefinition columnDefinition,
+ int columnNumber,
+ Field field
) {
- assert columnDefn != null;
+ assert columnDefinition != null;
assert field != null;
assert columnNumber > 0;
- this.columnDefn = columnDefn;
+ this.columnDefinition = columnDefinition;
this.field = field;
this.columnNumber = columnNumber;
- this.hash = Objects.hash(this.columnNumber, this.columnDefn, this.field);
+ this.hash = Objects.hash(this.columnNumber, this.columnDefinition, this.field);
}
- /**
- * Get this mapping's {@link Field}.
- *
- * @return the field; never null
- */
public Field field() {
return field;
}
- /**
- * Get this mapping's {@link ColumnDefinition result set column definition}.
- *
- * @return the column definition; never null
- */
- public ColumnDefinition columnDefn() {
- return columnDefn;
+ public ColumnDefinition columnDefinition() {
+ return columnDefinition;
}
- /**
- * Get the 1-based number of the column within the result set. This can be used to access the
- * corresponding value from the {@link ResultSet}.
- *
- * @return the column number within the {@link ResultSet}; always positive
- */
public int columnNumber() {
return columnNumber;
}
@@ -88,13 +69,13 @@ public boolean equals(Object obj) {
if (obj instanceof ColumnMapping) {
ColumnMapping that = (ColumnMapping) obj;
return this.columnNumber == that.columnNumber && Objects.equals(
- this.columnDefn, that.columnDefn) && Objects.equals(this.field, that.field);
+ this.columnDefinition, that.columnDefinition) && Objects.equals(this.field, that.field);
}
return false;
}
@Override
public String toString() {
- return field.getName() + " (col=" + columnNumber + ", " + columnDefn + ")";
+ return field.getName() + " (col=" + columnNumber + ", " + columnDefinition + ")";
}
}
diff --git a/connectors/rocketmq-connect-jdbc/src/main/java/org/apache/rocketmq/connect/jdbc/source/common/IncrementContext.java b/connectors/rocketmq-connect-jdbc/src/main/java/org/apache/rocketmq/connect/jdbc/source/common/IncrementContext.java
new file mode 100644
index 000000000..89cedc82d
--- /dev/null
+++ b/connectors/rocketmq-connect-jdbc/src/main/java/org/apache/rocketmq/connect/jdbc/source/common/IncrementContext.java
@@ -0,0 +1,93 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.rocketmq.connect.jdbc.source.common;
+
+import java.util.List;
+import java.util.Map;
+import java.util.TimeZone;
+import org.apache.rocketmq.connect.jdbc.schema.table.TableId;
+
+public class IncrementContext extends QueryContext {
+ public IncrementContext(
+ QueryMode mode,
+ TableId tableId,
+ String querySql,
+ String topicPrefix,
+ String offsetSuffix,
+ String querySuffix,
+ int batchMaxSize,
+ List timestampColumnNames,
+ String incrementingColumnName,
+ Map offsetMap,
+ Long timestampDelay,
+ TimeZone timeZone
+ ) {
+ super(mode, tableId, querySql, topicPrefix, offsetSuffix, querySuffix, batchMaxSize);
+ this.timestampColumnNames = timestampColumnNames;
+ this.incrementingColumnName = incrementingColumnName;
+ this.offsetMap = offsetMap;
+ this.timestampDelay = timestampDelay;
+ this.timeZone = timeZone;
+ }
+
+ private List timestampColumnNames;
+ private String incrementingColumnName;
+ private Map offsetMap;
+ private Long timestampDelay;
+ private TimeZone timeZone;
+
+ public List getTimestampColumnNames() {
+ return timestampColumnNames;
+ }
+
+ public void setTimestampColumnNames(List timestampColumnNames) {
+ this.timestampColumnNames = timestampColumnNames;
+ }
+
+ public String getIncrementingColumnName() {
+ return incrementingColumnName;
+ }
+
+ public void setIncrementingColumnName(String incrementingColumnName) {
+ this.incrementingColumnName = incrementingColumnName;
+ }
+
+ public Map getOffsetMap() {
+ return offsetMap;
+ }
+
+ public void setOffsetMap(Map offsetMap) {
+ this.offsetMap = offsetMap;
+ }
+
+ public Long getTimestampDelay() {
+ return timestampDelay;
+ }
+
+ public void setTimestampDelay(Long timestampDelay) {
+ this.timestampDelay = timestampDelay;
+ }
+
+ public TimeZone getTimeZone() {
+ return timeZone;
+ }
+
+ public void setTimeZone(TimeZone timeZone) {
+ this.timeZone = timeZone;
+ }
+}
diff --git a/connectors/rocketmq-connect-jdbc/src/main/java/org/apache/rocketmq/connect/jdbc/source/common/QueryContext.java b/connectors/rocketmq-connect-jdbc/src/main/java/org/apache/rocketmq/connect/jdbc/source/common/QueryContext.java
new file mode 100644
index 000000000..203e9b6df
--- /dev/null
+++ b/connectors/rocketmq-connect-jdbc/src/main/java/org/apache/rocketmq/connect/jdbc/source/common/QueryContext.java
@@ -0,0 +1,111 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.rocketmq.connect.jdbc.source.common;
+
+import org.apache.rocketmq.connect.jdbc.schema.table.TableId;
+
+public class QueryContext {
+
+ public QueryContext() {
+ }
+
+ public QueryContext(QueryMode mode, TableId tableId, String querySql, String topicPrefix, String offsetSuffix,
+ String querySuffix, int batchMaxSize) {
+ this.mode = mode;
+ this.tableId = tableId;
+ this.querySql = querySql;
+ this.topicPrefix = topicPrefix;
+ this.offsetSuffix = offsetSuffix;
+ this.querySuffix = querySuffix;
+ this.batchMaxSize = batchMaxSize;
+ this.lastUpdate = 0;
+ }
+
+ private QueryMode mode;
+ private TableId tableId;
+ private String querySql;
+ private String topicPrefix;
+ private String offsetSuffix;
+ private String querySuffix;
+ private int batchMaxSize;
+ private long lastUpdate = 0;
+
+ public QueryMode getMode() {
+ return mode;
+ }
+
+ public void setMode(QueryMode mode) {
+ this.mode = mode;
+ }
+
+ public TableId getTableId() {
+ return tableId;
+ }
+
+ public void setTableId(TableId tableId) {
+ this.tableId = tableId;
+ }
+
+ public String getQuerySql() {
+ return querySql;
+ }
+
+ public void setQuerySql(String querySql) {
+ this.querySql = querySql;
+ }
+
+ public String getTopicPrefix() {
+ return topicPrefix;
+ }
+
+ public void setTopicPrefix(String topicPrefix) {
+ this.topicPrefix = topicPrefix;
+ }
+
+ public String getOffsetSuffix() {
+ return offsetSuffix;
+ }
+
+ public void setOffsetSuffix(String offsetSuffix) {
+ this.offsetSuffix = offsetSuffix;
+ }
+
+ public String getQuerySuffix() {
+ return querySuffix;
+ }
+
+ public void setQuerySuffix(String querySuffix) {
+ this.querySuffix = querySuffix;
+ }
+
+ public int getBatchMaxSize() {
+ return batchMaxSize;
+ }
+
+ public void setBatchMaxSize(int batchMaxSize) {
+ this.batchMaxSize = batchMaxSize;
+ }
+
+ public long getLastUpdate() {
+ return lastUpdate;
+ }
+
+ public void setLastUpdate(long lastUpdate) {
+ this.lastUpdate = lastUpdate;
+ }
+}
diff --git a/connectors/rocketmq-connect-jdbc/rocketmq-connect-jdbc-mysql/src/main/java/org/apache/rocketmq/connect/jdbc/mysql/source/MysqlJdbcSourceConnector.java b/connectors/rocketmq-connect-jdbc/src/main/java/org/apache/rocketmq/connect/jdbc/source/common/QueryMode.java
similarity index 66%
rename from connectors/rocketmq-connect-jdbc/rocketmq-connect-jdbc-mysql/src/main/java/org/apache/rocketmq/connect/jdbc/mysql/source/MysqlJdbcSourceConnector.java
rename to connectors/rocketmq-connect-jdbc/src/main/java/org/apache/rocketmq/connect/jdbc/source/common/QueryMode.java
index 0732509b6..3f76387d7 100644
--- a/connectors/rocketmq-connect-jdbc/rocketmq-connect-jdbc-mysql/src/main/java/org/apache/rocketmq/connect/jdbc/mysql/source/MysqlJdbcSourceConnector.java
+++ b/connectors/rocketmq-connect-jdbc/src/main/java/org/apache/rocketmq/connect/jdbc/source/common/QueryMode.java
@@ -14,17 +14,10 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package org.apache.rocketmq.connect.jdbc.mysql.source;
-import io.openmessaging.connector.api.component.task.Task;
-import org.apache.rocketmq.connect.jdbc.source.BaseSourceConnector;
+package org.apache.rocketmq.connect.jdbc.source.common;
-/**
- * mysql jdbc source connector
- */
-public class MysqlJdbcSourceConnector extends BaseSourceConnector {
- @Override
- public Class extends Task> taskClass() {
- return MysqlJdbcSourceTask.class;
- }
-}
+public enum QueryMode {
+ TABLE,
+ QUERY
+}
\ No newline at end of file
diff --git a/connectors/rocketmq-connect-jdbc/src/main/java/org/apache/rocketmq/connect/jdbc/source/common/SchemaMapping.java b/connectors/rocketmq-connect-jdbc/src/main/java/org/apache/rocketmq/connect/jdbc/source/common/SchemaMapping.java
new file mode 100644
index 000000000..983208559
--- /dev/null
+++ b/connectors/rocketmq-connect-jdbc/src/main/java/org/apache/rocketmq/connect/jdbc/source/common/SchemaMapping.java
@@ -0,0 +1,98 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.rocketmq.connect.jdbc.source.common;
+
+import io.openmessaging.connector.api.data.Field;
+import io.openmessaging.connector.api.data.Schema;
+import io.openmessaging.connector.api.data.SchemaBuilder;
+import java.sql.Connection;
+import java.sql.ResultSetMetaData;
+import java.sql.SQLException;
+import java.util.Collection;
+import java.util.LinkedHashMap;
+import java.util.Map;
+import java.util.concurrent.atomic.AtomicInteger;
+import org.apache.rocketmq.connect.jdbc.converter.JdbcColumnConverter;
+import org.apache.rocketmq.connect.jdbc.dialect.DatabaseDialect;
+import org.apache.rocketmq.connect.jdbc.schema.column.ColumnDefinition;
+import org.apache.rocketmq.connect.jdbc.schema.column.ColumnId;
+import org.apache.rocketmq.connect.jdbc.schema.table.TableId;
+
+/**
+ * schema mapping
+ */
+public final class SchemaMapping {
+
+ private final Schema schema;
+ private final Map columnMappings;
+
+ private SchemaMapping(
+ Schema schema,
+ Map columnMappings
+ ) {
+ assert schema != null;
+ assert columnMappings != null;
+ assert !columnMappings.isEmpty();
+ this.schema = schema;
+ this.columnMappings = columnMappings;
+ }
+
+ public static SchemaMapping create(
+ Connection conn,
+ TableId tableId,
+ ResultSetMetaData metadata,
+ DatabaseDialect dialect
+ ) throws SQLException {
+ String schemaName = tableId != null ? tableId.tableName() : null;
+ // Get columns
+ Map columnDefinitionMap = dialect.describeColumns(conn, tableId, metadata);
+ Map columnMappingMap = new LinkedHashMap<>();
+
+ // Build schema
+ SchemaBuilder builder = SchemaBuilder.struct().name(schemaName);
+ JdbcColumnConverter jdbcColumnConverter = dialect.createJdbcColumnConverter();
+ AtomicInteger columnNumber = new AtomicInteger(0);
+ for (ColumnDefinition columnDefinition : columnDefinitionMap.values()) {
+ String fieldName = jdbcColumnConverter.convertToConnectFieldSchema(columnDefinition, builder);
+ if (fieldName == null) {
+ continue;
+ }
+ Field field = builder.field(fieldName);
+ ColumnMapping columnMapping = new ColumnMapping(columnDefinition, columnNumber.incrementAndGet(), field);
+ columnMappingMap.put(fieldName, columnMapping);
+ }
+ return new SchemaMapping(builder.build(), columnMappingMap);
+ }
+
+ /**
+ * schema
+ *
+ * @return
+ */
+ public Schema schema() {
+ return schema;
+ }
+
+ public Collection columnMappings() {
+ return columnMappings.values();
+ }
+
+ @Override
+ public String toString() {
+ return "Mapping for " + schema.getName();
+ }
+}
diff --git a/connectors/rocketmq-connect-jdbc/src/main/java/org/apache/rocketmq/connect/jdbc/source/common/TableLoadMode.java b/connectors/rocketmq-connect-jdbc/src/main/java/org/apache/rocketmq/connect/jdbc/source/common/TableLoadMode.java
new file mode 100644
index 000000000..7bac84690
--- /dev/null
+++ b/connectors/rocketmq-connect-jdbc/src/main/java/org/apache/rocketmq/connect/jdbc/source/common/TableLoadMode.java
@@ -0,0 +1,50 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.rocketmq.connect.jdbc.source.common;
+
+/**
+ * table load mode
+ */
+public enum TableLoadMode {
+ MODE_BULK("bulk"),
+ MODE_TIMESTAMP("timestamp"),
+ MODE_INCREMENTING("incrementing"),
+ MODE_TIMESTAMP_INCREMENTING("timestamp+incrementing");
+ private String name;
+
+ TableLoadMode(String name) {
+ this.name = 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);
+ }
+
+ public String getName() {
+ return name;
+ }
+
+ public void setName(String name) {
+ this.name = name;
+ }
+}
\ No newline at end of file
diff --git a/connectors/rocketmq-connect-jdbc/rocketmq-connect-jdbc-core/src/main/java/org/apache/rocketmq/connect/jdbc/source/offset/SourceOffsetCompute.java b/connectors/rocketmq-connect-jdbc/src/main/java/org/apache/rocketmq/connect/jdbc/source/offset/SourceOffsetCompute.java
similarity index 81%
rename from connectors/rocketmq-connect-jdbc/rocketmq-connect-jdbc-core/src/main/java/org/apache/rocketmq/connect/jdbc/source/offset/SourceOffsetCompute.java
rename to connectors/rocketmq-connect-jdbc/src/main/java/org/apache/rocketmq/connect/jdbc/source/offset/SourceOffsetCompute.java
index 0598542ec..d7f99f79f 100644
--- a/connectors/rocketmq-connect-jdbc/rocketmq-connect-jdbc-core/src/main/java/org/apache/rocketmq/connect/jdbc/source/offset/SourceOffsetCompute.java
+++ b/connectors/rocketmq-connect-jdbc/src/main/java/org/apache/rocketmq/connect/jdbc/source/offset/SourceOffsetCompute.java
@@ -16,27 +16,11 @@
*/
package org.apache.rocketmq.connect.jdbc.source.offset;
-
import com.google.common.collect.Maps;
import io.openmessaging.connector.api.component.task.source.SourceTaskContext;
import io.openmessaging.connector.api.data.RecordOffset;
import io.openmessaging.connector.api.data.RecordPartition;
import io.openmessaging.connector.api.errors.ConnectException;
-import org.apache.commons.lang3.StringUtils;
-import org.apache.rocketmq.connect.jdbc.common.JdbcSourceConfigConstants;
-import org.apache.rocketmq.connect.jdbc.dialect.DatabaseDialect;
-import org.apache.rocketmq.connect.jdbc.dialect.provider.CachedConnectionProvider;
-import org.apache.rocketmq.connect.jdbc.schema.column.ColumnDefinition;
-import org.apache.rocketmq.connect.jdbc.schema.column.ColumnId;
-import org.apache.rocketmq.connect.jdbc.schema.table.TableId;
-import org.apache.rocketmq.connect.jdbc.source.JdbcSourceConfig;
-import org.apache.rocketmq.connect.jdbc.source.JdbcSourceTaskConfig;
-import org.apache.rocketmq.connect.jdbc.source.querier.Querier;
-import org.apache.rocketmq.connect.jdbc.util.ExpressionBuilder;
-import org.apache.rocketmq.connect.jdbc.util.QuoteMethod;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
import java.sql.Connection;
import java.sql.SQLException;
import java.util.Calendar;
@@ -49,6 +33,22 @@
import java.util.Objects;
import java.util.Set;
import java.util.TimeZone;
+import org.apache.commons.lang3.StringUtils;
+import org.apache.rocketmq.connect.jdbc.common.JdbcSourceConfigConstants;
+import org.apache.rocketmq.connect.jdbc.dialect.DatabaseDialect;
+import org.apache.rocketmq.connect.jdbc.connection.CachedConnectionProvider;
+import org.apache.rocketmq.connect.jdbc.schema.column.ColumnDefinition;
+import org.apache.rocketmq.connect.jdbc.schema.column.ColumnId;
+import org.apache.rocketmq.connect.jdbc.schema.table.TableId;
+import org.apache.rocketmq.connect.jdbc.source.JdbcSourceTaskConfig;
+import org.apache.rocketmq.connect.jdbc.source.common.QueryMode;
+import org.apache.rocketmq.connect.jdbc.source.common.TableLoadMode;
+import org.apache.rocketmq.connect.jdbc.util.ExpressionBuilder;
+import org.apache.rocketmq.connect.jdbc.util.QuoteMethod;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import static org.apache.rocketmq.connect.jdbc.source.JdbcSourceConfig.TIMESTAMP_INITIAL_CURRENT;
/**
* offset compute utils
@@ -105,8 +105,8 @@ public static Map> initOffset(
List tables = config.getTables();
String query = config.getQuery();
- JdbcSourceConfig.TableLoadMode mode = JdbcSourceConfig.TableLoadMode.findTableLoadModeByName(config.getMode());
- Querier.QueryMode queryMode = !StringUtils.isEmpty(query) ? Querier.QueryMode.QUERY : Querier.QueryMode.TABLE;
+ TableLoadMode mode = TableLoadMode.findTableLoadModeByName(config.getMode());
+ QueryMode queryMode = !StringUtils.isEmpty(query) ? QueryMode.QUERY : QueryMode.TABLE;
// step 1 -——-- compute partitions
Map partitionsByTableFqn = buildTablePartitions(mode, queryMode, tables, dialect, config.getOffsetSuffix(), config.getTopicPrefix());
@@ -116,7 +116,7 @@ public static Map> initOffset(
offsets = context.offsetStorageReader().readOffsets(partitionsByTableFqn.values());
}
// step 3 ----- compute offset init value
- List tablesOrQuery = queryMode == Querier.QueryMode.QUERY ? Collections.singletonList(query) : tables;
+ List tablesOrQuery = queryMode == QueryMode.QUERY ? Collections.singletonList(query) : tables;
return initOffsetValues(
cachedConnectionProvider,
dialect, queryMode,
@@ -128,13 +128,13 @@ public static Map> initOffset(
}
private static Map> initOffsetValues(
- CachedConnectionProvider cachedConnectionProvider,
- DatabaseDialect dialect,
- Querier.QueryMode queryMode,
- Map partitionsByTableFqn,
- Map offsets,
- JdbcSourceTaskConfig config,
- List tablesOrQuery) {
+ CachedConnectionProvider cachedConnectionProvider,
+ DatabaseDialect dialect,
+ QueryMode queryMode,
+ Map partitionsByTableFqn,
+ Map offsets,
+ JdbcSourceTaskConfig config,
+ List tablesOrQuery) {
Map> offsetsValues = Maps.newHashMap();
@@ -219,20 +219,20 @@ private static void validateNonNullable(
} finally {
conn.setAutoCommit(autoCommit);
}
- if ((incrementalMode.equals(JdbcSourceConfig.TableLoadMode.MODE_INCREMENTING.getName())
- || incrementalMode.equals(JdbcSourceConfig.TableLoadMode.MODE_TIMESTAMP_INCREMENTING.getName()))
- && incrementingOptional) {
+ if ((incrementalMode.equals(TableLoadMode.MODE_INCREMENTING.getName())
+ || incrementalMode.equals(TableLoadMode.MODE_TIMESTAMP_INCREMENTING.getName()))
+ && incrementingOptional) {
throw new ConnectException("Cannot make incremental queries using incrementing column "
- + incrementingColumn + " on " + table + " because this column "
- + "is nullable.");
+ + incrementingColumn + " on " + table + " because this column "
+ + "is nullable.");
}
- if ((incrementalMode.equals(JdbcSourceConfig.TableLoadMode.MODE_TIMESTAMP.getName())
- || incrementalMode.equals(JdbcSourceConfig.TableLoadMode.MODE_TIMESTAMP_INCREMENTING.getName()))
- && !atLeastOneTimestampNotOptional) {
+ if ((incrementalMode.equals(TableLoadMode.MODE_TIMESTAMP.getName())
+ || incrementalMode.equals(TableLoadMode.MODE_TIMESTAMP_INCREMENTING.getName()))
+ && !atLeastOneTimestampNotOptional) {
throw new ConnectException("Cannot make incremental queries using timestamp columns "
- + timestampColumns + " on " + table + " because all of these "
- + "columns "
- + "nullable.");
+ + timestampColumns + " on " + table + " because all of these "
+ + "columns "
+ + "nullable.");
}
} catch (SQLException e) {
throw new ConnectException("Failed trying to validate that columns used for offsets are NOT"
@@ -242,14 +242,14 @@ private static void validateNonNullable(
private static Map computeInitialOffset(
- CachedConnectionProvider cachedConnectionProvider,
- DatabaseDialect dialect,
- Querier.QueryMode queryMode,
- String tableOrQuery,
- Map partitionOffset,
- TimeZone timezone,
- Long timestampInitial,
- List timestampColumns
+ CachedConnectionProvider cachedConnectionProvider,
+ DatabaseDialect dialect,
+ QueryMode queryMode,
+ String tableOrQuery,
+ Map partitionOffset,
+ TimeZone timezone,
+ Long timestampInitial,
+ List timestampColumns
) {
if (Objects.nonNull(partitionOffset)) {
return partitionOffset;
@@ -258,7 +258,7 @@ private static Map computeInitialOffset(
// no offsets found
if (timestampInitial != null) {
// start at the specified timestamp
- if (timestampInitial == JdbcSourceConfig.TIMESTAMP_INITIAL_CURRENT) {
+ if (timestampInitial == TIMESTAMP_INITIAL_CURRENT) {
// use the current time
try {
final Connection con = cachedConnectionProvider.getConnection();
@@ -273,7 +273,7 @@ private static Map computeInitialOffset(
log.info("No offsets found for '{}', so using configured timestamp {}", tableOrQuery,
timestampInitial);
} else {
- if (queryMode != Querier.QueryMode.TABLE || timestampColumns == null || timestampColumns.isEmpty()) {
+ if (queryMode != QueryMode.TABLE || timestampColumns == null || timestampColumns.isEmpty()) {
return initialPartitionOffset;
}
try {
@@ -301,22 +301,22 @@ private static Map computeInitialOffset(
* @return
*/
private static Map buildTablePartitions(
- JdbcSourceConfig.TableLoadMode tableLoadMode,
- Querier.QueryMode queryMode,
- List tables,
- DatabaseDialect dialect,
- String offsetSuffix, String topicPrefix) {
+ TableLoadMode tableLoadMode,
+ QueryMode queryMode,
+ List tables,
+ DatabaseDialect dialect,
+ String offsetSuffix, String topicPrefix) {
Map partitionsByTableFqn = new HashMap<>();
- if (tableLoadMode == JdbcSourceConfig.TableLoadMode.MODE_INCREMENTING
- || tableLoadMode == JdbcSourceConfig.TableLoadMode.MODE_TIMESTAMP
- || tableLoadMode == JdbcSourceConfig.TableLoadMode.MODE_TIMESTAMP_INCREMENTING) {
+ if (tableLoadMode == TableLoadMode.MODE_INCREMENTING
+ || tableLoadMode == TableLoadMode.MODE_TIMESTAMP
+ || tableLoadMode == TableLoadMode.MODE_TIMESTAMP_INCREMENTING) {
switch (queryMode) {
case TABLE:
for (String table : tables) {
// Find possible partition maps for different offset protocols
// We need to search by all offset protocol partition keys to support compatibility
- TableId tableId = dialect.parseToTableId(table);
+ TableId tableId = dialect.parseTableNameToTableId(table);
RecordPartition tablePartition = new RecordPartition(SourceOffsetCompute.sourcePartitions(topicPrefix, tableId, offsetSuffix));
partitionsByTableFqn.put(table, tablePartition);
}
diff --git a/connectors/rocketmq-connect-jdbc/rocketmq-connect-jdbc-core/src/main/java/org/apache/rocketmq/connect/jdbc/source/offset/TimestampIncrementingOffset.java b/connectors/rocketmq-connect-jdbc/src/main/java/org/apache/rocketmq/connect/jdbc/source/offset/TimestampIncrementingOffset.java
similarity index 99%
rename from connectors/rocketmq-connect-jdbc/rocketmq-connect-jdbc-core/src/main/java/org/apache/rocketmq/connect/jdbc/source/offset/TimestampIncrementingOffset.java
rename to connectors/rocketmq-connect-jdbc/src/main/java/org/apache/rocketmq/connect/jdbc/source/offset/TimestampIncrementingOffset.java
index 0dab7afde..82a8aafa2 100644
--- a/connectors/rocketmq-connect-jdbc/rocketmq-connect-jdbc-core/src/main/java/org/apache/rocketmq/connect/jdbc/source/offset/TimestampIncrementingOffset.java
+++ b/connectors/rocketmq-connect-jdbc/src/main/java/org/apache/rocketmq/connect/jdbc/source/offset/TimestampIncrementingOffset.java
@@ -16,16 +16,15 @@
*/
package org.apache.rocketmq.connect.jdbc.source.offset;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
import java.sql.Timestamp;
import java.util.HashMap;
import java.util.Map;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
/**
- * time stamp incrementing offset
+ * Timestamp incrementing offset
*/
public class TimestampIncrementingOffset {
private static final Logger log = LoggerFactory.getLogger(TimestampIncrementingOffset.class);
diff --git a/connectors/rocketmq-connect-jdbc/rocketmq-connect-jdbc-core/src/main/java/org/apache/rocketmq/connect/jdbc/source/querier/BulkQuerier.java b/connectors/rocketmq-connect-jdbc/src/main/java/org/apache/rocketmq/connect/jdbc/source/querier/BulkQuerier.java
similarity index 73%
rename from connectors/rocketmq-connect-jdbc/rocketmq-connect-jdbc-core/src/main/java/org/apache/rocketmq/connect/jdbc/source/querier/BulkQuerier.java
rename to connectors/rocketmq-connect-jdbc/src/main/java/org/apache/rocketmq/connect/jdbc/source/querier/BulkQuerier.java
index 9afc6ede4..fdbef6d98 100644
--- a/connectors/rocketmq-connect-jdbc/rocketmq-connect-jdbc-core/src/main/java/org/apache/rocketmq/connect/jdbc/source/querier/BulkQuerier.java
+++ b/connectors/rocketmq-connect-jdbc/src/main/java/org/apache/rocketmq/connect/jdbc/source/querier/BulkQuerier.java
@@ -22,20 +22,19 @@
import io.openmessaging.connector.api.data.Schema;
import io.openmessaging.connector.api.data.Struct;
import io.openmessaging.connector.api.errors.ConnectException;
-import org.apache.rocketmq.connect.jdbc.common.JdbcSourceConfigConstants;
-import org.apache.rocketmq.connect.jdbc.dialect.DatabaseDialect;
-import org.apache.rocketmq.connect.jdbc.source.metadata.SchemaMapping;
-import org.apache.rocketmq.connect.jdbc.util.ExpressionBuilder;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
import java.io.IOException;
import java.sql.Connection;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.HashMap;
import java.util.Map;
-
+import org.apache.rocketmq.connect.jdbc.common.JdbcSourceConfigConstants;
+import org.apache.rocketmq.connect.jdbc.dialect.DatabaseDialect;
+import org.apache.rocketmq.connect.jdbc.source.common.ColumnMapping;
+import org.apache.rocketmq.connect.jdbc.source.common.QueryContext;
+import org.apache.rocketmq.connect.jdbc.util.ExpressionBuilder;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
/**
* bulk mode
@@ -43,35 +42,28 @@
public class BulkQuerier extends Querier {
private static final Logger log = LoggerFactory.getLogger(BulkQuerier.class);
- public BulkQuerier(
- DatabaseDialect dialect,
- QueryMode mode,
- String name,
- String topicPrefix,
- String suffix,
- String offsetSuffix
- ) {
- super(dialect, mode, name, topicPrefix, suffix, offsetSuffix);
+ public BulkQuerier(DatabaseDialect dialect, QueryContext context) {
+ super(dialect, context);
}
@Override
protected void createPreparedStatement(Connection db) throws SQLException {
ExpressionBuilder builder = dialect.expressionBuilder();
- switch (mode) {
+ switch (context.getMode()) {
case TABLE:
- dialect.buildSelectTable(builder, tableId);
+ dialect.buildSelectTable(builder, context.getTableId());
break;
case QUERY:
- builder.append(query);
+ builder.append(context.getQuerySql());
break;
default:
- throw new ConnectException("Unknown mode: " + mode);
+ throw new ConnectException("Unknown mode: " + context.getMode());
}
String queryStr = builder.toString();
recordQuery(queryStr);
log.debug("{} prepared SQL query: {}", this, queryStr);
- stmt = dialect.createPreparedStatement(db, queryStr);
+ stmt = dialect.createPreparedStatement(db, queryStr, context.getBatchMaxSize());
}
@Override
@@ -87,9 +79,10 @@ protected ResultSet executeQuery() throws SQLException {
public ConnectRecord extractRecord() throws SQLException {
Schema schema = schemaMapping.schema();
Struct payload = new Struct(schema);
- for (SchemaMapping.FieldSetter setter : schemaMapping.fieldSetters()) {
+ for (ColumnMapping columnMapping : schemaMapping.columnMappings()) {
try {
- setter.setField(payload, resultSet);
+ Object value = jdbcColumnConverter.convertToConnectFieldValue(resultSet, columnMapping.columnDefinition(), columnMapping.columnNumber());
+ payload.put(columnMapping.field(), value);
} catch (IOException e) {
log.warn("Error mapping fields into Connect record", e);
throw new ConnectException(e);
@@ -98,30 +91,27 @@ public ConnectRecord extractRecord() throws SQLException {
throw new SQLException(e);
}
}
- // TODO: key from primary key? partition?
+
final String topic;
final Map partition = new HashMap<>();
- switch (mode) {
+ switch (context.getMode()) {
case TABLE:
- // backwards compatible
- String name = tableId.tableName();
- topic = topicPrefix + name;
- partition.put(JdbcSourceConfigConstants.TABLE_NAME_KEY(this.offsetSuffix), name);
+ String name = context.getTableId().tableName();
+ topic = context.getTopicPrefix() + name;
+ partition.put(JdbcSourceConfigConstants.TABLE_NAME_KEY(context.getOffsetSuffix()), name);
partition.put("topic", topic);
break;
case QUERY:
- partition.put(JdbcSourceConfigConstants.QUERY_NAME_KEY(this.offsetSuffix),
- JdbcSourceConfigConstants.QUERY_NAME_VALUE);
- topic = topicPrefix;
+ partition.put(JdbcSourceConfigConstants.QUERY_NAME_KEY(context.getOffsetSuffix()),
+ JdbcSourceConfigConstants.QUERY_NAME_VALUE);
+ topic = context.getTopicPrefix();
partition.put("topic", topic);
break;
default:
- throw new ConnectException("Unexpected query mode: " + mode);
+ throw new ConnectException("Unexpected query mode: " + context.getMode());
}
// build record
ConnectRecord record = new ConnectRecord(
- // offset partition
- // offset partition
new RecordPartition(partition),
new RecordOffset(new HashMap<>()),
System.currentTimeMillis(),
@@ -133,8 +123,8 @@ public ConnectRecord extractRecord() throws SQLException {
@Override
public String toString() {
- return "BulkTableQuerier{" + "table='" + tableId + '\'' + ", query='" + query + '\''
- + ", topicPrefix='" + topicPrefix + '\'' + '}';
+ return "BulkTableQuerier{" + "table='" + context.getMode() + '\'' + ", query='" + context.getQuerySql() + '\''
+ + ", topicPrefix='" + context.getTopicPrefix() + '\'' + '}';
}
}
diff --git a/connectors/rocketmq-connect-jdbc/rocketmq-connect-jdbc-core/src/main/java/org/apache/rocketmq/connect/jdbc/source/querier/Querier.java b/connectors/rocketmq-connect-jdbc/src/main/java/org/apache/rocketmq/connect/jdbc/source/querier/Querier.java
similarity index 71%
rename from connectors/rocketmq-connect-jdbc/rocketmq-connect-jdbc-core/src/main/java/org/apache/rocketmq/connect/jdbc/source/querier/Querier.java
rename to connectors/rocketmq-connect-jdbc/src/main/java/org/apache/rocketmq/connect/jdbc/source/querier/Querier.java
index 8052ec2d7..13fa3c64a 100644
--- a/connectors/rocketmq-connect-jdbc/rocketmq-connect-jdbc-core/src/main/java/org/apache/rocketmq/connect/jdbc/source/querier/Querier.java
+++ b/connectors/rocketmq-connect-jdbc/src/main/java/org/apache/rocketmq/connect/jdbc/source/querier/Querier.java
@@ -17,62 +17,37 @@
package org.apache.rocketmq.connect.jdbc.source.querier;
import io.openmessaging.connector.api.data.ConnectRecord;
-import org.apache.rocketmq.connect.jdbc.dialect.DatabaseDialect;
-import org.apache.rocketmq.connect.jdbc.dialect.provider.CachedConnectionProvider;
-import org.apache.rocketmq.connect.jdbc.schema.table.TableId;
-import org.apache.rocketmq.connect.jdbc.source.metadata.SchemaMapping;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
+import org.apache.rocketmq.connect.jdbc.converter.JdbcColumnConverter;
+import org.apache.rocketmq.connect.jdbc.dialect.DatabaseDialect;
+import org.apache.rocketmq.connect.jdbc.connection.CachedConnectionProvider;
+import org.apache.rocketmq.connect.jdbc.source.common.QueryContext;
+import org.apache.rocketmq.connect.jdbc.source.common.SchemaMapping;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
public abstract class Querier {
- public enum QueryMode {
- TABLE,
- QUERY
- }
-
private final Logger log = LoggerFactory.getLogger(Querier.class);
-
protected final DatabaseDialect dialect;
- protected final QueryMode mode;
- protected final String query;
- protected final String topicPrefix;
- protected final TableId tableId;
- protected final String suffix;
- protected String offsetSuffix;
- // Mutable state
-
- protected long lastUpdate;
+ protected final QueryContext context;
protected Connection db;
protected PreparedStatement stmt;
protected ResultSet resultSet;
protected SchemaMapping schemaMapping;
+ protected JdbcColumnConverter jdbcColumnConverter;
private String loggedQueryString;
- public Querier(
- DatabaseDialect dialect,
- QueryMode mode,
- String nameOrQuery,
- String topicPrefix,
- String suffix,
- String offsetSuffix
- ) {
+ public Querier(DatabaseDialect dialect, QueryContext context) {
this.dialect = dialect;
- this.mode = mode;
- this.tableId = mode.equals(QueryMode.TABLE) ? dialect.parseToTableId(nameOrQuery) : null;
- this.query = mode.equals(QueryMode.QUERY) ? nameOrQuery : null;
- this.topicPrefix = topicPrefix;
- this.lastUpdate = 0;
- this.suffix = suffix;
- this.offsetSuffix = offsetSuffix;
+ this.context = context;
+ this.jdbcColumnConverter = dialect.createJdbcColumnConverter();
}
public long getLastUpdate() {
- return lastUpdate;
+ return context.getLastUpdate();
}
public PreparedStatement getOrCreatePreparedStatement(Connection db) throws SQLException {
@@ -94,7 +69,7 @@ public void maybeStartQuery(CachedConnectionProvider provider) throws SQLExcepti
this.db = provider.getConnection();
stmt = getOrCreatePreparedStatement(db);
resultSet = executeQuery();
- schemaMapping = SchemaMapping.create(this.db, tableId, resultSet.getMetaData(), dialect);
+ schemaMapping = SchemaMapping.create(this.db, context.getTableId(), resultSet.getMetaData(), dialect);
}
}
@@ -110,10 +85,8 @@ public void reset(long now) {
closeResultSetQuietly();
closeStatementQuietly();
releaseLocksQuietly();
- // TODO: Can we cache this and quickly check that it's identical for the next query
- // instead of constructing from scratch since it's almost always the same
schemaMapping = null;
- lastUpdate = now;
+ context.setLastUpdate(now);
}
private void releaseLocksQuietly() {
@@ -157,8 +130,4 @@ protected void recordQuery(String query) {
loggedQueryString = query;
}
}
-
- public void setOffsetSuffix(String offsetSuffix) {
- this.offsetSuffix = offsetSuffix;
- }
}
diff --git a/connectors/rocketmq-connect-jdbc/rocketmq-connect-jdbc-core/src/main/java/org/apache/rocketmq/connect/jdbc/source/querier/TimestampIncrementingQuerier.java b/connectors/rocketmq-connect-jdbc/src/main/java/org/apache/rocketmq/connect/jdbc/source/querier/TimestampIncrementingQuerier.java
similarity index 68%
rename from connectors/rocketmq-connect-jdbc/rocketmq-connect-jdbc-core/src/main/java/org/apache/rocketmq/connect/jdbc/source/querier/TimestampIncrementingQuerier.java
rename to connectors/rocketmq-connect-jdbc/src/main/java/org/apache/rocketmq/connect/jdbc/source/querier/TimestampIncrementingQuerier.java
index dfb602066..ddee30351 100644
--- a/connectors/rocketmq-connect-jdbc/rocketmq-connect-jdbc-core/src/main/java/org/apache/rocketmq/connect/jdbc/source/querier/TimestampIncrementingQuerier.java
+++ b/connectors/rocketmq-connect-jdbc/src/main/java/org/apache/rocketmq/connect/jdbc/source/querier/TimestampIncrementingQuerier.java
@@ -22,19 +22,6 @@
import io.openmessaging.connector.api.data.Schema;
import io.openmessaging.connector.api.data.Struct;
import io.openmessaging.connector.api.errors.ConnectException;
-import org.apache.rocketmq.connect.jdbc.dialect.DatabaseDialect;
-import org.apache.rocketmq.connect.jdbc.dialect.provider.CachedConnectionProvider;
-import org.apache.rocketmq.connect.jdbc.schema.column.ColumnDefinition;
-import org.apache.rocketmq.connect.jdbc.schema.column.ColumnId;
-import org.apache.rocketmq.connect.jdbc.source.TimestampIncrementingCriteria;
-import org.apache.rocketmq.connect.jdbc.source.metadata.SchemaMapping;
-import org.apache.rocketmq.connect.jdbc.source.offset.SourceOffsetCompute;
-import org.apache.rocketmq.connect.jdbc.source.offset.TimestampIncrementingOffset;
-import org.apache.rocketmq.connect.jdbc.util.DateTimeUtils;
-import org.apache.rocketmq.connect.jdbc.util.ExpressionBuilder;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
import java.io.IOException;
import java.sql.Connection;
import java.sql.ResultSet;
@@ -42,10 +29,23 @@
import java.sql.SQLException;
import java.sql.Timestamp;
import java.util.ArrayList;
-import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.TimeZone;
+import org.apache.rocketmq.connect.jdbc.dialect.DatabaseDialect;
+import org.apache.rocketmq.connect.jdbc.connection.CachedConnectionProvider;
+import org.apache.rocketmq.connect.jdbc.schema.column.ColumnDefinition;
+import org.apache.rocketmq.connect.jdbc.schema.column.ColumnId;
+import org.apache.rocketmq.connect.jdbc.source.TimestampIncrementingCriteria;
+import org.apache.rocketmq.connect.jdbc.source.common.ColumnMapping;
+import org.apache.rocketmq.connect.jdbc.source.common.IncrementContext;
+import org.apache.rocketmq.connect.jdbc.source.common.SchemaMapping;
+import org.apache.rocketmq.connect.jdbc.source.offset.SourceOffsetCompute;
+import org.apache.rocketmq.connect.jdbc.source.offset.TimestampIncrementingOffset;
+import org.apache.rocketmq.connect.jdbc.util.DateTimeUtils;
+import org.apache.rocketmq.connect.jdbc.util.ExpressionBuilder;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
public class TimestampIncrementingQuerier extends Querier implements TimestampIncrementingCriteria.CriteriaValues {
private static final Logger log = LoggerFactory.getLogger(
@@ -59,66 +59,51 @@ public class TimestampIncrementingQuerier extends Querier implements TimestampIn
private TimestampIncrementingOffset offset;
private TimestampIncrementingCriteria criteria;
private Map partition;
- private final TimeZone timeZone;
+ private final TimeZone timeZone;
- public TimestampIncrementingQuerier(DatabaseDialect dialect,
- QueryMode mode,
- String name,
- String topicPrefix,
- List timestampColumnNames,
- String incrementingColumnName,
- Map offsetMap,
- Long timestampDelay,
- TimeZone timeZone,
- String suffix,
- String offsetSuffix) {
- super(dialect, mode, name, topicPrefix, suffix, offsetSuffix);
- this.incrementingColumnName = incrementingColumnName;
- this.timestampColumnNames = timestampColumnNames != null
- ? timestampColumnNames : Collections.emptyList();
- this.timestampDelay = timestampDelay;
- this.offset = TimestampIncrementingOffset.fromMap(offsetMap);
-
+ public TimestampIncrementingQuerier(DatabaseDialect dialect, IncrementContext context) {
+ super(dialect, context);
+ this.incrementingColumnName = context.getIncrementingColumnName();
+ this.timestampColumnNames = context.getTimestampColumnNames();
+ this.timestampDelay = context.getTimestampDelay();
+ this.offset = TimestampIncrementingOffset.fromMap(context.getOffsetMap());
this.timestampColumns = new ArrayList<>();
for (String timestampColumn : this.timestampColumnNames) {
if (timestampColumn != null && !timestampColumn.isEmpty()) {
- timestampColumns.add(new ColumnId(tableId, timestampColumn));
+ timestampColumns.add(new ColumnId(context.getTableId(), timestampColumn));
}
}
-
- switch (mode) {
+ switch (context.getMode()) {
case TABLE:
- partition = SourceOffsetCompute.sourcePartitions(topicPrefix, tableId, this.offsetSuffix);
+ partition = SourceOffsetCompute.sourcePartitions(context.getTopicPrefix(), context.getTableId(), context.getOffsetSuffix());
break;
case QUERY:
- partition = SourceOffsetCompute.sourceQueryPartitions(topicPrefix, this.offsetSuffix);
+ partition = SourceOffsetCompute.sourceQueryPartitions(context.getTopicPrefix(), context.getOffsetSuffix());
break;
default:
- throw new ConnectException("Unexpected query mode: " + mode);
+ throw new ConnectException("Unexpected query mode: " + context.getMode());
}
- this.timeZone = timeZone;
+ this.timeZone = context.getTimeZone();
}
@Override
protected void createPreparedStatement(Connection db) throws SQLException {
findDefaultAutoIncrementingColumn(db);
-
ColumnId incrementingColumn = null;
if (incrementingColumnName != null && !incrementingColumnName.isEmpty()) {
- incrementingColumn = new ColumnId(tableId, incrementingColumnName);
+ incrementingColumn = new ColumnId(context.getTableId(), incrementingColumnName);
}
-
ExpressionBuilder builder = dialect.expressionBuilder();
- switch (mode) {
+ switch (context.getMode()) {
case TABLE:
- dialect.buildSelectTable(builder, tableId);
+ dialect.buildSelectTable(builder, context.getTableId());
break;
case QUERY:
- builder.append(query);
+ builder.append(context.getQuerySql());
break;
default:
- throw new ConnectException("Unknown mode encountered when preparing query: " + mode);
+ throw new ConnectException("Unknown mode encountered when preparing query: " + context.getMode());
}
// Append the criteria using the columns ...
@@ -128,7 +113,7 @@ protected void createPreparedStatement(Connection db) throws SQLException {
String queryString = builder.toString();
recordQuery(queryString);
log.debug("{} prepared SQL query: {}", this, queryString);
- stmt = dialect.createPreparedStatement(db, queryString);
+ stmt = dialect.createPreparedStatement(db, queryString, context.getBatchMaxSize());
}
@Override
@@ -139,7 +124,7 @@ public void maybeStartQuery(CachedConnectionProvider provider) throws SQLExcepti
resultSet = executeQuery();
ResultSetMetaData metadata = resultSet.getMetaData();
dialect.validateColumnTypes(metadata, timestampColumns);
- schemaMapping = SchemaMapping.create(this.db, tableId, metadata, dialect);
+ schemaMapping = SchemaMapping.create(this.db, context.getTableId(), metadata, dialect);
}
}
@@ -147,23 +132,22 @@ private void findDefaultAutoIncrementingColumn(Connection db) throws SQLExceptio
// Default when unspecified uses an autoincrementing column
if (incrementingColumnName != null && incrementingColumnName.isEmpty()) {
// Find the first auto-incremented column ...
- for (ColumnDefinition defn : dialect.describeColumns(
- db,
- tableId.catalogName(),
- tableId.schemaName(),
- tableId.tableName(),
- null).values()) {
- if (defn.isAutoIncrement()) {
- incrementingColumnName = defn.id().name();
+ for (ColumnDefinition columnDefinition : dialect.describeColumns(
+ db,
+ context.getTableId().catalogName(),
+ context.getTableId().schemaName(),
+ context.getTableId().tableName(),
+ null).values()) {
+ if (columnDefinition.isAutoIncrement()) {
+ incrementingColumnName = columnDefinition.id().name();
break;
}
}
}
- // If still not found, query the table and use the result set metadata.
- // This doesn't work if the table is empty.
+
if (incrementingColumnName != null && incrementingColumnName.isEmpty()) {
- log.debug("Falling back to describe '{}' table by querying {}", tableId, db);
- for (ColumnDefinition defn : dialect.describeColumnsByQuerying(db, tableId).values()) {
+ log.debug("Falling back to describe '{}' table by querying {}", context.getTableId(), db);
+ for (ColumnDefinition defn : dialect.describeColumnsByQuerying(db, context.getTableId()).values()) {
if (defn.isAutoIncrement()) {
incrementingColumnName = defn.id().name();
break;
@@ -186,9 +170,10 @@ protected ResultSet executeQuery() throws SQLException {
public ConnectRecord extractRecord() throws SQLException {
Schema schema = schemaMapping.schema();
Struct payload = new Struct(schema);
- for (SchemaMapping.FieldSetter setter : schemaMapping.fieldSetters()) {
+ for (ColumnMapping columnMapping : schemaMapping.columnMappings()) {
try {
- setter.setField(payload, resultSet);
+ Object value = jdbcColumnConverter.convertToConnectFieldValue(resultSet, columnMapping.columnDefinition(), columnMapping.columnNumber());
+ payload.put(columnMapping.field(), value);
} catch (IOException e) {
log.warn("Error mapping fields into Connect record", e);
throw new ConnectException(e);
@@ -197,30 +182,36 @@ public ConnectRecord extractRecord() throws SQLException {
throw new SQLException(e);
}
}
- offset = criteria.extractValues(schemaMapping.schema(), payload, offset);
+
+ offset = criteria.extractValues(schema, payload, offset);
// build record
return new ConnectRecord(
- // offset partition
- new RecordPartition(partition),
- new RecordOffset(offset.toMap()),
- System.currentTimeMillis(),
- schema,
- payload
+ // offset partition
+ new RecordPartition(partition),
+ new RecordOffset(offset.toMap()),
+ System.currentTimeMillis(),
+ schema,
+ payload
);
}
-
+ /**
+ * get begin timestamp from offset topic
+ *
+ * @return
+ */
@Override
public Timestamp beginTimestampValue() {
return offset.getTimestampOffset();
}
+ //Get end timestamp from db
@Override
public Timestamp endTimestampValue(Timestamp beginTime) throws SQLException {
long endTimestamp;
final long currentDbTime = dialect.currentTimeOnDB(
- stmt.getConnection(),
- DateTimeUtils.getTimeZoneCalendar(timeZone)
+ stmt.getConnection(),
+ DateTimeUtils.getTimeZoneCalendar(timeZone)
).getTime();
endTimestamp = currentDbTime - timestampDelay;
return new Timestamp(endTimestamp);
@@ -234,14 +225,14 @@ public Long lastIncrementedValue() {
@Override
public String toString() {
return "TimestampIncrementingQuerier{"
- + "table=" + tableId
- + ", query='" + query + '\''
- + ", topicPrefix='" + topicPrefix + '\''
- + ", incrementingColumn='" + (incrementingColumnName != null
- ? incrementingColumnName
- : "") + '\''
- + ", timestampColumns=" + timestampColumnNames
- + '}';
+ + "table=" + context.getTableId()
+ + ", query='" + context.getQuerySql() + '\''
+ + ", topicPrefix='" + context.getTopicPrefix() + '\''
+ + ", incrementingColumn='" + (incrementingColumnName != null
+ ? incrementingColumnName
+ : "") + '\''
+ + ", timestampColumns=" + timestampColumnNames
+ + '}';
}
}
diff --git a/connectors/rocketmq-connect-jdbc/rocketmq-connect-jdbc-core/src/main/java/org/apache/rocketmq/connect/jdbc/util/BytesUtil.java b/connectors/rocketmq-connect-jdbc/src/main/java/org/apache/rocketmq/connect/jdbc/util/BytesUtil.java
similarity index 99%
rename from connectors/rocketmq-connect-jdbc/rocketmq-connect-jdbc-core/src/main/java/org/apache/rocketmq/connect/jdbc/util/BytesUtil.java
rename to connectors/rocketmq-connect-jdbc/src/main/java/org/apache/rocketmq/connect/jdbc/util/BytesUtil.java
index 3e08bdcba..da4241ba3 100644
--- a/connectors/rocketmq-connect-jdbc/rocketmq-connect-jdbc-core/src/main/java/org/apache/rocketmq/connect/jdbc/util/BytesUtil.java
+++ b/connectors/rocketmq-connect-jdbc/src/main/java/org/apache/rocketmq/connect/jdbc/util/BytesUtil.java
@@ -17,9 +17,7 @@
package org.apache.rocketmq.connect.jdbc.util;
public class BytesUtil {
-
private static final char[] HEX_CODE = "0123456789ABCDEF".toCharArray();
-
public static String toHex(byte[] data) {
StringBuilder r = new StringBuilder(data.length * 2);
for (byte b : data) {
diff --git a/connectors/rocketmq-connect-jdbc/rocketmq-connect-jdbc-core/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/util/ColumnDefAdjuster.java
similarity index 90%
rename from connectors/rocketmq-connect-jdbc/rocketmq-connect-jdbc-core/src/main/java/org/apache/rocketmq/connect/jdbc/schema/column/ColumnDefAdjuster.java
rename to connectors/rocketmq-connect-jdbc/src/main/java/org/apache/rocketmq/connect/jdbc/util/ColumnDefAdjuster.java
index 5e8b6c31c..4ca49b1ee 100644
--- a/connectors/rocketmq-connect-jdbc/rocketmq-connect-jdbc-core/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/util/ColumnDefAdjuster.java
@@ -14,7 +14,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package org.apache.rocketmq.connect.jdbc.schema.column;
+package org.apache.rocketmq.connect.jdbc.util;
import java.sql.Connection;
import java.sql.DatabaseMetaData;
@@ -22,6 +22,7 @@
import java.sql.SQLException;
import java.util.HashMap;
import java.util.Map;
+import org.apache.rocketmq.connect.jdbc.schema.column.ColumnDefinition;
public class ColumnDefAdjuster {
Map nullable = new HashMap<>();
@@ -35,9 +36,7 @@ public static ColumnDefAdjuster create(Connection conn,
String tablePattern,
String columnPattern) {
ColumnDefAdjuster adjuster = new ColumnDefAdjuster();
- try (ResultSet rs = conn.getMetaData().getColumns(
- catalogPattern, schemaPattern, tablePattern, columnPattern)) {
- final int rsColumnCount = rs.getMetaData().getColumnCount();
+ try (ResultSet rs = conn.getMetaData().getColumns(catalogPattern, schemaPattern, tablePattern, columnPattern)) {
while (rs.next()) {
final String columnName = rs.getString(4);
ColumnDefinition.Nullability nullability;
diff --git a/connectors/rocketmq-connect-jdbc/rocketmq-connect-jdbc-core/src/main/java/org/apache/rocketmq/connect/jdbc/util/ConnectorGroupUtils.java b/connectors/rocketmq-connect-jdbc/src/main/java/org/apache/rocketmq/connect/jdbc/util/ConnectorGroupUtils.java
similarity index 100%
rename from connectors/rocketmq-connect-jdbc/rocketmq-connect-jdbc-core/src/main/java/org/apache/rocketmq/connect/jdbc/util/ConnectorGroupUtils.java
rename to connectors/rocketmq-connect-jdbc/src/main/java/org/apache/rocketmq/connect/jdbc/util/ConnectorGroupUtils.java
diff --git a/connectors/rocketmq-connect-jdbc/rocketmq-connect-jdbc-core/src/main/java/org/apache/rocketmq/connect/jdbc/util/DateTimeUtils.java b/connectors/rocketmq-connect-jdbc/src/main/java/org/apache/rocketmq/connect/jdbc/util/DateTimeUtils.java
similarity index 100%
rename from connectors/rocketmq-connect-jdbc/rocketmq-connect-jdbc-core/src/main/java/org/apache/rocketmq/connect/jdbc/util/DateTimeUtils.java
rename to connectors/rocketmq-connect-jdbc/src/main/java/org/apache/rocketmq/connect/jdbc/util/DateTimeUtils.java
diff --git a/connectors/rocketmq-connect-jdbc/src/main/java/org/apache/rocketmq/connect/jdbc/util/ExpressionBuilder.java b/connectors/rocketmq-connect-jdbc/src/main/java/org/apache/rocketmq/connect/jdbc/util/ExpressionBuilder.java
new file mode 100644
index 000000000..ae788f35c
--- /dev/null
+++ b/connectors/rocketmq-connect-jdbc/src/main/java/org/apache/rocketmq/connect/jdbc/util/ExpressionBuilder.java
@@ -0,0 +1,331 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.rocketmq.connect.jdbc.util;
+
+import org.apache.rocketmq.connect.jdbc.schema.column.ColumnId;
+
+public class ExpressionBuilder {
+
+ @FunctionalInterface
+ public interface Expressable {
+
+ void appendTo(
+ ExpressionBuilder builder,
+ boolean useQuotes
+ );
+ default void appendTo(
+ ExpressionBuilder builder,
+ QuoteMethod useQuotes
+ ) {
+ switch (useQuotes) {
+ case ALWAYS:
+ appendTo(builder, true);
+ break;
+ case NEVER:
+ default:
+ // do nothing
+ break;
+ }
+ }
+ }
+
+ @FunctionalInterface
+ public interface Transform {
+ void apply(
+ ExpressionBuilder builder,
+ T input
+ );
+ }
+ public interface ListBuilder {
+ ListBuilder delimitedBy(String delimiter);
+ ListBuilder transformedBy(Transform transform);
+
+ ExpressionBuilder of(Iterable extends T> objects);
+ default ExpressionBuilder of(Iterable extends T> objects1, Iterable extends T> objects2) {
+ of(objects1);
+ return of(objects2);
+ }
+
+ default ExpressionBuilder of(
+ Iterable extends T> objects1,
+ Iterable extends T> objects2,
+ Iterable extends T> objects3
+ ) {
+ of(objects1);
+ of(objects2);
+ return of(objects3);
+ }
+ }
+
+ public static Transform quote() {
+ return (builder, input) -> builder.appendColumnName(input);
+ }
+
+ public static Transform columnNames() {
+ return (builder, input) -> builder.appendColumnName(input.name());
+ }
+
+ public static Transform columnNamesWith(final String appended) {
+ return (builder, input) -> {
+ builder.appendColumnName(input.name());
+ builder.append(appended);
+ };
+ }
+
+ public static Transform placeholderInsteadOfColumnNames(final String str) {
+ return (builder, input) -> builder.append(str);
+ }
+
+ public static Transform columnNamesWithPrefix(final String prefix) {
+ return (builder, input) -> {
+ builder.append(prefix);
+ builder.appendColumnName(input.name());
+ };
+ }
+
+ public static ExpressionBuilder create() {
+ return new ExpressionBuilder();
+ }
+
+ protected static final QuoteMethod DEFAULT_QUOTE_METHOD = QuoteMethod.ALWAYS;
+
+ private final IdentifierRules rules;
+ private final StringBuilder sb = new StringBuilder();
+ private QuoteMethod quoteSqlIdentifiers = DEFAULT_QUOTE_METHOD;
+
+ public ExpressionBuilder() {
+ this(null);
+ }
+
+ public ExpressionBuilder(IdentifierRules rules) {
+ this.rules = rules != null ? rules : IdentifierRules.DEFAULT;
+ }
+
+ public ExpressionBuilder setQuoteIdentifiers(QuoteMethod method) {
+ this.quoteSqlIdentifiers = method != null ? method : DEFAULT_QUOTE_METHOD;
+ return this;
+ }
+
+ public ExpressionBuilder escapeQuotesWith(String prefix) {
+ if (prefix == null || prefix.isEmpty()) {
+ return this;
+ }
+ return new ExpressionBuilder(this.rules.escapeQuotesWith(prefix));
+ }
+ public ExpressionBuilder appendIdentifierDelimiter() {
+ sb.append(rules.identifierDelimiter());
+ return this;
+ }
+
+ public ExpressionBuilder appendLeadingQuote() {
+ return appendLeadingQuote(QuoteMethod.ALWAYS);
+ }
+
+
+ protected ExpressionBuilder appendLeadingQuote(QuoteMethod method) {
+ switch (method) {
+ case ALWAYS:
+ sb.append(rules.leadingQuoteString());
+ break;
+ case NEVER:
+ default:
+ break;
+ }
+ return this;
+ }
+
+ public ExpressionBuilder appendTrailingQuote() {
+ return appendTrailingQuote(QuoteMethod.ALWAYS);
+ }
+
+ protected ExpressionBuilder appendTrailingQuote(QuoteMethod method) {
+ switch (method) {
+ case ALWAYS:
+ sb.append(rules.trailingQuoteString());
+ break;
+ case NEVER:
+ default:
+ break;
+ }
+ return this;
+ }
+
+ public ExpressionBuilder appendStringQuote() {
+ sb.append("'");
+ return this;
+ }
+
+ public ExpressionBuilder appendStringQuoted(Object name) {
+ appendStringQuote();
+ sb.append(name);
+ appendStringQuote();
+ return this;
+ }
+
+ @Deprecated
+ public ExpressionBuilder appendIdentifier(
+ String name,
+ boolean quoted
+ ) {
+ return appendIdentifier(name, quoted ? QuoteMethod.ALWAYS : QuoteMethod.NEVER);
+ }
+
+ public ExpressionBuilder appendIdentifier(
+ String name,
+ QuoteMethod quoted
+ ) {
+ appendLeadingQuote(quoted);
+ sb.append(name);
+ appendTrailingQuote(quoted);
+ return this;
+ }
+
+ public ExpressionBuilder appendTableName(String name) {
+ return appendTableName(name, quoteSqlIdentifiers);
+ }
+
+ public ExpressionBuilder appendTableName(String name, QuoteMethod quote) {
+ appendLeadingQuote(quote);
+ sb.append(name);
+ appendTrailingQuote(quote);
+ return this;
+ }
+
+ public ExpressionBuilder appendColumnName(String name) {
+ return appendColumnName(name, quoteSqlIdentifiers);
+ }
+
+ public ExpressionBuilder appendColumnName(String name, QuoteMethod quote) {
+ appendLeadingQuote(quote);
+ sb.append(name);
+ appendTrailingQuote(quote);
+ return this;
+ }
+
+ public ExpressionBuilder appendIdentifierQuoted(String name) {
+ appendLeadingQuote();
+ sb.append(name);
+ appendTrailingQuote();
+ return this;
+ }
+
+ public ExpressionBuilder appendBinaryLiteral(byte[] value) {
+ return append("x'").append(BytesUtil.toHex(value)).append("'");
+ }
+
+ public ExpressionBuilder appendNewLine() {
+ sb.append(System.lineSeparator());
+ return this;
+ }
+
+ @Deprecated
+ public ExpressionBuilder append(
+ Object obj,
+ boolean useQuotes
+ ) {
+ return append(obj, useQuotes ? QuoteMethod.ALWAYS : QuoteMethod.NEVER);
+ }
+
+ public ExpressionBuilder append(
+ Object obj,
+ QuoteMethod useQuotes
+ ) {
+ if (obj instanceof Expressable) {
+ ((Expressable) obj).appendTo(this, useQuotes);
+ } else if (obj != null) {
+ sb.append(obj);
+ }
+ return this;
+ }
+
+ public ExpressionBuilder append(Object obj) {
+ return append(obj, quoteSqlIdentifiers);
+ }
+
+ public ExpressionBuilder append(
+ T obj,
+ Transform transform
+ ) {
+ if (transform != null) {
+ transform.apply(this, obj);
+ } else {
+ append(obj);
+ }
+ return this;
+ }
+
+ protected class BasicListBuilder implements ListBuilder {
+ private final String delimiter;
+ private final Transform transform;
+ private boolean first = true;
+
+ BasicListBuilder() {
+ this(", ", null);
+ }
+
+ BasicListBuilder(String delimiter, Transform transform) {
+ this.delimiter = delimiter;
+ this.transform = transform != null ? transform : ExpressionBuilder::append;
+ }
+
+ @Override
+ public ListBuilder delimitedBy(String delimiter) {
+ return new BasicListBuilder(delimiter, transform);
+ }
+
+ @Override
+ public ListBuilder transformedBy(Transform transform) {
+ return new BasicListBuilder<>(delimiter, transform);
+ }
+
+ @Override
+ public ExpressionBuilder of(Iterable extends T> objects) {
+ for (T obj : objects) {
+ if (first) {
+ first = false;
+ } else {
+ append(delimiter);
+ }
+ append(obj, transform);
+ }
+ return ExpressionBuilder.this;
+ }
+ }
+
+ public ListBuilder appendList() {
+ return new BasicListBuilder<>();
+ }
+
+ public ExpressionBuilder appendMultiple(
+ String delimiter,
+ String expression,
+ int times
+ ) {
+ for (int i = 0; i < times; i++) {
+ if (i > 0) {
+ append(delimiter);
+ }
+ append(expression);
+ }
+ return this;
+ }
+
+ @Override
+ public String toString() {
+ return sb.toString();
+ }
+}
diff --git a/connectors/rocketmq-connect-jdbc/rocketmq-connect-jdbc-core/src/main/java/org/apache/rocketmq/connect/jdbc/util/IdentifierRules.java b/connectors/rocketmq-connect-jdbc/src/main/java/org/apache/rocketmq/connect/jdbc/util/IdentifierRules.java
similarity index 70%
rename from connectors/rocketmq-connect-jdbc/rocketmq-connect-jdbc-core/src/main/java/org/apache/rocketmq/connect/jdbc/util/IdentifierRules.java
rename to connectors/rocketmq-connect-jdbc/src/main/java/org/apache/rocketmq/connect/jdbc/util/IdentifierRules.java
index 25533f56c..e87a0dfef 100644
--- a/connectors/rocketmq-connect-jdbc/rocketmq-connect-jdbc-core/src/main/java/org/apache/rocketmq/connect/jdbc/util/IdentifierRules.java
+++ b/connectors/rocketmq-connect-jdbc/src/main/java/org/apache/rocketmq/connect/jdbc/util/IdentifierRules.java
@@ -23,7 +23,6 @@
* The rules for how identifiers are parsed and quoted.
*/
public class IdentifierRules {
-
public static final String UNSUPPORTED_QUOTE = " ";
private static final String DEFAULT_QUOTE = "\"";
private static final String DEFAULT_ID_DELIM = ".";
@@ -36,25 +35,6 @@ public class IdentifierRules {
private final String trailingQuoteString;
private final String identifierDelimiter;
- /**
- * Create new identifier rules using the supplied quote string for both leading and trailing
- * quotes, and the '{@link #DEFAULT_ID_DELIM}' character for identifier delimiters.
- *
- * @param quoteString the string used for leading and trailing quotes; may be null if {@link
- * #DEFAULT_QUOTE} is to be used
- */
- public IdentifierRules(String quoteString) {
- this(DEFAULT_ID_DELIM, quoteString, quoteString);
- }
-
- /**
- * Create new identifier rules using the supplied parameters.
- *
- * @param delimiter the delimiter used within fully qualified names; may be null if {@link
- * #DEFAULT_ID_DELIM} is to be used
- * @param quoteString the string used for leading and trailing quotes; may be null if {@link
- * #DEFAULT_QUOTE} is to be used
- */
public IdentifierRules(
String delimiter,
String quoteString
@@ -62,16 +42,6 @@ public IdentifierRules(
this(delimiter, quoteString, quoteString);
}
- /**
- * Create new identifier rules using the supplied parameters.
- *
- * @param identifierDelimiter the delimiter used within fully qualified names; may be null if
- * {@link #DEFAULT_ID_DELIM} is to be used
- * @param leadingQuoteString the string used for leading quotes; may be null if {@link
- * #DEFAULT_QUOTE} is to be used
- * @param trailingQuoteString the string used for leading quotes; may be null if {@link
- * #DEFAULT_QUOTE} is to be used
- */
public IdentifierRules(
String identifierDelimiter,
String leadingQuoteString,
@@ -109,21 +79,10 @@ public String trailingQuoteString() {
return trailingQuoteString;
}
- /**
- * Get an expression builder that uses these identifier rules.
- *
- * @return the new expression builder; never null
- */
public ExpressionBuilder expressionBuilder() {
return new ExpressionBuilder(this);
}
- /**
- * Parse the unqualified or fully qualified name into its segments.
- *
- * @param fqn the unqualified or fully-qualified name; may not be null
- * @return the segments in the supplied name; never null, but possibly empty
- */
public List parseQualifiedIdentifier(String fqn) {
String orig = fqn;
String delim = identifierDelimiter();
@@ -167,12 +126,6 @@ public List parseQualifiedIdentifier(String fqn) {
return parts;
}
- /**
- * Return a new IdentifierRules that escapes quotes with the specified prefix.
- *
- * @param prefix the prefix
- * @return the new IdentifierRules, or this builder if the prefix is null or empty
- */
public IdentifierRules escapeQuotesWith(String prefix) {
if (prefix == null || prefix.isEmpty()) {
return this;
diff --git a/connectors/rocketmq-connect-jdbc/rocketmq-connect-jdbc-core/src/main/java/org/apache/rocketmq/connect/jdbc/util/JdbcDriverInfo.java b/connectors/rocketmq-connect-jdbc/src/main/java/org/apache/rocketmq/connect/jdbc/util/JdbcDriverInfo.java
similarity index 99%
rename from connectors/rocketmq-connect-jdbc/rocketmq-connect-jdbc-core/src/main/java/org/apache/rocketmq/connect/jdbc/util/JdbcDriverInfo.java
rename to connectors/rocketmq-connect-jdbc/src/main/java/org/apache/rocketmq/connect/jdbc/util/JdbcDriverInfo.java
index aa77bf289..9c286ca3a 100644
--- a/connectors/rocketmq-connect-jdbc/rocketmq-connect-jdbc-core/src/main/java/org/apache/rocketmq/connect/jdbc/util/JdbcDriverInfo.java
+++ b/connectors/rocketmq-connect-jdbc/src/main/java/org/apache/rocketmq/connect/jdbc/util/JdbcDriverInfo.java
@@ -21,7 +21,6 @@
* A summary of the version information about a JDBC driver and the database.
*/
public class JdbcDriverInfo {
-
private final int jdbcMajorVersion;
private final int jdbcMinorVersion;
private final String jdbcDriverName;
diff --git a/connectors/rocketmq-connect-jdbc/src/main/java/org/apache/rocketmq/connect/jdbc/util/JdbcUrlInfo.java b/connectors/rocketmq-connect-jdbc/src/main/java/org/apache/rocketmq/connect/jdbc/util/JdbcUrlInfo.java
new file mode 100644
index 000000000..5b2ab50dd
--- /dev/null
+++ b/connectors/rocketmq-connect-jdbc/src/main/java/org/apache/rocketmq/connect/jdbc/util/JdbcUrlInfo.java
@@ -0,0 +1,43 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.rocketmq.connect.jdbc.util;
+
+public interface JdbcUrlInfo {
+
+ /**
+ * Get the subprotocol in the JDBC URL.
+ *
+ * @return the subprotocol
+ */
+ String subprotocol();
+
+ /**
+ * Get the subname in the JDBC URL, which is everything after the ':' character following the
+ * subprotocol.
+ *
+ * @return the subname
+ */
+ String subname();
+
+ /**
+ * Get the full JDBC URL.
+ *
+ * @return the URL.
+ */
+ String url();
+}
\ No newline at end of file
diff --git a/connectors/rocketmq-connect-jdbc/rocketmq-connect-jdbc-core/src/main/java/org/apache/rocketmq/connect/jdbc/util/NumericMapping.java b/connectors/rocketmq-connect-jdbc/src/main/java/org/apache/rocketmq/connect/jdbc/util/NumericMapping.java
similarity index 90%
rename from connectors/rocketmq-connect-jdbc/rocketmq-connect-jdbc-core/src/main/java/org/apache/rocketmq/connect/jdbc/util/NumericMapping.java
rename to connectors/rocketmq-connect-jdbc/src/main/java/org/apache/rocketmq/connect/jdbc/util/NumericMapping.java
index 8f3033648..e29a8044f 100644
--- a/connectors/rocketmq-connect-jdbc/rocketmq-connect-jdbc-core/src/main/java/org/apache/rocketmq/connect/jdbc/util/NumericMapping.java
+++ b/connectors/rocketmq-connect-jdbc/src/main/java/org/apache/rocketmq/connect/jdbc/util/NumericMapping.java
@@ -16,11 +16,10 @@
*/
package org.apache.rocketmq.connect.jdbc.util;
-import org.apache.rocketmq.connect.jdbc.source.JdbcSourceConfig;
-
import java.util.HashMap;
import java.util.Locale;
import java.util.Map;
+import org.apache.rocketmq.connect.jdbc.source.JdbcSourceConfig;
public enum NumericMapping {
NONE,
@@ -37,12 +36,10 @@ public enum NumericMapping {
}
public static NumericMapping get(String prop) {
- // not adding a check for null value because the recommender/validator should catch those.
return REVERSE.get(prop.toLowerCase(Locale.ROOT));
}
public static NumericMapping get(JdbcSourceConfig config) {
- // We use 'null' as default to be able to check the old config if the new one is unset.
if (config.getNumericMapping() != null) {
return NumericMapping.valueOf(config.getNumericMapping());
}
diff --git a/connectors/rocketmq-connect-jdbc/rocketmq-connect-jdbc-core/src/main/java/org/apache/rocketmq/connect/jdbc/util/QuoteMethod.java b/connectors/rocketmq-connect-jdbc/src/main/java/org/apache/rocketmq/connect/jdbc/util/QuoteMethod.java
similarity index 99%
rename from connectors/rocketmq-connect-jdbc/rocketmq-connect-jdbc-core/src/main/java/org/apache/rocketmq/connect/jdbc/util/QuoteMethod.java
rename to connectors/rocketmq-connect-jdbc/src/main/java/org/apache/rocketmq/connect/jdbc/util/QuoteMethod.java
index ccc9d89f3..a759d8c7c 100644
--- a/connectors/rocketmq-connect-jdbc/rocketmq-connect-jdbc-core/src/main/java/org/apache/rocketmq/connect/jdbc/util/QuoteMethod.java
+++ b/connectors/rocketmq-connect-jdbc/src/main/java/org/apache/rocketmq/connect/jdbc/util/QuoteMethod.java
@@ -19,6 +19,11 @@
public enum QuoteMethod {
ALWAYS("always"),
NEVER("never");
+ private final String name;
+
+ QuoteMethod(String name) {
+ this.name = name;
+ }
public static QuoteMethod get(String name) {
for (QuoteMethod method : values()) {
@@ -29,12 +34,6 @@ public static QuoteMethod get(String name) {
throw new IllegalArgumentException("No matching QuoteMethod found for '" + name + "'");
}
- private final String name;
-
- QuoteMethod(String name) {
- this.name = name;
- }
-
@Override
public String toString() {
return name;
diff --git a/connectors/rocketmq-connect-jdbc/rocketmq-connect-jdbc-core/src/main/java/org/apache/rocketmq/connect/jdbc/util/TableType.java b/connectors/rocketmq-connect-jdbc/src/main/java/org/apache/rocketmq/connect/jdbc/util/TableType.java
similarity index 79%
rename from connectors/rocketmq-connect-jdbc/rocketmq-connect-jdbc-core/src/main/java/org/apache/rocketmq/connect/jdbc/util/TableType.java
rename to connectors/rocketmq-connect-jdbc/src/main/java/org/apache/rocketmq/connect/jdbc/util/TableType.java
index 7bbf14b8e..037074e2a 100644
--- a/connectors/rocketmq-connect-jdbc/rocketmq-connect-jdbc-core/src/main/java/org/apache/rocketmq/connect/jdbc/util/TableType.java
+++ b/connectors/rocketmq-connect-jdbc/src/main/java/org/apache/rocketmq/connect/jdbc/util/TableType.java
@@ -67,19 +67,4 @@ public static EnumSet parse(Collection values) {
return EnumSet.copyOf(types);
}
- public static String asJdbcTableTypeNames(EnumSet types, String delim) {
- return types.stream()
- .map(TableType::jdbcName)
- .sorted()
- .collect(Collectors.joining(delim));
- }
-
- public static String[] asJdbcTableTypeArray(EnumSet types) {
- return types.stream()
- .map(TableType::jdbcName)
- .sorted()
- .collect(Collectors.toList())
- .toArray(new String[types.size()]);
- }
-
}
\ No newline at end of file
diff --git a/connectors/rocketmq-connect-jdbc/src/main/resources/META-INF/services/org.apache.rocketmq.connect.jdbc.dialect.DatabaseDialectFactory b/connectors/rocketmq-connect-jdbc/src/main/resources/META-INF/services/org.apache.rocketmq.connect.jdbc.dialect.DatabaseDialectFactory
new file mode 100644
index 000000000..81f14ddad
--- /dev/null
+++ b/connectors/rocketmq-connect-jdbc/src/main/resources/META-INF/services/org.apache.rocketmq.connect.jdbc.dialect.DatabaseDialectFactory
@@ -0,0 +1,2 @@
+org.apache.rocketmq.connect.jdbc.dialect.mysql.MysqlDatabaseDialectFactory
+org.apache.rocketmq.connect.jdbc.dialect.openmldb.OpenMLDBDatabaseDialectFactory
diff --git a/connectors/rocketmq-connect-jdbc/rocketmq-connect-jdbc-mysql/src/main/resources/mysql-jdbc-sink.conf b/connectors/rocketmq-connect-jdbc/src/main/resources/examples/mysql/mysql-jdbc-sink.conf
similarity index 96%
rename from connectors/rocketmq-connect-jdbc/rocketmq-connect-jdbc-mysql/src/main/resources/mysql-jdbc-sink.conf
rename to connectors/rocketmq-connect-jdbc/src/main/resources/examples/mysql/mysql-jdbc-sink.conf
index 98d1e6c67..39e1ce967 100644
--- a/connectors/rocketmq-connect-jdbc/rocketmq-connect-jdbc-mysql/src/main/resources/mysql-jdbc-sink.conf
+++ b/connectors/rocketmq-connect-jdbc/src/main/resources/examples/mysql/mysql-jdbc-sink.conf
@@ -1,5 +1,5 @@
{
- "connector.class":"org.apache.rocketmq.connect.jdbc.mysql.sink.MysqlJdbcSinkConnector",
+ "connector.class":"org.apache.rocketmq.connect.jdbc.mysql.sink.JdbcSinkConnector",
"max.tasks":"2",
"connect.topicnames":"employee-test-topic-json-00002",
"connection.url":"jdbc:mysql://localhost:3306/test_database_001",
diff --git a/connectors/rocketmq-connect-jdbc/rocketmq-connect-jdbc-mysql/src/main/resources/mysql-jdbc-source.conf b/connectors/rocketmq-connect-jdbc/src/main/resources/examples/mysql/mysql-jdbc-source.conf
similarity index 94%
rename from connectors/rocketmq-connect-jdbc/rocketmq-connect-jdbc-mysql/src/main/resources/mysql-jdbc-source.conf
rename to connectors/rocketmq-connect-jdbc/src/main/resources/examples/mysql/mysql-jdbc-source.conf
index 0a6c7625e..5f3b7d3d7 100644
--- a/connectors/rocketmq-connect-jdbc/rocketmq-connect-jdbc-mysql/src/main/resources/mysql-jdbc-source.conf
+++ b/connectors/rocketmq-connect-jdbc/src/main/resources/examples/mysql/mysql-jdbc-source.conf
@@ -1,5 +1,5 @@
{
- "connector.class":"org.apache.rocketmq.connect.jdbc.mysql.source.MysqlJdbcSourceConnector",
+ "connector.class":"org.apache.rocketmq.connect.jdbc.mysql.source.JdbcSourceConnector",
"max.tasks":"1",
"connect.topicname":"employee-test-topic-json-00002",
"connection.url":"jdbc:mysql://localhost:3306",
diff --git a/connectors/rocketmq-connect-jdbc/src/main/resources/examples/openmldb/openmldb-jdbc-sink.conf b/connectors/rocketmq-connect-jdbc/src/main/resources/examples/openmldb/openmldb-jdbc-sink.conf
new file mode 100644
index 000000000..1f8fe8fa0
--- /dev/null
+++ b/connectors/rocketmq-connect-jdbc/src/main/resources/examples/openmldb/openmldb-jdbc-sink.conf
@@ -0,0 +1,12 @@
+{
+ "connector.class":"org.apache.rocketmq.connect.jdbc.mysql.sink.JdbcSinkConnector",
+ "max.tasks":"2",
+ "connect.topicnames":"employee_test",
+ "connection.url":"jdbc:openmldb:///rocketmq_test?zk=127.0.0.1:2181&zkPath=/openmldb_cluster",
+ "insert.mode":"INSERT",
+ "db.timezone":"UTC",
+ "table.types":"TABLE",
+ "auto.create":"true",
+ "key.converter":"org.apache.rocketmq.connect.runtime.converter.record.json.JsonConverter",
+ "value.converter":"org.apache.rocketmq.connect.runtime.converter.record.json.JsonConverter"
+}
diff --git a/connectors/rocketmq-connect-jdbc/rocketmq-connect-jdbc-core/src/test/java/org/apache/rocketmq/connect/jdbc/connector/sink/JdbcSinkTest.java b/connectors/rocketmq-connect-jdbc/src/test/java/org/apache/rocketmq/connect/jdbc/connector/sink/JdbcSinkTest.java
similarity index 100%
rename from connectors/rocketmq-connect-jdbc/rocketmq-connect-jdbc-core/src/test/java/org/apache/rocketmq/connect/jdbc/connector/sink/JdbcSinkTest.java
rename to connectors/rocketmq-connect-jdbc/src/test/java/org/apache/rocketmq/connect/jdbc/connector/sink/JdbcSinkTest.java
diff --git a/connectors/rocketmq-connect-jdbc/rocketmq-connect-jdbc-core/src/test/java/org/apache/rocketmq/connect/jdbc/connector/sink/OpenMLDBJdbcSinkTest.java b/connectors/rocketmq-connect-jdbc/src/test/java/org/apache/rocketmq/connect/jdbc/connector/sink/OpenMLDBJdbcSinkTest.java
similarity index 100%
rename from connectors/rocketmq-connect-jdbc/rocketmq-connect-jdbc-core/src/test/java/org/apache/rocketmq/connect/jdbc/connector/sink/OpenMLDBJdbcSinkTest.java
rename to connectors/rocketmq-connect-jdbc/src/test/java/org/apache/rocketmq/connect/jdbc/connector/sink/OpenMLDBJdbcSinkTest.java
diff --git a/connectors/rocketmq-connect-jdbc/rocketmq-connect-jdbc-core/src/test/java/org/apache/rocketmq/connect/jdbc/connector/source/JdbcSourceConnectorTest.java b/connectors/rocketmq-connect-jdbc/src/test/java/org/apache/rocketmq/connect/jdbc/connector/source/JdbcSourceConnectorTest.java
similarity index 100%
rename from connectors/rocketmq-connect-jdbc/rocketmq-connect-jdbc-core/src/test/java/org/apache/rocketmq/connect/jdbc/connector/source/JdbcSourceConnectorTest.java
rename to connectors/rocketmq-connect-jdbc/src/test/java/org/apache/rocketmq/connect/jdbc/connector/source/JdbcSourceConnectorTest.java
diff --git a/connectors/rocketmq-connect-jdbc/rocketmq-connect-jdbc-core/src/test/java/org/apache/rocketmq/connect/jdbc/connector/source/JdbcSourceTaskTest.java b/connectors/rocketmq-connect-jdbc/src/test/java/org/apache/rocketmq/connect/jdbc/connector/source/JdbcSourceTaskTest.java
similarity index 100%
rename from connectors/rocketmq-connect-jdbc/rocketmq-connect-jdbc-core/src/test/java/org/apache/rocketmq/connect/jdbc/connector/source/JdbcSourceTaskTest.java
rename to connectors/rocketmq-connect-jdbc/src/test/java/org/apache/rocketmq/connect/jdbc/connector/source/JdbcSourceTaskTest.java
diff --git a/rocketmq-connect-runtime/src/main/java/org/apache/rocketmq/connect/runtime/connectorwrapper/WorkerSourceTask.java b/rocketmq-connect-runtime/src/main/java/org/apache/rocketmq/connect/runtime/connectorwrapper/WorkerSourceTask.java
index c06110971..79317bdb9 100644
--- a/rocketmq-connect-runtime/src/main/java/org/apache/rocketmq/connect/runtime/connectorwrapper/WorkerSourceTask.java
+++ b/rocketmq-connect-runtime/src/main/java/org/apache/rocketmq/connect/runtime/connectorwrapper/WorkerSourceTask.java
@@ -26,7 +26,6 @@
import io.openmessaging.connector.api.errors.ConnectException;
import io.openmessaging.connector.api.errors.RetriableException;
import io.openmessaging.connector.api.storage.OffsetStorageReader;
-
import java.util.HashSet;
import java.util.List;
import java.util.Optional;
@@ -38,7 +37,6 @@
import java.util.concurrent.TimeoutException;
import java.util.concurrent.atomic.AtomicLong;
import java.util.concurrent.atomic.AtomicReference;
-
import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.rocketmq.client.exception.MQClientException;
@@ -125,30 +123,30 @@ public class WorkerSourceTask extends WorkerTask {
private final CountDownLatch stopRequestedLatch;
private final AtomicReference producerSendException;
private final RecordOffsetManagement offsetManagement;
+ private final Set topicCache;
/**
* A RocketMQ producer to send message to dest MQ.
*/
- private DefaultMQProducer producer;
+ private final DefaultMQProducer producer;
private List toSendRecord;
private volatile RecordOffsetManagement.CommittableOffsets committableOffsets;
- private final Set