This is an automated email from the ASF dual-hosted git repository.
davidzollo pushed a commit to branch dev
in repository https://gitbox.apache.org/repos/asf/seatunnel.git
The following commit(s) were added to refs/heads/dev by this push:
new 5a87a77a52 [Fix][Connector-V2] Forward SQL Server JDBC URL properties
(#11674)
5a87a77a52 is described below
commit 5a87a77a52bb5b3184dc006ea36158f1be865d8a
Author: Jast <[email protected]>
AuthorDate: Sun Aug 23 09:16:38 2026 +0800
[Fix][Connector-V2] Forward SQL Server JDBC URL properties (#11674)
Co-authored-by: zhangshenghang <[email protected]>
---
docs/en/connectors/source/SqlServer-CDC.md | 2 +-
docs/zh/connectors/source/SqlServer-CDC.md | 2 +-
.../config/SqlServerSourceConfigFactory.java | 18 ++++++
.../source/SqlServerIncrementalSource.java | 40 +++++++++++++
.../SqlServerIncrementalSourceFactoryTest.java | 65 ++++++++++++++++++++++
5 files changed, 125 insertions(+), 2 deletions(-)
diff --git a/docs/en/connectors/source/SqlServer-CDC.md
b/docs/en/connectors/source/SqlServer-CDC.md
index bda3c1005f..9819dac8e7 100644
--- a/docs/en/connectors/source/SqlServer-CDC.md
+++ b/docs/en/connectors/source/SqlServer-CDC.md
@@ -81,7 +81,7 @@ case-sensitive databases, make sure the configured identifier
case matches the d
| table-names | List | Yes | - |
Tables to monitor. Use the full table identifier:
`databaseName.schemaName.tableName`. This option is mutually exclusive with
`table-pattern`.
[...]
| table-pattern | String | Yes | - |
Regular expression for tables to monitor. Use the full table identifier in the
pattern, for example `column_type_test\\.dbo\\..*`. This option is mutually
exclusive with `table-names`.
[...]
| table-names-config | List | No | - |
Table config list. for example: [{"table": "db1.schema1.table1","primaryKeys":
["key1"],"snapshotSplitColumn": "key2"}]. The snapshotSplitColumn option must
be configured with a unique key. If a non-unique column is provided, the
configuration is ignored and SeaTunnel automatically selects an appropriate
split column internally.
[...]
-| url | String | Yes | - |
URL has to be with database, like
"jdbc:sqlserver://localhost:1433;databaseName=test".
[...]
+| url | String | Yes | - |
URL has to be with database, like
"jdbc:sqlserver://localhost:1433;databaseName=test". SQL Server JDBC connection
properties in this URL, such as `encrypt` and `trustServerCertificate`, are
forwarded to the CDC connection. An explicitly configured property in
`debezium`, such as `database.trustServerCertificate`, takes precedence. |
| startup.mode | Enum | No | INITIAL |
Optional startup mode for SqlServer CDC consumer. Valid values are `initial`,
`earliest`, `latest`, and `timestamp`.<br/>`initial`: read the table snapshot
first and then continue with incremental changes.<br/>`earliest`: start from
the earliest available CDC LSN.<br/>`latest`: only read changes after the job
starts.<br/>`timestamp`: start from the LSN resolved from `startup.timestamp`.
[...]
| startup.timestamp | Long | No | - |
Start from the specified epoch timestamp (in milliseconds). This timestamp is
converted with `server-time-zone` when `startup.mode = timestamp`.<br/> **Note,
This option is required when** the **"startup.mode" option used
`'timestamp'`.**
[...]
| stop.mode | Enum | No | NEVER |
Optional stop mode for SqlServer CDC consumer, valid enumerations are "never".
[...]
diff --git a/docs/zh/connectors/source/SqlServer-CDC.md
b/docs/zh/connectors/source/SqlServer-CDC.md
index f321463460..f98fa9118a 100644
--- a/docs/zh/connectors/source/SqlServer-CDC.md
+++ b/docs/zh/connectors/source/SqlServer-CDC.md
@@ -79,7 +79,7 @@ Sql Server CDC 连接器允许从 SqlServer 数据库读取快照数据和增量
| table-names | List | 是 | -
| 要监控的表,使用完整表标识:`databaseName.schemaName.tableName`。该参数与 `table-pattern` 互斥。
|
| table-pattern | String | 是 | -
| 要监控的表名正则表达式,需要匹配完整表标识,例如 `column_type_test\\.dbo\\..*`。该参数与 `table-names`
互斥。
|
| table-names-config | List | 否 | -
| 表配置列表。例如:[{"table": "db1.schema1.table1","primaryKeys":
["key1"],"snapshotSplitColumn": "key2"}]。 snapshotSplitColumn
选项必须配置为唯一键(主键或唯一索引)。 如果指定了非唯一列,该配置将被忽略,SeaTunnel 会在内部自动选择合适的拆分列。
|
-| url | String | 是 | -
| URL 必须包含数据库,如 "jdbc:sqlserver://localhost:1433;databaseName=test"。
|
+| url | String | 是 | -
| URL 必须包含数据库,如 "jdbc:sqlserver://localhost:1433;databaseName=test"。URL 中的 SQL
Server JDBC 连接属性(如 `encrypt`、`trustServerCertificate`)会传递给 CDC 连接;`debezium`
中显式配置的属性(如 `database.trustServerCertificate`)优先。 |
| startup.mode | Enum | 否 |
INITIAL | SqlServer CDC 消费者的可选启动模式,有效值为 `initial`、`earliest`、`latest` 和
`timestamp`。<br/>`initial`:先读取表快照,再继续读取增量变更。<br/>`earliest`:从最早可用的 CDC LSN
开始读取。<br/>`latest`:只读取作业启动后的变更。<br/>`timestamp`:从 `startup.timestamp` 解析出的 LSN
开始读取。
|
| startup.timestamp | Long | 否 | -
| 从指定的纪元时间戳(以毫秒为单位)开始。当 `startup.mode = timestamp` 时,该时间戳会按 `server-time-zone`
转换。<br/> **注意,当 "startup.mode" 选项使用 `'timestamp'` 时,此选项是必需的。**
|
| stop.mode | Enum | 否 | NEVER
| SqlServer CDC 消费者的可选停止模式,有效枚举为 "never"。
|
diff --git
a/seatunnel-connectors-v2/connector-cdc/connector-cdc-sqlserver/src/main/java/org/apache/seatunnel/connectors/seatunnel/cdc/sqlserver/config/SqlServerSourceConfigFactory.java
b/seatunnel-connectors-v2/connector-cdc/connector-cdc-sqlserver/src/main/java/org/apache/seatunnel/connectors/seatunnel/cdc/sqlserver/config/SqlServerSourceConfigFactory.java
index 1f57099abe..cbc99d8d30 100644
---
a/seatunnel-connectors-v2/connector-cdc/connector-cdc-sqlserver/src/main/java/org/apache/seatunnel/connectors/seatunnel/cdc/sqlserver/config/SqlServerSourceConfigFactory.java
+++
b/seatunnel-connectors-v2/connector-cdc/connector-cdc-sqlserver/src/main/java/org/apache/seatunnel/connectors/seatunnel/cdc/sqlserver/config/SqlServerSourceConfigFactory.java
@@ -37,6 +37,20 @@ public class SqlServerSourceConfigFactory extends
JdbcSourceConfigFactory {
private static final String DRIVER_CLASS_NAME =
"com.microsoft.sqlserver.jdbc.SQLServerDriver";
public static final String SCHEMA_CHANGE_KEY = "include.schema.changes";
+ // Holds JDBC properties forwarded from the connector URL; applied before
dbzProperties in
+ // create() so an explicit debezium.* setting still wins over URL-derived
ones.
+ private Properties jdbcUrlProperties;
+
+ /**
+ * Adds SQL Server JDBC properties parsed from the connector URL.
+ *
+ * <p>Explicit Debezium properties are applied afterwards and therefore
take precedence.
+ */
+ public SqlServerSourceConfigFactory jdbcUrlProperties(Properties
jdbcUrlProperties) {
+ this.jdbcUrlProperties = jdbcUrlProperties;
+ return this;
+ }
+
@Override
public SqlServerSourceConfig create(int subtask) {
Properties props = new Properties();
@@ -76,6 +90,10 @@ public class SqlServerSourceConfigFactory extends
JdbcSourceConfigFactory {
props.setProperty("table.include.list", tableIncludeList);
}
+ if (jdbcUrlProperties != null) {
+ jdbcUrlProperties.forEach(props::put);
+ }
+
if (dbzProperties != null) {
dbzProperties.forEach(props::put);
}
diff --git
a/seatunnel-connectors-v2/connector-cdc/connector-cdc-sqlserver/src/main/java/org/apache/seatunnel/connectors/seatunnel/cdc/sqlserver/source/SqlServerIncrementalSource.java
b/seatunnel-connectors-v2/connector-cdc/connector-cdc-sqlserver/src/main/java/org/apache/seatunnel/connectors/seatunnel/cdc/sqlserver/source/SqlServerIncrementalSource.java
index 2945d26777..c744dd0730 100644
---
a/seatunnel-connectors-v2/connector-cdc/connector-cdc-sqlserver/src/main/java/org/apache/seatunnel/connectors/seatunnel/cdc/sqlserver/source/SqlServerIncrementalSource.java
+++
b/seatunnel-connectors-v2/connector-cdc/connector-cdc-sqlserver/src/main/java/org/apache/seatunnel/connectors/seatunnel/cdc/sqlserver/source/SqlServerIncrementalSource.java
@@ -56,6 +56,7 @@ import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.Optional;
+import java.util.Properties;
import java.util.function.Function;
import java.util.stream.Collectors;
@@ -93,9 +94,48 @@ public class SqlServerIncrementalSource<T> extends
IncrementalSource<T, JdbcSour
configFactory.originUrl(urlInfo.getOrigin());
configFactory.hostname(urlInfo.getHost());
configFactory.port(urlInfo.getPort());
+
configFactory.jdbcUrlProperties(toDebeziumJdbcProperties(urlInfo.getSuffix()));
return configFactory;
}
+ /**
+ * Converts SQL Server JDBC URL properties to Debezium connection
properties.
+ *
+ * <p>The Debezium SQL Server connector rebuilds its JDBC connection from
{@code database.*}
+ * properties. Carrying the URL suffix into that namespace preserves
driver options such as
+ * {@code encrypt} and {@code trustServerCertificate}.
+ */
+ static Properties toDebeziumJdbcProperties(String suffix) {
+ Properties properties = new Properties();
+ for (String property : suffix.split(";")) {
+ int separator = property.indexOf('=');
+ if (separator <= 0) {
+ continue;
+ }
+ String key = property.substring(0, separator).trim();
+ if (isConnectionAddressProperty(key)) {
+ continue;
+ }
+ properties.setProperty("database." + key,
property.substring(separator + 1).trim());
+ }
+ return properties;
+ }
+
+ private static boolean isConnectionAddressProperty(String key) {
+ // Exclude URL properties that are identity-related: the SQL Server
JDBC driver accepts
+ // these inside the connection-string but SeaTunnel sources them from
dedicated options
+ // (URL/host/port/username/password). Forwarding them here would
silently override the
+ // operator-configured values and create a credential-handling
surprise.
+ return "database".equalsIgnoreCase(key)
+ || "databaseName".equalsIgnoreCase(key)
+ || "serverName".equalsIgnoreCase(key)
+ || "port".equalsIgnoreCase(key)
+ || "portNumber".equalsIgnoreCase(key)
+ || "instanceName".equalsIgnoreCase(key)
+ || "user".equalsIgnoreCase(key)
+ || "password".equalsIgnoreCase(key);
+ }
+
@SuppressWarnings("unchecked")
@Override
public DebeziumDeserializationSchema<T>
createDebeziumDeserializationSchema(
diff --git
a/seatunnel-connectors-v2/connector-cdc/connector-cdc-sqlserver/src/test/java/org/apache/seatunnel/connectors/seatunnel/cdc/sqlserver/source/SqlServerIncrementalSourceFactoryTest.java
b/seatunnel-connectors-v2/connector-cdc/connector-cdc-sqlserver/src/test/java/org/apache/seatunnel/connectors/seatunnel/cdc/sqlserver/source/SqlServerIncrementalSourceFactoryTest.java
index e814f76ee5..bdf26b9718 100644
---
a/seatunnel-connectors-v2/connector-cdc/connector-cdc-sqlserver/src/test/java/org/apache/seatunnel/connectors/seatunnel/cdc/sqlserver/source/SqlServerIncrementalSourceFactoryTest.java
+++
b/seatunnel-connectors-v2/connector-cdc/connector-cdc-sqlserver/src/test/java/org/apache/seatunnel/connectors/seatunnel/cdc/sqlserver/source/SqlServerIncrementalSourceFactoryTest.java
@@ -20,6 +20,8 @@ package
org.apache.seatunnel.connectors.seatunnel.cdc.sqlserver.source;
import org.apache.seatunnel.api.configuration.ReadonlyConfig;
import org.apache.seatunnel.api.table.catalog.Catalog;
import org.apache.seatunnel.api.table.factory.FactoryUtil;
+import
org.apache.seatunnel.connectors.seatunnel.cdc.sqlserver.config.SqlServerSourceConfig;
+import
org.apache.seatunnel.connectors.seatunnel.cdc.sqlserver.config.SqlServerSourceConfigFactory;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.Test;
@@ -28,6 +30,7 @@ import java.util.Arrays;
import java.util.HashMap;
import java.util.Map;
import java.util.Optional;
+import java.util.Properties;
class SqlServerIncrementalSourceFactoryTest {
@Test
@@ -58,4 +61,66 @@ class SqlServerIncrementalSourceFactoryTest {
Assertions.assertTrue(catalog.isPresent());
catalog.ifPresent(Catalog::close);
}
+
+ @Test
+ public void testForwardJdbcUrlPropertiesToDebeziumConnection() {
+ Properties debeziumProperties = new Properties();
+ debeziumProperties.setProperty("database.encrypt", "false");
+ SqlServerSourceConfigFactory configFactory =
+ (SqlServerSourceConfigFactory)
+ new SqlServerSourceConfigFactory()
+ .hostname("localhost")
+ .port(1433)
+ .username("sa")
+ .password("Password!")
+ .databaseList("seatunnel");
+ configFactory.jdbcUrlProperties(
+ SqlServerIncrementalSource.toDebeziumJdbcProperties(
+
"databaseName=seatunnel;encrypt=true;trustServerCertificate=true"));
+ configFactory.debeziumProperties(debeziumProperties);
+ SqlServerSourceConfig sourceConfig = configFactory.create(0);
+
+ Assertions.assertEquals(
+ "false",
sourceConfig.getDbzConfiguration().getString("database.encrypt"));
+ Assertions.assertEquals(
+ "true",
+
sourceConfig.getDbzConfiguration().getString("database.trustServerCertificate"));
+ Assertions.assertNull(
+
sourceConfig.getDbzConfiguration().getString("database.databaseName"));
+ }
+
+ /**
+ * URL-embedded {@code user} / {@code password} segments must never
override the
+ * operator-configured {@code username} / {@code password} options — doing
so would silently
+ * change the credentials the job connects with.
+ */
+ @Test
+ public void testJdbcUrlCredentialsDoNotOverrideConfiguredCredentials() {
+ SqlServerSourceConfigFactory configFactory =
+ (SqlServerSourceConfigFactory)
+ new SqlServerSourceConfigFactory()
+ .hostname("localhost")
+ .port(1433)
+ .username("configured-user")
+ .password("configured-password")
+ .databaseList("seatunnel");
+ configFactory.jdbcUrlProperties(
+ SqlServerIncrementalSource.toDebeziumJdbcProperties(
+ "user=url-user;password=url-password;encrypt=true"));
+ SqlServerSourceConfig sourceConfig = configFactory.create(0);
+
+ // The configured username/password must win — URL-embedded
user/password must be
+ // filtered out before they are forwarded into the Debezium Properties
bag.
+ Assertions.assertEquals(
+ "configured-user",
sourceConfig.getDbzConfiguration().getString("database.user"));
+ Assertions.assertEquals(
+ "configured-password",
+
sourceConfig.getDbzConfiguration().getString("database.password"));
+
Assertions.assertNull(sourceConfig.getDbzConfiguration().getString("database.url-user"));
+ Assertions.assertNull(
+
sourceConfig.getDbzConfiguration().getString("database.url-password"));
+ // Forwarding of unrelated URL properties is still in effect.
+ Assertions.assertEquals(
+ "true",
sourceConfig.getDbzConfiguration().getString("database.encrypt"));
+ }
}