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