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.tasks.api;
021
022import ca.uhn.fhir.i18n.Msg;
023import ca.uhn.fhir.jpa.migrate.DriverTypeEnum;
024import ca.uhn.fhir.jpa.migrate.taskdef.AddColumnTask;
025import ca.uhn.fhir.jpa.migrate.taskdef.AddForeignKeyTask;
026import ca.uhn.fhir.jpa.migrate.taskdef.AddIdGeneratorTask;
027import ca.uhn.fhir.jpa.migrate.taskdef.AddIndexTask;
028import ca.uhn.fhir.jpa.migrate.taskdef.AddTableByColumnTask;
029import ca.uhn.fhir.jpa.migrate.taskdef.AddTableRawSqlTask;
030import ca.uhn.fhir.jpa.migrate.taskdef.BaseTableTask;
031import ca.uhn.fhir.jpa.migrate.taskdef.BaseTask;
032import ca.uhn.fhir.jpa.migrate.taskdef.ColumnTypeEnum;
033import ca.uhn.fhir.jpa.migrate.taskdef.DropColumnTask;
034import ca.uhn.fhir.jpa.migrate.taskdef.DropForeignKeyTask;
035import ca.uhn.fhir.jpa.migrate.taskdef.DropIdGeneratorTask;
036import ca.uhn.fhir.jpa.migrate.taskdef.DropIndexTask;
037import ca.uhn.fhir.jpa.migrate.taskdef.DropTableTask;
038import ca.uhn.fhir.jpa.migrate.taskdef.ExecuteRawSqlTask;
039import ca.uhn.fhir.jpa.migrate.taskdef.InitializeSchemaTask;
040import ca.uhn.fhir.jpa.migrate.taskdef.MigratePostgresTextClobToBinaryClobTask;
041import ca.uhn.fhir.jpa.migrate.taskdef.ModifyColumnTask;
042import ca.uhn.fhir.jpa.migrate.taskdef.NopTask;
043import ca.uhn.fhir.jpa.migrate.taskdef.RenameColumnTask;
044import ca.uhn.fhir.jpa.migrate.taskdef.RenameIndexTask;
045import org.apache.commons.lang3.Validate;
046import org.intellij.lang.annotations.Language;
047
048import java.util.Arrays;
049import java.util.Collections;
050import java.util.HashMap;
051import java.util.List;
052import java.util.Map;
053import java.util.Set;
054import java.util.stream.Collectors;
055
056public class Builder {
057
058        private final String myRelease;
059        private final BaseMigrationTasks.IAcceptsTasks mySink;
060
061        public Builder(String theRelease, BaseMigrationTasks.IAcceptsTasks theSink) {
062                myRelease = theRelease;
063                mySink = theSink;
064        }
065
066        public BuilderWithTableName onTable(String theTableName) {
067                return new BuilderWithTableName(myRelease, mySink, theTableName);
068        }
069
070        public void addTask(BaseTask theTask) {
071                mySink.addTask(theTask);
072        }
073
074        public BuilderAddTableRawSql addTableRawSql(String theVersion, String theTableName) {
075                return new BuilderAddTableRawSql(theVersion, theTableName);
076        }
077
078        public BuilderCompleteTask executeRawSql(String theVersion, @Language("SQL") String theSql) {
079                ExecuteRawSqlTask task = executeRawSqlOptional(false, theVersion, theSql);
080                return new BuilderCompleteTask(task);
081        }
082
083        public void executeRawSqlStub(String theVersion, @Language("SQL") String theSql) {
084                executeRawSqlOptional(true, theVersion, theSql);
085        }
086
087        private ExecuteRawSqlTask executeRawSqlOptional(
088                        boolean theDoNothing, String theVersion, @Language("SQL") String theSql) {
089                ExecuteRawSqlTask task = new ExecuteRawSqlTask(myRelease, theVersion).addSql(theSql);
090                task.setDoNothing(theDoNothing);
091                mySink.addTask(task);
092                return task;
093        }
094
095        public InitializeSchemaTask initializeSchema(
096                        String theVersion, ISchemaInitializationProvider theSchemaInitializationProvider) {
097                InitializeSchemaTask task = new InitializeSchemaTask(myRelease, theVersion, theSchemaInitializationProvider);
098                mySink.addTask(task);
099                return task;
100        }
101
102        @SuppressWarnings("unused")
103        public InitializeSchemaTask initializeSchema(
104                        String theVersion, String theSchemaName, ISchemaInitializationProvider theSchemaInitializationProvider) {
105                InitializeSchemaTask task = new InitializeSchemaTask(myRelease, theVersion, theSchemaInitializationProvider);
106                task.setDescription("Initialize " + theSchemaName + " schema");
107                mySink.addTask(task);
108                return task;
109        }
110
111        public Builder executeRawSql(String theVersion, DriverTypeEnum theDriver, @Language("SQL") String theSql) {
112                mySink.addTask(new ExecuteRawSqlTask(myRelease, theVersion).addSql(theDriver, theSql));
113                return this;
114        }
115
116        /**
117         * Builder method to define a raw SQL execution migration that needs to take place against multiple database types,
118         * and the SQL they need to use is not equal. Provide a map of driver types to SQL statements.
119         *
120         * @param theVersion The version of the migration.
121         * @param theDriverToSql Map of driver types to SQL statements.
122         */
123        public Builder executeRawSql(String theVersion, Map<DriverTypeEnum, String> theDriverToSql) {
124                Map<DriverTypeEnum, List<String>> singleSqlStatementMap = new HashMap<>();
125                theDriverToSql.entrySet().stream().forEach(entry -> {
126                        singleSqlStatementMap.put(entry.getKey(), Collections.singletonList(entry.getValue()));
127                });
128                return executeRawSqls(theVersion, singleSqlStatementMap);
129        }
130
131        /**
132         * Builder method to define a raw SQL execution migration that needs to take place against multiple database types,
133         * and the SQL they need to use is not equal, and there are multiple sql commands for a given database.
134         * Provide a map of driver types to list of SQL statements.
135         *
136         * @param theVersion The version of the migration.
137         * @param theDriverToSqls Map of driver types to list of SQL statements.
138         */
139        public Builder executeRawSqls(String theVersion, Map<DriverTypeEnum, List<String>> theDriverToSqls) {
140                ExecuteRawSqlTask executeRawSqlTask = new ExecuteRawSqlTask(myRelease, theVersion);
141                theDriverToSqls.entrySet().stream().forEach(entry -> {
142                        entry.getValue().forEach(sql -> executeRawSqlTask.addSql(entry.getKey(), sql));
143                });
144                mySink.addTask(executeRawSqlTask);
145                return this;
146        }
147
148        // Flyway doesn't support these kinds of migrations
149        @Deprecated
150        public Builder startSectionWithMessage(String theMessage) {
151                // Do nothing
152                return this;
153        }
154
155        public BuilderAddTableByColumns addTableByColumns(
156                        String theVersion, String theTableName, String... thePkColumnNames) {
157                return new BuilderAddTableByColumns(
158                                myRelease, theVersion, mySink, theTableName, Arrays.asList(thePkColumnNames));
159        }
160
161        public void addIdGenerator(String theVersion, String theGeneratorName) {
162                AddIdGeneratorTask task = new AddIdGeneratorTask(myRelease, theVersion, theGeneratorName);
163                addTask(task);
164        }
165
166        public DropIdGeneratorTask dropIdGenerator(String theVersion, String theIdGeneratorName) {
167                DropIdGeneratorTask task = new DropIdGeneratorTask(myRelease, theVersion, theIdGeneratorName);
168                addTask(task);
169                return task;
170        }
171
172        public void addNop(String theVersion) {
173                addTask(new NopTask(myRelease, theVersion));
174        }
175
176        public static class BuilderWithTableName implements BaseMigrationTasks.IAcceptsTasks {
177                private final String myRelease;
178                private final BaseMigrationTasks.IAcceptsTasks mySink;
179                private final String myTableName;
180
181                public BuilderWithTableName(String theRelease, BaseMigrationTasks.IAcceptsTasks theSink, String theTableName) {
182                        myRelease = theRelease;
183                        mySink = theSink;
184                        myTableName = theTableName;
185                }
186
187                public String getTableName() {
188                        return myTableName;
189                }
190
191                public BuilderCompleteTask dropIndex(String theVersion, String theIndexName) {
192                        BaseTask task = dropIndexOptional(false, theVersion, theIndexName);
193                        return new BuilderCompleteTask(task);
194                }
195
196                /**
197                 * Drop index without taking write lock on PG, Oracle, MSSQL.
198                 */
199                public BuilderCompleteTask dropIndexOnline(String theVersion, String theIndexName) {
200                        DropIndexTask task = dropIndexOptional(false, theVersion, theIndexName);
201                        task.setOnline(true);
202                        return new BuilderCompleteTask(task);
203                }
204
205                public void dropIndexStub(String theVersion, String theIndexName) {
206                        dropIndexOptional(true, theVersion, theIndexName);
207                }
208
209                private DropIndexTask dropIndexOptional(boolean theDoNothing, String theVersion, String theIndexName) {
210                        DropIndexTask task = new DropIndexTask(myRelease, theVersion);
211                        task.setIndexName(theIndexName);
212                        task.setTableName(myTableName);
213                        task.setDoNothing(theDoNothing);
214                        addTask(task);
215                        return task;
216                }
217
218                /**
219                 * @deprecated Do not rename indexes - It is too hard to figure out what happened if something goes wrong
220                 */
221                @Deprecated
222                public void renameIndex(String theVersion, String theOldIndexName, String theNewIndexName) {
223                        renameIndexOptional(false, theVersion, theOldIndexName, theNewIndexName);
224                }
225
226                /**
227                 * @deprecated Do not rename indexes - It is too hard to figure out what happened if something goes wrong
228                 */
229                public void renameIndexStub(String theVersion, String theOldIndexName, String theNewIndexName) {
230                        renameIndexOptional(true, theVersion, theOldIndexName, theNewIndexName);
231                }
232
233                private void renameIndexOptional(
234                                boolean theDoNothing, String theVersion, String theOldIndexName, String theNewIndexName) {
235                        RenameIndexTask task = new RenameIndexTask(myRelease, theVersion);
236                        task.setOldIndexName(theOldIndexName);
237                        task.setNewIndexName(theNewIndexName);
238                        task.setTableName(myTableName);
239                        task.setDoNothing(theDoNothing);
240                        addTask(task);
241                }
242
243                public void dropThisTable(String theVersion) {
244                        DropTableTask task = new DropTableTask(myRelease, theVersion);
245                        task.setTableName(myTableName);
246                        addTask(task);
247                }
248
249                public BuilderWithTableName.BuilderAddIndexWithName addIndex(String theVersion, String theIndexName) {
250                        return new BuilderWithTableName.BuilderAddIndexWithName(theVersion, theIndexName);
251                }
252
253                public BuilderWithTableName.BuilderAddColumnWithName addColumn(String theVersion, String theColumnName) {
254                        return new BuilderWithTableName.BuilderAddColumnWithName(myRelease, theVersion, theColumnName, this);
255                }
256
257                public BuilderCompleteTask dropColumn(String theVersion, String theColumnName) {
258                        Validate.notBlank(theColumnName);
259                        DropColumnTask task = new DropColumnTask(myRelease, theVersion);
260                        task.setTableName(myTableName);
261                        task.setColumnName(theColumnName);
262                        addTask(task);
263                        return new BuilderCompleteTask(task);
264                }
265
266                @Override
267                public void addTask(BaseTask theTask) {
268                        ((BaseTableTask) theTask).setTableName(myTableName);
269                        mySink.addTask(theTask);
270                }
271
272                public BuilderWithTableName.BuilderModifyColumnWithName modifyColumn(String theVersion, String theColumnName) {
273                        return new BuilderWithTableName.BuilderModifyColumnWithName(theVersion, theColumnName);
274                }
275
276                public BuilderWithTableName.BuilderAddForeignKey addForeignKey(String theVersion, String theForeignKeyName) {
277                        return new BuilderWithTableName.BuilderAddForeignKey(theVersion, theForeignKeyName);
278                }
279
280                public BuilderWithTableName renameColumn(String theVersion, String theOldName, String theNewName) {
281                        return renameColumn(theVersion, theOldName, theNewName, false, false);
282                }
283
284                /**
285                 * @param theOldName                            The old column name
286                 * @param theNewName                            The new column name
287                 * @param isOkayIfNeitherColumnExists           Setting this to true means that it's not an error if neither column exists
288                 * @param theDeleteTargetColumnFirstIfBothExist Setting this to true causes the migrator to be ok with the target column existing. It will make sure that there is no data in the column with the new name, then delete it if so in order to make room for the renamed column. If there is data it will still bomb out.
289                 */
290                public BuilderWithTableName renameColumn(
291                                String theVersion,
292                                String theOldName,
293                                String theNewName,
294                                boolean isOkayIfNeitherColumnExists,
295                                boolean theDeleteTargetColumnFirstIfBothExist) {
296                        RenameColumnTask task = new RenameColumnTask(myRelease, theVersion);
297                        task.setTableName(myTableName);
298                        task.setOldName(theOldName);
299                        task.setNewName(theNewName);
300                        task.setOkayIfNeitherColumnExists(isOkayIfNeitherColumnExists);
301                        task.setDeleteTargetColumnFirstIfBothExist(theDeleteTargetColumnFirstIfBothExist);
302                        addTask(task);
303                        return this;
304                }
305
306                /**
307                 * @param theFkName          the name of the foreign key
308                 * @param theParentTableName the name of the table that exports the foreign key
309                 */
310                public void dropForeignKey(String theVersion, String theFkName, String theParentTableName) {
311                        DropForeignKeyTask task = new DropForeignKeyTask(myRelease, theVersion);
312                        task.setConstraintName(theFkName);
313                        task.setTableName(getTableName());
314                        task.setParentTableName(theParentTableName);
315                        addTask(task);
316                }
317
318                public void migratePostgresTextClobToBinaryClob(String theVersion, String theColumnName) {
319                        MigratePostgresTextClobToBinaryClobTask task =
320                                        new MigratePostgresTextClobToBinaryClobTask(myRelease, theVersion);
321                        task.setTableName(getTableName());
322                        task.setColumnName(theColumnName);
323                        addTask(task);
324                }
325
326                public class BuilderAddIndexWithName {
327                        private final String myVersion;
328                        private final String myIndexName;
329
330                        public BuilderAddIndexWithName(String theVersion, String theIndexName) {
331                                myVersion = theVersion;
332                                myIndexName = theIndexName;
333                        }
334
335                        public BuilderWithTableName.BuilderAddIndexWithName.BuilderAddIndexUnique unique(boolean theUnique) {
336                                return new BuilderWithTableName.BuilderAddIndexWithName.BuilderAddIndexUnique(myVersion, theUnique);
337                        }
338
339                        public class BuilderAddIndexUnique {
340                                private final String myVersion;
341                                private final boolean myUnique;
342                                private String[] myIncludeColumns;
343                                private boolean myOnline;
344
345                                public BuilderAddIndexUnique(String theVersion, boolean theUnique) {
346                                        myVersion = theVersion;
347                                        myUnique = theUnique;
348                                }
349
350                                public void withColumnsStub(String... theColumnNames) {
351                                        withColumnsOptional(true, theColumnNames);
352                                }
353
354                                public BuilderCompleteTask withColumns(String... theColumnNames) {
355                                        BaseTask task = withColumnsOptional(false, theColumnNames);
356                                        return new BuilderCompleteTask(task);
357                                }
358
359                                private AddIndexTask withColumnsOptional(boolean theDoNothing, String... theColumnNames) {
360                                        AddIndexTask task = new AddIndexTask(myRelease, myVersion);
361                                        task.setTableName(myTableName);
362                                        task.setIndexName(myIndexName);
363                                        task.setUnique(myUnique);
364                                        task.setColumns(theColumnNames);
365                                        task.setDoNothing(theDoNothing);
366                                        task.setOnline(myOnline);
367                                        if (myIncludeColumns != null) {
368                                                task.setIncludeColumns(myIncludeColumns);
369                                        }
370                                        addTask(task);
371                                        return task;
372                                }
373
374                                public BuilderAddIndexUnique includeColumns(String... theIncludeColumns) {
375                                        myIncludeColumns = theIncludeColumns;
376                                        return this;
377                                }
378
379                                /**
380                                 * Add the index without locking the table.
381                                 */
382                                public BuilderAddIndexUnique online(boolean theOnlineFlag) {
383                                        myOnline = theOnlineFlag;
384                                        return this;
385                                }
386                        }
387                }
388
389                public class BuilderModifyColumnWithName {
390                        private final String myVersion;
391                        private final String myColumnName;
392
393                        public BuilderModifyColumnWithName(String theVersion, String theColumnName) {
394                                myVersion = theVersion;
395                                myColumnName = theColumnName;
396                        }
397
398                        public String getColumnName() {
399                                return myColumnName;
400                        }
401
402                        public BuilderWithTableName.BuilderModifyColumnWithName.BuilderModifyColumnWithNameAndNullable nullable() {
403                                return new BuilderWithTableName.BuilderModifyColumnWithName.BuilderModifyColumnWithNameAndNullable(
404                                                myVersion, true);
405                        }
406
407                        public BuilderWithTableName.BuilderModifyColumnWithName.BuilderModifyColumnWithNameAndNullable
408                                        nonNullable() {
409                                return new BuilderWithTableName.BuilderModifyColumnWithName.BuilderModifyColumnWithNameAndNullable(
410                                                myVersion, false);
411                        }
412
413                        public class BuilderModifyColumnWithNameAndNullable {
414                                private final String myVersion;
415                                private final boolean myNullable;
416                                private boolean myFailureAllowed;
417
418                                public BuilderModifyColumnWithNameAndNullable(String theVersion, boolean theNullable) {
419                                        myVersion = theVersion;
420                                        myNullable = theNullable;
421                                }
422
423                                public void withType(ColumnTypeEnum theColumnType) {
424                                        withType(theColumnType, null);
425                                }
426
427                                public void withType(ColumnTypeEnum theColumnType, Integer theLength) {
428                                        if (theColumnType == ColumnTypeEnum.STRING) {
429                                                if (theLength == null || theLength == 0) {
430                                                        throw new IllegalArgumentException(
431                                                                        Msg.code(52) + "Can not specify length 0 for column of type " + theColumnType);
432                                                }
433                                        } else {
434                                                if (theLength != null) {
435                                                        throw new IllegalArgumentException(
436                                                                        Msg.code(53) + "Can not specify length for column of type " + theColumnType);
437                                                }
438                                        }
439
440                                        ModifyColumnTask task = new ModifyColumnTask(myRelease, myVersion);
441                                        task.setColumnName(myColumnName);
442                                        task.setTableName(myTableName);
443                                        if (theLength != null) {
444                                                task.setColumnLength(theLength);
445                                        }
446                                        task.setNullable(myNullable);
447                                        task.setColumnType(theColumnType);
448                                        task.setFailureAllowed(myFailureAllowed);
449                                        addTask(task);
450                                }
451
452                                public BuilderModifyColumnWithNameAndNullable failureAllowed() {
453                                        myFailureAllowed = true;
454                                        return this;
455                                }
456                        }
457                }
458
459                public class BuilderAddForeignKey {
460                        private final String myVersion;
461                        private final String myForeignKeyName;
462
463                        public BuilderAddForeignKey(String theVersion, String theForeignKeyName) {
464                                myVersion = theVersion;
465                                myForeignKeyName = theForeignKeyName;
466                        }
467
468                        public BuilderWithTableName.BuilderAddForeignKey.BuilderAddForeignKeyToColumn toColumn(
469                                        String theColumnName) {
470                                return new BuilderWithTableName.BuilderAddForeignKey.BuilderAddForeignKeyToColumn(
471                                                myVersion, theColumnName);
472                        }
473
474                        public class BuilderAddForeignKeyToColumn extends BuilderWithTableName.BuilderModifyColumnWithName {
475                                public BuilderAddForeignKeyToColumn(String theVersion, String theColumnName) {
476                                        super(theVersion, theColumnName);
477                                }
478
479                                public BuilderCompleteTask references(String theForeignTable, String theForeignColumn) {
480                                        AddForeignKeyTask task = new AddForeignKeyTask(myRelease, myVersion);
481                                        task.setTableName(myTableName);
482                                        task.setConstraintName(myForeignKeyName);
483                                        task.setColumnName(getColumnName());
484                                        task.setForeignTableName(theForeignTable);
485                                        task.setForeignColumnName(theForeignColumn);
486                                        addTask(task);
487                                        return new BuilderCompleteTask(task);
488                                }
489                        }
490                }
491
492                public static class BuilderAddColumnWithName {
493                        private final String myRelease;
494                        private final String myVersion;
495                        private final String myColumnName;
496                        private final BaseMigrationTasks.IAcceptsTasks myTaskSink;
497
498                        public BuilderAddColumnWithName(
499                                        String theRelease,
500                                        String theVersion,
501                                        String theColumnName,
502                                        BaseMigrationTasks.IAcceptsTasks theTaskSink) {
503                                myRelease = theRelease;
504                                myVersion = theVersion;
505                                myColumnName = theColumnName;
506                                myTaskSink = theTaskSink;
507                        }
508
509                        public BuilderWithTableName.BuilderAddColumnWithName.BuilderAddColumnWithNameNullable nullable() {
510                                return new BuilderWithTableName.BuilderAddColumnWithName.BuilderAddColumnWithNameNullable(
511                                                myRelease, myVersion, true);
512                        }
513
514                        public BuilderWithTableName.BuilderAddColumnWithName.BuilderAddColumnWithNameNullable nonNullable() {
515                                return new BuilderWithTableName.BuilderAddColumnWithName.BuilderAddColumnWithNameNullable(
516                                                myRelease, myVersion, false);
517                        }
518
519                        public class BuilderAddColumnWithNameNullable {
520                                private final boolean myNullable;
521                                private final String myRelease;
522                                private final String myVersion;
523
524                                public BuilderAddColumnWithNameNullable(String theRelease, String theVersion, boolean theNullable) {
525                                        myRelease = theRelease;
526                                        myVersion = theVersion;
527                                        myNullable = theNullable;
528                                }
529
530                                public BuilderCompleteTask type(ColumnTypeEnum theColumnType) {
531                                        return type(theColumnType, null);
532                                }
533
534                                public BuilderCompleteTask type(ColumnTypeEnum theColumnType, Integer theLength) {
535                                        AddColumnTask task = new AddColumnTask(myRelease, myVersion);
536                                        task.setColumnName(myColumnName);
537                                        task.setNullable(myNullable);
538                                        task.setColumnType(theColumnType);
539                                        if (theLength != null) {
540                                                task.setColumnLength(theLength);
541                                        }
542                                        myTaskSink.addTask(task);
543
544                                        return new BuilderCompleteTask(task);
545                                }
546                        }
547                }
548        }
549
550        public static class BuilderCompleteTask {
551
552                private final BaseTask myTask;
553
554                public BuilderCompleteTask(BaseTask theTask) {
555                        myTask = theTask;
556                }
557
558                public BuilderCompleteTask failureAllowed() {
559                        myTask.setFailureAllowed(true);
560                        return this;
561                }
562
563                public BuilderCompleteTask doNothing() {
564                        myTask.setDoNothing(true);
565                        return this;
566                }
567
568                public BuilderCompleteTask onlyAppliesToPlatforms(DriverTypeEnum... theTypes) {
569                        Set<DriverTypeEnum> typesSet = Arrays.stream(theTypes).collect(Collectors.toSet());
570                        myTask.setOnlyAppliesToPlatforms(typesSet);
571                        return this;
572                }
573
574                public BuilderCompleteTask runEvenDuringSchemaInitialization() {
575                        myTask.setRunDuringSchemaInitialization(true);
576                        return this;
577                }
578        }
579
580        public class BuilderAddTableRawSql {
581
582                private final AddTableRawSqlTask myTask;
583
584                protected BuilderAddTableRawSql(String theVersion, String theTableName) {
585                        myTask = new AddTableRawSqlTask(myRelease, theVersion);
586                        myTask.setTableName(theTableName);
587                        addTask(myTask);
588                }
589
590                public BuilderAddTableRawSql addSql(DriverTypeEnum theDriverTypeEnum, @Language("SQL") String theSql) {
591                        myTask.addSql(theDriverTypeEnum, theSql);
592                        return this;
593                }
594
595                public void addSql(@Language("SQL") String theSql) {
596                        myTask.addSql(theSql);
597                }
598        }
599
600        public class BuilderAddTableByColumns extends BuilderWithTableName implements BaseMigrationTasks.IAcceptsTasks {
601                private final String myVersion;
602                private final AddTableByColumnTask myTask;
603
604                public BuilderAddTableByColumns(
605                                String theRelease,
606                                String theVersion,
607                                BaseMigrationTasks.IAcceptsTasks theSink,
608                                String theTableName,
609                                List<String> thePkColumnNames) {
610                        super(theRelease, theSink, theTableName);
611                        myVersion = theVersion;
612                        myTask = new AddTableByColumnTask(myRelease, theVersion);
613                        myTask.setTableName(theTableName);
614                        myTask.setPkColumns(thePkColumnNames);
615                        theSink.addTask(myTask);
616                }
617
618                public BuilderAddColumnWithName addColumn(String theColumnName) {
619                        return new BuilderAddColumnWithName(myRelease, myVersion, theColumnName, this);
620                }
621
622                @Override
623                public void addTask(BaseTask theTask) {
624                        if (theTask instanceof AddColumnTask) {
625                                myTask.addAddColumnTask((AddColumnTask) theTask);
626                        } else {
627                                super.addTask(theTask);
628                        }
629                }
630
631                public BuilderAddTableByColumns failureAllowed() {
632                        myTask.setFailureAllowed(true);
633                        return this;
634                }
635        }
636
637        public String getRelease() {
638                return myRelease;
639        }
640}