Compare commits
3 Commits
6ca1c2f64d
...
4bb4bcaf05
Author | SHA1 | Date |
---|---|---|
Primož Delopst | 4bb4bcaf05 | |
volodymyr-korzh | 6bb72e445d | |
Primož Delopst | a6dea96fb5 |
|
@ -20,6 +20,7 @@
|
||||||
package ca.uhn.fhir.jpa.embedded;
|
package ca.uhn.fhir.jpa.embedded;
|
||||||
|
|
||||||
import ca.uhn.fhir.jpa.migrate.DriverTypeEnum;
|
import ca.uhn.fhir.jpa.migrate.DriverTypeEnum;
|
||||||
|
import ca.uhn.fhir.jpa.util.DatabaseSupportUtil;
|
||||||
import ca.uhn.fhir.test.utilities.docker.DockerRequiredCondition;
|
import ca.uhn.fhir.test.utilities.docker.DockerRequiredCondition;
|
||||||
import ca.uhn.fhir.util.VersionEnum;
|
import ca.uhn.fhir.util.VersionEnum;
|
||||||
import org.junit.jupiter.api.extension.AfterAllCallback;
|
import org.junit.jupiter.api.extension.AfterAllCallback;
|
||||||
|
@ -54,7 +55,7 @@ public class HapiEmbeddedDatabasesExtension implements AfterAllCallback {
|
||||||
myEmbeddedDatabases.add(new H2EmbeddedDatabase());
|
myEmbeddedDatabases.add(new H2EmbeddedDatabase());
|
||||||
myEmbeddedDatabases.add(new PostgresEmbeddedDatabase());
|
myEmbeddedDatabases.add(new PostgresEmbeddedDatabase());
|
||||||
myEmbeddedDatabases.add(new MsSqlEmbeddedDatabase());
|
myEmbeddedDatabases.add(new MsSqlEmbeddedDatabase());
|
||||||
if (OracleCondition.canUseOracle()) {
|
if (DatabaseSupportUtil.canUseOracle()) {
|
||||||
myEmbeddedDatabases.add(new OracleEmbeddedDatabase());
|
myEmbeddedDatabases.add(new OracleEmbeddedDatabase());
|
||||||
} else {
|
} else {
|
||||||
String message =
|
String message =
|
||||||
|
@ -136,7 +137,7 @@ public class HapiEmbeddedDatabasesExtension implements AfterAllCallback {
|
||||||
arguments.add(Arguments.of(DriverTypeEnum.POSTGRES_9_4));
|
arguments.add(Arguments.of(DriverTypeEnum.POSTGRES_9_4));
|
||||||
arguments.add(Arguments.of(DriverTypeEnum.MSSQL_2012));
|
arguments.add(Arguments.of(DriverTypeEnum.MSSQL_2012));
|
||||||
|
|
||||||
if (OracleCondition.canUseOracle()) {
|
if (DatabaseSupportUtil.canUseOracle()) {
|
||||||
arguments.add(Arguments.of(DriverTypeEnum.ORACLE_12C));
|
arguments.add(Arguments.of(DriverTypeEnum.ORACLE_12C));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -20,6 +20,7 @@
|
||||||
package ca.uhn.fhir.jpa.embedded;
|
package ca.uhn.fhir.jpa.embedded;
|
||||||
|
|
||||||
import ca.uhn.fhir.jpa.migrate.DriverTypeEnum;
|
import ca.uhn.fhir.jpa.migrate.DriverTypeEnum;
|
||||||
|
import ca.uhn.fhir.jpa.util.DatabaseSupportUtil;
|
||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
import org.slf4j.LoggerFactory;
|
import org.slf4j.LoggerFactory;
|
||||||
import org.testcontainers.containers.MSSQLServerContainer;
|
import org.testcontainers.containers.MSSQLServerContainer;
|
||||||
|
@ -43,9 +44,19 @@ public class MsSqlEmbeddedDatabase extends JpaEmbeddedDatabase {
|
||||||
private final MSSQLServerContainer myContainer;
|
private final MSSQLServerContainer myContainer;
|
||||||
|
|
||||||
public MsSqlEmbeddedDatabase() {
|
public MsSqlEmbeddedDatabase() {
|
||||||
DockerImageName msSqlImage = DockerImageName.parse("mcr.microsoft.com/azure-sql-edge:latest")
|
|
||||||
.asCompatibleSubstituteFor("mcr.microsoft.com/mssql/server");
|
// azure-sql-edge docker image does not support kernel 6.7+
|
||||||
myContainer = new MSSQLServerContainer(msSqlImage).acceptLicense();
|
// as a result, mssql container fails to start most of the time
|
||||||
|
// mssql/server:2019 image support kernel 6.7+, so use it for amd64 architecture
|
||||||
|
// See: https://github.com/microsoft/mssql-docker/issues/868
|
||||||
|
if (DatabaseSupportUtil.canUseMsSql2019()) {
|
||||||
|
myContainer = new MSSQLServerContainer("mcr.microsoft.com/mssql/server:2019-latest").acceptLicense();
|
||||||
|
} else {
|
||||||
|
DockerImageName msSqlImage = DockerImageName.parse("mcr.microsoft.com/azure-sql-edge:latest")
|
||||||
|
.asCompatibleSubstituteFor("mcr.microsoft.com/mssql/server");
|
||||||
|
myContainer = new MSSQLServerContainer(msSqlImage).acceptLicense();
|
||||||
|
}
|
||||||
|
|
||||||
myContainer.start();
|
myContainer.start();
|
||||||
super.initialize(
|
super.initialize(
|
||||||
DriverTypeEnum.MSSQL_2012,
|
DriverTypeEnum.MSSQL_2012,
|
||||||
|
|
|
@ -19,8 +19,7 @@
|
||||||
*/
|
*/
|
||||||
package ca.uhn.fhir.jpa.embedded;
|
package ca.uhn.fhir.jpa.embedded;
|
||||||
|
|
||||||
import org.apache.commons.lang3.StringUtils;
|
import ca.uhn.fhir.jpa.util.DatabaseSupportUtil;
|
||||||
import org.apache.commons.lang3.SystemUtils;
|
|
||||||
import org.junit.jupiter.api.extension.ConditionEvaluationResult;
|
import org.junit.jupiter.api.extension.ConditionEvaluationResult;
|
||||||
import org.junit.jupiter.api.extension.ExecutionCondition;
|
import org.junit.jupiter.api.extension.ExecutionCondition;
|
||||||
import org.junit.jupiter.api.extension.ExtensionContext;
|
import org.junit.jupiter.api.extension.ExtensionContext;
|
||||||
|
@ -33,25 +32,8 @@ public class OracleCondition implements ExecutionCondition {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public ConditionEvaluationResult evaluateExecutionCondition(ExtensionContext theExtensionContext) {
|
public ConditionEvaluationResult evaluateExecutionCondition(ExtensionContext theExtensionContext) {
|
||||||
return canUseOracle()
|
return DatabaseSupportUtil.canUseOracle()
|
||||||
? ConditionEvaluationResult.enabled(ENABLED_MSG)
|
? ConditionEvaluationResult.enabled(ENABLED_MSG)
|
||||||
: ConditionEvaluationResult.disabled(DISABLED_MSG);
|
: ConditionEvaluationResult.disabled(DISABLED_MSG);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static boolean canUseOracle() {
|
|
||||||
if (!isMac()) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
return isColimaConfigured();
|
|
||||||
}
|
|
||||||
|
|
||||||
private static boolean isMac() {
|
|
||||||
return SystemUtils.IS_OS_MAC || SystemUtils.IS_OS_MAC_OSX;
|
|
||||||
}
|
|
||||||
|
|
||||||
private static boolean isColimaConfigured() {
|
|
||||||
return StringUtils.isNotBlank(System.getenv("TESTCONTAINERS_DOCKER_SOCKET_OVERRIDE"))
|
|
||||||
&& StringUtils.isNotBlank(System.getenv("DOCKER_HOST"))
|
|
||||||
&& System.getenv("DOCKER_HOST").contains("colima");
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,34 @@
|
||||||
|
package ca.uhn.fhir.jpa.util;
|
||||||
|
|
||||||
|
import org.apache.commons.lang3.StringUtils;
|
||||||
|
import org.apache.commons.lang3.SystemUtils;
|
||||||
|
|
||||||
|
public final class DatabaseSupportUtil {
|
||||||
|
|
||||||
|
private DatabaseSupportUtil() {}
|
||||||
|
|
||||||
|
public static boolean canUseMsSql2019() {
|
||||||
|
return isSupportAmd64Architecture();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static boolean canUseOracle() {
|
||||||
|
return isSupportAmd64Architecture();
|
||||||
|
}
|
||||||
|
|
||||||
|
private static boolean isSupportAmd64Architecture() {
|
||||||
|
if (!isMac()) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return isColimaConfigured();
|
||||||
|
}
|
||||||
|
|
||||||
|
private static boolean isMac() {
|
||||||
|
return SystemUtils.IS_OS_MAC || SystemUtils.IS_OS_MAC_OSX;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static boolean isColimaConfigured() {
|
||||||
|
return StringUtils.isNotBlank(System.getenv("TESTCONTAINERS_DOCKER_SOCKET_OVERRIDE"))
|
||||||
|
&& StringUtils.isNotBlank(System.getenv("DOCKER_HOST"))
|
||||||
|
&& System.getenv("DOCKER_HOST").contains("colima");
|
||||||
|
}
|
||||||
|
}
|
|
@ -24,9 +24,11 @@ import jakarta.annotation.Nonnull;
|
||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
import org.slf4j.LoggerFactory;
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
|
import java.sql.Connection;
|
||||||
import java.sql.SQLException;
|
import java.sql.SQLException;
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
import java.util.Optional;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Migration task that handles cross-database logic for adding a new primary key.
|
* Migration task that handles cross-database logic for adding a new primary key.
|
||||||
|
@ -37,7 +39,7 @@ public class AddPrimaryKeyTask extends BaseTableTask {
|
||||||
private final List<String> myPrimaryKeyColumnsInOrder;
|
private final List<String> myPrimaryKeyColumnsInOrder;
|
||||||
|
|
||||||
public AddPrimaryKeyTask(
|
public AddPrimaryKeyTask(
|
||||||
String theProductVersion, String theSchemaVersion, String theTableName, String... theColumnsInOrder) {
|
String theProductVersion, String theSchemaVersion, String theTableName, String... theColumnsInOrder) {
|
||||||
super(theProductVersion, theSchemaVersion);
|
super(theProductVersion, theSchemaVersion);
|
||||||
setTableName(theTableName);
|
setTableName(theTableName);
|
||||||
|
|
||||||
|
@ -46,32 +48,39 @@ public class AddPrimaryKeyTask extends BaseTableTask {
|
||||||
|
|
||||||
@Nonnull
|
@Nonnull
|
||||||
private String generateSql() {
|
private String generateSql() {
|
||||||
switch (getDriverType()) {
|
try (Connection connection = getConnectionProperties().getDataSource().getConnection()) {
|
||||||
case MYSQL_5_7:
|
switch (getDriverType()) {
|
||||||
case MARIADB_10_1:
|
case MYSQL_5_7:
|
||||||
case POSTGRES_9_4:
|
case MARIADB_10_1:
|
||||||
case DERBY_EMBEDDED:
|
case POSTGRES_9_4:
|
||||||
case H2_EMBEDDED:
|
case DERBY_EMBEDDED:
|
||||||
case ORACLE_12C:
|
case H2_EMBEDDED:
|
||||||
case MSSQL_2012:
|
case ORACLE_12C:
|
||||||
case COCKROACHDB_21_1:
|
case MSSQL_2012:
|
||||||
return String.format(
|
case COCKROACHDB_21_1:
|
||||||
|
return String.format(
|
||||||
"ALTER TABLE %s ADD PRIMARY KEY (%s)",
|
"ALTER TABLE %s ADD PRIMARY KEY (%s)",
|
||||||
getTableName(), String.join(", ", myPrimaryKeyColumnsInOrder));
|
Optional.of(connection.getSchema())
|
||||||
default:
|
.map(schema -> String.format("%s.%s", schema, getTableName()))
|
||||||
throw new IllegalStateException(String.format(
|
.orElse(getTableName()),
|
||||||
|
String.join(", ", myPrimaryKeyColumnsInOrder));
|
||||||
|
default:
|
||||||
|
throw new IllegalStateException(String.format(
|
||||||
"%s Unknown driver type. Cannot add primary key for task %s",
|
"%s Unknown driver type. Cannot add primary key for task %s",
|
||||||
Msg.code(2531), getMigrationVersion()));
|
Msg.code(2531), getMigrationVersion()));
|
||||||
|
}
|
||||||
|
} catch (SQLException e) {
|
||||||
|
throw new IllegalStateException(e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void doExecute() throws SQLException {
|
protected void doExecute() throws SQLException {
|
||||||
logInfo(
|
logInfo(
|
||||||
ourLog,
|
ourLog,
|
||||||
"Going to add a primary key on table {} for columns {}",
|
"Going to add a primary key on table {} for columns {}",
|
||||||
getTableName(),
|
getTableName(),
|
||||||
myPrimaryKeyColumnsInOrder);
|
myPrimaryKeyColumnsInOrder);
|
||||||
|
|
||||||
executeSql(getTableName(), generateSql());
|
executeSql(getTableName(), generateSql());
|
||||||
}
|
}
|
||||||
|
|
|
@ -20,14 +20,17 @@
|
||||||
package ca.uhn.fhir.jpa.migrate.taskdef;
|
package ca.uhn.fhir.jpa.migrate.taskdef;
|
||||||
|
|
||||||
import ca.uhn.fhir.i18n.Msg;
|
import ca.uhn.fhir.i18n.Msg;
|
||||||
import ca.uhn.fhir.jpa.migrate.DriverTypeEnum;
|
import com.google.common.collect.ImmutableList;
|
||||||
import jakarta.annotation.Nonnull;
|
import jakarta.annotation.Nonnull;
|
||||||
import jakarta.annotation.Nullable;
|
import jakarta.annotation.Nullable;
|
||||||
import org.intellij.lang.annotations.Language;
|
import org.intellij.lang.annotations.Language;
|
||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
import org.slf4j.LoggerFactory;
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
|
import java.sql.Connection;
|
||||||
|
import java.sql.ResultSet;
|
||||||
import java.sql.SQLException;
|
import java.sql.SQLException;
|
||||||
|
import java.util.Optional;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Migration task that handles cross-database logic for dropping a primary key.
|
* Migration task that handles cross-database logic for dropping a primary key.
|
||||||
|
@ -52,27 +55,13 @@ public class DropPrimaryKeyTask extends BaseTableTask {
|
||||||
|
|
||||||
@Nullable
|
@Nullable
|
||||||
@Language("SQL")
|
@Language("SQL")
|
||||||
final String primaryKeyNameSql = generatePrimaryKeyNameSql();
|
final String primaryKeyName = getPrimaryKeyName();
|
||||||
|
|
||||||
@Nullable
|
|
||||||
final String primaryKeyName = primaryKeyNameSql != null
|
|
||||||
? newJdbcTemplate()
|
|
||||||
.queryForObject(primaryKeyNameSql, String.class, getTableNameWithDatabaseExpectedCase())
|
|
||||||
: null;
|
|
||||||
|
|
||||||
ourLog.debug("primaryKeyName: {} for driver: {}", primaryKeyName, getDriverType());
|
ourLog.debug("primaryKeyName: {} for driver: {}", primaryKeyName, getDriverType());
|
||||||
|
|
||||||
return generateDropPrimaryKeySql(primaryKeyName);
|
return generateDropPrimaryKeySql(primaryKeyName);
|
||||||
}
|
}
|
||||||
|
|
||||||
private String getTableNameWithDatabaseExpectedCase() {
|
|
||||||
if (DriverTypeEnum.ORACLE_12C == getDriverType()) {
|
|
||||||
return getTableName().toUpperCase();
|
|
||||||
}
|
|
||||||
|
|
||||||
return getTableName().toLowerCase();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void doExecute() throws SQLException {
|
protected void doExecute() throws SQLException {
|
||||||
logInfo(ourLog, "Going to DROP the PRIMARY KEY on table {}", getTableName());
|
logInfo(ourLog, "Going to DROP the PRIMARY KEY on table {}", getTableName());
|
||||||
|
@ -81,56 +70,63 @@ public class DropPrimaryKeyTask extends BaseTableTask {
|
||||||
}
|
}
|
||||||
|
|
||||||
private String generateDropPrimaryKeySql(@Nullable String thePrimaryKeyName) {
|
private String generateDropPrimaryKeySql(@Nullable String thePrimaryKeyName) {
|
||||||
switch (getDriverType()) {
|
try (Connection connection = getConnectionProperties().getDataSource().getConnection()) {
|
||||||
case MARIADB_10_1:
|
switch (getDriverType()) {
|
||||||
case DERBY_EMBEDDED:
|
case MARIADB_10_1:
|
||||||
case H2_EMBEDDED:
|
case DERBY_EMBEDDED:
|
||||||
@Language("SQL")
|
case H2_EMBEDDED:
|
||||||
final String sqlH2 = "ALTER TABLE %s DROP PRIMARY KEY";
|
@Language("SQL")
|
||||||
return String.format(sqlH2, getTableName());
|
final String sqlH2 = "ALTER TABLE %s DROP PRIMARY KEY";
|
||||||
case POSTGRES_9_4:
|
return String.format(
|
||||||
case ORACLE_12C:
|
sqlH2,
|
||||||
case MSSQL_2012:
|
Optional.of(connection.getSchema())
|
||||||
case MYSQL_5_7:
|
.map(schema -> String.format("%s.%s", schema, getTableName()))
|
||||||
assert thePrimaryKeyName != null;
|
.orElse(getTableName()));
|
||||||
@Language("SQL")
|
case POSTGRES_9_4:
|
||||||
final String sql = "ALTER TABLE %s DROP CONSTRAINT %s";
|
case ORACLE_12C:
|
||||||
return String.format(sql, getTableName(), thePrimaryKeyName);
|
case MSSQL_2012:
|
||||||
default:
|
case MYSQL_5_7:
|
||||||
throw new IllegalStateException(String.format(
|
assert thePrimaryKeyName != null;
|
||||||
|
@Language("SQL")
|
||||||
|
final String sql = "ALTER TABLE %s DROP CONSTRAINT %s";
|
||||||
|
return String.format(
|
||||||
|
sql,
|
||||||
|
Optional.of(connection.getSchema())
|
||||||
|
.map(schema -> String.format("%s.%s", schema, getTableName()))
|
||||||
|
.orElse(getTableName()),
|
||||||
|
thePrimaryKeyName);
|
||||||
|
default:
|
||||||
|
throw new IllegalStateException(String.format(
|
||||||
"%s Unknown driver type: %s. Cannot drop primary key: %s for task %s",
|
"%s Unknown driver type: %s. Cannot drop primary key: %s for task %s",
|
||||||
Msg.code(2529), getDriverType(), getMigrationVersion(), getTableName()));
|
Msg.code(2529), getDriverType(), getMigrationVersion(), getTableName()));
|
||||||
|
}
|
||||||
|
|
||||||
|
} catch (SQLException e) {
|
||||||
|
throw new IllegalStateException(e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Language("SQL")
|
@SuppressWarnings({"NestedTryStatement", "MethodWithMultipleLoops"})
|
||||||
@Nullable
|
@Nullable
|
||||||
private String generatePrimaryKeyNameSql() {
|
private String getPrimaryKeyName() {
|
||||||
switch (getDriverType()) {
|
String primaryKey = null;
|
||||||
case MYSQL_5_7:
|
try (Connection connection = getConnectionProperties().getDataSource().getConnection()) {
|
||||||
case MARIADB_10_1:
|
for (String tableName : ImmutableList.of(
|
||||||
case DERBY_EMBEDDED:
|
getTableName().toLowerCase(), getTableName().toUpperCase())) {
|
||||||
case COCKROACHDB_21_1:
|
try (ResultSet resultSet = connection
|
||||||
case H2_EMBEDDED:
|
.getMetaData()
|
||||||
return null; // Irrelevant: We don't need to run the SQL for these databases.
|
.getPrimaryKeys(connection.getCatalog(), connection.getSchema(), tableName)) {
|
||||||
case POSTGRES_9_4:
|
while (resultSet.next()) {
|
||||||
return "SELECT constraint_name " + "FROM information_schema.table_constraints "
|
primaryKey = resultSet.getString(6);
|
||||||
+ "WHERE table_schema = 'public' "
|
}
|
||||||
+ "AND constraint_type = 'PRIMARY KEY' "
|
} catch (SQLException e) {
|
||||||
+ "AND table_name = ?";
|
throw new IllegalStateException(e);
|
||||||
case ORACLE_12C:
|
}
|
||||||
return "SELECT constraint_name " + "FROM user_constraints "
|
}
|
||||||
+ "WHERE constraint_type = 'P' "
|
} catch (SQLException e) {
|
||||||
+ "AND table_name = ?";
|
throw new IllegalStateException(e);
|
||||||
case MSSQL_2012:
|
|
||||||
return "SELECT tc.constraint_name " + "FROM information_schema.table_constraints tc "
|
|
||||||
+ "JOIN information_schema.constraint_column_usage ccu ON tc.constraint_name = ccu.constraint_name "
|
|
||||||
+ "WHERE tc.constraint_type = 'PRIMARY KEY' "
|
|
||||||
+ "AND tc.table_name = ?";
|
|
||||||
default:
|
|
||||||
throw new IllegalStateException(String.format(
|
|
||||||
"%s Unknown driver type: %s Cannot find primary key to drop for task %s",
|
|
||||||
Msg.code(2530), getDriverType(), getMigrationVersion()));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return primaryKey;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue