001/*- 002 * #%L 003 * HAPI FHIR Server - SQL Migration 004 * %% 005 * Copyright (C) 2014 - 2024 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.jpa.migrate.DriverTypeEnum; 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.ArrayList; 032import java.util.Comparator; 033import java.util.List; 034import java.util.stream.Collectors; 035 036public class AddTableByColumnTask extends BaseTableTask { 037 038 private static final Logger ourLog = LoggerFactory.getLogger(AddTableByColumnTask.class); 039 040 private final List<AddColumnTask> myAddColumnTasks = new ArrayList<>(); 041 private List<String> myPkColumns; 042 private final List<ForeignKeyContainer> myFKColumns = new ArrayList<>(); 043 private final Comparator<AddColumnTask> myColumnSortingRules; 044 045 public AddTableByColumnTask() { 046 this(null); 047 } 048 049 public AddTableByColumnTask(Comparator<AddColumnTask> theColumnSortingRules) { 050 this(null, null, theColumnSortingRules); 051 setDryRun(true); 052 myCheckForExistingTables = false; 053 } 054 055 public AddTableByColumnTask(String theProductVersion, String theSchemaVersion) { 056 this(theProductVersion, theSchemaVersion, null); 057 } 058 059 public AddTableByColumnTask( 060 String theProductVersion, String theSchemaVersion, Comparator<AddColumnTask> theColumnSortingRules) { 061 super(theProductVersion, theSchemaVersion); 062 myColumnSortingRules = theColumnSortingRules; 063 } 064 065 @Override 066 public void validate() { 067 super.validate(); 068 setDescription("Add table " + getTableName()); 069 } 070 071 public void addAddColumnTask(AddColumnTask theTask) { 072 Validate.notNull(theTask); 073 myAddColumnTasks.add(theTask); 074 } 075 076 public void setPkColumns(List<String> thePkColumns) { 077 myPkColumns = thePkColumns; 078 } 079 080 public void addForeignKey(ForeignKeyContainer theForeignKeyContainer) { 081 myFKColumns.add(theForeignKeyContainer); 082 } 083 084 public List<String> getPkColumns() { 085 return myPkColumns; 086 } 087 088 public String generateSQLCreateScript() { 089 StringBuilder sb = new StringBuilder(); 090 sb.append("CREATE TABLE "); 091 sb.append(getTableName()); 092 sb.append(" ("); 093 if (myPrettyPrint) { 094 sb.append("\n"); 095 } else { 096 sb.append(" "); 097 } 098 099 for (AddColumnTask next : getOrderedAddColumnTasks()) { 100 next.setDriverType(getDriverType()); 101 next.setTableName(getTableName()); 102 next.validate(); 103 104 if (myPrettyPrint) { 105 sb.append("\t"); 106 } 107 108 sb.append(next.getColumnName()); 109 sb.append(" "); 110 sb.append(next.getTypeStatement()); 111 sb.append(","); 112 if (myPrettyPrint) { 113 sb.append("\n"); 114 } else { 115 sb.append(" "); 116 } 117 } 118 119 // primary keys 120 if (myPrettyPrint) { 121 sb.append("\t"); 122 } else { 123 sb.append(" "); 124 } 125 sb.append("PRIMARY KEY ("); 126 for (int i = 0; i < myPkColumns.size(); i++) { 127 if (i > 0) { 128 sb.append(", "); 129 } 130 sb.append(myPkColumns.get(i)); 131 } 132 133 boolean hasForeignKeys = !myFKColumns.isEmpty(); 134 135 sb.append(")"); 136 if (hasForeignKeys) { 137 sb.append(","); 138 } 139 if (myPrettyPrint) { 140 sb.append("\n"); 141 } else { 142 sb.append(" "); 143 } 144 145 DriverTypeEnum sqlEngine = getDriverType(); 146 147 // foreign keys 148 if (!myFKColumns.isEmpty()) { 149 for (int i = 0; i < myFKColumns.size(); i++) { 150 if (i > 0) { 151 sb.append(", "); 152 } 153 ForeignKeyContainer fk = myFKColumns.get(i); 154 if (myPrettyPrint) { 155 sb.append("\t"); 156 } 157 sb.append(fk.generateSQL(sqlEngine, myPrettyPrint)); 158 if (myPrettyPrint) { 159 sb.append("\n"); 160 } else { 161 sb.append(" "); 162 } 163 } 164 } 165 166 sb.append(")"); 167 168 switch (sqlEngine) { 169 case MARIADB_10_1: 170 case MYSQL_5_7: 171 sb.append(" engine=InnoDB"); 172 break; 173 case DERBY_EMBEDDED: 174 case POSTGRES_9_4: 175 case ORACLE_12C: 176 case MSSQL_2012: 177 case H2_EMBEDDED: 178 case COCKROACHDB_21_1: 179 break; 180 } 181 182 return sb.toString(); 183 } 184 185 @Override 186 public void doExecute() throws SQLException { 187 if (myCheckForExistingTables 188 && JdbcUtils.getTableNames(getConnectionProperties()).contains(getTableName())) { 189 logInfo(ourLog, "Already have table named {} - No action performed", getTableName()); 190 return; 191 } 192 193 executeSql(getTableName(), generateSQLCreateScript()); 194 } 195 196 @Override 197 protected void generateEquals(EqualsBuilder theBuilder, BaseTask theOtherObject) { 198 super.generateEquals(theBuilder, theOtherObject); 199 AddTableByColumnTask otherObject = (AddTableByColumnTask) theOtherObject; 200 theBuilder.append(myAddColumnTasks, otherObject.myAddColumnTasks); 201 theBuilder.append(myPkColumns, otherObject.myPkColumns); 202 } 203 204 @Override 205 protected void generateHashCode(HashCodeBuilder theBuilder) { 206 super.generateHashCode(theBuilder); 207 theBuilder.append(myAddColumnTasks); 208 theBuilder.append(myPkColumns); 209 } 210 211 private List<AddColumnTask> getOrderedAddColumnTasks() { 212 if (myColumnSortingRules == null) { 213 return myAddColumnTasks; 214 } 215 216 return myAddColumnTasks.stream().sorted(myColumnSortingRules).collect(Collectors.toUnmodifiableList()); 217 } 218}