This is an automated email from the ASF dual-hosted git repository.
FrankChen021 pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/druid.git
The following commit(s) were added to refs/heads/master by this push:
new fbf472cb677 build(deps): bump org.mariadb.jdbc:mariadb-java-client
from 2.7.3 to 3.5.10 (#20036)
fbf472cb677 is described below
commit fbf472cb6778aec81e853dafa7f03a8a63595123
Author: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
AuthorDate: Sun Aug 30 09:57:41 2026 +0800
build(deps): bump org.mariadb.jdbc:mariadb-java-client from 2.7.3 to 3.5.10
(#20036)
* build(deps): bump org.mariadb.jdbc:mariadb-java-client
Bumps
[org.mariadb.jdbc:mariadb-java-client](https://github.com/mariadb-corporation/mariadb-connector-j)
from 2.7.3 to 3.5.10.
- [Release
notes](https://github.com/mariadb-corporation/mariadb-connector-j/releases)
-
[Changelog](https://github.com/mariadb-corporation/mariadb-connector-j/blob/main/CHANGELOG.md)
-
[Commits](https://github.com/mariadb-corporation/mariadb-connector-j/compare/2.7.3...3.5.10)
---
updated-dependencies:
- dependency-name: org.mariadb.jdbc:mariadb-java-client
dependency-version: 3.5.10
dependency-type: direct:production
update-type: version-update:semver-major
...
Signed-off-by: dependabot[bot] <[email protected]>
* fix: support MariaDB Connector/J 3.x
* style: order MariaDB connector imports
* fix: support MariaDB 3.x streaming fetch size
* docs: explain MariaDB streaming fetch size
* test: explain MariaDB 3.x URL rejection
* test: cover MariaDB Connector/J 2.x compatibility
* style: normalize MariaDB compatibility test header
* fix: support MariaDB 3.x MySQL URI validation
* fix: allow MariaDB 3.x SSL mode property
---------
Signed-off-by: dependabot[bot] <[email protected]>
Co-authored-by: dependabot[bot]
<49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: Frank Chen <[email protected]>
---
distribution/docker/Dockerfile.mariadb | 6 +-
docs/configuration/index.md | 2 +-
docs/development/extensions-core/mysql.md | 10 +-
.../metadata/storage/mysql/MySQLConnector.java | 12 ++
.../MySQLInputSourceDatabaseConnectorTest.java | 22 ++++
.../metadata/storage/mysql/MySQLConnectorTest.java | 36 +++++-
pom.xml | 2 +-
processing/pom.xml | 23 ++++
.../org/apache/druid/utils/ConnectionUriUtils.java | 34 ++++--
.../utils/ConnectionUriUtilsMariaDb2xTest.java | 123 +++++++++++++++++++++
.../apache/druid/utils/ConnectionUriUtilsTest.java | 104 ++++++++---------
.../initialization/JdbcAccessSecurityConfig.java | 3 +-
.../druid/metadata/input/SqlInputSourceTest.java | 2 +-
13 files changed, 300 insertions(+), 79 deletions(-)
diff --git a/distribution/docker/Dockerfile.mariadb
b/distribution/docker/Dockerfile.mariadb
index 68a2ca73a3e..27852f4a43c 100644
--- a/distribution/docker/Dockerfile.mariadb
+++ b/distribution/docker/Dockerfile.mariadb
@@ -22,9 +22,9 @@ FROM $DRUID_RELEASE
WORKDIR /opt/druid/extensions/mysql-metadata-storage
-ARG
MARIA_URL=https://repo1.maven.org/maven2/org/mariadb/jdbc/mariadb-java-client/2.7.3/mariadb-java-client-2.7.3.jar
-ARG MARIA_JAR=mariadb-java-client-2.7.3.jar
-ARG MARIA_SHA=4a2edc05bd882ad19371d2615c2635dccf8d74f0
+ARG
MARIA_URL=https://repo1.maven.org/maven2/org/mariadb/jdbc/mariadb-java-client/3.5.10/mariadb-java-client-3.5.10.jar
+ARG MARIA_JAR=mariadb-java-client-3.5.10.jar
+ARG MARIA_SHA=c6a687f7ccb65394103cd516e0583e61e6d92c24
RUN wget -q ${MARIA_URL} \
&& echo "${MARIA_SHA} ${MARIA_JAR}" | sha1sum -c \
diff --git a/docs/configuration/index.md b/docs/configuration/index.md
index 4c565b55bdb..0bbc7fc7958 100644
--- a/docs/configuration/index.md
+++ b/docs/configuration/index.md
@@ -487,7 +487,7 @@ These properties do not apply to metadata storage
connections.
|Property|Possible values| Description
|Default|
|--------|---------------|--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|-------|
|`druid.access.jdbc.enforceAllowedProperties`|Boolean| When true, Druid
applies `druid.access.jdbc.allowedProperties` to JDBC connections starting with
`jdbc:postgresql:`, `jdbc:mysql:`, or `jdbc:mariadb:`. When false, Druid allows
any kind of JDBC connections without JDBC property validation. This config is
for backward compatibility especially during upgrades since enforcing allow
list can break existing ingestion jobs or lookups based on JDBC. This config is
deprecated and will be rem [...]
-|`druid.access.jdbc.allowedProperties`|List of JDBC properties| Defines a list
of allowed JDBC properties. Druid always enforces the list for all JDBC
connections starting with `jdbc:postgresql:`, `jdbc:mysql:`, and
`jdbc:mariadb:` if `druid.access.jdbc.enforceAllowedProperties` is set to
true.<br/><br/>This option is tested against MySQL connector 8.2.0, MariaDB
connector 2.7.4, and PostgreSQL connector 42.2.14. Other connector versions
might not work. [...]
+|`druid.access.jdbc.allowedProperties`|List of JDBC properties| Defines a list
of allowed JDBC properties. Druid always enforces the list for all JDBC
connections starting with `jdbc:postgresql:`, `jdbc:mysql:`, and
`jdbc:mariadb:` if `druid.access.jdbc.enforceAllowedProperties` is set to
true.<br/><br/>This option is tested against MySQL connector 8.2.0, MariaDB
connector 3.5.10, and PostgreSQL connector 42.2.14. Other connector versions
might not work. [...]
|`druid.access.jdbc.allowUnknownJdbcUrlFormat`|Boolean| When false, Druid only
accepts JDBC connections starting with `jdbc:postgresql:` or `jdbc:mysql:`.
When true, Druid allows JDBC connections to any kind of database, but only
enforces `druid.access.jdbc.allowedProperties` for PostgreSQL and
MySQL/MariaDB.
[...]
### Task logging
diff --git a/docs/development/extensions-core/mysql.md
b/docs/development/extensions-core/mysql.md
index 77e26446bbb..8782e30b5ab 100644
--- a/docs/development/extensions-core/mysql.md
+++ b/docs/development/extensions-core/mysql.md
@@ -55,22 +55,22 @@ Copy or create a symbolic link to this file inside the
`lib` folder in the distr
### Install MariaDB Connector/J
This extension also supports using the MariaDB connector jar.
-The current version of Druid uses version 2.7.3.
+The current version of Druid uses version 3.5.10.
Other versions may not work with this extension.
You can download the library from one of the following sources:
- [MariaDB
website](https://mariadb.com/downloads/connectors/connectors-data-access/java8-connector)
Click **Show All Files** to access older product versions.
-- [Maven Central (direct
download)](https://repo1.maven.org/maven2/org/mariadb/jdbc/mariadb-java-client/2.7.3/mariadb-java-client-2.7.3.jar)
+- [Maven Central (direct
download)](https://repo1.maven.org/maven2/org/mariadb/jdbc/mariadb-java-client/3.5.10/mariadb-java-client-3.5.10.jar)
-The download includes the MariaDB connector JAR file with a name like
`maria-java-client-2.7.3.jar`.
+The download includes the MariaDB connector JAR file with a name like
`mariadb-java-client-3.5.10.jar`.
Copy or create a symbolic link to this file inside the `lib` folder in the
distribution root directory.
To configure the `mysql-metadata-storage` extension to use the MariaDB
connector library instead of MySQL, set
`druid.metadata.mysql.driver.driverClassName=org.mariadb.jdbc.Driver`.
-The protocol of the connection string is `jdbc:mysql:` or `jdbc:mariadb:`,
-depending on your specific version of the MariaDB client library.
+MariaDB Connector/J 3.x accepts `jdbc:mariadb:` connection strings by default.
+To use a `jdbc:mysql:` connection string, add the `permitMysqlScheme` option
to the URL.
For more information on the parameters to configure a connection,
[see the MariaDB
documentation](https://mariadb.com/kb/en/about-mariadb-connector-j/#connection-strings)
for your connector version.
diff --git
a/extensions-core/mysql-metadata-storage/src/main/java/org/apache/druid/metadata/storage/mysql/MySQLConnector.java
b/extensions-core/mysql-metadata-storage/src/main/java/org/apache/druid/metadata/storage/mysql/MySQLConnector.java
index c9599eb0fd7..3030c124d0a 100644
---
a/extensions-core/mysql-metadata-storage/src/main/java/org/apache/druid/metadata/storage/mysql/MySQLConnector.java
+++
b/extensions-core/mysql-metadata-storage/src/main/java/org/apache/druid/metadata/storage/mysql/MySQLConnector.java
@@ -50,12 +50,15 @@ public class MySQLConnector extends SQLMetadataConnector
private static final String MYSQL_TRANSIENT_EXCEPTION_CLASS_NAME
= "java.sql.SQLTransientException";
private static final String MARIA_DB_PACKET_EXCEPTION_CLASS_NAME
+ = "org.mariadb.jdbc.export.MaxAllowedPacketException";
+ private static final String LEGACY_MARIA_DB_PACKET_EXCEPTION_CLASS_NAME
= "org.mariadb.jdbc.internal.util.exceptions.MaxAllowedPacketException";
private static final String MYSQL_PACKET_EXCEPTION_CLASS_NAME
= "com.mysql.jdbc.PacketTooBigException";
@Nullable
private final Class<?> myTransientExceptionClass;
+ private final boolean mariaDbDriver;
private final DBI dbi;
@Inject
@@ -70,6 +73,7 @@ public class MySQLConnector extends SQLMetadataConnector
super(config, dbTables, centralizedDatasourceSchemaConfig);
log.info("Loading MySQL metadata connector driver %s",
driverConfig.getDriverClassName());
tryLoadDriverClass(driverConfig.getDriverClassName(), true);
+ mariaDbDriver =
MySQLConnectorDriverConfig.MARIA_DB_DRIVER.equals(driverConfig.getDriverClassName());
if (driverConfig.getDriverClassName().contains("mysql")) {
myTransientExceptionClass =
tryLoadDriverClass(MYSQL_TRANSIENT_EXCEPTION_CLASS_NAME, false);
@@ -180,6 +184,13 @@ public class MySQLConnector extends SQLMetadataConnector
@Override
public int getStreamingFetchSize()
{
+ if (mariaDbDriver) {
+ // MariaDB Connector/J 2.7.3 had a compatibility branch in
+ // Statement.setFetchSize(Integer.MIN_VALUE) that stored fetchSize=1,
while 3.x requires non-negative values.
+ // Source:
https://github.com/mariadb-corporation/mariadb-connector-j/blob/2.7.3/src/main/java/org/mariadb/jdbc/MariaDbStatement.java#L1296-L1305
+ return 1;
+ }
+
// this is MySQL's way of indicating you want results streamed back
// see
http://dev.mysql.com/doc/connector-j/5.1/en/connector-j-reference-implementation-notes.html
return Integer.MIN_VALUE;
@@ -241,6 +252,7 @@ public class MySQLConnector extends SQLMetadataConnector
final String className = t.getClass().getName();
return MARIA_DB_PACKET_EXCEPTION_CLASS_NAME.equals(className)
+ || LEGACY_MARIA_DB_PACKET_EXCEPTION_CLASS_NAME.equals(className)
|| MYSQL_PACKET_EXCEPTION_CLASS_NAME.equals(className)
|| isRootCausePacketTooBigException(t.getCause());
}
diff --git
a/extensions-core/mysql-metadata-storage/src/test/java/org/apache/druid/metadata/input/MySQLInputSourceDatabaseConnectorTest.java
b/extensions-core/mysql-metadata-storage/src/test/java/org/apache/druid/metadata/input/MySQLInputSourceDatabaseConnectorTest.java
index ae4783f138e..a804e7b5112 100644
---
a/extensions-core/mysql-metadata-storage/src/test/java/org/apache/druid/metadata/input/MySQLInputSourceDatabaseConnectorTest.java
+++
b/extensions-core/mysql-metadata-storage/src/test/java/org/apache/druid/metadata/input/MySQLInputSourceDatabaseConnectorTest.java
@@ -33,6 +33,8 @@ import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
+import org.junit.jupiter.params.ParameterizedTest;
+import org.junit.jupiter.params.provider.ValueSource;
import org.mockito.Mock;
import org.mockito.junit.jupiter.MockitoExtension;
@@ -228,6 +230,26 @@ public class MySQLInputSourceDatabaseConnectorTest
);
}
+ @ParameterizedTest
+ @ValueSource(strings = {"useSSL=true", "sslMode=verify-full"})
+ public void testSuccessMariaDbSslPropertyWithDefaultAllowlist(final String
sslProperty)
+ {
+ final MetadataStorageConnectorConfig connectorConfig = new
MetadataStorageConnectorConfig()
+ {
+ @Override
+ public String getConnectURI()
+ {
+ return "jdbc:mariadb://localhost:3306/test?" + sslProperty;
+ }
+ };
+
+ new MySQLInputSourceDatabaseConnector(
+ connectorConfig,
+ MySQLConnectorDriverConfig.MARIA_DB_DRIVER,
+ new JdbcAccessSecurityConfig(),
+ mySQLConnectorDriverConfig
+ );
+ }
@Test
public void testFailOnlyInvalidProperty()
diff --git
a/extensions-core/mysql-metadata-storage/src/test/java/org/apache/druid/metadata/storage/mysql/MySQLConnectorTest.java
b/extensions-core/mysql-metadata-storage/src/test/java/org/apache/druid/metadata/storage/mysql/MySQLConnectorTest.java
index 5251347110f..f0b61321fc8 100644
---
a/extensions-core/mysql-metadata-storage/src/test/java/org/apache/druid/metadata/storage/mysql/MySQLConnectorTest.java
+++
b/extensions-core/mysql-metadata-storage/src/test/java/org/apache/druid/metadata/storage/mysql/MySQLConnectorTest.java
@@ -26,6 +26,7 @@ import
org.apache.druid.segment.metadata.CentralizedDatasourceSchemaConfig;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.MethodSource;
+import org.mariadb.jdbc.export.MaxAllowedPacketException;
import java.sql.SQLException;
import java.sql.SQLTransientConnectionException;
@@ -123,10 +124,14 @@ public class MySQLConnectorTest
centralizedDatasourceSchemaConfig
);
- // The test method should return true only for
- // mariadb.MaxAllowedPacketException or mysql.PacketTooBigException.
- // Verifying this requires creating a mock Class object, but Class is final
- // and has only a private constructor. It would be overkill to try to mock
it.
+ Assertions.assertTrue(
+ connector.isRootCausePacketTooBigException(new
MaxAllowedPacketException("packet too large", false))
+ );
+ Assertions.assertTrue(
+ connector.isRootCausePacketTooBigException(
+ new SQLException("packet too large", new
MaxAllowedPacketException("packet too large", false))
+ )
+ );
// Verify some of the false cases
Assertions.assertFalse(
@@ -186,4 +191,27 @@ public class MySQLConnectorTest
);
Assertions.assertEquals("LIMIT 100", connector.limitClause(100));
}
+
+ @MethodSource("constructorFeeder")
+ @ParameterizedTest(name = "{0}")
+ public void testGetStreamingFetchSize(CentralizedDatasourceSchemaConfig
centralizedDatasourceSchemaConfig)
+ {
+ final MySQLConnector mysqlConnector = new MySQLConnector(
+ CONNECTOR_CONFIG_SUPPLIER,
+ TABLES_CONFIG_SUPPLIER,
+ new MySQLConnectorSslConfig(),
+ MYSQL_DRIVER_CONFIG,
+ centralizedDatasourceSchemaConfig
+ );
+ final MySQLConnector mariaDbConnector = new MySQLConnector(
+ CONNECTOR_CONFIG_SUPPLIER,
+ TABLES_CONFIG_SUPPLIER,
+ new MySQLConnectorSslConfig(),
+ MARIADB_DRIVER_CONFIG,
+ centralizedDatasourceSchemaConfig
+ );
+
+ Assertions.assertEquals(Integer.MIN_VALUE,
mysqlConnector.getStreamingFetchSize());
+ Assertions.assertEquals(1, mariaDbConnector.getStreamingFetchSize());
+ }
}
diff --git a/pom.xml b/pom.xml
index 4913b3f8b64..8e7f9bc198f 100644
--- a/pom.xml
+++ b/pom.xml
@@ -111,7 +111,7 @@
<codehaus.jackson.version>1.9.13</codehaus.jackson.version>
<log4j.version>2.26.1</log4j.version>
<mysql.version>26.7.0</mysql.version>
- <mariadb.version>2.7.3</mariadb.version>
+ <mariadb.version>3.5.10</mariadb.version>
<netty3.version>3.10.6.Final</netty3.version>
<netty4.version>4.2.17.Final</netty4.version>
<postgresql.version>42.7.13</postgresql.version>
diff --git a/processing/pom.xml b/processing/pom.xml
index 2d43137e955..55b533ccdcd 100644
--- a/processing/pom.xml
+++ b/processing/pom.xml
@@ -37,6 +37,7 @@
<sigar.base.version>1.6.5</sigar.base.version>
<sigar.version>1.6.5.132-6</sigar.version>
<oshi.version>7.5.0</oshi.version>
+ <mariadb.legacy.version>2.7.3</mariadb.legacy.version>
</properties>
<dependencies>
@@ -510,6 +511,23 @@
</outputDirectory>
</configuration>
</execution>
+ <execution>
+ <id>copy-mariadb-legacy-test-driver</id>
+ <phase>generate-test-resources</phase>
+ <goals>
+ <goal>copy</goal>
+ </goals>
+ <configuration>
+ <artifactItems>
+ <artifactItem>
+ <groupId>org.mariadb.jdbc</groupId>
+ <artifactId>mariadb-java-client</artifactId>
+ <version>${mariadb.legacy.version}</version>
+
<outputDirectory>${project.build.directory}/mariadb-legacy</outputDirectory>
+ </artifactItem>
+ </artifactItems>
+ </configuration>
+ </execution>
</executions>
</plugin>
<plugin>
@@ -520,6 +538,11 @@
<!-- use normal classpath instead of manifest jar for
JvmUtilsTest.testSystemClassPath -->
<useManifestOnlyJar>false</useManifestOnlyJar>
+ <systemPropertyVariables>
+ <druid.test.mariadbLegacyDriver>
+
${project.build.directory}/mariadb-legacy/mariadb-java-client-${mariadb.legacy.version}.jar
+ </druid.test.mariadbLegacyDriver>
+ </systemPropertyVariables>
</configuration>
</plugin>
<plugin>
diff --git
a/processing/src/main/java/org/apache/druid/utils/ConnectionUriUtils.java
b/processing/src/main/java/org/apache/druid/utils/ConnectionUriUtils.java
index 80c296ef653..5f2dce3b4d8 100644
--- a/processing/src/main/java/org/apache/druid/utils/ConnectionUriUtils.java
+++ b/processing/src/main/java/org/apache/druid/utils/ConnectionUriUtils.java
@@ -36,6 +36,14 @@ import java.util.Set;
public final class ConnectionUriUtils
{
private static final String MARIADB_EXTRAS = "nonMappedOptions";
+ private static final Set<String> MARIADB_3X_EXCLUDED_FIELDS = Set.of(
+ "database",
+ "haMode",
+ "$jacocoData",
+ "addresses",
+ "initialUrl",
+ "codecs"
+ );
// Note: MySQL JDBC connector 8 supports 7 other protocols than just
`jdbc:mysql:`
//
(https://dev.mysql.com/doc/connector-j/8.0/en/connector-j-reference-jdbc-url-format.html).
// We should consider either expanding recognized mysql protocols or
restricting allowed protocols to
@@ -79,8 +87,7 @@ public final class ConnectionUriUtils
* parameter whitelist validation.
* <p>
* uris starting with {@link #MYSQL_PREFIX} will first try to use the MySQL
Connector/J driver (5.x), then fallback
- * to MariaDB Connector/J (version 2.x) which also accepts jdbc:mysql uris.
This method does not attempt to use
- * MariaDB Connector/J 3.x alpha driver (at the time of these javadocs, it
only handles the jdbc:mariadb prefix)
+ * to MariaDB Connector/J (version 2.x or 3.x) which also accepts jdbc:mysql
uris.
* <p>
* uris starting with {@link #POSTGRES_PREFIX} will use the postgresql
driver to parse the uri
* <p>
@@ -101,10 +108,21 @@ public final class ConnectionUriUtils
return tryParseMariaDb2xConnectionUri(connectionUri);
}
catch (ClassNotFoundException notFoundMaria2x) {
- throw new RuntimeException(
- "Failed to find MySQL driver class. Please check the MySQL
connector version 8.2.0 is in the classpath",
- notFoundMysql
- );
+ try {
+ return tryParseMariaDb3xConnectionUri(connectionUri);
+ }
+ catch (ClassNotFoundException notFoundMaria3x) {
+ throw new RuntimeException(
+ "Failed to find MySQL driver class. Please check the MySQL
connector version 8.2.0 is in the classpath",
+ notFoundMysql
+ );
+ }
+ catch (IllegalArgumentException iaeMaria3x) {
+ throw iaeMaria3x;
+ }
+ catch (Throwable otherMaria3x) {
+ throw new RuntimeException(otherMaria3x);
+ }
}
catch (IllegalArgumentException iaeMaria2x) {
throw iaeMaria2x;
@@ -129,7 +147,7 @@ public final class ConnectionUriUtils
}
catch (ClassNotFoundException notFoundMaria3x) {
throw new RuntimeException(
- "Failed to find MariaDB driver class. Please check the MariaDB
connector version 2.7.3 is in the classpath",
+ "Failed to find MariaDB driver class. Please check the MariaDB
connector is in the classpath",
notFoundMaria2x
);
}
@@ -310,7 +328,7 @@ public final class ConnectionUriUtils
Object defaultConfiguration =
buildMethod.invoke(configurationBuilderClass.getConstructor().newInstance());
for (Field field : fields) {
- if (field.getName().equals(MARIADB_EXTRAS)) {
+ if (field.getName().equals(MARIADB_EXTRAS) ||
MARIADB_3X_EXCLUDED_FIELDS.contains(field.getName())) {
continue;
}
try {
diff --git
a/processing/src/test/java/org/apache/druid/utils/ConnectionUriUtilsMariaDb2xTest.java
b/processing/src/test/java/org/apache/druid/utils/ConnectionUriUtilsMariaDb2xTest.java
new file mode 100644
index 00000000000..340fb35d1d8
--- /dev/null
+++
b/processing/src/test/java/org/apache/druid/utils/ConnectionUriUtilsMariaDb2xTest.java
@@ -0,0 +1,123 @@
+/*
+ * 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.druid.utils;
+
+import org.junit.jupiter.api.Assertions;
+import org.junit.jupiter.api.Test;
+
+import java.lang.reflect.Method;
+import java.net.URL;
+import java.net.URLClassLoader;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.util.Set;
+import java.util.jar.JarFile;
+
+public class ConnectionUriUtilsMariaDb2xTest
+{
+ private static final String MYSQL_URI =
+
"jdbc:mysql://localhost:3306/test?user=druid&password=diurd&keyonly&otherOptions=wat";
+ private static final String MARIA_URI =
+
"jdbc:mariadb://localhost:3306/test?user=druid&password=diurd&keyonly&otherOptions=wat";
+ private static final Set<String> EXPECTED_PROPERTIES = Set.of("user",
"password", "keyonly", "otherOptions");
+
+ @Test
+ public void testMariaDb2xDriverUriDispatch() throws Exception
+ {
+ final Path driverJar =
Path.of(System.getProperty("druid.test.mariadbLegacyDriver"));
+ Assertions.assertTrue(Files.isRegularFile(driverJar), "MariaDB Connector/J
2.x test driver is missing");
+
+ try (final JarFile jarFile = new JarFile(driverJar.toFile())) {
+ Assertions.assertEquals(
+ "2.7.3",
+ jarFile.getManifest().getMainAttributes().getValue("Bundle-Version")
+ );
+ }
+
+ final URL processingClasses =
ConnectionUriUtils.class.getProtectionDomain().getCodeSource().getLocation();
+ try (final MariaDb2xClassLoader classLoader = new MariaDb2xClassLoader(
+ new URL[]{processingClasses, driverJar.toUri().toURL()},
+ ConnectionUriUtils.class.getClassLoader()
+ )) {
+ final Class<?> isolatedUtils =
classLoader.loadClass(ConnectionUriUtils.class.getName());
+ final Method directParser =
isolatedUtils.getMethod("tryParseMariaDb2xConnectionUri", String.class);
+ final Method dispatcher =
isolatedUtils.getMethod("tryParseJdbcUriParameters", String.class,
boolean.class);
+
+ for (final String uri : new String[]{MYSQL_URI, MARIA_URI}) {
+ Assertions.assertEquals(EXPECTED_PROPERTIES, invoke(directParser,
uri));
+ Assertions.assertEquals(EXPECTED_PROPERTIES, invoke(dispatcher, uri,
false));
+ }
+
+ Assertions.assertThrows(
+ ClassNotFoundException.class,
+ () ->
ConnectionUriUtils.class.getClassLoader().loadClass("org.mariadb.jdbc.UrlParser")
+ );
+ final Class<?> urlParser =
classLoader.loadClass("org.mariadb.jdbc.UrlParser");
+ Assertions.assertSame(classLoader, urlParser.getClassLoader());
+ Assertions.assertNotSame(
+ classLoader,
+ Class.forName("org.mariadb.jdbc.Configuration").getClassLoader()
+ );
+ Assertions.assertThrows(
+ ClassNotFoundException.class,
+ () -> classLoader.loadClass("org.mariadb.jdbc.Configuration")
+ );
+ Assertions.assertEquals(
+ driverJar.toRealPath(),
+
Path.of(urlParser.getProtectionDomain().getCodeSource().getLocation().toURI()).toRealPath()
+ );
+ }
+ }
+
+ @SuppressWarnings("unchecked")
+ private static Set<String> invoke(final Method method, final Object...
arguments) throws Exception
+ {
+ return (Set<String>) method.invoke(null, arguments);
+ }
+
+ private static final class MariaDb2xClassLoader extends URLClassLoader
+ {
+ private MariaDb2xClassLoader(final URL[] urls, final ClassLoader parent)
+ {
+ super(urls, parent);
+ }
+
+ @Override
+ protected Class<?> loadClass(final String name, final boolean resolve)
throws ClassNotFoundException
+ {
+ synchronized (getClassLoadingLock(name)) {
+ Class<?> loadedClass = findLoadedClass(name);
+ if (loadedClass == null) {
+ if (name.equals(ConnectionUriUtils.class.getName()) ||
name.startsWith("org.mariadb.jdbc.")) {
+ loadedClass = findClass(name);
+ } else if (name.startsWith("com.mysql.cj.")) {
+ throw new ClassNotFoundException(name);
+ } else {
+ loadedClass = super.loadClass(name, false);
+ }
+ }
+ if (resolve) {
+ resolveClass(loadedClass);
+ }
+ return loadedClass;
+ }
+ }
+ }
+}
diff --git
a/processing/src/test/java/org/apache/druid/utils/ConnectionUriUtilsTest.java
b/processing/src/test/java/org/apache/druid/utils/ConnectionUriUtilsTest.java
index 963561b8a2e..eec4767e607 100644
---
a/processing/src/test/java/org/apache/druid/utils/ConnectionUriUtilsTest.java
+++
b/processing/src/test/java/org/apache/druid/utils/ConnectionUriUtilsTest.java
@@ -33,6 +33,8 @@ public class ConnectionUriUtilsTest
public static class ThrowIfURLHasNotAllowedPropertiesTest
{
private static final String MYSQL_URI =
"jdbc:mysql://localhost:3306/test?user=druid&password=diurd&keyonly&otherOptions=wat";
+ private static final String MYSQL_URI_WITH_MARIA_DB_3X_SCHEME =
+
"jdbc:mysql://localhost:3306/test?user=druid&password=diurd&keyonly&otherOptions=wat&permitMysqlScheme";
private static final String MARIA_URI =
"jdbc:mariadb://localhost:3306/test?user=druid&password=diurd&keyonly&otherOptions=wat";
private static final String POSTGRES_URI =
"jdbc:postgresql://localhost:3306/test?user=druid&password=diurd&keyonly&otherOptions=wat";
private static final String UNKNOWN_URI =
"jdbc:druid://localhost:8888/query/v2/sql/avatica?user=druid&password=diurd&keyonly&otherOptions=wat";
@@ -122,35 +124,53 @@ public class ConnectionUriUtilsTest
@Test
public void testMySqlFallbackMySqlMaria2x()
{
- MockedStatic<ConnectionUriUtils> utils =
Mockito.mockStatic(ConnectionUriUtils.class);
- utils.when(() -> ConnectionUriUtils.tryParseJdbcUriParameters(MYSQL_URI,
false)).thenCallRealMethod();
- utils.when(() ->
ConnectionUriUtils.tryParseMySqlConnectionUri(MYSQL_URI)).thenThrow(ClassNotFoundException.class);
- utils.when(() ->
ConnectionUriUtils.tryParseMariaDb2xConnectionUri(MYSQL_URI)).thenCallRealMethod();
+ try (MockedStatic<ConnectionUriUtils> utils =
Mockito.mockStatic(ConnectionUriUtils.class)) {
+ utils.when(() ->
ConnectionUriUtils.tryParseJdbcUriParameters(MYSQL_URI,
false)).thenCallRealMethod();
+ utils.when(() ->
ConnectionUriUtils.tryParseMySqlConnectionUri(MYSQL_URI))
+ .thenThrow(ClassNotFoundException.class);
+ utils.when(() ->
ConnectionUriUtils.tryParseMariaDb2xConnectionUri(MYSQL_URI))
+ .thenReturn(ImmutableSet.of("user", "password", "keyonly",
"otherOptions"));
- Set<String> props =
ConnectionUriUtils.tryParseJdbcUriParameters(MYSQL_URI, false);
- // this would be 9 if didn't fall back to mariadb
- Assertions.assertEquals(4, props.size());
- utils.close();
+ Set<String> props =
ConnectionUriUtils.tryParseJdbcUriParameters(MYSQL_URI, false);
+ // this would be 9 if didn't fall back to mariadb
+ Assertions.assertEquals(4, props.size());
+ }
+ }
+
+ @Test
+ public void testMySqlFallbackMySqlMaria3x()
+ {
+ try (MockedStatic<ConnectionUriUtils> utils =
Mockito.mockStatic(ConnectionUriUtils.class)) {
+ utils.when(() ->
ConnectionUriUtils.tryParseJdbcUriParameters(MYSQL_URI_WITH_MARIA_DB_3X_SCHEME,
false))
+ .thenCallRealMethod();
+ utils.when(() ->
ConnectionUriUtils.tryParseMySqlConnectionUri(MYSQL_URI_WITH_MARIA_DB_3X_SCHEME))
+ .thenThrow(ClassNotFoundException.class);
+ utils.when(() ->
ConnectionUriUtils.tryParseMariaDb2xConnectionUri(MYSQL_URI_WITH_MARIA_DB_3X_SCHEME))
+ .thenThrow(ClassNotFoundException.class);
+ utils.when(() ->
ConnectionUriUtils.tryParseMariaDb3xConnectionUri(MYSQL_URI_WITH_MARIA_DB_3X_SCHEME))
+ .thenCallRealMethod();
+
+ Set<String> props =
ConnectionUriUtils.tryParseJdbcUriParameters(MYSQL_URI_WITH_MARIA_DB_3X_SCHEME,
false);
+ Assertions.assertEquals(
+ ImmutableSet.of("user", "password", "keyonly", "otherOptions",
"permitMysqlScheme"),
+ props
+ );
+ }
}
@Test
public void testMariaFallbackMaria3x()
{
- MockedStatic<ConnectionUriUtils> utils =
Mockito.mockStatic(ConnectionUriUtils.class);
- utils.when(() -> ConnectionUriUtils.tryParseJdbcUriParameters(MARIA_URI,
false)).thenCallRealMethod();
- utils.when(() ->
ConnectionUriUtils.tryParseMariaDb2xConnectionUri(MARIA_URI)).thenThrow(ClassNotFoundException.class);
- utils.when(() ->
ConnectionUriUtils.tryParseMariaDb3xConnectionUri(MARIA_URI)).thenCallRealMethod();
+ try (MockedStatic<ConnectionUriUtils> utils =
Mockito.mockStatic(ConnectionUriUtils.class)) {
+ utils.when(() ->
ConnectionUriUtils.tryParseJdbcUriParameters(MARIA_URI,
false)).thenCallRealMethod();
+ utils.when(() ->
ConnectionUriUtils.tryParseMariaDb2xConnectionUri(MARIA_URI))
+ .thenThrow(ClassNotFoundException.class);
+ utils.when(() ->
ConnectionUriUtils.tryParseMariaDb3xConnectionUri(MARIA_URI)).thenCallRealMethod();
- try {
Set<String> props =
ConnectionUriUtils.tryParseJdbcUriParameters(MARIA_URI, false);
// this would be 4 if didn't fall back to mariadb 3x
- Assertions.assertEquals(8, props.size());
+ Assertions.assertEquals(4, props.size());
}
- catch (RuntimeException e) {
-
- Assertions.assertTrue(e.getMessage().contains("Failed to find MariaDB
driver class"));
- }
- utils.close();
}
@Test
@@ -195,31 +215,12 @@ public class ConnectionUriUtilsTest
Assertions.assertFalse(props.contains("keyonly"));
}
- @Test
- public void testMariaDb2xDriver() throws Throwable
- {
- Set<String> props =
ConnectionUriUtils.tryParseMariaDb2xConnectionUri(MYSQL_URI);
- // mariadb doesn't spit out any extras other than what the user specified
- Assertions.assertEquals(4, props.size());
- Assertions.assertTrue(props.contains("user"));
- Assertions.assertTrue(props.contains("password"));
- Assertions.assertTrue(props.contains("otherOptions"));
- Assertions.assertTrue(props.contains("keyonly"));
- props = ConnectionUriUtils.tryParseMariaDb2xConnectionUri(MARIA_URI);
- Assertions.assertEquals(4, props.size());
- Assertions.assertTrue(props.contains("user"));
- Assertions.assertTrue(props.contains("password"));
- Assertions.assertTrue(props.contains("otherOptions"));
- Assertions.assertTrue(props.contains("keyonly"));
- }
-
@Test
public void testMariaDb3xDriver() throws Exception
{
- // at the time of adding this test, mariadb connector/j 3.x does not
actually parse jdbc:mysql uris
- // so this would throw an IAE.class instead of
ClassNotFoundException.class if the connector is swapped out
- // in maven dependencies
- Assertions.assertThrows(ClassNotFoundException.class, () ->
+ // MariaDB Connector/J 3.x rejects plain jdbc:mysql URLs by default.
MYSQL_URI lacks permitMysqlScheme,
+ // so Configuration.parse returns null, which Druid translates to IAE.
+ Assertions.assertThrows(IAE.class, () ->
ConnectionUriUtils.tryParseMariaDb3xConnectionUri(MYSQL_URI)
);
}
@@ -227,19 +228,12 @@ public class ConnectionUriUtilsTest
@Test
public void testMariaDb3xDriverMariaUri() throws Exception
{
- // mariadb 3.x driver cannot be loaded alongside 2.x, so this will fail
with class not found
- // however, if we swap out version in pom then we end up with 8 keys
where
- // "database", "addresses", "codecs", and "initialUrl" are added as
extras
- // we should perhaps consider adding them to built-in allowed lists in
the future when this driver is no longer
- // an alpha release
- Assertions.assertThrows(ClassNotFoundException.class, () -> {
- Set<String> props =
ConnectionUriUtils.tryParseMariaDb3xConnectionUri(MARIA_URI);
- Assertions.assertEquals(8, props.size());
- Assertions.assertTrue(props.contains("user"));
- Assertions.assertTrue(props.contains("password"));
- Assertions.assertTrue(props.contains("otherOptions"));
- Assertions.assertTrue(props.contains("keyonly"));
- });
+ Set<String> props =
ConnectionUriUtils.tryParseMariaDb3xConnectionUri(MARIA_URI);
+ Assertions.assertEquals(4, props.size());
+ Assertions.assertTrue(props.contains("user"));
+ Assertions.assertTrue(props.contains("password"));
+ Assertions.assertTrue(props.contains("otherOptions"));
+ Assertions.assertTrue(props.contains("keyonly"));
}
@Test
@@ -257,7 +251,7 @@ public class ConnectionUriUtilsTest
@Test
public void testMariaDbInvalidArgs() throws Exception
{
- Assertions.assertThrows(IAE.class, () ->
ConnectionUriUtils.tryParseMariaDb2xConnectionUri(POSTGRES_URI));
+ Assertions.assertThrows(IAE.class, () ->
ConnectionUriUtils.tryParseMariaDb3xConnectionUri(POSTGRES_URI));
}
}
}
diff --git
a/server/src/main/java/org/apache/druid/server/initialization/JdbcAccessSecurityConfig.java
b/server/src/main/java/org/apache/druid/server/initialization/JdbcAccessSecurityConfig.java
index 42b0044b1d0..6b7213ada50 100644
---
a/server/src/main/java/org/apache/druid/server/initialization/JdbcAccessSecurityConfig.java
+++
b/server/src/main/java/org/apache/druid/server/initialization/JdbcAccessSecurityConfig.java
@@ -33,9 +33,10 @@ import java.util.Set;
public class JdbcAccessSecurityConfig
{
static final Set<String> DEFAULT_ALLOWED_PROPERTIES = ImmutableSet.of(
- // MySQL
+ // MySQL and MariaDB
"useSSL",
"requireSSL",
+ "sslMode",
// PostgreSQL
"ssl",
diff --git
a/server/src/test/java/org/apache/druid/metadata/input/SqlInputSourceTest.java
b/server/src/test/java/org/apache/druid/metadata/input/SqlInputSourceTest.java
index 5611a06c2e9..b8d3af57f4c 100644
---
a/server/src/test/java/org/apache/druid/metadata/input/SqlInputSourceTest.java
+++
b/server/src/test/java/org/apache/druid/metadata/input/SqlInputSourceTest.java
@@ -264,7 +264,7 @@ public class SqlInputSourceTest
)
);
Assertions.assertEquals(
- "The property [user] is not in the allowed list [useSSL, requireSSL,
ssl, sslmode]",
+ "The property [user] is not in the allowed list [useSSL, requireSSL,
sslMode, ssl, sslmode]",
t.getMessage()
);
}
---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]