From d216f8f748f053a965087423c5eaf122e8e0b93a Mon Sep 17 00:00:00 2001 From: vramik Date: Mon, 30 Aug 2021 09:32:41 +0200 Subject: [PATCH] KEYCLOAK-19104 Add custom ForeignKeySnapshotGenerator --- .../CustomForeignKeySnapshotGenerator.java | 121 ++++++++++++++++++ .../DefaultLiquibaseConnectionProvider.java | 5 + testsuite/utils/pom.xml | 5 + 3 files changed, 131 insertions(+) create mode 100644 model/jpa/src/main/java/org/keycloak/connections/jpa/updater/liquibase/CustomForeignKeySnapshotGenerator.java diff --git a/model/jpa/src/main/java/org/keycloak/connections/jpa/updater/liquibase/CustomForeignKeySnapshotGenerator.java b/model/jpa/src/main/java/org/keycloak/connections/jpa/updater/liquibase/CustomForeignKeySnapshotGenerator.java new file mode 100644 index 0000000000..f682e5f459 --- /dev/null +++ b/model/jpa/src/main/java/org/keycloak/connections/jpa/updater/liquibase/CustomForeignKeySnapshotGenerator.java @@ -0,0 +1,121 @@ +/* + * 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; + +import java.sql.DatabaseMetaData; +import java.sql.SQLException; +import liquibase.database.Database; +import liquibase.database.DatabaseConnection; +import liquibase.database.core.MSSQLDatabase; +import liquibase.database.jvm.JdbcConnection; +import liquibase.exception.DatabaseException; +import liquibase.snapshot.jvm.ForeignKeySnapshotGenerator; +import liquibase.structure.DatabaseObject; +import liquibase.structure.core.ForeignKeyConstraintType; + +/** + * This class overrides original ForeignKeySnapshotGenerator from liquibase 3.5.5. + * It contains fix https://liquibase.jira.com/browse/CORE-3141 + */ +public class CustomForeignKeySnapshotGenerator extends ForeignKeySnapshotGenerator { + + public CustomForeignKeySnapshotGenerator() { + super(); + } + + @Override + public int getPriority(Class objectType, Database database) { + return super.getPriority(objectType, database) + 1; + } + + @Override + protected ForeignKeyConstraintType convertToForeignKeyConstraintType(Integer jdbcType, Database database) throws DatabaseException { + if (jdbcType == null) { + return ForeignKeyConstraintType.importedKeyRestrict; + } + if (driverUsesSpFkeys(database)) { + switch (jdbcType) { + case 0: + return ForeignKeyConstraintType.importedKeyCascade; + case 1: + return ForeignKeyConstraintType.importedKeyNoAction; + case 2: + return ForeignKeyConstraintType.importedKeySetNull; + case 3: + return ForeignKeyConstraintType.importedKeySetDefault; + default: + throw new DatabaseException("Unknown constraint type: " + jdbcType); + } + } else { + switch (jdbcType) { + case DatabaseMetaData.importedKeyCascade: + return ForeignKeyConstraintType.importedKeyCascade; + case DatabaseMetaData.importedKeyNoAction: + return ForeignKeyConstraintType.importedKeyNoAction; + case DatabaseMetaData.importedKeyRestrict: + if (database instanceof MSSQLDatabase) { + //mssql doesn't support restrict. Not sure why it comes back with this type sometimes + return ForeignKeyConstraintType.importedKeyNoAction; + } else { + return ForeignKeyConstraintType.importedKeyRestrict; + } + case DatabaseMetaData.importedKeySetDefault: + return ForeignKeyConstraintType.importedKeySetDefault; + case DatabaseMetaData.importedKeySetNull: + return ForeignKeyConstraintType.importedKeySetNull; + default: + throw new DatabaseException("Unknown constraint type: " + jdbcType); + } + } + } + + /* + * Sql server JDBC drivers prior to 6.3.3 used sp_fkeys to determine the delete/cascade metadata. + * The sp_fkeys stored procedure spec says that returned integer values of 0, 1, 2, or 4 + * translate to cascade, noAction, SetNull, or SetDefault which are not the values in the JDBC + * standard. + * + * If this method returns true, the sp_fkeys values should be used. Otherwise use the standard jdbc logic + * + * The change in logic went in with https://github.com/Microsoft/mssql-jdbc/pull/490 + */ + private boolean driverUsesSpFkeys(Database database) throws DatabaseException { + if (!(database instanceof MSSQLDatabase)) { + return false; + } + DatabaseConnection connection = database.getConnection(); + if (!(connection instanceof JdbcConnection)) { + return false; + } + + try { + DatabaseMetaData metaData = ((JdbcConnection) connection).getMetaData(); + int driverMajorVersion = metaData.getDriverMajorVersion(); + int driverMinorVersion= metaData.getDriverMinorVersion(); + String driverName = metaData.getDriverName(); + + if (!driverName.startsWith("Microsoft")) { + return false; + } + + return !(driverMajorVersion > 6 || (driverMajorVersion == 6 && driverMinorVersion >= 3)); + } catch (SQLException e) { + throw new DatabaseException(e); + } + } + +} diff --git a/model/jpa/src/main/java/org/keycloak/connections/jpa/updater/liquibase/conn/DefaultLiquibaseConnectionProvider.java b/model/jpa/src/main/java/org/keycloak/connections/jpa/updater/liquibase/conn/DefaultLiquibaseConnectionProvider.java index 2c1ded6d8c..75acb6724c 100644 --- a/model/jpa/src/main/java/org/keycloak/connections/jpa/updater/liquibase/conn/DefaultLiquibaseConnectionProvider.java +++ b/model/jpa/src/main/java/org/keycloak/connections/jpa/updater/liquibase/conn/DefaultLiquibaseConnectionProvider.java @@ -35,6 +35,7 @@ import liquibase.servicelocator.ServiceLocator; import liquibase.sqlgenerator.SqlGeneratorFactory; import org.jboss.logging.Logger; import org.keycloak.Config; +import org.keycloak.connections.jpa.updater.liquibase.CustomForeignKeySnapshotGenerator; import org.keycloak.connections.jpa.updater.liquibase.LiquibaseJpaUpdaterProvider; import org.keycloak.connections.jpa.updater.liquibase.PostgresPlusDatabase; import org.keycloak.connections.jpa.updater.liquibase.MySQL8VarcharType; @@ -50,6 +51,7 @@ import org.keycloak.models.KeycloakSessionFactory; import java.sql.Connection; import java.util.concurrent.atomic.AtomicBoolean; import liquibase.changelog.ChangeLogHistoryServiceFactory; +import liquibase.snapshot.SnapshotGeneratorFactory; /** * @author Marek Posolda @@ -126,6 +128,9 @@ public class DefaultLiquibaseConnectionProvider implements LiquibaseConnectionPr // Adding CustomCreateIndexChange for handling conditional indices creation ChangeFactory.getInstance().register(CustomCreateIndexChange.class); + + // Contains fix for https://liquibase.jira.com/browse/CORE-3141 + SnapshotGeneratorFactory.getInstance().register(new CustomForeignKeySnapshotGenerator()); } @Override diff --git a/testsuite/utils/pom.xml b/testsuite/utils/pom.xml index d960aa25e3..8d0094d981 100755 --- a/testsuite/utils/pom.xml +++ b/testsuite/utils/pom.xml @@ -249,6 +249,11 @@ mariadb-java-client ${mariadb.driver.version} + + com.microsoft.sqlserver + mssql-jdbc + ${mssql.driver.version} +