KEYCLOAK-16935 Fix liquibase to work with MySQL 8.0.23+
This commit is contained in:
parent
068a1811f2
commit
ff4c0e4412
6 changed files with 134 additions and 3 deletions
|
@ -44,6 +44,7 @@ import org.jboss.logging.Logger;
|
|||
import org.keycloak.common.util.reflections.Reflections;
|
||||
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.LiquibaseConnectionProvider;
|
||||
import org.keycloak.connections.jpa.util.JpaUtils;
|
||||
import org.keycloak.models.KeycloakSession;
|
||||
|
@ -274,6 +275,8 @@ public class LiquibaseJpaUpdaterProvider implements JpaUpdaterProvider {
|
|||
private void resetLiquibaseServices(Liquibase liquibase) {
|
||||
Method resetServices = Reflections.findDeclaredMethod(Liquibase.class, "resetServices");
|
||||
Reflections.invokeMethod(true, resetServices, liquibase);
|
||||
|
||||
ChangeLogHistoryServiceFactory.getInstance().register(new CustomChangeLogHistoryService());
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
|
|
|
@ -0,0 +1,120 @@
|
|||
/*
|
||||
* Copyright 2021 Red Hat, Inc. and/or its affiliates
|
||||
* and other contributors as indicated by the @author tags.
|
||||
*
|
||||
* Licensed 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.keycloak.connections.jpa.updater.liquibase.conn;
|
||||
|
||||
import org.keycloak.common.util.reflections.Reflections;
|
||||
import java.lang.reflect.Field;
|
||||
import java.text.DateFormat;
|
||||
import java.text.ParseException;
|
||||
import java.text.SimpleDateFormat;
|
||||
import java.time.LocalDateTime;
|
||||
import java.time.ZoneId;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.Date;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import liquibase.ContextExpression;
|
||||
import liquibase.Labels;
|
||||
import liquibase.change.CheckSum;
|
||||
import liquibase.changelog.ChangeSet;
|
||||
import liquibase.changelog.RanChangeSet;
|
||||
import liquibase.changelog.StandardChangeLogHistoryService;
|
||||
import liquibase.database.Database;
|
||||
import liquibase.database.core.MySQLDatabase;
|
||||
import liquibase.exception.DatabaseException;
|
||||
import liquibase.logging.LogFactory;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author hmlnarik
|
||||
*/
|
||||
public class CustomChangeLogHistoryService extends StandardChangeLogHistoryService {
|
||||
|
||||
private List<RanChangeSet> ranChangeSetList;
|
||||
|
||||
@Override
|
||||
public List<RanChangeSet> getRanChangeSets() throws DatabaseException {
|
||||
Database database = getDatabase();
|
||||
if (! (database instanceof MySQLDatabase) || database.getDatabaseMajorVersion() < 8) {
|
||||
return super.getRanChangeSets();
|
||||
}
|
||||
if (this.ranChangeSetList == null) {
|
||||
String databaseChangeLogTableName = getDatabase().escapeTableName(getLiquibaseCatalogName(), getLiquibaseSchemaName(), getDatabaseChangeLogTableName());
|
||||
List<RanChangeSet> ranChangeSetList = new ArrayList<>();
|
||||
if (hasDatabaseChangeLogTable()) {
|
||||
LogFactory.getLogger().info("Reading from " + databaseChangeLogTableName);
|
||||
List<Map<String, ?>> results = queryDatabaseChangeLogTable(database);
|
||||
for (Map rs : results) {
|
||||
String fileName = rs.get("FILENAME").toString();
|
||||
String author = rs.get("AUTHOR").toString();
|
||||
String id = rs.get("ID").toString();
|
||||
String md5sum = rs.get("MD5SUM") == null || getDatabaseChecksumsCompatible() ? null : rs.get("MD5SUM").toString();
|
||||
String description = rs.get("DESCRIPTION") == null ? null : rs.get("DESCRIPTION").toString();
|
||||
String comments = rs.get("COMMENTS") == null ? null : rs.get("COMMENTS").toString();
|
||||
Object tmpDateExecuted = rs.get("DATEEXECUTED");
|
||||
Date dateExecuted = null;
|
||||
if (tmpDateExecuted instanceof Date) {
|
||||
dateExecuted = (Date) tmpDateExecuted;
|
||||
} else if (tmpDateExecuted instanceof LocalDateTime) {
|
||||
dateExecuted = Date.from(((LocalDateTime) tmpDateExecuted).atZone(ZoneId.systemDefault()).toInstant());
|
||||
} else {
|
||||
DateFormat df = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
|
||||
try {
|
||||
dateExecuted = df.parse((String) tmpDateExecuted);
|
||||
} catch (ParseException e) {
|
||||
}
|
||||
}
|
||||
String tmpOrderExecuted = rs.get("ORDEREXECUTED").toString();
|
||||
Integer orderExecuted = (tmpOrderExecuted == null ? null : Integer.valueOf(tmpOrderExecuted));
|
||||
String tag = rs.get("TAG") == null ? null : rs.get("TAG").toString();
|
||||
String execType = rs.get("EXECTYPE") == null ? null : rs.get("EXECTYPE").toString();
|
||||
ContextExpression contexts = new ContextExpression((String) rs.get("CONTEXTS"));
|
||||
Labels labels = new Labels((String) rs.get("LABELS"));
|
||||
String deploymentId = (String) rs.get("DEPLOYMENT_ID");
|
||||
|
||||
try {
|
||||
RanChangeSet ranChangeSet = new RanChangeSet(fileName, id, author, CheckSum.parse(md5sum), dateExecuted, tag, ChangeSet.ExecType.valueOf(execType), description, comments, contexts, labels, deploymentId);
|
||||
ranChangeSet.setOrderExecuted(orderExecuted);
|
||||
ranChangeSetList.add(ranChangeSet);
|
||||
} catch (IllegalArgumentException e) {
|
||||
LogFactory.getLogger().severe("Unknown EXECTYPE from database: " + execType);
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
this.ranChangeSetList = ranChangeSetList;
|
||||
}
|
||||
return Collections.unmodifiableList(ranChangeSetList);
|
||||
}
|
||||
|
||||
private boolean getDatabaseChecksumsCompatible() {
|
||||
Field f = Reflections.findDeclaredField(StandardChangeLogHistoryService.class, "databaseChecksumsCompatible");
|
||||
if (f != null) {
|
||||
f.setAccessible(true);
|
||||
Boolean databaseChecksumsCompatible = Reflections.getFieldValue(f, this, Boolean.class);
|
||||
return databaseChecksumsCompatible == null ? true : databaseChecksumsCompatible;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getPriority() {
|
||||
return super.getPriority() + 1; // Ensure bigger priority than StandardChangeLogHistoryService
|
||||
}
|
||||
}
|
|
@ -48,6 +48,7 @@ import org.keycloak.models.KeycloakSession;
|
|||
import org.keycloak.models.KeycloakSessionFactory;
|
||||
|
||||
import java.sql.Connection;
|
||||
import liquibase.changelog.ChangeLogHistoryServiceFactory;
|
||||
|
||||
/**
|
||||
* @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
|
||||
|
@ -117,6 +118,8 @@ public class DefaultLiquibaseConnectionProvider implements LiquibaseConnectionPr
|
|||
// Use "SELECT FOR UPDATE" for locking database
|
||||
SqlGeneratorFactory.getInstance().register(new CustomLockDatabaseChangeLogGenerator());
|
||||
|
||||
ChangeLogHistoryServiceFactory.getInstance().register(new CustomChangeLogHistoryService());
|
||||
|
||||
// Adding CustomCreateIndexChange for handling conditional indices creation
|
||||
ChangeFactory.getInstance().register(CustomCreateIndexChange.class);
|
||||
}
|
||||
|
|
2
pom.xml
2
pom.xml
|
@ -128,7 +128,7 @@
|
|||
|
||||
<jetty9.version>${jetty92.version}</jetty9.version>
|
||||
<liquibase.version>3.5.5</liquibase.version>
|
||||
<mysql.version>8.0.18</mysql.version>
|
||||
<mysql.version>8.0.23</mysql.version>
|
||||
<osgi.version>4.2.0</osgi.version>
|
||||
<pax.web.version>7.1.0</pax.web.version>
|
||||
<postgresql.version>42.2.8</postgresql.version>
|
||||
|
|
|
@ -35,6 +35,7 @@ import org.keycloak.common.util.reflections.Reflections;
|
|||
import org.keycloak.connections.jpa.entityprovider.JpaEntityProvider;
|
||||
import org.keycloak.connections.jpa.updater.JpaUpdaterProvider;
|
||||
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.LiquibaseConnectionProvider;
|
||||
import org.keycloak.connections.jpa.util.JpaUtils;
|
||||
import org.keycloak.models.KeycloakSession;
|
||||
|
@ -283,6 +284,8 @@ public class QuarkusJpaUpdaterProvider implements JpaUpdaterProvider {
|
|||
private void resetLiquibaseServices(Liquibase liquibase) {
|
||||
Method resetServices = Reflections.findDeclaredMethod(Liquibase.class, "resetServices");
|
||||
Reflections.invokeMethod(true, resetServices, liquibase);
|
||||
|
||||
ChangeLogHistoryServiceFactory.getInstance().register(new CustomChangeLogHistoryService());
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
|
|
|
@ -24,7 +24,6 @@ import javax.xml.parsers.SAXParserFactory;
|
|||
|
||||
import liquibase.database.core.MariaDBDatabase;
|
||||
import liquibase.database.core.MySQLDatabase;
|
||||
import liquibase.database.core.PostgresDatabase;
|
||||
import org.jboss.logging.Logger;
|
||||
import org.keycloak.Config;
|
||||
import org.keycloak.connections.jpa.JpaConnectionProvider;
|
||||
|
@ -33,18 +32,19 @@ import org.keycloak.connections.jpa.updater.liquibase.MySQL8VarcharType;
|
|||
import org.keycloak.connections.jpa.updater.liquibase.PostgresPlusDatabase;
|
||||
import org.keycloak.connections.jpa.updater.liquibase.UpdatedMariaDBDatabase;
|
||||
import org.keycloak.connections.jpa.updater.liquibase.UpdatedMySqlDatabase;
|
||||
import org.keycloak.connections.jpa.updater.liquibase.conn.CustomChangeLogHistoryService;
|
||||
import org.keycloak.connections.jpa.updater.liquibase.conn.LiquibaseConnectionProvider;
|
||||
import org.keycloak.connections.jpa.updater.liquibase.conn.LiquibaseConnectionProviderFactory;
|
||||
import org.keycloak.models.KeycloakSession;
|
||||
import org.keycloak.models.KeycloakSessionFactory;
|
||||
|
||||
import liquibase.Liquibase;
|
||||
import liquibase.changelog.ChangeLogHistoryServiceFactory;
|
||||
import liquibase.database.Database;
|
||||
import liquibase.database.DatabaseFactory;
|
||||
import liquibase.database.jvm.JdbcConnection;
|
||||
import liquibase.datatype.DataTypeFactory;
|
||||
import liquibase.exception.LiquibaseException;
|
||||
import liquibase.logging.LogFactory;
|
||||
import liquibase.parser.ChangeLogParser;
|
||||
import liquibase.parser.ChangeLogParserFactory;
|
||||
import liquibase.parser.core.xml.XMLChangeLogSAXParser;
|
||||
|
@ -91,6 +91,8 @@ public class QuarkusLiquibaseConnectionProvider implements LiquibaseConnectionPr
|
|||
if (database.getDatabaseProductName().equals(MySQLDatabase.PRODUCT_NAME)) {
|
||||
// Adding CustomVarcharType for MySQL 8 and newer
|
||||
DataTypeFactory.getInstance().register(MySQL8VarcharType.class);
|
||||
|
||||
ChangeLogHistoryServiceFactory.getInstance().register(new CustomChangeLogHistoryService());
|
||||
} else if (database.getDatabaseProductName().equals(MariaDBDatabase.PRODUCT_NAME)) {
|
||||
// Adding CustomVarcharType for MySQL 8 and newer
|
||||
DataTypeFactory.getInstance().register(MySQL8VarcharType.class);
|
||||
|
|
Loading…
Reference in a new issue