diff --git a/model/jpa/src/main/java/org/keycloak/connections/jpa/DefaultJpaConnectionProviderFactory.java b/model/jpa/src/main/java/org/keycloak/connections/jpa/DefaultJpaConnectionProviderFactory.java index 33a4745227..c7ecf0e4ed 100755 --- a/model/jpa/src/main/java/org/keycloak/connections/jpa/DefaultJpaConnectionProviderFactory.java +++ b/model/jpa/src/main/java/org/keycloak/connections/jpa/DefaultJpaConnectionProviderFactory.java @@ -59,6 +59,7 @@ import java.util.Collection; import java.util.HashMap; import java.util.LinkedHashMap; import java.util.Map; +import liquibase.GlobalConfiguration; /** * @author Stian Thorgersen @@ -434,7 +435,12 @@ public class DefaultJpaConnectionProviderFactory implements JpaConnectionProvide @Override public String getSchema() { - return config.get("schema"); + String schema = config.get("schema"); + if (schema != null && schema.contains("-") && ! Boolean.parseBoolean(System.getProperty(GlobalConfiguration.PRESERVE_SCHEMA_CASE.getKey()))) { + System.setProperty(GlobalConfiguration.PRESERVE_SCHEMA_CASE.getKey(), "true"); + logger.warnf("The passed schema '%s' contains a dash. Setting liquibase config option PRESERVE_SCHEMA_CASE to true. See https://github.com/keycloak/keycloak/issues/20870 for more information.", schema); + } + return schema; } @Override diff --git a/model/jpa/src/main/java/org/keycloak/connections/jpa/util/JpaUtils.java b/model/jpa/src/main/java/org/keycloak/connections/jpa/util/JpaUtils.java index 11d7f00aff..4db97b08fd 100644 --- a/model/jpa/src/main/java/org/keycloak/connections/jpa/util/JpaUtils.java +++ b/model/jpa/src/main/java/org/keycloak/connections/jpa/util/JpaUtils.java @@ -34,7 +34,6 @@ import java.io.IOException; import java.io.InputStream; import java.net.URL; import java.util.ArrayList; -import java.util.Collections; import java.util.List; import java.util.Map; import java.util.Properties; @@ -52,7 +51,7 @@ public class JpaUtils { public static String getTableNameForNativeQuery(String tableName, EntityManager em) { String schema = (String) em.getEntityManagerFactory().getProperties().get(HIBERNATE_DEFAULT_SCHEMA); - return (schema==null) ? tableName : schema + "." + tableName; + return (schema==null) ? tableName : "\"" + schema + "\"." + tableName; } public static EntityManagerFactory createEntityManagerFactory(KeycloakSession session, String unitName, Map properties, boolean jta) { diff --git a/model/map-jpa/src/main/java/org/keycloak/models/map/storage/jpa/JpaMapStorageProviderFactory.java b/model/map-jpa/src/main/java/org/keycloak/models/map/storage/jpa/JpaMapStorageProviderFactory.java index 9aa8b47d87..9901e271a3 100644 --- a/model/map-jpa/src/main/java/org/keycloak/models/map/storage/jpa/JpaMapStorageProviderFactory.java +++ b/model/map-jpa/src/main/java/org/keycloak/models/map/storage/jpa/JpaMapStorageProviderFactory.java @@ -46,6 +46,7 @@ import jakarta.transaction.NotSupportedException; import jakarta.transaction.RollbackException; import jakarta.transaction.SystemException; import jakarta.transaction.Transaction; +import liquibase.GlobalConfiguration; import org.hibernate.cfg.AvailableSettings; import org.hibernate.internal.SessionImpl; @@ -401,7 +402,7 @@ public class JpaMapStorageProviderFactory implements } } - String schema = config.get("schema"); + String schema = getSchema(); if (schema != null) { properties.put(HIBERNATE_DEFAULT_SCHEMA, schema); } @@ -430,6 +431,15 @@ public class JpaMapStorageProviderFactory implements return emf; } + private String getSchema() { + String schema = config.get("schema"); + if (schema != null && schema.contains("-") && ! Boolean.parseBoolean(System.getProperty(GlobalConfiguration.PRESERVE_SCHEMA_CASE.getKey()))) { + System.setProperty(GlobalConfiguration.PRESERVE_SCHEMA_CASE.getKey(), "true"); + logger.warnf("The passed schema '%s' contains a dash. Setting liquibase config option PRESERVE_SCHEMA_CASE to true. See https://github.com/keycloak/keycloak/issues/20870 for more information.", schema); + } + return schema; + } + protected EntityManagerFactory getEntityManagerFactory() { return emf; } @@ -464,7 +474,7 @@ public class JpaMapStorageProviderFactory implements if (logger.isDebugEnabled()) printOperationalInfo(connection); MapJpaUpdaterProvider updater = session.getProvider(MapJpaUpdaterProvider.class); - MapJpaUpdaterProvider.Status status = updater.validate(modelType, connection, config.get("schema")); + MapJpaUpdaterProvider.Status status = updater.validate(modelType, connection, getSchema()); databaseShortName = updater.getDatabaseShortName(); if (!status.equals(VALID)) { @@ -544,10 +554,10 @@ public class JpaMapStorageProviderFactory implements private void update(Class modelType, Connection connection, KeycloakSession session) { if (modelType == MapLockEntity.class) { // as the MapLockEntity is used by the MapGlobalLockProvider itself, don't create a global lock for creating that schema - session.getProvider(MapJpaUpdaterProvider.class).update(modelType, connection, config.get("schema")); + session.getProvider(MapJpaUpdaterProvider.class).update(modelType, connection, getSchema()); } else { session.getProvider(GlobalLockProvider.class).withLock(modelType.getName(), lockedSession -> { - lockedSession.getProvider(MapJpaUpdaterProvider.class).update(modelType, connection, config.get("schema")); + lockedSession.getProvider(MapJpaUpdaterProvider.class).update(modelType, connection, getSchema()); return null; }); } diff --git a/model/map-jpa/src/main/java/org/keycloak/models/map/storage/jpa/JpaMapUtils.java b/model/map-jpa/src/main/java/org/keycloak/models/map/storage/jpa/JpaMapUtils.java index 818bd5a1e0..b788d3856c 100644 --- a/model/map-jpa/src/main/java/org/keycloak/models/map/storage/jpa/JpaMapUtils.java +++ b/model/map-jpa/src/main/java/org/keycloak/models/map/storage/jpa/JpaMapUtils.java @@ -26,7 +26,6 @@ import jakarta.persistence.EntityManagerFactory; import java.io.IOException; import java.io.InputStream; import java.net.URL; -import java.util.Collections; import java.util.Map; import java.util.Properties; import java.util.regex.Pattern; @@ -44,7 +43,7 @@ public class JpaMapUtils { public static String getSchemaForNativeQuery(EntityManager em) { String schema = (String) em.getEntityManagerFactory().getProperties().get(HIBERNATE_DEFAULT_SCHEMA); - return (schema == null) ? "" : schema + "."; + return (schema == null) ? "" : "\"" + schema + "\"."; } /** diff --git a/quarkus/runtime/src/main/java/org/keycloak/quarkus/runtime/storage/database/jpa/AbstractJpaConnectionProviderFactory.java b/quarkus/runtime/src/main/java/org/keycloak/quarkus/runtime/storage/database/jpa/AbstractJpaConnectionProviderFactory.java index dcf14f4609..6166689b6a 100644 --- a/quarkus/runtime/src/main/java/org/keycloak/quarkus/runtime/storage/database/jpa/AbstractJpaConnectionProviderFactory.java +++ b/quarkus/runtime/src/main/java/org/keycloak/quarkus/runtime/storage/database/jpa/AbstractJpaConnectionProviderFactory.java @@ -36,9 +36,13 @@ import org.keycloak.quarkus.runtime.configuration.Configuration; import io.quarkus.arc.Arc; import io.quarkus.hibernate.orm.PersistenceUnit; +import liquibase.GlobalConfiguration; +import org.jboss.logging.Logger; public abstract class AbstractJpaConnectionProviderFactory implements JpaConnectionProviderFactory { + private final Logger logger = Logger.getLogger(getClass()); + protected Config.Scope config; protected EntityManagerFactory entityManagerFactory; @@ -55,7 +59,12 @@ public abstract class AbstractJpaConnectionProviderFactory implements JpaConnect @Override public String getSchema() { - return Configuration.getRawValue("kc.db-schema"); + String schema = Configuration.getRawValue("kc.db-schema"); + if (schema != null && schema.contains("-") && ! Boolean.parseBoolean(System.getProperty(GlobalConfiguration.PRESERVE_SCHEMA_CASE.getKey()))) { + System.setProperty(GlobalConfiguration.PRESERVE_SCHEMA_CASE.getKey(), "true"); + logger.warnf("The passed schema '%s' contains a dash. Setting liquibase config option PRESERVE_SCHEMA_CASE to true. See https://github.com/keycloak/keycloak/issues/20870 for more information.", schema); + } + return schema; } @Override