001package ca.uhn.fhir.jpa.migrate.taskdef;
002
003/*-
004 * #%L
005 * HAPI FHIR Server - SQL Migration
006 * %%
007 * Copyright (C) 2014 - 2022 Smile CDR, Inc.
008 * %%
009 * Licensed under the Apache License, Version 2.0 (the "License");
010 * you may not use this file except in compliance with the License.
011 * You may obtain a copy of the License at
012 *
013 *      http://www.apache.org/licenses/LICENSE-2.0
014 *
015 * Unless required by applicable law or agreed to in writing, software
016 * distributed under the License is distributed on an "AS IS" BASIS,
017 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
018 * See the License for the specific language governing permissions and
019 * limitations under the License.
020 * #L%
021 */
022
023import ca.uhn.fhir.i18n.Msg;
024import ca.uhn.fhir.jpa.migrate.JdbcUtils;
025import org.apache.commons.lang3.Validate;
026import org.apache.commons.lang3.builder.EqualsBuilder;
027import org.apache.commons.lang3.builder.HashCodeBuilder;
028import org.slf4j.Logger;
029import org.slf4j.LoggerFactory;
030
031import java.sql.SQLException;
032import java.util.Set;
033
034import static org.apache.commons.lang3.StringUtils.isNotBlank;
035
036public class AddForeignKeyTask extends BaseTableColumnTask {
037
038        private static final Logger ourLog = LoggerFactory.getLogger(AddForeignKeyTask.class);
039        private String myConstraintName;
040        private String myForeignTableName;
041        private String myForeignColumnName;
042
043        public AddForeignKeyTask(String theProductVersion, String theSchemaVersion) {
044                super(theProductVersion, theSchemaVersion);
045        }
046
047        public void setConstraintName(String theConstraintName) {
048                myConstraintName = theConstraintName;
049        }
050
051        public void setForeignTableName(String theForeignTableName) {
052                myForeignTableName = theForeignTableName;
053        }
054
055        public void setForeignColumnName(String theForeignColumnName) {
056                myForeignColumnName = theForeignColumnName;
057        }
058
059        @Override
060        public void validate() {
061                super.validate();
062
063                Validate.isTrue(isNotBlank(myConstraintName));
064                Validate.isTrue(isNotBlank(myForeignTableName));
065                Validate.isTrue(isNotBlank(myForeignColumnName));
066                setDescription("Add foreign key " + myConstraintName + " from column " + getColumnName() + " of table " + 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 (`" + getColumnName() + "`) references " + myForeignTableName + " (`" + myForeignColumnName + "`)";
084                                break;
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}