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 9071a87f9a [Fix][Docker] Stop unrelocated openGauss driver from
hijacking PostgreSQL connections (#11522)
9071a87f9a is described below
commit 9071a87f9a202ccb920567cf7989b3f73defb760
Author: Jast <[email protected]>
AuthorDate: Sun Aug 16 10:38:34 2026 +0800
[Fix][Docker] Stop unrelocated openGauss driver from hijacking PostgreSQL
connections (#11522)
Co-authored-by: zhangshenghang <[email protected]>
---
seatunnel-dist/pom.xml | 25 +++
.../packaging/DriverJarPackagingTest.java | 175 +++++++++++++++++++++
2 files changed, 200 insertions(+)
diff --git a/seatunnel-dist/pom.xml b/seatunnel-dist/pom.xml
index 22903a8519..28f61ee1f7 100644
--- a/seatunnel-dist/pom.xml
+++ b/seatunnel-dist/pom.xml
@@ -116,6 +116,10 @@
<mysql.version>8.0.27</mysql.version>
<postgresql.version>42.4.3</postgresql.version>
<postgis.jdbc.version>2.5.1</postgis.jdbc.version>
+ <!-- Only the "-og" variant is fully relocated to
org.opengauss.*;
+ the plain variant ships org.postgresql.* classes and must
+ never land in lib/ (issue #11510). -->
+ <opengauss.jdbc.version>5.1.0-og</opengauss.jdbc.version>
<dm-jdbc.version>8.1.2.141</dm-jdbc.version>
<sqlserver.version>9.2.1.jre8</sqlserver.version>
<phoenix.version>5.2.5-HBase-2.x</phoenix.version>
@@ -669,6 +673,18 @@
<artifactId>connector-cdc-opengauss</artifactId>
<version>${project.version}</version>
<scope>provided</scope>
+ <!-- The non "-og" opengauss-jdbc variant bundles
unrelocated
+ org.postgresql.* classes (including
org/postgresql/Driver.class).
+ Once copied into lib/, it hijacks the real PostgreSQL
driver on
+ the flat classpath and breaks every PostgreSQL
connection
+ (issue #11510). The connector shades its own driver
copy, so the
+ transitive jar must not be distributed. -->
+ <exclusions>
+ <exclusion>
+ <groupId>org.opengauss</groupId>
+ <artifactId>opengauss-jdbc</artifactId>
+ </exclusion>
+ </exclusions>
</dependency>
<dependency>
<groupId>org.apache.seatunnel</groupId>
@@ -818,6 +834,15 @@
<version>${postgresql.version}</version>
<scope>provided</scope>
</dependency>
+ <!-- Relocated openGauss driver for the Jdbc connector's
openGauss
+ dialect. Contains no org.postgresql.* classes, so it
cannot
+ conflict with the PostgreSQL driver in lib/ (issue
#11510). -->
+ <dependency>
+ <groupId>org.opengauss</groupId>
+ <artifactId>opengauss-jdbc</artifactId>
+ <version>${opengauss.jdbc.version}</version>
+ <scope>provided</scope>
+ </dependency>
<dependency>
<groupId>net.postgis</groupId>
<artifactId>postgis-jdbc</artifactId>
diff --git
a/seatunnel-dist/src/test/java/org/apache/seatunnel/packaging/DriverJarPackagingTest.java
b/seatunnel-dist/src/test/java/org/apache/seatunnel/packaging/DriverJarPackagingTest.java
new file mode 100644
index 0000000000..a1747751a4
--- /dev/null
+++
b/seatunnel-dist/src/test/java/org/apache/seatunnel/packaging/DriverJarPackagingTest.java
@@ -0,0 +1,175 @@
+/*
+ * 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.seatunnel.packaging;
+
+import org.junit.jupiter.api.Assertions;
+import org.junit.jupiter.api.Test;
+
+import java.io.File;
+import java.nio.charset.StandardCharsets;
+import java.nio.file.Files;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+/**
+ * Guards the JDBC driver jars shipped in the distribution {@code lib/}
directory.
+ *
+ * <p>The non "-og" {@code org.opengauss:opengauss-jdbc} variant is a pgJDBC
fork that still bundles
+ * unrelocated {@code org.postgresql.*} classes, including {@code
org/postgresql/Driver.class}. If
+ * that variant reaches the distribution dependency graph it is copied into
{@code lib/} where the
+ * flat classpath lets it hijack {@code org.postgresql.*} class resolution,
breaking every
+ * PostgreSQL connection with "Protocol error. Session setup failed" (issue
#11510).
+ *
+ * <p>The check works on the pom text directly instead of an XML parser so it
stays reliable on the
+ * seatunnel-dist test classpath, which carries many connector jars that may
ship their own XML
+ * parser implementations.
+ */
+public class DriverJarPackagingTest {
+
+ private static final Pattern DEPENDENCY_BLOCK =
+ Pattern.compile("<dependency>(.*?)</dependency>", Pattern.DOTALL);
+
+ @Test
+ public void testCdcOpenGaussDoesNotLeakUnrelocatedDriver() throws
Exception {
+ String pom = readDistPom();
+
+ List<String> cdcOpenGaussDeps =
+ findDependencyBlocks(pom, "org.apache.seatunnel",
"connector-cdc-opengauss");
+ Assertions.assertFalse(
+ cdcOpenGaussDeps.isEmpty(),
+ "connector-cdc-opengauss dependency not found in
seatunnel-dist/pom.xml");
+
+ for (String block : cdcOpenGaussDeps) {
+ Assertions.assertTrue(
+ declaresExclusion(block, "org.opengauss",
"opengauss-jdbc"),
+ "connector-cdc-opengauss must exclude
org.opengauss:opengauss-jdbc, "
+ + "otherwise the unrelocated driver fork is copied
into lib/ and "
+ + "hijacks org.postgresql.* classes (issue
#11510)");
+ }
+ }
+
+ @Test
+ public void testOnlyRelocatedOpenGaussDriverIsDeclared() throws Exception {
+ String pom = readDistPom();
+
+ List<String> openGaussDeps = findDependencyBlocks(pom,
"org.opengauss", "opengauss-jdbc");
+ Assertions.assertFalse(
+ openGaussDeps.isEmpty(),
+ "expected the relocated openGauss driver to be declared in
seatunnel-dist/pom.xml "
+ + "so the Jdbc connector's openGauss dialect keeps
working");
+
+ for (String block : openGaussDeps) {
+ String version = resolveProperty(pom, extractTag(block,
"version"));
+ Assertions.assertNotNull(version, "opengauss-jdbc dependency must
pin a version");
+ Assertions.assertTrue(
+ version.endsWith("-og"),
+ "only the fully relocated '-og' variant of opengauss-jdbc
may be declared in "
+ + "seatunnel-dist, but found version: "
+ + version);
+ }
+ }
+
+ private String readDistPom() throws Exception {
+ File pom = locateDistPom();
+ return new String(Files.readAllBytes(pom.toPath()),
StandardCharsets.UTF_8);
+ }
+
+ private File locateDistPom() {
+ // Surefire runs with the module base directory as the working
directory.
+ File direct = new File("pom.xml");
+ if (isDistPom(direct)) {
+ return direct;
+ }
+ File nested = new File("seatunnel-dist/pom.xml");
+ if (isDistPom(nested)) {
+ return nested;
+ }
+ throw new IllegalStateException(
+ "Unable to locate seatunnel-dist/pom.xml from " + new
File(".").getAbsolutePath());
+ }
+
+ private boolean isDistPom(File candidate) {
+ if (!candidate.isFile()) {
+ return false;
+ }
+ try {
+ String content =
+ new String(Files.readAllBytes(candidate.toPath()),
StandardCharsets.UTF_8);
+ Matcher matcher =
Pattern.compile("<artifactId>([^<]+)</artifactId>").matcher(content);
+ // The parent block comes first; the module's own artifactId is
the first one
+ // outside of it (the second occurrence, or the first when there
is no parent).
+ if (matcher.find()) {
+ if ("seatunnel-dist".equals(matcher.group(1).trim())) {
+ return true;
+ }
+ return matcher.find() &&
"seatunnel-dist".equals(matcher.group(1).trim());
+ }
+ } catch (Exception e) {
+ return false;
+ }
+ return false;
+ }
+
+ private List<String> findDependencyBlocks(String pom, String groupId,
String artifactId) {
+ List<String> matched = new ArrayList<>();
+ Matcher matcher = DEPENDENCY_BLOCK.matcher(pom);
+ while (matcher.find()) {
+ String block = matcher.group(1);
+ if (groupId.equals(extractTag(block, "groupId"))
+ && artifactId.equals(extractTag(block, "artifactId"))) {
+ matched.add(block);
+ }
+ }
+ return matched;
+ }
+
+ private boolean declaresExclusion(String dependencyBlock, String groupId,
String artifactId) {
+ Matcher exclusions =
+ Pattern.compile("<exclusions>(.*?)</exclusions>",
Pattern.DOTALL)
+ .matcher(dependencyBlock);
+ while (exclusions.find()) {
+ String block = exclusions.group(1);
+ if (groupId.equals(extractTag(block, "groupId"))
+ && artifactId.equals(extractTag(block, "artifactId"))) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ private String extractTag(String block, String tag) {
+ Matcher matcher = Pattern.compile("<" + tag + ">([^<]+)</" + tag +
">").matcher(block);
+ return matcher.find() ? matcher.group(1).trim() : null;
+ }
+
+ private String resolveProperty(String pom, String value) {
+ if (value == null) {
+ return null;
+ }
+ Matcher placeholder = Pattern.compile("\\$\\{([^}]+)}").matcher(value);
+ if (!placeholder.find()) {
+ return value;
+ }
+ String quotedKey = Pattern.quote(placeholder.group(1));
+ Matcher property =
+ Pattern.compile("<" + quotedKey + ">([^<]+)</" + quotedKey +
">").matcher(pom);
+ return property.find() ? property.group(1).trim() : value;
+ }
+}