mirror of
https://github.com/keycloak/keycloak.git
synced 2025-12-16 20:15:46 -06:00
Add PK creation for databasechangelog in MySQL to keycloak-database-update.sql when manual migration is used.
Closes #44349 Signed-off-by: vramik <vramik@redhat.com>
This commit is contained in:
@@ -29,9 +29,9 @@ import java.util.Set;
|
||||
|
||||
import org.keycloak.connections.jpa.entityprovider.JpaEntityProvider;
|
||||
import org.keycloak.connections.jpa.updater.JpaUpdaterProvider;
|
||||
import org.keycloak.connections.jpa.updater.liquibase.conn.CustomChangeLogHistoryService;
|
||||
import org.keycloak.connections.jpa.updater.liquibase.conn.KeycloakLiquibase;
|
||||
import org.keycloak.connections.jpa.updater.liquibase.conn.LiquibaseConnectionProvider;
|
||||
import org.keycloak.connections.jpa.updater.liquibase.conn.MySQLCustomChangeLogHistoryService;
|
||||
import org.keycloak.connections.jpa.util.JpaUtils;
|
||||
import org.keycloak.models.KeycloakSession;
|
||||
|
||||
@@ -215,6 +215,13 @@ public class LiquibaseJpaUpdaterProvider implements JpaUpdaterProvider {
|
||||
// in org.keycloak.connections.jpa.updater.liquibase.lock.CustomLockService.init() called indirectly from
|
||||
// KeycloakApplication constructor (search for waitForLock() call). Hence it is not included in the creation script.
|
||||
|
||||
// For MySQL, add primary key to DATABASECHANGELOG table (handled by MySQLCustomChangeLogHistoryService at runtime)
|
||||
ChangeLogHistoryService changeLogHistoryService = ChangeLogHistoryServiceFactory.getInstance().getChangeLogService(database);
|
||||
if (changeLogHistoryService instanceof MySQLCustomChangeLogHistoryService customChangeLogHistoryService) {
|
||||
loggingExecutor.comment("Add primary key to DATABASECHANGELOG table for MySQL");
|
||||
loggingExecutor.execute(customChangeLogHistoryService.getAddDatabaseChangeLogPKStatement());
|
||||
}
|
||||
|
||||
executorService.setExecutor(LiquibaseConstants.JDBC_EXECUTOR, database, oldTemplate);
|
||||
}
|
||||
|
||||
@@ -286,7 +293,7 @@ public class LiquibaseJpaUpdaterProvider implements JpaUpdaterProvider {
|
||||
|
||||
private void resetLiquibaseServices(KeycloakLiquibase liquibase) {
|
||||
liquibase.resetServices();
|
||||
ChangeLogHistoryServiceFactory.getInstance().register(new CustomChangeLogHistoryService());
|
||||
ChangeLogHistoryServiceFactory.getInstance().register(new MySQLCustomChangeLogHistoryService());
|
||||
}
|
||||
|
||||
private List<ChangeSet> getLiquibaseUnrunChangeSets(Liquibase liquibase) throws LiquibaseException {
|
||||
|
||||
@@ -16,11 +16,16 @@
|
||||
*/
|
||||
package org.keycloak.connections.jpa.updater.liquibase.conn;
|
||||
|
||||
import org.keycloak.connections.jpa.updater.liquibase.LiquibaseConstants;
|
||||
|
||||
import liquibase.Scope;
|
||||
import liquibase.change.ColumnConfig;
|
||||
import liquibase.changelog.StandardChangeLogHistoryService;
|
||||
import liquibase.database.Database;
|
||||
import liquibase.database.core.MySQLDatabase;
|
||||
import liquibase.exception.DatabaseException;
|
||||
import liquibase.executor.ExecutorService;
|
||||
import liquibase.executor.LoggingExecutor;
|
||||
import liquibase.executor.jvm.ChangelogJdbcMdcListener;
|
||||
import liquibase.snapshot.InvalidExampleException;
|
||||
import liquibase.snapshot.SnapshotGeneratorFactory;
|
||||
@@ -33,7 +38,7 @@ import liquibase.structure.core.Table;
|
||||
*
|
||||
* @author hmlnarik
|
||||
*/
|
||||
public class CustomChangeLogHistoryService extends StandardChangeLogHistoryService {
|
||||
public class MySQLCustomChangeLogHistoryService extends StandardChangeLogHistoryService {
|
||||
|
||||
private boolean serviceInitialized;
|
||||
|
||||
@@ -50,6 +55,13 @@ public class CustomChangeLogHistoryService extends StandardChangeLogHistoryServi
|
||||
|
||||
serviceInitialized = true;
|
||||
|
||||
|
||||
// Skip execution in manual migration mode - the PK statement is added to the export by LiquibaseJpaUpdaterProvider
|
||||
ExecutorService executorService = Scope.getCurrentScope().getSingleton(ExecutorService.class);
|
||||
if (executorService.getExecutor(LiquibaseConstants.JDBC_EXECUTOR, getDatabase()) instanceof LoggingExecutor) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!existsDatabaseChangelogPK()) {
|
||||
createDatabaseChangelogPK();
|
||||
}
|
||||
@@ -74,8 +86,7 @@ public class CustomChangeLogHistoryService extends StandardChangeLogHistoryServi
|
||||
}
|
||||
|
||||
private void createDatabaseChangelogPK() throws DatabaseException {
|
||||
AddPrimaryKeyStatement pkStatement = new AddPrimaryKeyStatement(getLiquibaseCatalogName(), getLiquibaseSchemaName(), getDatabaseChangeLogTableName(),
|
||||
ColumnConfig.arrayFromNames("ID, AUTHOR, FILENAME"), "PK_DATABASECHANGELOG");
|
||||
AddPrimaryKeyStatement pkStatement = getAddDatabaseChangeLogPKStatement();
|
||||
try {
|
||||
ChangelogJdbcMdcListener.execute(getDatabase(), ex -> ex.execute(pkStatement));
|
||||
getDatabase().commit();
|
||||
@@ -86,4 +97,9 @@ public class CustomChangeLogHistoryService extends StandardChangeLogHistoryServi
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public AddPrimaryKeyStatement getAddDatabaseChangeLogPKStatement() {
|
||||
return new AddPrimaryKeyStatement(getLiquibaseCatalogName(), getLiquibaseSchemaName(), getDatabaseChangeLogTableName(),
|
||||
ColumnConfig.arrayFromNames("ID, AUTHOR, FILENAME"), "PK_DATABASECHANGELOG");
|
||||
}
|
||||
}
|
||||
@@ -15,4 +15,4 @@
|
||||
# limitations under the License.
|
||||
#
|
||||
|
||||
org.keycloak.connections.jpa.updater.liquibase.conn.CustomChangeLogHistoryService
|
||||
org.keycloak.connections.jpa.updater.liquibase.conn.MySQLCustomChangeLogHistoryService
|
||||
@@ -32,9 +32,9 @@ import org.keycloak.connections.jpa.entityprovider.JpaEntityProvider;
|
||||
import org.keycloak.connections.jpa.updater.JpaUpdaterProvider;
|
||||
import org.keycloak.connections.jpa.updater.liquibase.LiquibaseConstants;
|
||||
import org.keycloak.connections.jpa.updater.liquibase.ThreadLocalSessionContext;
|
||||
import org.keycloak.connections.jpa.updater.liquibase.conn.CustomChangeLogHistoryService;
|
||||
import org.keycloak.connections.jpa.updater.liquibase.conn.KeycloakLiquibase;
|
||||
import org.keycloak.connections.jpa.updater.liquibase.conn.LiquibaseConnectionProvider;
|
||||
import org.keycloak.connections.jpa.updater.liquibase.conn.MySQLCustomChangeLogHistoryService;
|
||||
import org.keycloak.connections.jpa.util.JpaUtils;
|
||||
import org.keycloak.models.KeycloakSession;
|
||||
|
||||
@@ -217,6 +217,13 @@ public class QuarkusJpaUpdaterProvider implements JpaUpdaterProvider {
|
||||
// in org.keycloak.connections.jpa.updater.liquibase.lock.CustomLockService.init() called indirectly from
|
||||
// KeycloakApplication constructor (search for waitForLock() call). Hence it is not included in the creation script.
|
||||
|
||||
// For MySQL, add primary key to DATABASECHANGELOG table (handled by MySQLCustomChangeLogHistoryService at runtime)
|
||||
ChangeLogHistoryService changeLogHistoryService = ChangeLogHistoryServiceFactory.getInstance().getChangeLogService(database);
|
||||
if (changeLogHistoryService instanceof MySQLCustomChangeLogHistoryService customChangeLogHistoryService) {
|
||||
loggingExecutor.comment("Add primary key to DATABASECHANGELOG table for MySQL");
|
||||
loggingExecutor.execute(customChangeLogHistoryService.getAddDatabaseChangeLogPKStatement());
|
||||
}
|
||||
|
||||
executorService.setExecutor(LiquibaseConstants.JDBC_EXECUTOR, database, oldTemplate);
|
||||
}
|
||||
|
||||
@@ -283,7 +290,7 @@ public class QuarkusJpaUpdaterProvider implements JpaUpdaterProvider {
|
||||
|
||||
private void resetLiquibaseServices(KeycloakLiquibase liquibase) {
|
||||
liquibase.resetServices();
|
||||
getChangeLogHistoryService().register(new CustomChangeLogHistoryService());
|
||||
getChangeLogHistoryService().register(new MySQLCustomChangeLogHistoryService());
|
||||
}
|
||||
|
||||
private ChangeLogHistoryServiceFactory getChangeLogHistoryService() {
|
||||
|
||||
@@ -85,7 +85,7 @@ public abstract class BasicDatabaseTest {
|
||||
cliResult.assertMessage("Import finished successfully");
|
||||
}
|
||||
|
||||
public void assertManualDbInitialization(CLIResult cliResult, RawDistRootPath rawDistRootPath) {
|
||||
public String assertManualDbInitialization(CLIResult cliResult, RawDistRootPath rawDistRootPath) {
|
||||
cliResult.assertMessage("Database not initialized, please initialize database with");
|
||||
|
||||
var output = readKeycloakDbUpdateScript(rawDistRootPath);
|
||||
@@ -94,6 +94,8 @@ public abstract class BasicDatabaseTest {
|
||||
assertThat(output, containsString("Keycloak database creation script - apply this script to empty DB"));
|
||||
assertThat(output, containsString("Change Log: META-INF/jpa-changelog-master.xml"));
|
||||
assertThat(output, containsString("Changeset META-INF/jpa-changelog-26.2.6.xml"));
|
||||
|
||||
return output;
|
||||
}
|
||||
|
||||
protected static String readKeycloakDbUpdateScript(RawDistRootPath path) {
|
||||
|
||||
@@ -11,6 +11,9 @@ import io.quarkus.test.junit.main.Launch;
|
||||
import org.junit.jupiter.api.Tag;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import static org.hamcrest.MatcherAssert.assertThat;
|
||||
import static org.hamcrest.Matchers.containsString;
|
||||
|
||||
@DistributionTest(removeBuildOptionsAfterBuild = true)
|
||||
@WithDatabase(alias = "mysql")
|
||||
public class MySQLDistTest extends MySQLTest {
|
||||
@@ -27,7 +30,11 @@ public class MySQLDistTest extends MySQLTest {
|
||||
@Test
|
||||
@Launch({"start", AbstractAutoBuildCommand.OPTIMIZED_BUILD_OPTION_LONG, "--spi-connections-jpa-quarkus-migration-strategy=manual", "--spi-connections-jpa-quarkus-initialize-empty=false", "--http-enabled=true", "--hostname-strict=false",})
|
||||
public void testKeycloakDbUpdateScript(CLIResult cliResult, RawDistRootPath rawDistRootPath) {
|
||||
assertManualDbInitialization(cliResult, rawDistRootPath);
|
||||
String output = assertManualDbInitialization(cliResult, rawDistRootPath);
|
||||
|
||||
// Verify MySQL primary key is included
|
||||
assertThat(output, containsString("Add primary key to DATABASECHANGELOG table for MySQL"));
|
||||
assertThat(output, containsString("ALTER TABLE keycloak.DATABASECHANGELOG ADD PRIMARY KEY (ID, AUTHOR, FILENAME);"));
|
||||
}
|
||||
|
||||
@Tag(DistributionTest.STORAGE)
|
||||
|
||||
Reference in New Issue
Block a user