001package ca.uhn.fhir.jpa.migrate;
002
003/*-
004 * #%L
005 * HAPI FHIR Server - SQL Migration
006 * %%
007 * Copyright (C) 2014 - 2021 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.jpa.migrate.taskdef.BaseTask;
024import ca.uhn.fhir.rest.server.exceptions.InternalErrorException;
025import org.slf4j.Logger;
026import org.slf4j.LoggerFactory;
027
028import java.sql.SQLException;
029import java.util.ArrayList;
030import java.util.List;
031import java.util.Objects;
032
033import static org.apache.commons.lang3.StringUtils.isBlank;
034
035public class Migrator {
036
037        private static final Logger ourLog = LoggerFactory.getLogger(Migrator.class);
038        private DriverTypeEnum myDriverType;
039        private String myConnectionUrl;
040        private String myUsername;
041        private String myPassword;
042        private List<BaseTask> myTasks = new ArrayList<>();
043        private DriverTypeEnum.ConnectionProperties myConnectionProperties;
044        private int myChangesCount;
045        private boolean myDryRun;
046        private List<BaseTask.ExecutedStatement> myExecutedStatements = new ArrayList<>();
047        private boolean myNoColumnShrink;
048
049        public int getChangesCount() {
050                return myChangesCount;
051        }
052
053        public void setDriverType(DriverTypeEnum theDriverType) {
054                myDriverType = theDriverType;
055        }
056
057        public void setConnectionUrl(String theConnectionUrl) {
058                myConnectionUrl = theConnectionUrl;
059        }
060
061        public void setUsername(String theUsername) {
062                myUsername = theUsername;
063        }
064
065        public void setPassword(String thePassword) {
066                myPassword = thePassword;
067        }
068
069        public void addTask(BaseTask theTask) {
070                myTasks.add(theTask);
071        }
072
073        public void setDryRun(boolean theDryRun) {
074                myDryRun = theDryRun;
075        }
076
077        public void migrate() {
078                ourLog.info("Starting migration with {} tasks", myTasks.size());
079
080                myConnectionProperties = myDriverType.newConnectionProperties(myConnectionUrl, myUsername, myPassword);
081                try {
082                        for (BaseTask next : myTasks) {
083                                next.setDriverType(myDriverType);
084                                next.setConnectionProperties(myConnectionProperties);
085                                next.setDryRun(myDryRun);
086                                next.setNoColumnShrink(myNoColumnShrink);
087                                try {
088                                        next.execute();
089                                } catch (SQLException e) {
090                                        String description = next.getDescription();
091                                        if (isBlank(description)) {
092                                                description = next.getClass().getSimpleName();
093                                        }
094                                        String prefix = "Failure executing task \"" + description + "\", aborting! Cause: ";
095                                        throw new InternalErrorException(prefix + e.toString(), e);
096                                }
097
098                                myChangesCount += next.getChangesCount();
099                                myExecutedStatements.addAll(next.getExecutedStatements());
100                        }
101                } finally {
102                        myConnectionProperties.close();
103                }
104
105                ourLog.info("Finished migration of {} tasks", myTasks.size());
106
107                if (myDryRun) {
108                        StringBuilder statementBuilder = new StringBuilder();
109                        String lastTable = null;
110                        for (BaseTask.ExecutedStatement next : myExecutedStatements) {
111                                if (!Objects.equals(lastTable, next.getTableName())) {
112                                        statementBuilder.append("\n\n-- Table: ").append(next.getTableName()).append("\n");
113                                        lastTable = next.getTableName();
114                                }
115
116                                statementBuilder.append(next.getSql()).append(";\n");
117
118                                for (Object nextArg : next.getArguments()) {
119                                        statementBuilder.append("  -- Arg: ").append(nextArg).append("\n");
120                                }
121                        }
122
123                        ourLog.info("SQL that would be executed:\n\n***********************************\n{}***********************************", statementBuilder);
124                }
125
126        }
127
128        public void addTasks(List<BaseTask> theTasks) {
129                theTasks.forEach(this::addTask);
130        }
131
132        public void setNoColumnShrink(boolean theNoColumnShrink) {
133                myNoColumnShrink = theNoColumnShrink;
134        }
135
136
137}