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.JdbcUtils; 024import org.apache.commons.lang3.Validate; 025import org.apache.commons.lang3.builder.EqualsBuilder; 026import org.apache.commons.lang3.builder.HashCodeBuilder; 027import org.slf4j.Logger; 028import org.slf4j.LoggerFactory; 029 030import java.sql.SQLException; 031import java.util.Set; 032 033import static org.apache.commons.lang3.StringUtils.isNotBlank; 034 035public class AddForeignKeyTask extends BaseTableColumnTask { 036 037 private static final Logger ourLog = LoggerFactory.getLogger(AddForeignKeyTask.class); 038 private String myConstraintName; 039 private String myForeignTableName; 040 private String myForeignColumnName; 041 042 public AddForeignKeyTask(String theProductVersion, String theSchemaVersion) { 043 super(theProductVersion, theSchemaVersion); 044 } 045 046 public void setConstraintName(String theConstraintName) { 047 myConstraintName = theConstraintName; 048 } 049 050 public void setForeignTableName(String theForeignTableName) { 051 myForeignTableName = theForeignTableName; 052 } 053 054 public void setForeignColumnName(String theForeignColumnName) { 055 myForeignColumnName = theForeignColumnName; 056 } 057 058 @Override 059 public void validate() { 060 super.validate(); 061 062 Validate.isTrue(isNotBlank(myConstraintName)); 063 Validate.isTrue(isNotBlank(myForeignTableName)); 064 Validate.isTrue(isNotBlank(myForeignColumnName)); 065 setDescription("Add foreign key " + myConstraintName + " from column " + getColumnName() + " of table " 066 + getTableName() + " to column " + myForeignColumnName + " of table " + myForeignTableName); 067 } 068 069 @Override 070 public void doExecute() throws SQLException { 071 072 Set<String> existing = JdbcUtils.getForeignKeys(getConnectionProperties(), myForeignTableName, getTableName()); 073 if (existing.contains(myConstraintName)) { 074 logInfo(ourLog, "Already have constraint named {} - No action performed", myConstraintName); 075 return; 076 } 077 078 String sql; 079 switch (getDriverType()) { 080 case MARIADB_10_1: 081 case MYSQL_5_7: 082 // Quote the column names as "SYSTEM" is a reserved word in MySQL 083 sql = "alter table " + getTableName() + " add constraint " + myConstraintName + " foreign key (`" 084 + getColumnName() + "`) references " + myForeignTableName + " (`" + myForeignColumnName + "`)"; 085 break; 086 case COCKROACHDB_21_1: 087 case POSTGRES_9_4: 088 case DERBY_EMBEDDED: 089 case H2_EMBEDDED: 090 case ORACLE_12C: 091 case MSSQL_2012: 092 sql = "alter table " + getTableName() + " add constraint " + myConstraintName + " foreign key (" 093 + getColumnName() + ") references " + myForeignTableName; 094 break; 095 default: 096 throw new IllegalStateException(Msg.code(68)); 097 } 098 099 try { 100 executeSql(getTableName(), sql); 101 } catch (Exception e) { 102 if (e.toString().contains("already exists")) { 103 ourLog.warn("Index {} already exists", myConstraintName); 104 } else { 105 throw e; 106 } 107 } 108 } 109 110 @Override 111 protected void generateHashCode(HashCodeBuilder theBuilder) { 112 super.generateHashCode(theBuilder); 113 theBuilder.append(myConstraintName); 114 theBuilder.append(myForeignTableName); 115 theBuilder.append(myForeignColumnName); 116 } 117 118 @Override 119 protected void generateEquals(EqualsBuilder theBuilder, BaseTask theOtherObject) { 120 AddForeignKeyTask otherObject = (AddForeignKeyTask) theOtherObject; 121 super.generateEquals(theBuilder, otherObject); 122 theBuilder.append(myConstraintName, otherObject.myConstraintName); 123 theBuilder.append(myForeignTableName, otherObject.myForeignTableName); 124 theBuilder.append(myForeignColumnName, otherObject.myForeignColumnName); 125 } 126}