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