001/*-
002 * #%L
003 * HAPI FHIR Server - SQL Migration
004 * %%
005 * Copyright (C) 2014 - 2023 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 " + getTableName() + " to column " + myForeignColumnName + " of table " + myForeignTableName);
066        }
067
068        @Override
069        public void doExecute() throws SQLException {
070
071                Set<String> existing = JdbcUtils.getForeignKeys(getConnectionProperties(), myForeignTableName, getTableName());
072                if (existing.contains(myConstraintName)) {
073                        logInfo(ourLog, "Already have constraint named {} - No action performed", myConstraintName);
074                        return;
075                }
076
077                String sql;
078                switch (getDriverType()) {
079                        case MARIADB_10_1:
080                        case MYSQL_5_7:
081                                // Quote the column names as "SYSTEM" is a reserved word in MySQL
082                                sql = "alter table " + getTableName() + " add constraint " + myConstraintName + " foreign key (`" + getColumnName() + "`) references " + myForeignTableName + " (`" + myForeignColumnName + "`)";
083                                break;
084                        case COCKROACHDB_21_1:
085                        case POSTGRES_9_4:
086                        case DERBY_EMBEDDED:
087                        case H2_EMBEDDED:
088                        case ORACLE_12C:
089                        case MSSQL_2012:
090                                sql = "alter table " + getTableName() + " add constraint " + myConstraintName + " foreign key (" + getColumnName() + ") references " + myForeignTableName;
091                                break;
092                        default:
093                                throw new IllegalStateException(Msg.code(68));
094                }
095
096
097                try {
098                        executeSql(getTableName(), sql);
099                } catch (Exception e) {
100                        if (e.toString().contains("already exists")) {
101                                ourLog.warn("Index {} already exists", myConstraintName);
102                        } else {
103                                throw e;
104                        }
105                }
106        }
107
108        @Override
109        protected void generateHashCode(HashCodeBuilder theBuilder) {
110                super.generateHashCode(theBuilder);
111                theBuilder.append(myConstraintName);
112                theBuilder.append(myForeignTableName);
113                theBuilder.append(myForeignColumnName);
114        }
115
116        @Override
117        protected void generateEquals(EqualsBuilder theBuilder, BaseTask theOtherObject) {
118                AddForeignKeyTask otherObject = (AddForeignKeyTask) theOtherObject;
119                super.generateEquals(theBuilder, otherObject);
120                theBuilder.append(myConstraintName, otherObject.myConstraintName);
121                theBuilder.append(myForeignTableName, otherObject.myForeignTableName);
122                theBuilder.append(myForeignColumnName, otherObject.myForeignColumnName);
123        }
124
125}