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