diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
index 08b64439cdf..8dfc79b709a 100644
--- a/.github/workflows/ci.yml
+++ b/.github/workflows/ci.yml
@@ -693,7 +693,7 @@ jobs:
timeout-minutes: 75
strategy:
matrix:
- db: [postgres, mysql, oracle, mssql, mariadb]
+ db: [postgres, mysql, oracle, mssql, mariadb, tidb]
fail-fast: false
steps:
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
diff --git a/docs/tests-db.md b/docs/tests-db.md
index 5da4534ff6d..ef32e968afb 100644
--- a/docs/tests-db.md
+++ b/docs/tests-db.md
@@ -53,6 +53,23 @@ Stop MySQl:
docker rm -f mariadb
+TiDB
+-----
+
+The simplest way to test with TiDB is to use the official [TiDB docker image](https://hub.docker.com/r/pingcap/tidb).
+
+Start TiDB:
+
+ docker run --name tidb -p 4000:4000 -d pingcap/tidb:v8.5.2
+
+Run tests:
+
+ mvn install -Dkeycloak.connectionsJpa.url=jdbc:mysql://`docker inspect --format '{{ .NetworkSettings.IPAddress }}' tidb`:4000/test -Dkeycloak.connectionsJpa.driver=com.mysql.jdbc.Driver -Dkeycloak.connectionsJpa.user=root -Dkeycloak.connectionsJpa.password=
+
+Stop TiDB:
+
+ docker rm -f tidb
+
Using built-in profiles to run database tests using docker containers
-------
diff --git a/model/jpa/src/main/resources/META-INF/jpa-changelog-20.0.0.xml b/model/jpa/src/main/resources/META-INF/jpa-changelog-20.0.0.xml
index 1c7977a005d..f07beba7c28 100644
--- a/model/jpa/src/main/resources/META-INF/jpa-changelog-20.0.0.xml
+++ b/model/jpa/src/main/resources/META-INF/jpa-changelog-20.0.0.xml
@@ -55,6 +55,17 @@
+
+
+
+
+
+
+
+
+
+
+
@@ -68,4 +79,19 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/pom.xml b/pom.xml
index 083c29efbe4..382493189d1 100644
--- a/pom.xml
+++ b/pom.xml
@@ -151,6 +151,8 @@
4.1.2
+ v8.5.2
+ mirror.gcr.io/pingcap/tidb:${tidb.version}
8.4
mirror.gcr.io/mysql:${mysql.version}
8.3.0
diff --git a/quarkus/tests/integration/pom.xml b/quarkus/tests/integration/pom.xml
index 5e5bcf9aa88..96396249310 100644
--- a/quarkus/tests/integration/pom.xml
+++ b/quarkus/tests/integration/pom.xml
@@ -190,6 +190,7 @@
${mysql.container}
quay.io/infinispan/server:${infinispan.version}
${mssql.container}
+ ${tidb.container}
diff --git a/quarkus/tests/junit5/pom.xml b/quarkus/tests/junit5/pom.xml
index 156b9ec1cd3..4f702145ac3 100644
--- a/quarkus/tests/junit5/pom.xml
+++ b/quarkus/tests/junit5/pom.xml
@@ -102,6 +102,10 @@
org.testcontainers
mssqlserver
+
+ org.testcontainers
+ tidb
+
diff --git a/quarkus/tests/junit5/src/main/java/org/keycloak/it/junit5/extension/DatabaseContainer.java b/quarkus/tests/junit5/src/main/java/org/keycloak/it/junit5/extension/DatabaseContainer.java
index c9ad59e4658..35b448c92f5 100644
--- a/quarkus/tests/junit5/src/main/java/org/keycloak/it/junit5/extension/DatabaseContainer.java
+++ b/quarkus/tests/junit5/src/main/java/org/keycloak/it/junit5/extension/DatabaseContainer.java
@@ -26,7 +26,7 @@ import org.testcontainers.containers.MSSQLServerContainer;
import org.testcontainers.containers.MariaDBContainer;
import org.testcontainers.containers.MySQLContainer;
import org.testcontainers.containers.PostgreSQLContainer;
-import org.testcontainers.images.PullPolicy;
+import org.testcontainers.tidb.TiDBContainer;
import org.testcontainers.utility.DockerImageName;
public class DatabaseContainer {
@@ -95,6 +95,7 @@ public class DatabaseContainer {
String MARIADB_IMAGE = System.getProperty("kc.db.mariadb.container.image");
String MYSQL_IMAGE = System.getProperty("kc.db.mysql.container.image");
String MSSQL_IMAGE = System.getProperty("kc.db.mssql.container.image");
+ String TIDB_IMAGE = System.getProperty("kc.db.tidb.container.image");
switch (alias) {
case "postgres":
@@ -109,6 +110,9 @@ public class DatabaseContainer {
case "mssql":
DockerImageName MSSQL = DockerImageName.parse(MSSQL_IMAGE).asCompatibleSubstituteFor("sqlserver");
return configureJdbcContainer(new MSSQLServerContainer<>(MSSQL));
+ case "tidb":
+ DockerImageName TIDB = DockerImageName.parse(TIDB_IMAGE).asCompatibleSubstituteFor("pingcap/tidb");
+ return configureJdbcContainer(new TiDBContainer(TIDB));
default:
throw new RuntimeException("Unsupported database: " + alias);
}
diff --git a/test-framework/README.md b/test-framework/README.md
index 7ba2c23cfe2..a3ce07226f9 100644
--- a/test-framework/README.md
+++ b/test-framework/README.md
@@ -385,6 +385,7 @@ Valid values:
| mysql | MySQL test container |
| oracle | Oracle test container |
| postgres | PostgreSQL test container |
+| tidb | TiDb test container |
Configuration:
diff --git a/test-framework/bom/pom.xml b/test-framework/bom/pom.xml
index e26adb3b838..73b71140ef2 100755
--- a/test-framework/bom/pom.xml
+++ b/test-framework/bom/pom.xml
@@ -75,6 +75,12 @@
${project.version}
test
+
+ org.keycloak.testframework
+ keycloak-test-framework-db-tidb
+ ${project.version}
+ test
+
org.keycloak.testframework
keycloak-test-framework-db-oracle
diff --git a/test-framework/core/src/main/resources/org/keycloak/testframework/database/database.properties b/test-framework/core/src/main/resources/org/keycloak/testframework/database/database.properties
index d2438e69e9c..5f4c7864e8e 100644
--- a/test-framework/core/src/main/resources/org/keycloak/testframework/database/database.properties
+++ b/test-framework/core/src/main/resources/org/keycloak/testframework/database/database.properties
@@ -2,4 +2,5 @@ mysql.container=${mysql.container}
postgres.container=${postgresql.container}
mariadb.container=${mariadb.container}
mssql.container=${mssql.container}
-oracle.container=${oracledb.container}
\ No newline at end of file
+oracle.container=${oracledb.container}
+tidb.container=${tidb.container}
\ No newline at end of file
diff --git a/test-framework/db-tidb/pom.xml b/test-framework/db-tidb/pom.xml
new file mode 100755
index 00000000000..ee69655908a
--- /dev/null
+++ b/test-framework/db-tidb/pom.xml
@@ -0,0 +1,45 @@
+
+
+
+
+
+ keycloak-test-framework-parent
+ org.keycloak.testframework
+ 999.0.0-SNAPSHOT
+ ../pom.xml
+
+ 4.0.0
+
+ keycloak-test-framework-db-tidb
+ Keycloak Test Framework - TiDB support
+ jar
+ TiDB support for Keycloak Test Framework
+
+
+
+ org.keycloak.testframework
+ keycloak-test-framework-core
+ ${project.version}
+
+
+ org.testcontainers
+ tidb
+
+
+
diff --git a/test-framework/db-tidb/src/main/java/org/keycloak/testframework/database/TiDBDatabaseSupplier.java b/test-framework/db-tidb/src/main/java/org/keycloak/testframework/database/TiDBDatabaseSupplier.java
new file mode 100644
index 00000000000..07d2d546c52
--- /dev/null
+++ b/test-framework/db-tidb/src/main/java/org/keycloak/testframework/database/TiDBDatabaseSupplier.java
@@ -0,0 +1,15 @@
+package org.keycloak.testframework.database;
+
+public class TiDBDatabaseSupplier extends AbstractDatabaseSupplier {
+
+ @Override
+ public String getAlias() {
+ return "tidb";
+ }
+
+ @Override
+ TestDatabase getTestDatabase() {
+ return new TiDBTestDatabase();
+ }
+
+}
diff --git a/test-framework/db-tidb/src/main/java/org/keycloak/testframework/database/TiDBTestDatabase.java b/test-framework/db-tidb/src/main/java/org/keycloak/testframework/database/TiDBTestDatabase.java
new file mode 100644
index 00000000000..b7e9b9a34b2
--- /dev/null
+++ b/test-framework/db-tidb/src/main/java/org/keycloak/testframework/database/TiDBTestDatabase.java
@@ -0,0 +1,69 @@
+package org.keycloak.testframework.database;
+
+import org.apache.commons.lang3.StringUtils;
+import org.jboss.logging.Logger;
+import org.testcontainers.containers.JdbcDatabaseContainer;
+import org.testcontainers.tidb.TiDBContainer;
+import org.testcontainers.utility.DockerImageName;
+
+class TiDBTestDatabase extends AbstractContainerTestDatabase {
+
+ private static final Logger LOGGER = Logger.getLogger(TiDBTestDatabase.class);
+
+ public static final String NAME = "tidb";
+
+ @Override
+ public JdbcDatabaseContainer> createContainer() {
+ return new TiDBContainer(DockerImageName.parse(DatabaseProperties.getContainerImageName(NAME)).asCompatibleSubstituteFor("pingcap/tidb")){
+ @Override
+ public TiDBContainer withDatabaseName(String databaseName) {
+ if(StringUtils.equals(this.getDatabaseName(), databaseName)) {
+ return this;
+ }
+ throw new UnsupportedOperationException("The TiDB docker image does not currently support this");
+ }
+
+ @Override
+ public TiDBContainer withUsername(String username) {
+ if(StringUtils.equals(this.getUsername(), username)) {
+ return this;
+ }
+ throw new UnsupportedOperationException("The TiDB docker image does not currently support this");
+ }
+
+ @Override
+ public TiDBContainer withPassword(String password) {
+ if(StringUtils.equals(this.getPassword(), password)) {
+ return this;
+ }
+ throw new UnsupportedOperationException("The TiDB docker image does not currently support this");
+ }
+ }.withExposedPorts(4000);
+ }
+
+ @Override
+ public String getDatabaseVendor() {
+ return "mysql";
+ }
+
+ @Override
+ public Logger getLogger() {
+ return LOGGER;
+ }
+
+
+ @Override
+ public String getDatabase() {
+ return "test";
+ }
+
+ @Override
+ public String getUsername() {
+ return "root";
+ }
+
+ @Override
+ public String getPassword() {
+ return "";
+ }
+}
diff --git a/test-framework/db-tidb/src/main/java/org/keycloak/testframework/database/TiDBTestFrameworkExtension.java b/test-framework/db-tidb/src/main/java/org/keycloak/testframework/database/TiDBTestFrameworkExtension.java
new file mode 100644
index 00000000000..84c8a128d2b
--- /dev/null
+++ b/test-framework/db-tidb/src/main/java/org/keycloak/testframework/database/TiDBTestFrameworkExtension.java
@@ -0,0 +1,14 @@
+package org.keycloak.testframework.database;
+
+import org.keycloak.testframework.TestFrameworkExtension;
+import org.keycloak.testframework.injection.Supplier;
+
+import java.util.List;
+
+public class TiDBTestFrameworkExtension implements TestFrameworkExtension {
+
+ @Override
+ public List> suppliers() {
+ return List.of(new TiDBDatabaseSupplier());
+ }
+}
diff --git a/test-framework/db-tidb/src/main/resources/META-INF/services/org.keycloak.testframework.TestFrameworkExtension b/test-framework/db-tidb/src/main/resources/META-INF/services/org.keycloak.testframework.TestFrameworkExtension
new file mode 100644
index 00000000000..cc441db8dbd
--- /dev/null
+++ b/test-framework/db-tidb/src/main/resources/META-INF/services/org.keycloak.testframework.TestFrameworkExtension
@@ -0,0 +1 @@
+org.keycloak.testframework.database.TiDBTestFrameworkExtension
\ No newline at end of file
diff --git a/test-framework/examples/tests/pom.xml b/test-framework/examples/tests/pom.xml
index a136ef7f3c8..6cc7f3fc17f 100644
--- a/test-framework/examples/tests/pom.xml
+++ b/test-framework/examples/tests/pom.xml
@@ -64,6 +64,10 @@
org.keycloak.testframework
keycloak-test-framework-db-mysql
+
+ org.keycloak.testframework
+ keycloak-test-framework-db-tidb
+
org.keycloak.testframework
keycloak-test-framework-db-oracle
diff --git a/test-framework/pom.xml b/test-framework/pom.xml
index ddd6f79d913..833f5983538 100755
--- a/test-framework/pom.xml
+++ b/test-framework/pom.xml
@@ -41,6 +41,7 @@
db-mysql
db-oracle
db-postgres
+ db-tidb
email-server
examples
oauth
diff --git a/tests/base/pom.xml b/tests/base/pom.xml
index 0f02e5eee0e..61c37f3c75a 100755
--- a/tests/base/pom.xml
+++ b/tests/base/pom.xml
@@ -76,6 +76,10 @@
org.keycloak.testframework
keycloak-test-framework-db-postgres
+
+ org.keycloak.testframework
+ keycloak-test-framework-db-tidb
+
org.keycloak.testframework
keycloak-test-framework-email-server
diff --git a/tests/base/src/test/java/org/keycloak/tests/db/CaseSensitiveSchemaTest.java b/tests/base/src/test/java/org/keycloak/tests/db/CaseSensitiveSchemaTest.java
index fec5a739399..53aca3b50a7 100644
--- a/tests/base/src/test/java/org/keycloak/tests/db/CaseSensitiveSchemaTest.java
+++ b/tests/base/src/test/java/org/keycloak/tests/db/CaseSensitiveSchemaTest.java
@@ -19,7 +19,8 @@ import org.keycloak.testframework.realm.RoleConfigBuilder;
@KeycloakIntegrationTest(config = CaseSensitiveSchemaTest.KeycloakConfig.class)
// MSSQL does not support setting the default schema per session
-@DisabledForDatabases("mssql")
+// TiDb does not support setting the default schema per session.
+@DisabledForDatabases({"mssql", "tidb"})
public class CaseSensitiveSchemaTest {
@InjectTestDatabase(lifecycle = LifeCycle.CLASS, config = DatabaseConfigurator.class)
TestDatabase db;
diff --git a/tests/base/src/test/java/org/keycloak/tests/db/PreserveSchemaCaseLiquibaseTest.java b/tests/base/src/test/java/org/keycloak/tests/db/PreserveSchemaCaseLiquibaseTest.java
index c5e1e3fff04..d7374c50c33 100644
--- a/tests/base/src/test/java/org/keycloak/tests/db/PreserveSchemaCaseLiquibaseTest.java
+++ b/tests/base/src/test/java/org/keycloak/tests/db/PreserveSchemaCaseLiquibaseTest.java
@@ -12,8 +12,9 @@ import org.keycloak.testframework.server.KeycloakServerConfigBuilder;
@KeycloakIntegrationTest(config = PreserveSchemaCaseLiquibaseTest.KeycloakConfig.class)
// MSSQL does not support setting the default schema per session.
+// TiDb does not support setting the default schema per session.
// Oracle image does not support configuring user/databases with '-'
-@DisabledForDatabases({ "mssql", "oracle" })
+@DisabledForDatabases({ "mssql", "oracle", "tidb" })
public class PreserveSchemaCaseLiquibaseTest extends CaseSensitiveSchemaTest {
@InjectTestDatabase(lifecycle = LifeCycle.CLASS, config = DatabaseConfigurator.class)
diff --git a/testsuite/integration-arquillian/pom.xml b/testsuite/integration-arquillian/pom.xml
index f0a08d5e9dd..3a8fafad7c9 100644
--- a/testsuite/integration-arquillian/pom.xml
+++ b/testsuite/integration-arquillian/pom.xml
@@ -416,6 +416,29 @@
(?si)Ready for start up.*ready [^\n]{0,30}connections
+
+ db-tidb
+
+ mysql
+ com.mysql.jdbc.Driver
+ test
+ root
+
+ jdbc:mysql://${auth.server.db.host}:${docker.database.port}/${keycloak.connectionsJpa.database}
+
+
+
+
+ com.mysql
+ mysql-connector-j
+ ${mysql-jdbc.version}
+ ${tidb.container}
+ 4000
+ false
+ start
+ server is running MySQL protocol
+
+
db-postgres
diff --git a/testsuite/integration-arquillian/servers/migration/pom.xml b/testsuite/integration-arquillian/servers/migration/pom.xml
index 94cf6e625c6..239ae8a78cc 100644
--- a/testsuite/integration-arquillian/servers/migration/pom.xml
+++ b/testsuite/integration-arquillian/servers/migration/pom.xml
@@ -59,8 +59,6 @@
keycloak.connectionsJpa.password
- ^(?!\s*$).+
- "keycloak.connectionsJpa.password" property cannot be empty string!
keycloak.connectionsJpa.url