001/*-
002 * #%L
003 * HAPI FHIR Server - SQL Migration
004 * %%
005 * Copyright (C) 2014 - 2026 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.dao;
021
022import ca.uhn.fhir.jpa.migrate.DriverTypeEnum;
023import ca.uhn.fhir.jpa.migrate.entity.HapiMigrationEntity;
024import ca.uhn.fhir.jpa.migrate.taskdef.ColumnTypeEnum;
025import ca.uhn.fhir.jpa.migrate.taskdef.ColumnTypeToDriverTypeToSqlType;
026import com.healthmarketscience.sqlbuilder.AlterTableQuery;
027import com.healthmarketscience.sqlbuilder.BinaryCondition;
028import com.healthmarketscience.sqlbuilder.CreateIndexQuery;
029import com.healthmarketscience.sqlbuilder.CreateTableQuery;
030import com.healthmarketscience.sqlbuilder.DeleteQuery;
031import com.healthmarketscience.sqlbuilder.FunctionCall;
032import com.healthmarketscience.sqlbuilder.InsertQuery;
033import com.healthmarketscience.sqlbuilder.SelectQuery;
034import com.healthmarketscience.sqlbuilder.dbspec.basic.DbColumn;
035import com.healthmarketscience.sqlbuilder.dbspec.basic.DbSchema;
036import com.healthmarketscience.sqlbuilder.dbspec.basic.DbSpec;
037import com.healthmarketscience.sqlbuilder.dbspec.basic.DbTable;
038import org.slf4j.Logger;
039import org.slf4j.LoggerFactory;
040
041import java.sql.Types;
042
043public class MigrationQueryBuilder {
044        private static final Logger ourLog = LoggerFactory.getLogger(MigrationQueryBuilder.class);
045
046        private final DbSpec mySpec;
047        private final DbSchema mySchema;
048        private final DbTable myTable;
049        private final DbColumn myVersionCol;
050        private final DbColumn myInstalledRankCol;
051        private final DbColumn myDescriptionCol;
052        private final DbColumn myTypeCol;
053        private final DbColumn myScriptCol;
054        private final DbColumn myChecksumCol;
055        private final DbColumn myInstalledByCol;
056        private final DbColumn myInstalledOnCol;
057        private final DbColumn myExecutionTimeCol;
058        private final DbColumn mySuccessCol;
059        private final DbColumn myResultCol;
060        private final String myDeleteAll;
061        private final String myHighestKeyQuery;
062        private final DriverTypeEnum myDriverType;
063        private final String myMigrationTablename;
064        private final String myBooleanType;
065
066        public MigrationQueryBuilder(DriverTypeEnum theDriverType, String theMigrationTablename) {
067                myDriverType = theDriverType;
068                myMigrationTablename = theMigrationTablename;
069
070                mySpec = new DbSpec();
071                mySchema = mySpec.addDefaultSchema();
072                myTable = mySchema.addTable("\"" + theMigrationTablename + "\"");
073
074                myInstalledRankCol = myTable.addColumn("\"installed_rank\"", Types.INTEGER, null);
075                myInstalledRankCol.notNull();
076
077                myVersionCol = myTable.addColumn("\"version\"", Types.VARCHAR, HapiMigrationEntity.VERSION_MAX_SIZE);
078
079                myDescriptionCol =
080                                myTable.addColumn("\"description\"", Types.VARCHAR, HapiMigrationEntity.DESCRIPTION_MAX_SIZE);
081                myDescriptionCol.notNull();
082
083                myTypeCol = myTable.addColumn("\"type\"", Types.VARCHAR, HapiMigrationEntity.TYPE_MAX_SIZE);
084                myTypeCol.notNull();
085
086                myScriptCol = myTable.addColumn("\"script\"", Types.VARCHAR, HapiMigrationEntity.SCRIPT_MAX_SIZE);
087                myScriptCol.notNull();
088
089                myChecksumCol = myTable.addColumn("\"checksum\"", Types.INTEGER, null);
090
091                myInstalledByCol =
092                                myTable.addColumn("\"installed_by\"", Types.VARCHAR, HapiMigrationEntity.INSTALLED_BY_MAX_SIZE);
093                myInstalledByCol.notNull();
094
095                myInstalledOnCol = myTable.addColumn("\"installed_on\"", Types.DATE, null);
096                myInstalledOnCol.notNull();
097
098                myExecutionTimeCol = myTable.addColumn("\"execution_time\"", Types.INTEGER, null);
099                myExecutionTimeCol.notNull();
100
101                myBooleanType = ColumnTypeToDriverTypeToSqlType.getColumnTypeToDriverTypeToSqlType()
102                                .get(ColumnTypeEnum.BOOLEAN)
103                                .get(theDriverType);
104                mySuccessCol = myTable.addColumn("\"success\"", myBooleanType, null);
105                mySuccessCol.notNull();
106
107                myResultCol = myTable.addColumn("\"result\"", Types.VARCHAR, HapiMigrationEntity.RESULT_MAX_SIZE);
108
109                myDeleteAll = new DeleteQuery(myTable).toString();
110                myHighestKeyQuery = buildHighestKeyQuery();
111        }
112
113        public String deleteAll() {
114                return myDeleteAll;
115        }
116
117        public String getHighestKeyQuery() {
118                return myHighestKeyQuery;
119        }
120
121        private String buildHighestKeyQuery() {
122                return new SelectQuery()
123                                .addCustomColumns(FunctionCall.max().addColumnParams(myInstalledRankCol))
124                                .validate()
125                                .toString();
126        }
127
128        public String insertPreparedStatement() {
129                return new InsertQuery(myTable)
130                                .addPreparedColumns(
131                                                myInstalledRankCol,
132                                                myVersionCol,
133                                                myDescriptionCol,
134                                                myTypeCol,
135                                                myScriptCol,
136                                                myChecksumCol,
137                                                myInstalledByCol,
138                                                myInstalledOnCol,
139                                                myExecutionTimeCol,
140                                                mySuccessCol,
141                                                myResultCol)
142                                .validate()
143                                .toString();
144        }
145
146        public String createTableStatement() {
147                return new CreateTableQuery(myTable, true).validate().toString();
148        }
149
150        public String addResultColumnStatement() {
151                return new AlterTableQuery(myTable).setAddColumn(myResultCol).validate().toString();
152        }
153
154        public String createIndexStatement() {
155                return new CreateIndexQuery(myTable, myMigrationTablename.toUpperCase() + "_PK_INDEX")
156                                .setIndexType(CreateIndexQuery.IndexType.UNIQUE)
157                                .addColumns(myInstalledRankCol)
158                                .validate()
159                                .toString();
160        }
161
162        public String findAllQuery() {
163                return new SelectQuery()
164                                .addFromTable(myTable)
165                                .addCondition(BinaryCondition.notEqualTo(myInstalledRankCol, HapiMigrationEntity.CREATE_TABLE_PID))
166                                .addAllColumns()
167                                .validate()
168                                .toString();
169        }
170
171        public String deleteLockRecordStatement(Integer theLockPid, String theLockDescription) {
172                return new DeleteQuery(myTable)
173                                .addCondition(BinaryCondition.equalTo(myInstalledRankCol, theLockPid))
174                                .addCondition(BinaryCondition.equalTo(myDescriptionCol, theLockDescription))
175                                .validate()
176                                .toString();
177        }
178
179        public String findByPidAndNotDescriptionQuery(Integer theLockPid, String theLockDescription) {
180                return new SelectQuery()
181                                .addFromTable(myTable)
182                                .addCondition(BinaryCondition.equalTo(myInstalledRankCol, theLockPid))
183                                .addCondition(BinaryCondition.notEqualTo(myDescriptionCol, theLockDescription))
184                                .addAllColumns()
185                                .validate()
186                                .toString();
187        }
188}