001/*- 002 * #%L 003 * HAPI FHIR Server - SQL Migration 004 * %% 005 * Copyright (C) 2014 - 2025 Smile CDR, Inc. 006 * %% 007 * Licensed under the Apache License, Version 2.0 (the "License"); 008 * you may not use this file except in compliance with the License. 009 * You may obtain a copy of the License at 010 * 011 * http://www.apache.org/licenses/LICENSE-2.0 012 * 013 * Unless required by applicable law or agreed to in writing, software 014 * distributed under the License is distributed on an "AS IS" BASIS, 015 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 016 * See the License for the specific language governing permissions and 017 * limitations under the License. 018 * #L% 019 */ 020package ca.uhn.fhir.jpa.migrate.taskdef; 021 022import ca.uhn.fhir.i18n.Msg; 023import ca.uhn.fhir.jpa.migrate.DriverTypeEnum; 024import jakarta.annotation.Nonnull; 025import jakarta.annotation.Nullable; 026import org.intellij.lang.annotations.Language; 027import org.slf4j.Logger; 028import org.slf4j.LoggerFactory; 029 030import java.sql.SQLException; 031 032/** 033 * Migration task that handles cross-database logic for dropping a primary key. 034 * <p> 035 * The process involves 2 steps for most databases: 036 * <ol> 037 * <li>Running SQL to introspect the metadata tables to determine the name of the primary key.</li> 038 * <li>Running an ALTER TABLE to drop the constraint found above by name.</li> 039 * </ol> 040 */ 041public class DropPrimaryKeyTask extends BaseTableTask { 042 private static final Logger ourLog = LoggerFactory.getLogger(DropPrimaryKeyTask.class); 043 044 public DropPrimaryKeyTask(String theProductVersion, String theSchemaVersion, String theTableName) { 045 super(theProductVersion, theSchemaVersion); 046 setTableName(theTableName); 047 } 048 049 @Nonnull 050 private String generateSql() { 051 ourLog.debug("DropPrimaryKeyTask.generateSql()"); 052 053 @Nullable 054 @Language("SQL") 055 final String primaryKeyNameSql = generatePrimaryKeyNameSql(); 056 057 @Nullable 058 final String primaryKeyName = primaryKeyNameSql != null 059 ? newJdbcTemplate() 060 .queryForObject(primaryKeyNameSql, String.class, getTableNameWithDatabaseExpectedCase()) 061 : null; 062 063 ourLog.debug("primaryKeyName: {} for driver: {}", primaryKeyName, getDriverType()); 064 065 return generateDropPrimaryKeySql(primaryKeyName); 066 } 067 068 private String getTableNameWithDatabaseExpectedCase() { 069 if (DriverTypeEnum.ORACLE_12C == getDriverType()) { 070 return getTableName().toUpperCase(); 071 } 072 073 return getTableName().toLowerCase(); 074 } 075 076 @Override 077 protected void doExecute() throws SQLException { 078 logInfo(ourLog, "Going to DROP the PRIMARY KEY on table {}", getTableName()); 079 080 executeSql(getTableName(), generateSql()); 081 } 082 083 private String generateDropPrimaryKeySql(@Nullable String thePrimaryKeyName) { 084 switch (getDriverType()) { 085 case MARIADB_10_1: 086 case DERBY_EMBEDDED: 087 case H2_EMBEDDED: 088 @Language("SQL") 089 final String sqlH2 = "ALTER TABLE %s DROP PRIMARY KEY"; 090 return String.format(sqlH2, getTableName()); 091 case POSTGRES_9_4: 092 case ORACLE_12C: 093 case MSSQL_2012: 094 case MYSQL_5_7: 095 assert thePrimaryKeyName != null; 096 @Language("SQL") 097 final String sql = "ALTER TABLE %s DROP CONSTRAINT %s"; 098 return String.format(sql, getTableName(), thePrimaryKeyName); 099 default: 100 throw new IllegalStateException(String.format( 101 "%s Unknown driver type: %s. Cannot drop primary key: %s for task %s", 102 Msg.code(2529), getDriverType(), getMigrationVersion(), getTableName())); 103 } 104 } 105 106 @Language("SQL") 107 @Nullable 108 private String generatePrimaryKeyNameSql() { 109 switch (getDriverType()) { 110 case MYSQL_5_7: 111 case MARIADB_10_1: 112 case DERBY_EMBEDDED: 113 case COCKROACHDB_21_1: 114 case H2_EMBEDDED: 115 return null; // Irrelevant: We don't need to run the SQL for these databases. 116 case POSTGRES_9_4: 117 return "SELECT constraint_name " + "FROM information_schema.table_constraints " 118 + "WHERE table_schema = coalesce(current_schema(), 'public') " 119 + "AND constraint_type = 'PRIMARY KEY' " 120 + "AND table_name = ?"; 121 case ORACLE_12C: 122 return "SELECT constraint_name " + "FROM user_constraints " 123 + "WHERE constraint_type = 'P' " 124 + "AND table_name = ?"; 125 case MSSQL_2012: 126 return "SELECT tc.constraint_name " + "FROM information_schema.table_constraints tc " 127 + "JOIN information_schema.constraint_column_usage ccu ON tc.constraint_name = ccu.constraint_name " 128 + "WHERE tc.constraint_type = 'PRIMARY KEY' " 129 + "AND tc.table_name = ?"; 130 default: 131 throw new IllegalStateException(String.format( 132 "%s Unknown driver type: %s Cannot find primary key to drop for task %s", 133 Msg.code(2530), getDriverType(), getMigrationVersion())); 134 } 135 } 136}