diff --git a/hapi-deployable-pom/pom.xml b/hapi-deployable-pom/pom.xml index 30e1ccb5d6d..259a53de8ae 100644 --- a/hapi-deployable-pom/pom.xml +++ b/hapi-deployable-pom/pom.xml @@ -5,7 +5,7 @@ ca.uhn.hapi.fhir hapi-fhir - 7.3.7-SNAPSHOT + 7.3.8-SNAPSHOT ../pom.xml diff --git a/hapi-fhir-android/pom.xml b/hapi-fhir-android/pom.xml index 9060665d1b3..2319bf9a4cc 100644 --- a/hapi-fhir-android/pom.xml +++ b/hapi-fhir-android/pom.xml @@ -5,7 +5,7 @@ ca.uhn.hapi.fhir hapi-deployable-pom - 7.3.7-SNAPSHOT + 7.3.8-SNAPSHOT ../hapi-deployable-pom/pom.xml diff --git a/hapi-fhir-base/pom.xml b/hapi-fhir-base/pom.xml index fcc60e05e2b..03e4d113f4c 100644 --- a/hapi-fhir-base/pom.xml +++ b/hapi-fhir-base/pom.xml @@ -5,7 +5,7 @@ ca.uhn.hapi.fhir hapi-deployable-pom - 7.3.7-SNAPSHOT + 7.3.8-SNAPSHOT ../hapi-deployable-pom/pom.xml diff --git a/hapi-fhir-bom/pom.xml b/hapi-fhir-bom/pom.xml index 3a00980cded..1dec9586365 100644 --- a/hapi-fhir-bom/pom.xml +++ b/hapi-fhir-bom/pom.xml @@ -4,7 +4,7 @@ 4.0.0 ca.uhn.hapi.fhir hapi-fhir-bom - 7.3.7-SNAPSHOT + 7.3.8-SNAPSHOT pom HAPI FHIR BOM @@ -12,7 +12,7 @@ ca.uhn.hapi.fhir hapi-deployable-pom - 7.3.7-SNAPSHOT + 7.3.8-SNAPSHOT ../hapi-deployable-pom/pom.xml diff --git a/hapi-fhir-checkstyle/pom.xml b/hapi-fhir-checkstyle/pom.xml index 2a21a1796fe..4bdbe08114a 100644 --- a/hapi-fhir-checkstyle/pom.xml +++ b/hapi-fhir-checkstyle/pom.xml @@ -5,7 +5,7 @@ ca.uhn.hapi.fhir hapi-fhir - 7.3.7-SNAPSHOT + 7.3.8-SNAPSHOT ../pom.xml diff --git a/hapi-fhir-cli/hapi-fhir-cli-api/pom.xml b/hapi-fhir-cli/hapi-fhir-cli-api/pom.xml index 3555f67f63c..e53b6e39633 100644 --- a/hapi-fhir-cli/hapi-fhir-cli-api/pom.xml +++ b/hapi-fhir-cli/hapi-fhir-cli-api/pom.xml @@ -4,7 +4,7 @@ ca.uhn.hapi.fhir hapi-deployable-pom - 7.3.7-SNAPSHOT + 7.3.8-SNAPSHOT ../../hapi-deployable-pom/pom.xml diff --git a/hapi-fhir-cli/hapi-fhir-cli-api/src/main/java/ca/uhn/fhir/cli/BaseFlywayMigrateDatabaseCommand.java b/hapi-fhir-cli/hapi-fhir-cli-api/src/main/java/ca/uhn/fhir/cli/BaseFlywayMigrateDatabaseCommand.java index 5d049b79246..dbc5be5ad41 100644 --- a/hapi-fhir-cli/hapi-fhir-cli-api/src/main/java/ca/uhn/fhir/cli/BaseFlywayMigrateDatabaseCommand.java +++ b/hapi-fhir-cli/hapi-fhir-cli-api/src/main/java/ca/uhn/fhir/cli/BaseFlywayMigrateDatabaseCommand.java @@ -26,8 +26,6 @@ import org.apache.commons.cli.CommandLine; import org.apache.commons.cli.Options; import org.apache.commons.cli.ParseException; import org.apache.commons.lang3.StringUtils; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; import java.util.Arrays; import java.util.List; @@ -40,12 +38,12 @@ import static org.apache.commons.lang3.StringUtils.defaultString; * NB since 2019-12-05: This class is kind of weirdly named now, since it can either use Flyway or not use Flyway */ public abstract class BaseFlywayMigrateDatabaseCommand extends BaseCommand { - private static final Logger ourLog = LoggerFactory.getLogger(BaseFlywayMigrateDatabaseCommand.class); public static final String MIGRATE_DATABASE = "migrate-database"; public static final String NO_COLUMN_SHRINK = "no-column-shrink"; - public static final String STRICT_ORDER = "strict-order"; public static final String SKIP_VERSIONS = "skip-versions"; + public static final String ENABLE_HEAVYWEIGHT_MIGRATIONS = "enable-heavyweight-migrations"; + private Set myFlags; private String myMigrationTableName; @@ -100,6 +98,12 @@ public abstract class BaseFlywayMigrateDatabaseCommand extends B SKIP_VERSIONS, "Versions", "A comma separated list of schema versions to skip. E.g. 4_1_0.20191214.2,4_1_0.20191214.4"); + addOptionalOption( + retVal, + null, + ENABLE_HEAVYWEIGHT_MIGRATIONS, + false, + "If this flag is set, additional migration tasks will be executed that are considered unnecessary to execute on a database with a significant amount of data loaded. This option is not generally necessary."); return retVal; } @@ -125,6 +129,7 @@ public abstract class BaseFlywayMigrateDatabaseCommand extends B boolean dryRun = theCommandLine.hasOption("r"); boolean noColumnShrink = theCommandLine.hasOption(BaseFlywayMigrateDatabaseCommand.NO_COLUMN_SHRINK); + boolean runHeavyweight = theCommandLine.hasOption(ENABLE_HEAVYWEIGHT_MIGRATIONS); String flags = theCommandLine.getOptionValue("x"); myFlags = Arrays.stream(defaultString(flags).split(",")) @@ -139,6 +144,7 @@ public abstract class BaseFlywayMigrateDatabaseCommand extends B migrator.createMigrationTableIfRequired(); migrator.setDryRun(dryRun); + migrator.setRunHeavyweightSkippableTasks(runHeavyweight); migrator.setNoColumnShrink(noColumnShrink); String skipVersions = theCommandLine.getOptionValue(BaseFlywayMigrateDatabaseCommand.SKIP_VERSIONS); addTasks(migrator, skipVersions); diff --git a/hapi-fhir-cli/hapi-fhir-cli-app/pom.xml b/hapi-fhir-cli/hapi-fhir-cli-app/pom.xml index b6c73678960..45ecdebddca 100644 --- a/hapi-fhir-cli/hapi-fhir-cli-app/pom.xml +++ b/hapi-fhir-cli/hapi-fhir-cli-app/pom.xml @@ -6,7 +6,7 @@ ca.uhn.hapi.fhir hapi-fhir-cli - 7.3.7-SNAPSHOT + 7.3.8-SNAPSHOT ../pom.xml diff --git a/hapi-fhir-cli/pom.xml b/hapi-fhir-cli/pom.xml index 6745d62518b..2eb4c555392 100644 --- a/hapi-fhir-cli/pom.xml +++ b/hapi-fhir-cli/pom.xml @@ -5,7 +5,7 @@ ca.uhn.hapi.fhir hapi-fhir - 7.3.7-SNAPSHOT + 7.3.8-SNAPSHOT ../pom.xml diff --git a/hapi-fhir-client-okhttp/pom.xml b/hapi-fhir-client-okhttp/pom.xml index 9a7ac810865..b2fa315c0ee 100644 --- a/hapi-fhir-client-okhttp/pom.xml +++ b/hapi-fhir-client-okhttp/pom.xml @@ -4,7 +4,7 @@ ca.uhn.hapi.fhir hapi-deployable-pom - 7.3.7-SNAPSHOT + 7.3.8-SNAPSHOT ../hapi-deployable-pom/pom.xml diff --git a/hapi-fhir-client/pom.xml b/hapi-fhir-client/pom.xml index de0d7f14afe..b5667ad3e0c 100644 --- a/hapi-fhir-client/pom.xml +++ b/hapi-fhir-client/pom.xml @@ -4,7 +4,7 @@ ca.uhn.hapi.fhir hapi-deployable-pom - 7.3.7-SNAPSHOT + 7.3.8-SNAPSHOT ../hapi-deployable-pom/pom.xml diff --git a/hapi-fhir-converter/pom.xml b/hapi-fhir-converter/pom.xml index 9883e898092..3cfc40c6258 100644 --- a/hapi-fhir-converter/pom.xml +++ b/hapi-fhir-converter/pom.xml @@ -5,7 +5,7 @@ ca.uhn.hapi.fhir hapi-deployable-pom - 7.3.7-SNAPSHOT + 7.3.8-SNAPSHOT ../hapi-deployable-pom/pom.xml diff --git a/hapi-fhir-dist/pom.xml b/hapi-fhir-dist/pom.xml index 9e001be27ab..f008db1fd69 100644 --- a/hapi-fhir-dist/pom.xml +++ b/hapi-fhir-dist/pom.xml @@ -5,7 +5,7 @@ ca.uhn.hapi.fhir hapi-fhir - 7.3.7-SNAPSHOT + 7.3.8-SNAPSHOT ../pom.xml diff --git a/hapi-fhir-docs/pom.xml b/hapi-fhir-docs/pom.xml index ba6c6912dbc..ab973c0f6b8 100644 --- a/hapi-fhir-docs/pom.xml +++ b/hapi-fhir-docs/pom.xml @@ -5,7 +5,7 @@ ca.uhn.hapi.fhir hapi-deployable-pom - 7.3.7-SNAPSHOT + 7.3.8-SNAPSHOT ../hapi-deployable-pom/pom.xml diff --git a/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/7_4_0/5926-fix-jpa-nullable-primitive-columns.yaml b/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/7_4_0/5926-fix-jpa-nullable-primitive-columns.yaml new file mode 100644 index 00000000000..a444a25f548 --- /dev/null +++ b/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/7_4_0/5926-fix-jpa-nullable-primitive-columns.yaml @@ -0,0 +1,4 @@ +--- +type: fix +issue: 5926 +title: "A number of columns in the JPA schema use primitive types (and therefore can never have a null value) but aren't marked as non-null." diff --git a/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/7_4_0/5926-skip-migrators-when-initializing.yaml b/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/7_4_0/5926-skip-migrators-when-initializing.yaml new file mode 100644 index 00000000000..cd3506ee7af --- /dev/null +++ b/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/7_4_0/5926-skip-migrators-when-initializing.yaml @@ -0,0 +1,7 @@ +--- +type: fix +issue: 5926 +title: "A regression in HAPI FHIR 6.4.0 meant that ther JPA server schema migrator ran all tasks + even when the database was initially empty and the schema was being initialized by script. + This did not produce any incorrect results, but did impact the amount of time taken to initialize + an empty database. This has been corrected." diff --git a/hapi-fhir-jacoco/pom.xml b/hapi-fhir-jacoco/pom.xml index 7d35c5c03fa..7cc8d05502a 100644 --- a/hapi-fhir-jacoco/pom.xml +++ b/hapi-fhir-jacoco/pom.xml @@ -11,7 +11,7 @@ ca.uhn.hapi.fhir hapi-deployable-pom - 7.3.7-SNAPSHOT + 7.3.8-SNAPSHOT ../hapi-deployable-pom/pom.xml diff --git a/hapi-fhir-jaxrsserver-base/pom.xml b/hapi-fhir-jaxrsserver-base/pom.xml index aa02e821f97..300c0cb720b 100644 --- a/hapi-fhir-jaxrsserver-base/pom.xml +++ b/hapi-fhir-jaxrsserver-base/pom.xml @@ -4,7 +4,7 @@ ca.uhn.hapi.fhir hapi-deployable-pom - 7.3.7-SNAPSHOT + 7.3.8-SNAPSHOT ../hapi-deployable-pom/pom.xml diff --git a/hapi-fhir-jpa/pom.xml b/hapi-fhir-jpa/pom.xml index 145cdb4224f..5885d7a365e 100644 --- a/hapi-fhir-jpa/pom.xml +++ b/hapi-fhir-jpa/pom.xml @@ -5,7 +5,7 @@ ca.uhn.hapi.fhir hapi-deployable-pom - 7.3.7-SNAPSHOT + 7.3.8-SNAPSHOT ../hapi-deployable-pom/pom.xml diff --git a/hapi-fhir-jpaserver-base/pom.xml b/hapi-fhir-jpaserver-base/pom.xml index d628e03ee5c..230c0a53a73 100644 --- a/hapi-fhir-jpaserver-base/pom.xml +++ b/hapi-fhir-jpaserver-base/pom.xml @@ -5,7 +5,7 @@ ca.uhn.hapi.fhir hapi-deployable-pom - 7.3.7-SNAPSHOT + 7.3.8-SNAPSHOT ../hapi-deployable-pom/pom.xml diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/entity/Batch2JobInstanceEntity.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/entity/Batch2JobInstanceEntity.java index bcf0a6cc0d9..3da31581d26 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/entity/Batch2JobInstanceEntity.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/entity/Batch2JobInstanceEntity.java @@ -119,13 +119,13 @@ public class Batch2JobInstanceEntity implements Serializable { @Column(name = "WORK_CHUNKS_PURGED", nullable = false) private boolean myWorkChunksPurged; - @Column(name = "PROGRESS_PCT") + @Column(name = "PROGRESS_PCT", nullable = false) private double myProgress; @Column(name = "ERROR_MSG", length = ERROR_MSG_MAX_LENGTH, nullable = true) private String myErrorMessage; - @Column(name = "ERROR_COUNT") + @Column(name = "ERROR_COUNT", nullable = false) private int myErrorCount; @Column(name = "EST_REMAINING", length = TIME_REMAINING_LENGTH, nullable = true) diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/entity/Batch2WorkChunkEntity.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/entity/Batch2WorkChunkEntity.java index db60706f476..26bccd58bf9 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/entity/Batch2WorkChunkEntity.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/entity/Batch2WorkChunkEntity.java @@ -138,6 +138,10 @@ public class Batch2WorkChunkEntity implements Serializable { /** * The number of times the work chunk has had its state set back to POLL_WAITING. + *

+ * TODO: Note that this column was added in 7.2.0, so it is nullable in order to + * account for existing rows that were added before the column was added. In + * the future we should make this non-null. */ @Column(name = "POLL_ATTEMPTS", nullable = true) private Integer myPollAttempts; @@ -145,7 +149,9 @@ public class Batch2WorkChunkEntity implements Serializable { /** * Default constructor for Hibernate. */ - public Batch2WorkChunkEntity() {} + public Batch2WorkChunkEntity() { + myPollAttempts = 0; + } /** * Projection constructor for no-data path. @@ -184,7 +190,7 @@ public class Batch2WorkChunkEntity implements Serializable { myRecordsProcessed = theRecordsProcessed; myWarningMessage = theWarningMessage; myNextPollTime = theNextPollTime; - myPollAttempts = thePollAttempts; + myPollAttempts = thePollAttempts != null ? thePollAttempts : 0; } public static Batch2WorkChunkEntity fromWorkChunk(WorkChunk theWorkChunk) { diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/entity/HapiFhirEnversRevision.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/entity/HapiFhirEnversRevision.java index 3a930be0e27..68c7ac12f7e 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/entity/HapiFhirEnversRevision.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/entity/HapiFhirEnversRevision.java @@ -58,7 +58,7 @@ public class HapiFhirEnversRevision implements Serializable { @SequenceGenerator(name = "SEQ_HFJ_REVINFO", sequenceName = "SEQ_HFJ_REVINFO") @GeneratedValue(strategy = GenerationType.AUTO, generator = "SEQ_HFJ_REVINFO") @RevisionNumber - @Column(name = "REV") + @Column(name = "REV", nullable = false) private long myRev; @RevisionTimestamp diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/entity/TermConcept.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/entity/TermConcept.java index d238278bcfe..5f056979fea 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/entity/TermConcept.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/entity/TermConcept.java @@ -117,11 +117,12 @@ public class TermConcept implements Serializable { @ManyToOne(fetch = FetchType.LAZY) @JoinColumn( name = "CODESYSTEM_PID", + nullable = false, referencedColumnName = "PID", foreignKey = @ForeignKey(name = "FK_CONCEPT_PID_CS_PID")) private TermCodeSystemVersion myCodeSystem; - @Column(name = "CODESYSTEM_PID", insertable = false, updatable = false) + @Column(name = "CODESYSTEM_PID", insertable = false, updatable = false, nullable = false) @GenericField(name = "myCodeSystemVersionPid") private long myCodeSystemVersionPid; diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/migrate/tasks/HapiFhirJpaMigrationTasks.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/migrate/tasks/HapiFhirJpaMigrationTasks.java index 0db112c60fd..7bcebb19fda 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/migrate/tasks/HapiFhirJpaMigrationTasks.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/migrate/tasks/HapiFhirJpaMigrationTasks.java @@ -25,7 +25,6 @@ import ca.uhn.fhir.jpa.entity.BulkImportJobEntity; import ca.uhn.fhir.jpa.entity.Search; import ca.uhn.fhir.jpa.migrate.DriverTypeEnum; import ca.uhn.fhir.jpa.migrate.taskdef.ArbitrarySqlTask; -import ca.uhn.fhir.jpa.migrate.taskdef.BaseTask; import ca.uhn.fhir.jpa.migrate.taskdef.CalculateHashesTask; import ca.uhn.fhir.jpa.migrate.taskdef.CalculateOrdinalDatesTask; import ca.uhn.fhir.jpa.migrate.taskdef.ColumnTypeEnum; @@ -33,6 +32,7 @@ import ca.uhn.fhir.jpa.migrate.taskdef.ForceIdMigrationCopyTask; import ca.uhn.fhir.jpa.migrate.taskdef.ForceIdMigrationFixTask; import ca.uhn.fhir.jpa.migrate.tasks.api.BaseMigrationTasks; import ca.uhn.fhir.jpa.migrate.tasks.api.Builder; +import ca.uhn.fhir.jpa.migrate.tasks.api.TaskFlagEnum; import ca.uhn.fhir.jpa.model.config.PartitionSettings; import ca.uhn.fhir.jpa.model.entity.BaseResourceIndexedSearchParam; import ca.uhn.fhir.jpa.model.entity.ResourceHistoryTable; @@ -135,6 +135,121 @@ public class HapiFhirJpaMigrationTasks extends BaseMigrationTasks { .toColumn("RES_ID") .references("HFJ_RESOURCE", "RES_ID"); } + + /* + * Make a bunch of columns non-nullable. Note that we won't actually apply this migration + * on the live system as it would take a loooooong time to execute these on heavily loaded + * databases. + */ + // Skipping numbers 20240601.1 and 20240601.2 as they were found to not + // be needed during code review. + version.onTable("HFJ_RESOURCE") + .modifyColumn("20240601.3", "SP_HAS_LINKS") + .nonNullable() + .withType(ColumnTypeEnum.BOOLEAN) + .heavyweightSkipByDefault() + .failureAllowed(); + version.onTable("HFJ_RESOURCE") + .modifyColumn("20240601.4", "SP_COORDS_PRESENT") + .nonNullable() + .withType(ColumnTypeEnum.BOOLEAN) + .heavyweightSkipByDefault() + .failureAllowed(); + version.onTable("HFJ_RESOURCE") + .modifyColumn("20240601.5", "SP_DATE_PRESENT") + .nonNullable() + .withType(ColumnTypeEnum.BOOLEAN) + .heavyweightSkipByDefault() + .failureAllowed(); + version.onTable("HFJ_RESOURCE") + .modifyColumn("20240601.6", "SP_NUMBER_PRESENT") + .nonNullable() + .withType(ColumnTypeEnum.BOOLEAN) + .heavyweightSkipByDefault() + .failureAllowed(); + version.onTable("HFJ_RESOURCE") + .modifyColumn("20240601.7", "SP_QUANTITY_PRESENT") + .nonNullable() + .withType(ColumnTypeEnum.BOOLEAN) + .heavyweightSkipByDefault() + .failureAllowed(); + version.onTable("HFJ_RESOURCE") + .modifyColumn("20240601.8", "SP_QUANTITY_NRML_PRESENT") + .nonNullable() + .withType(ColumnTypeEnum.BOOLEAN) + .heavyweightSkipByDefault() + .failureAllowed(); + version.onTable("HFJ_RESOURCE") + .modifyColumn("20240601.9", "SP_STRING_PRESENT") + .nonNullable() + .withType(ColumnTypeEnum.BOOLEAN) + .heavyweightSkipByDefault() + .failureAllowed(); + version.onTable("HFJ_RESOURCE") + .modifyColumn("20240601.10", "SP_TOKEN_PRESENT") + .nonNullable() + .withType(ColumnTypeEnum.BOOLEAN) + .heavyweightSkipByDefault() + .failureAllowed(); + version.onTable("HFJ_RESOURCE") + .modifyColumn("20240601.11", "SP_URI_PRESENT") + .nonNullable() + .withType(ColumnTypeEnum.BOOLEAN) + .heavyweightSkipByDefault() + .failureAllowed(); + version.onTable("HFJ_RESOURCE") + .modifyColumn("20240601.12", "RES_VER") + .nonNullable() + .withType(ColumnTypeEnum.LONG) + .heavyweightSkipByDefault() + .failureAllowed(); + version.onTable("TRM_CONCEPT") + .modifyColumn("20240601.13", "CODESYSTEM_PID") + .nonNullable() + .withType(ColumnTypeEnum.LONG) + .heavyweightSkipByDefault() + .failureAllowed(); + version.onTable("BT2_JOB_INSTANCE") + .modifyColumn("20240601.14", "PROGRESS_PCT") + .nonNullable() + .withType(ColumnTypeEnum.DOUBLE) + .heavyweightSkipByDefault() + .failureAllowed(); + version.onTable("BT2_JOB_INSTANCE") + .modifyColumn("20240601.15", "ERROR_COUNT") + .nonNullable() + .withType(ColumnTypeEnum.INT) + .heavyweightSkipByDefault() + .failureAllowed(); + version.onTable("HFJ_BINARY_STORAGE_BLOB") + .modifyColumn("20240601.16", "BLOB_SIZE") + .nonNullable() + .withType(ColumnTypeEnum.LONG) + .heavyweightSkipByDefault() + .failureAllowed(); + + /* + * Add RES_ID to two indexes on HFJ_RES_VER which support history operations. + * This makes server and type level _history work properly on large databases + * on postgres. These are both marked as heavyweightSkipByDefault because the + * necessary reindexing would be very expensive for a rarely used FHIR feature. + */ + version.onTable("HFJ_RES_VER") + .dropIndex("20240601.17", "IDX_RESVER_TYPE_DATE") + .heavyweightSkipByDefault(); + version.onTable("HFJ_RES_VER") + .addIndex("20240601.18", "IDX_RESVER_TYPE_DATE") + .unique(false) + .withColumns("RES_TYPE", "RES_UPDATED", "RES_ID") + .heavyweightSkipByDefault(); + version.onTable("HFJ_RES_VER") + .dropIndex("20240601.19", "IDX_RESVER_DATE") + .heavyweightSkipByDefault(); + version.onTable("HFJ_RES_VER") + .addIndex("20240601.20", "IDX_RESVER_DATE") + .unique(false) + .withColumns("RES_UPDATED", "RES_ID") + .heavyweightSkipByDefault(); } protected void init720() { @@ -162,15 +277,15 @@ public class HapiFhirJpaMigrationTasks extends BaseMigrationTasks { binaryStorageBlobTable .renameColumn("20240404.1", "BLOB_ID", "CONTENT_ID") .getLastAddedTask() - .ifPresent(BaseTask::doNothing); + .ifPresent(t -> t.addFlag(TaskFlagEnum.DO_NOTHING)); binaryStorageBlobTable .renameColumn("20240404.2", "BLOB_SIZE", "CONTENT_SIZE") .getLastAddedTask() - .ifPresent(BaseTask::doNothing); + .ifPresent(t -> t.addFlag(TaskFlagEnum.DO_NOTHING)); binaryStorageBlobTable .renameColumn("20240404.3", "BLOB_HASH", "CONTENT_HASH") .getLastAddedTask() - .ifPresent(BaseTask::doNothing); + .ifPresent(t -> t.addFlag(TaskFlagEnum.DO_NOTHING)); binaryStorageBlobTable .modifyColumn("20240404.4", "BLOB_DATA") @@ -262,7 +377,8 @@ public class HapiFhirJpaMigrationTasks extends BaseMigrationTasks { // Move forced_id constraints to hfj_resource and the new fhir_id column // Note: we leave the HFJ_FORCED_ID.IDX_FORCEDID_TYPE_FID index in place to support old writers for a while. - version.addTask(new ForceIdMigrationCopyTask(version.getRelease(), "20231018.1").setDoNothing(true)); + version.addTask( + new ForceIdMigrationCopyTask(version.getRelease(), "20231018.1").addFlag(TaskFlagEnum.DO_NOTHING)); Builder.BuilderWithTableName hfjResource = version.onTable("HFJ_RESOURCE"); // commented out to make numeric space for the fix task below. @@ -331,7 +447,8 @@ public class HapiFhirJpaMigrationTasks extends BaseMigrationTasks { } // This fix was bad for MSSQL, it has been set to do nothing. - version.addTask(new ForceIdMigrationFixTask(version.getRelease(), "20231213.1").setDoNothing(true)); + version.addTask( + new ForceIdMigrationFixTask(version.getRelease(), "20231213.1").addFlag(TaskFlagEnum.DO_NOTHING)); // This fix will work for MSSQL or Oracle. version.addTask(new ForceIdMigrationFixTask(version.getRelease(), "20231222.1")); @@ -814,8 +931,8 @@ public class HapiFhirJpaMigrationTasks extends BaseMigrationTasks { version.onTable("HFJ_RES_VER") .modifyColumn("20230421.1", "RES_TEXT_VC") .nullable() - .failureAllowed() - .withType(ColumnTypeEnum.TEXT); + .withType(ColumnTypeEnum.TEXT) + .failureAllowed(); { // add hash_norm to res_id to speed up joins on a second string. @@ -1751,8 +1868,8 @@ public class HapiFhirJpaMigrationTasks extends BaseMigrationTasks { version.onTable("HFJ_RES_LINK") .modifyColumn("20210505.1", "SRC_PATH") .nonNullable() - .failureAllowed() - .withType(ColumnTypeEnum.STRING, 500); + .withType(ColumnTypeEnum.STRING, 500) + .failureAllowed(); } private void init530() { @@ -1813,8 +1930,8 @@ public class HapiFhirJpaMigrationTasks extends BaseMigrationTasks { quantityTable .modifyColumn("20210116.1", "SP_VALUE") .nullable() - .failureAllowed() - .withType(ColumnTypeEnum.DOUBLE); + .withType(ColumnTypeEnum.DOUBLE) + .failureAllowed(); // HFJ_RES_LINK version.onTable("HFJ_RES_LINK") @@ -2011,8 +2128,8 @@ public class HapiFhirJpaMigrationTasks extends BaseMigrationTasks { version.onTable("HFJ_RES_VER") .modifyColumn("20200220.1", "RES_ID") .nonNullable() - .failureAllowed() - .withType(ColumnTypeEnum.LONG); + .withType(ColumnTypeEnum.LONG) + .failureAllowed(); // // Drop unused column @@ -2168,38 +2285,38 @@ public class HapiFhirJpaMigrationTasks extends BaseMigrationTasks { version.onTable("HFJ_SPIDX_STRING") .modifyColumn("20200420.36", "SP_MISSING") .nonNullable() - .failureAllowed() - .withType(ColumnTypeEnum.BOOLEAN); + .withType(ColumnTypeEnum.BOOLEAN) + .failureAllowed(); version.onTable("HFJ_SPIDX_COORDS") .modifyColumn("20200420.37", "SP_MISSING") .nonNullable() - .failureAllowed() - .withType(ColumnTypeEnum.BOOLEAN); + .withType(ColumnTypeEnum.BOOLEAN) + .failureAllowed(); version.onTable("HFJ_SPIDX_NUMBER") .modifyColumn("20200420.38", "SP_MISSING") .nonNullable() - .failureAllowed() - .withType(ColumnTypeEnum.BOOLEAN); + .withType(ColumnTypeEnum.BOOLEAN) + .failureAllowed(); version.onTable("HFJ_SPIDX_TOKEN") .modifyColumn("20200420.39", "SP_MISSING") .nonNullable() - .failureAllowed() - .withType(ColumnTypeEnum.BOOLEAN); + .withType(ColumnTypeEnum.BOOLEAN) + .failureAllowed(); version.onTable("HFJ_SPIDX_DATE") .modifyColumn("20200420.40", "SP_MISSING") .nonNullable() - .failureAllowed() - .withType(ColumnTypeEnum.BOOLEAN); + .withType(ColumnTypeEnum.BOOLEAN) + .failureAllowed(); version.onTable("HFJ_SPIDX_URI") .modifyColumn("20200420.41", "SP_MISSING") .nonNullable() - .failureAllowed() - .withType(ColumnTypeEnum.BOOLEAN); + .withType(ColumnTypeEnum.BOOLEAN) + .failureAllowed(); version.onTable("HFJ_SPIDX_QUANTITY") .modifyColumn("20200420.42", "SP_MISSING") .nonNullable() - .failureAllowed() - .withType(ColumnTypeEnum.BOOLEAN); + .withType(ColumnTypeEnum.BOOLEAN) + .failureAllowed(); // Add support for integer comparisons during day-precision date search. Builder.BuilderWithTableName spidxDate = version.onTable("HFJ_SPIDX_DATE"); @@ -2309,38 +2426,38 @@ public class HapiFhirJpaMigrationTasks extends BaseMigrationTasks { version.onTable("HFJ_SPIDX_NUMBER") .modifyColumn("20190920.1", "RES_ID") .nonNullable() - .failureAllowed() - .withType(ColumnTypeEnum.LONG); + .withType(ColumnTypeEnum.LONG) + .failureAllowed(); version.onTable("HFJ_SPIDX_COORDS") .modifyColumn("20190920.2", "RES_ID") .nonNullable() - .failureAllowed() - .withType(ColumnTypeEnum.LONG); + .withType(ColumnTypeEnum.LONG) + .failureAllowed(); version.onTable("HFJ_SPIDX_TOKEN") .modifyColumn("20190920.3", "RES_ID") .nonNullable() - .failureAllowed() - .withType(ColumnTypeEnum.LONG); + .withType(ColumnTypeEnum.LONG) + .failureAllowed(); version.onTable("HFJ_SPIDX_STRING") .modifyColumn("20190920.4", "RES_ID") .nonNullable() - .failureAllowed() - .withType(ColumnTypeEnum.LONG); + .withType(ColumnTypeEnum.LONG) + .failureAllowed(); version.onTable("HFJ_SPIDX_DATE") .modifyColumn("20190920.5", "RES_ID") .nonNullable() - .failureAllowed() - .withType(ColumnTypeEnum.LONG); + .withType(ColumnTypeEnum.LONG) + .failureAllowed(); version.onTable("HFJ_SPIDX_QUANTITY") .modifyColumn("20190920.6", "RES_ID") .nonNullable() - .failureAllowed() - .withType(ColumnTypeEnum.LONG); + .withType(ColumnTypeEnum.LONG) + .failureAllowed(); version.onTable("HFJ_SPIDX_URI") .modifyColumn("20190920.7", "RES_ID") .nonNullable() - .failureAllowed() - .withType(ColumnTypeEnum.LONG); + .withType(ColumnTypeEnum.LONG) + .failureAllowed(); // HFJ_SEARCH version.onTable("HFJ_SEARCH") @@ -2469,33 +2586,33 @@ public class HapiFhirJpaMigrationTasks extends BaseMigrationTasks { version.onTable("HFJ_RESOURCE") .modifyColumn("20191002.1", "RES_TYPE") .nonNullable() - .failureAllowed() - .withType(ColumnTypeEnum.STRING, 40); + .withType(ColumnTypeEnum.STRING, 40) + .failureAllowed(); version.onTable("HFJ_RES_VER") .modifyColumn("20191002.2", "RES_TYPE") .nonNullable() - .failureAllowed() - .withType(ColumnTypeEnum.STRING, 40); + .withType(ColumnTypeEnum.STRING, 40) + .failureAllowed(); version.onTable("HFJ_HISTORY_TAG") .modifyColumn("20191002.3", "RES_TYPE") .nonNullable() - .failureAllowed() - .withType(ColumnTypeEnum.STRING, 40); + .withType(ColumnTypeEnum.STRING, 40) + .failureAllowed(); version.onTable("HFJ_RES_LINK") .modifyColumn("20191002.4", "SOURCE_RESOURCE_TYPE") .nonNullable() - .failureAllowed() - .withType(ColumnTypeEnum.STRING, 40); + .withType(ColumnTypeEnum.STRING, 40) + .failureAllowed(); version.onTable("HFJ_RES_LINK") .modifyColumn("20191002.5", "TARGET_RESOURCE_TYPE") .nonNullable() - .failureAllowed() - .withType(ColumnTypeEnum.STRING, 40); + .withType(ColumnTypeEnum.STRING, 40) + .failureAllowed(); version.onTable("HFJ_RES_TAG") .modifyColumn("20191002.6", "RES_TYPE") .nonNullable() - .failureAllowed() - .withType(ColumnTypeEnum.STRING, 40); + .withType(ColumnTypeEnum.STRING, 40) + .failureAllowed(); // TermConceptDesignation version.startSectionWithMessage("Processing table: TRM_CONCEPT_DESIG"); @@ -2765,18 +2882,18 @@ public class HapiFhirJpaMigrationTasks extends BaseMigrationTasks { version.onTable("HFJ_SPIDX_COORDS") .modifyColumn("20190814.9", "RES_TYPE") .nonNullable() - .failureAllowed() - .withType(ColumnTypeEnum.STRING, 100); + .withType(ColumnTypeEnum.STRING, 100) + .failureAllowed(); version.onTable("HFJ_SPIDX_DATE") .modifyColumn("20190814.10", "RES_TYPE") .nonNullable() - .failureAllowed() - .withType(ColumnTypeEnum.STRING, 100); + .withType(ColumnTypeEnum.STRING, 100) + .failureAllowed(); version.onTable("HFJ_SPIDX_STRING") .modifyColumn("20190814.11", "RES_TYPE") .nonNullable() - .failureAllowed() - .withType(ColumnTypeEnum.STRING, 100); + .withType(ColumnTypeEnum.STRING, 100) + .failureAllowed(); version.onTable("HFJ_SPIDX_STRING") .addColumn("20190814.12", "HASH_IDENTITY") .nullable() @@ -2788,50 +2905,50 @@ public class HapiFhirJpaMigrationTasks extends BaseMigrationTasks { version.onTable("HFJ_SPIDX_COORDS") .modifyColumn("20190814.14", "RES_TYPE") .nonNullable() - .failureAllowed() - .withType(ColumnTypeEnum.STRING, 100); + .withType(ColumnTypeEnum.STRING, 100) + .failureAllowed(); version.onTable("HFJ_SPIDX_QUANTITY") .modifyColumn("20190814.15", "RES_TYPE") .nonNullable() - .failureAllowed() - .withType(ColumnTypeEnum.STRING, 100); + .withType(ColumnTypeEnum.STRING, 100) + .failureAllowed(); version.onTable("HFJ_SPIDX_QUANTITY").dropColumn("20190814.16", "HASH_UNITS_AND_VALPREFIX"); version.onTable("HFJ_SPIDX_QUANTITY").dropColumn("20190814.17", "HASH_VALPREFIX"); version.onTable("HFJ_SPIDX_NUMBER") .modifyColumn("20190814.18", "RES_TYPE") .nonNullable() - .failureAllowed() - .withType(ColumnTypeEnum.STRING, 100); + .withType(ColumnTypeEnum.STRING, 100) + .failureAllowed(); version.onTable("HFJ_SPIDX_TOKEN") .modifyColumn("20190814.19", "RES_TYPE") .nonNullable() - .failureAllowed() - .withType(ColumnTypeEnum.STRING, 100); + .withType(ColumnTypeEnum.STRING, 100) + .failureAllowed(); version.onTable("HFJ_SPIDX_URI") .modifyColumn("20190814.20", "RES_TYPE") .nonNullable() - .failureAllowed() - .withType(ColumnTypeEnum.STRING, 100); + .withType(ColumnTypeEnum.STRING, 100) + .failureAllowed(); version.onTable("HFJ_SPIDX_URI") .modifyColumn("20190814.21", "SP_URI") .nullable() - .failureAllowed() - .withType(ColumnTypeEnum.STRING, 254); + .withType(ColumnTypeEnum.STRING, 254) + .failureAllowed(); version.onTable("TRM_CODESYSTEM") .modifyColumn("20190814.22", "CODE_SYSTEM_URI") .nonNullable() - .failureAllowed() - .withType(ColumnTypeEnum.STRING, 200); + .withType(ColumnTypeEnum.STRING, 200) + .failureAllowed(); version.onTable("TRM_CODESYSTEM") .modifyColumn("20190814.23", "CS_NAME") .nullable() - .failureAllowed() - .withType(ColumnTypeEnum.STRING, 200); + .withType(ColumnTypeEnum.STRING, 200) + .failureAllowed(); version.onTable("TRM_CODESYSTEM_VER") .modifyColumn("20190814.24", "CS_VERSION_ID") .nullable() - .failureAllowed() - .withType(ColumnTypeEnum.STRING, 200); + .withType(ColumnTypeEnum.STRING, 200) + .failureAllowed(); } private void init360() { // 20180918 - 20181112 diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/search/reindex/InstanceReindexServiceImpl.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/search/reindex/InstanceReindexServiceImpl.java index 8a69d6ef8e0..aeb8b9e31cd 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/search/reindex/InstanceReindexServiceImpl.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/search/reindex/InstanceReindexServiceImpl.java @@ -524,8 +524,12 @@ public class InstanceReindexServiceImpl implements IInstanceReindexService { String theParamTypeName) { Parameters.ParametersParameterComponent retVal = super.addIndexValue(theAction, theParent, theParam, theParamTypeName); - retVal.addPart().setName("Latitude").setValue(new DecimalType(theParam.getLatitude())); - retVal.addPart().setName("Longitude").setValue(new DecimalType(theParam.getLongitude())); + if (theParam.getLatitude() != null) { + retVal.addPart().setName("Latitude").setValue(new DecimalType(theParam.getLatitude())); + } + if (theParam.getLongitude() != null) { + retVal.addPart().setName("Longitude").setValue(new DecimalType(theParam.getLongitude())); + } return retVal; } } diff --git a/hapi-fhir-jpaserver-elastic-test-utilities/pom.xml b/hapi-fhir-jpaserver-elastic-test-utilities/pom.xml index e3ba27996ee..f0f55e9506f 100644 --- a/hapi-fhir-jpaserver-elastic-test-utilities/pom.xml +++ b/hapi-fhir-jpaserver-elastic-test-utilities/pom.xml @@ -6,7 +6,7 @@ ca.uhn.hapi.fhir hapi-deployable-pom - 7.3.7-SNAPSHOT + 7.3.8-SNAPSHOT ../hapi-deployable-pom/pom.xml diff --git a/hapi-fhir-jpaserver-hfql/pom.xml b/hapi-fhir-jpaserver-hfql/pom.xml index 2ae9ad02678..aaa57b40118 100644 --- a/hapi-fhir-jpaserver-hfql/pom.xml +++ b/hapi-fhir-jpaserver-hfql/pom.xml @@ -3,7 +3,7 @@ ca.uhn.hapi.fhir hapi-deployable-pom - 7.3.7-SNAPSHOT + 7.3.8-SNAPSHOT ../hapi-deployable-pom/pom.xml diff --git a/hapi-fhir-jpaserver-ips/pom.xml b/hapi-fhir-jpaserver-ips/pom.xml index 88e32f7e2d6..2aea1bf2f19 100644 --- a/hapi-fhir-jpaserver-ips/pom.xml +++ b/hapi-fhir-jpaserver-ips/pom.xml @@ -3,7 +3,7 @@ ca.uhn.hapi.fhir hapi-deployable-pom - 7.3.7-SNAPSHOT + 7.3.8-SNAPSHOT ../hapi-deployable-pom/pom.xml diff --git a/hapi-fhir-jpaserver-mdm/pom.xml b/hapi-fhir-jpaserver-mdm/pom.xml index 669f52ea6ba..7a7c9f7ab64 100644 --- a/hapi-fhir-jpaserver-mdm/pom.xml +++ b/hapi-fhir-jpaserver-mdm/pom.xml @@ -6,7 +6,7 @@ ca.uhn.hapi.fhir hapi-deployable-pom - 7.3.7-SNAPSHOT + 7.3.8-SNAPSHOT ../hapi-deployable-pom/pom.xml diff --git a/hapi-fhir-jpaserver-model/pom.xml b/hapi-fhir-jpaserver-model/pom.xml index 1df82dac9f4..c453a3c30ab 100644 --- a/hapi-fhir-jpaserver-model/pom.xml +++ b/hapi-fhir-jpaserver-model/pom.xml @@ -5,7 +5,7 @@ ca.uhn.hapi.fhir hapi-deployable-pom - 7.3.7-SNAPSHOT + 7.3.8-SNAPSHOT ../hapi-deployable-pom/pom.xml diff --git a/hapi-fhir-jpaserver-model/src/main/java/ca/uhn/fhir/jpa/model/entity/BinaryStorageEntity.java b/hapi-fhir-jpaserver-model/src/main/java/ca/uhn/fhir/jpa/model/entity/BinaryStorageEntity.java index c6b046e0bff..f6d77040763 100644 --- a/hapi-fhir-jpaserver-model/src/main/java/ca/uhn/fhir/jpa/model/entity/BinaryStorageEntity.java +++ b/hapi-fhir-jpaserver-model/src/main/java/ca/uhn/fhir/jpa/model/entity/BinaryStorageEntity.java @@ -45,7 +45,7 @@ public class BinaryStorageEntity { @Column(name = "RESOURCE_ID", length = 100, nullable = false) private String myResourceId; - @Column(name = "BLOB_SIZE", nullable = true) + @Column(name = "BLOB_SIZE", nullable = false) private long mySize; @Column(name = "CONTENT_TYPE", nullable = false, length = 100) diff --git a/hapi-fhir-jpaserver-model/src/main/java/ca/uhn/fhir/jpa/model/entity/ResourceHistoryTable.java b/hapi-fhir-jpaserver-model/src/main/java/ca/uhn/fhir/jpa/model/entity/ResourceHistoryTable.java index 6348ce579b3..8ef37d8cdca 100644 --- a/hapi-fhir-jpaserver-model/src/main/java/ca/uhn/fhir/jpa/model/entity/ResourceHistoryTable.java +++ b/hapi-fhir-jpaserver-model/src/main/java/ca/uhn/fhir/jpa/model/entity/ResourceHistoryTable.java @@ -60,9 +60,9 @@ import java.util.Collection; columnNames = {"RES_ID", "RES_VER"}) }, indexes = { - @Index(name = "IDX_RESVER_TYPE_DATE", columnList = "RES_TYPE,RES_UPDATED"), + @Index(name = "IDX_RESVER_TYPE_DATE", columnList = "RES_TYPE,RES_UPDATED,RES_ID"), @Index(name = "IDX_RESVER_ID_DATE", columnList = "RES_ID,RES_UPDATED"), - @Index(name = "IDX_RESVER_DATE", columnList = "RES_UPDATED") + @Index(name = "IDX_RESVER_DATE", columnList = "RES_UPDATED,RES_ID") }) public class ResourceHistoryTable extends BaseHasResource implements Serializable { public static final String IDX_RESVER_ID_VER = "IDX_RESVER_ID_VER"; diff --git a/hapi-fhir-jpaserver-model/src/main/java/ca/uhn/fhir/jpa/model/entity/ResourceIndexedComboStringUnique.java b/hapi-fhir-jpaserver-model/src/main/java/ca/uhn/fhir/jpa/model/entity/ResourceIndexedComboStringUnique.java index 830fc6270aa..e61941cb611 100644 --- a/hapi-fhir-jpaserver-model/src/main/java/ca/uhn/fhir/jpa/model/entity/ResourceIndexedComboStringUnique.java +++ b/hapi-fhir-jpaserver-model/src/main/java/ca/uhn/fhir/jpa/model/entity/ResourceIndexedComboStringUnique.java @@ -135,12 +135,14 @@ public class ResourceIndexedComboStringUnique extends BasePartitionable myIndexString = theIndexString; } + @Override public ResourceTable getResource() { return myResource; } + @Override public void setResource(ResourceTable theResource) { - Validate.notNull(theResource); + Validate.notNull(theResource, "theResource must not be null"); myResource = theResource; } diff --git a/hapi-fhir-jpaserver-model/src/main/java/ca/uhn/fhir/jpa/model/entity/ResourceIndexedSearchParamCoords.java b/hapi-fhir-jpaserver-model/src/main/java/ca/uhn/fhir/jpa/model/entity/ResourceIndexedSearchParamCoords.java index 40718b09df8..a66e5f6f564 100644 --- a/hapi-fhir-jpaserver-model/src/main/java/ca/uhn/fhir/jpa/model/entity/ResourceIndexedSearchParamCoords.java +++ b/hapi-fhir-jpaserver-model/src/main/java/ca/uhn/fhir/jpa/model/entity/ResourceIndexedSearchParamCoords.java @@ -21,6 +21,7 @@ package ca.uhn.fhir.jpa.model.entity; import ca.uhn.fhir.jpa.model.config.PartitionSettings; import ca.uhn.fhir.model.api.IQueryParameterType; +import jakarta.annotation.Nullable; import jakarta.persistence.Column; import jakarta.persistence.Embeddable; import jakarta.persistence.Entity; @@ -56,13 +57,11 @@ public class ResourceIndexedSearchParamCoords extends BaseResourceIndexedSearchP private static final long serialVersionUID = 1L; - @Column(name = "SP_LATITUDE") - // @FullTextField - public double myLatitude; + @Column(name = "SP_LATITUDE", nullable = true) + public Double myLatitude; - @Column(name = "SP_LONGITUDE") - // @FullTextField - public double myLongitude; + @Column(name = "SP_LONGITUDE", nullable = true) + public Double myLongitude; @Id @SequenceGenerator(name = "SEQ_SPIDX_COORDS", sequenceName = "SEQ_SPIDX_COORDS") @@ -162,7 +161,8 @@ public class ResourceIndexedSearchParamCoords extends BaseResourceIndexedSearchP myId = theId; } - public double getLatitude() { + @Nullable + public Double getLatitude() { return myLatitude; } @@ -171,7 +171,8 @@ public class ResourceIndexedSearchParamCoords extends BaseResourceIndexedSearchP return this; } - public double getLongitude() { + @Nullable + public Double getLongitude() { return myLongitude; } diff --git a/hapi-fhir-jpaserver-model/src/main/java/ca/uhn/fhir/jpa/model/entity/ResourceTable.java b/hapi-fhir-jpaserver-model/src/main/java/ca/uhn/fhir/jpa/model/entity/ResourceTable.java index 39f85198aec..e897ae71786 100644 --- a/hapi-fhir-jpaserver-model/src/main/java/ca/uhn/fhir/jpa/model/entity/ResourceTable.java +++ b/hapi-fhir-jpaserver-model/src/main/java/ca/uhn/fhir/jpa/model/entity/ResourceTable.java @@ -142,7 +142,7 @@ public class ResourceTable extends BaseHasResource implements Serializable, IBas @OptimisticLock(excluded = true) private String myHashSha256; - @Column(name = "SP_HAS_LINKS") + @Column(name = "SP_HAS_LINKS", nullable = false) @OptimisticLock(excluded = true) private boolean myHasLinks; @@ -203,7 +203,7 @@ public class ResourceTable extends BaseHasResource implements Serializable, IBas @OptimisticLock(excluded = true) private Collection myParamsCoords; - @Column(name = "SP_COORDS_PRESENT") + @Column(name = "SP_COORDS_PRESENT", nullable = false) @OptimisticLock(excluded = true) private boolean myParamsCoordsPopulated; @@ -215,7 +215,7 @@ public class ResourceTable extends BaseHasResource implements Serializable, IBas @OptimisticLock(excluded = true) private Collection myParamsDate; - @Column(name = "SP_DATE_PRESENT") + @Column(name = "SP_DATE_PRESENT", nullable = false) @OptimisticLock(excluded = true) private boolean myParamsDatePopulated; @@ -227,7 +227,7 @@ public class ResourceTable extends BaseHasResource implements Serializable, IBas orphanRemoval = false) private Collection myParamsNumber; - @Column(name = "SP_NUMBER_PRESENT") + @Column(name = "SP_NUMBER_PRESENT", nullable = false) @OptimisticLock(excluded = true) private boolean myParamsNumberPopulated; @@ -239,7 +239,7 @@ public class ResourceTable extends BaseHasResource implements Serializable, IBas @OptimisticLock(excluded = true) private Collection myParamsQuantity; - @Column(name = "SP_QUANTITY_PRESENT") + @Column(name = "SP_QUANTITY_PRESENT", nullable = false) @OptimisticLock(excluded = true) private boolean myParamsQuantityPopulated; @@ -260,7 +260,7 @@ public class ResourceTable extends BaseHasResource implements Serializable, IBas * NOTE : use Boolean class instead of boolean primitive, in order to set the existing rows to null * since 5.3.0 */ - @Column(name = "SP_QUANTITY_NRML_PRESENT") + @Column(name = "SP_QUANTITY_NRML_PRESENT", nullable = false) @OptimisticLock(excluded = true) private Boolean myParamsQuantityNormalizedPopulated = Boolean.FALSE; @@ -272,7 +272,7 @@ public class ResourceTable extends BaseHasResource implements Serializable, IBas @OptimisticLock(excluded = true) private Collection myParamsString; - @Column(name = "SP_STRING_PRESENT") + @Column(name = "SP_STRING_PRESENT", nullable = false) @OptimisticLock(excluded = true) private boolean myParamsStringPopulated; @@ -284,7 +284,7 @@ public class ResourceTable extends BaseHasResource implements Serializable, IBas @OptimisticLock(excluded = true) private Collection myParamsToken; - @Column(name = "SP_TOKEN_PRESENT") + @Column(name = "SP_TOKEN_PRESENT", nullable = false) @OptimisticLock(excluded = true) private boolean myParamsTokenPopulated; @@ -296,7 +296,7 @@ public class ResourceTable extends BaseHasResource implements Serializable, IBas @OptimisticLock(excluded = true) private Collection myParamsUri; - @Column(name = "SP_URI_PRESENT") + @Column(name = "SP_URI_PRESENT", nullable = false) @OptimisticLock(excluded = true) private boolean myParamsUriPopulated; @@ -404,7 +404,7 @@ public class ResourceTable extends BaseHasResource implements Serializable, IBas private Boolean mySearchUrlPresent = false; @Version - @Column(name = "RES_VER") + @Column(name = "RES_VER", nullable = false) private long myVersion; @OneToMany(mappedBy = "myResourceTable", fetch = FetchType.LAZY) diff --git a/hapi-fhir-jpaserver-searchparam/pom.xml b/hapi-fhir-jpaserver-searchparam/pom.xml index 675432baedc..7199a9ad0bc 100755 --- a/hapi-fhir-jpaserver-searchparam/pom.xml +++ b/hapi-fhir-jpaserver-searchparam/pom.xml @@ -5,7 +5,7 @@ ca.uhn.hapi.fhir hapi-deployable-pom - 7.3.7-SNAPSHOT + 7.3.8-SNAPSHOT ../hapi-deployable-pom/pom.xml diff --git a/hapi-fhir-jpaserver-subscription/pom.xml b/hapi-fhir-jpaserver-subscription/pom.xml index 928e4754d88..3840df8a715 100644 --- a/hapi-fhir-jpaserver-subscription/pom.xml +++ b/hapi-fhir-jpaserver-subscription/pom.xml @@ -5,7 +5,7 @@ ca.uhn.hapi.fhir hapi-deployable-pom - 7.3.7-SNAPSHOT + 7.3.8-SNAPSHOT ../hapi-deployable-pom/pom.xml diff --git a/hapi-fhir-jpaserver-test-dstu2/pom.xml b/hapi-fhir-jpaserver-test-dstu2/pom.xml index 6d526c2967a..ed44b54b06d 100644 --- a/hapi-fhir-jpaserver-test-dstu2/pom.xml +++ b/hapi-fhir-jpaserver-test-dstu2/pom.xml @@ -6,7 +6,7 @@ ca.uhn.hapi.fhir hapi-deployable-pom - 7.3.7-SNAPSHOT + 7.3.8-SNAPSHOT ../hapi-deployable-pom/pom.xml diff --git a/hapi-fhir-jpaserver-test-dstu3/pom.xml b/hapi-fhir-jpaserver-test-dstu3/pom.xml index 75747b68a7f..ebf988b81f9 100644 --- a/hapi-fhir-jpaserver-test-dstu3/pom.xml +++ b/hapi-fhir-jpaserver-test-dstu3/pom.xml @@ -6,7 +6,7 @@ ca.uhn.hapi.fhir hapi-deployable-pom - 7.3.7-SNAPSHOT + 7.3.8-SNAPSHOT ../hapi-deployable-pom/pom.xml diff --git a/hapi-fhir-jpaserver-test-r4/pom.xml b/hapi-fhir-jpaserver-test-r4/pom.xml index 9ac14b62183..a05fcda4c4b 100644 --- a/hapi-fhir-jpaserver-test-r4/pom.xml +++ b/hapi-fhir-jpaserver-test-r4/pom.xml @@ -6,7 +6,7 @@ ca.uhn.hapi.fhir hapi-deployable-pom - 7.3.7-SNAPSHOT + 7.3.8-SNAPSHOT ../hapi-deployable-pom/pom.xml diff --git a/hapi-fhir-jpaserver-test-r4b/pom.xml b/hapi-fhir-jpaserver-test-r4b/pom.xml index 75d284dddda..5ad8360e422 100644 --- a/hapi-fhir-jpaserver-test-r4b/pom.xml +++ b/hapi-fhir-jpaserver-test-r4b/pom.xml @@ -6,7 +6,7 @@ ca.uhn.hapi.fhir hapi-deployable-pom - 7.3.7-SNAPSHOT + 7.3.8-SNAPSHOT ../hapi-deployable-pom/pom.xml diff --git a/hapi-fhir-jpaserver-test-r5/pom.xml b/hapi-fhir-jpaserver-test-r5/pom.xml index d8ed3a11ea3..0585c6f87aa 100644 --- a/hapi-fhir-jpaserver-test-r5/pom.xml +++ b/hapi-fhir-jpaserver-test-r5/pom.xml @@ -6,7 +6,7 @@ ca.uhn.hapi.fhir hapi-deployable-pom - 7.3.7-SNAPSHOT + 7.3.8-SNAPSHOT ../hapi-deployable-pom/pom.xml diff --git a/hapi-fhir-jpaserver-test-utilities/pom.xml b/hapi-fhir-jpaserver-test-utilities/pom.xml index 323a1e00051..ea643589d1b 100644 --- a/hapi-fhir-jpaserver-test-utilities/pom.xml +++ b/hapi-fhir-jpaserver-test-utilities/pom.xml @@ -6,7 +6,7 @@ ca.uhn.hapi.fhir hapi-deployable-pom - 7.3.7-SNAPSHOT + 7.3.8-SNAPSHOT ../hapi-deployable-pom/pom.xml diff --git a/hapi-fhir-jpaserver-uhnfhirtest/pom.xml b/hapi-fhir-jpaserver-uhnfhirtest/pom.xml index da796517f5b..174a429705a 100644 --- a/hapi-fhir-jpaserver-uhnfhirtest/pom.xml +++ b/hapi-fhir-jpaserver-uhnfhirtest/pom.xml @@ -5,7 +5,7 @@ ca.uhn.hapi.fhir hapi-fhir - 7.3.7-SNAPSHOT + 7.3.8-SNAPSHOT ../pom.xml diff --git a/hapi-fhir-server-cds-hooks/pom.xml b/hapi-fhir-server-cds-hooks/pom.xml index 3bef5a5c5df..adbe430ad0f 100644 --- a/hapi-fhir-server-cds-hooks/pom.xml +++ b/hapi-fhir-server-cds-hooks/pom.xml @@ -7,7 +7,7 @@ ca.uhn.hapi.fhir hapi-deployable-pom - 7.3.7-SNAPSHOT + 7.3.8-SNAPSHOT ../hapi-deployable-pom/pom.xml diff --git a/hapi-fhir-server-mdm/pom.xml b/hapi-fhir-server-mdm/pom.xml index bddca577b5b..b7e48476ad6 100644 --- a/hapi-fhir-server-mdm/pom.xml +++ b/hapi-fhir-server-mdm/pom.xml @@ -7,7 +7,7 @@ ca.uhn.hapi.fhir hapi-deployable-pom - 7.3.7-SNAPSHOT + 7.3.8-SNAPSHOT ../hapi-deployable-pom/pom.xml diff --git a/hapi-fhir-server-openapi/pom.xml b/hapi-fhir-server-openapi/pom.xml index c1f03e6eca8..c9f0f0a14c7 100644 --- a/hapi-fhir-server-openapi/pom.xml +++ b/hapi-fhir-server-openapi/pom.xml @@ -5,7 +5,7 @@ ca.uhn.hapi.fhir hapi-deployable-pom - 7.3.7-SNAPSHOT + 7.3.8-SNAPSHOT ../hapi-deployable-pom/pom.xml diff --git a/hapi-fhir-server/pom.xml b/hapi-fhir-server/pom.xml index bc844b33a29..84c16998ebc 100644 --- a/hapi-fhir-server/pom.xml +++ b/hapi-fhir-server/pom.xml @@ -5,7 +5,7 @@ ca.uhn.hapi.fhir hapi-deployable-pom - 7.3.7-SNAPSHOT + 7.3.8-SNAPSHOT ../hapi-deployable-pom/pom.xml diff --git a/hapi-fhir-serviceloaders/hapi-fhir-caching-api/pom.xml b/hapi-fhir-serviceloaders/hapi-fhir-caching-api/pom.xml index a4eb90d2992..288431d981c 100644 --- a/hapi-fhir-serviceloaders/hapi-fhir-caching-api/pom.xml +++ b/hapi-fhir-serviceloaders/hapi-fhir-caching-api/pom.xml @@ -7,7 +7,7 @@ hapi-fhir-serviceloaders ca.uhn.hapi.fhir - 7.3.7-SNAPSHOT + 7.3.8-SNAPSHOT ../pom.xml diff --git a/hapi-fhir-serviceloaders/hapi-fhir-caching-caffeine/pom.xml b/hapi-fhir-serviceloaders/hapi-fhir-caching-caffeine/pom.xml index 614cfc967b7..46a455edf0a 100644 --- a/hapi-fhir-serviceloaders/hapi-fhir-caching-caffeine/pom.xml +++ b/hapi-fhir-serviceloaders/hapi-fhir-caching-caffeine/pom.xml @@ -7,7 +7,7 @@ hapi-fhir-serviceloaders ca.uhn.hapi.fhir - 7.3.7-SNAPSHOT + 7.3.8-SNAPSHOT ../pom.xml @@ -21,7 +21,7 @@ ca.uhn.hapi.fhir hapi-fhir-caching-api - 7.3.7-SNAPSHOT + 7.3.8-SNAPSHOT diff --git a/hapi-fhir-serviceloaders/hapi-fhir-caching-guava/pom.xml b/hapi-fhir-serviceloaders/hapi-fhir-caching-guava/pom.xml index 322eb544e6c..1629077b0fd 100644 --- a/hapi-fhir-serviceloaders/hapi-fhir-caching-guava/pom.xml +++ b/hapi-fhir-serviceloaders/hapi-fhir-caching-guava/pom.xml @@ -7,7 +7,7 @@ hapi-fhir-serviceloaders ca.uhn.hapi.fhir - 7.3.7-SNAPSHOT + 7.3.8-SNAPSHOT ../pom.xml diff --git a/hapi-fhir-serviceloaders/hapi-fhir-caching-testing/pom.xml b/hapi-fhir-serviceloaders/hapi-fhir-caching-testing/pom.xml index 782403fec87..d805e44028c 100644 --- a/hapi-fhir-serviceloaders/hapi-fhir-caching-testing/pom.xml +++ b/hapi-fhir-serviceloaders/hapi-fhir-caching-testing/pom.xml @@ -7,7 +7,7 @@ hapi-fhir ca.uhn.hapi.fhir - 7.3.7-SNAPSHOT + 7.3.8-SNAPSHOT ../../pom.xml diff --git a/hapi-fhir-serviceloaders/pom.xml b/hapi-fhir-serviceloaders/pom.xml index 9a56ecfd799..87384aa0ef1 100644 --- a/hapi-fhir-serviceloaders/pom.xml +++ b/hapi-fhir-serviceloaders/pom.xml @@ -5,7 +5,7 @@ hapi-deployable-pom ca.uhn.hapi.fhir - 7.3.7-SNAPSHOT + 7.3.8-SNAPSHOT ../hapi-deployable-pom/pom.xml diff --git a/hapi-fhir-spring-boot/hapi-fhir-spring-boot-autoconfigure/pom.xml b/hapi-fhir-spring-boot/hapi-fhir-spring-boot-autoconfigure/pom.xml index 3e2354e4901..e49c57173a6 100644 --- a/hapi-fhir-spring-boot/hapi-fhir-spring-boot-autoconfigure/pom.xml +++ b/hapi-fhir-spring-boot/hapi-fhir-spring-boot-autoconfigure/pom.xml @@ -5,7 +5,7 @@ ca.uhn.hapi.fhir hapi-deployable-pom - 7.3.7-SNAPSHOT + 7.3.8-SNAPSHOT ../../hapi-deployable-pom/pom.xml diff --git a/hapi-fhir-spring-boot/hapi-fhir-spring-boot-samples/hapi-fhir-spring-boot-sample-client-apache/pom.xml b/hapi-fhir-spring-boot/hapi-fhir-spring-boot-samples/hapi-fhir-spring-boot-sample-client-apache/pom.xml index 12079ac6303..b042bb09416 100644 --- a/hapi-fhir-spring-boot/hapi-fhir-spring-boot-samples/hapi-fhir-spring-boot-sample-client-apache/pom.xml +++ b/hapi-fhir-spring-boot/hapi-fhir-spring-boot-samples/hapi-fhir-spring-boot-sample-client-apache/pom.xml @@ -5,7 +5,7 @@ ca.uhn.hapi.fhir hapi-fhir-spring-boot-samples - 7.3.7-SNAPSHOT + 7.3.8-SNAPSHOT hapi-fhir-spring-boot-sample-client-apache diff --git a/hapi-fhir-spring-boot/hapi-fhir-spring-boot-samples/hapi-fhir-spring-boot-sample-client-okhttp/pom.xml b/hapi-fhir-spring-boot/hapi-fhir-spring-boot-samples/hapi-fhir-spring-boot-sample-client-okhttp/pom.xml index f7631efb99b..0d55565aa40 100644 --- a/hapi-fhir-spring-boot/hapi-fhir-spring-boot-samples/hapi-fhir-spring-boot-sample-client-okhttp/pom.xml +++ b/hapi-fhir-spring-boot/hapi-fhir-spring-boot-samples/hapi-fhir-spring-boot-sample-client-okhttp/pom.xml @@ -5,7 +5,7 @@ ca.uhn.hapi.fhir hapi-fhir-spring-boot-samples - 7.3.7-SNAPSHOT + 7.3.8-SNAPSHOT diff --git a/hapi-fhir-spring-boot/hapi-fhir-spring-boot-samples/hapi-fhir-spring-boot-sample-server-jersey/pom.xml b/hapi-fhir-spring-boot/hapi-fhir-spring-boot-samples/hapi-fhir-spring-boot-sample-server-jersey/pom.xml index e7be98afcd8..9776f578481 100644 --- a/hapi-fhir-spring-boot/hapi-fhir-spring-boot-samples/hapi-fhir-spring-boot-sample-server-jersey/pom.xml +++ b/hapi-fhir-spring-boot/hapi-fhir-spring-boot-samples/hapi-fhir-spring-boot-sample-server-jersey/pom.xml @@ -5,7 +5,7 @@ ca.uhn.hapi.fhir hapi-fhir-spring-boot-samples - 7.3.7-SNAPSHOT + 7.3.8-SNAPSHOT diff --git a/hapi-fhir-spring-boot/hapi-fhir-spring-boot-samples/pom.xml b/hapi-fhir-spring-boot/hapi-fhir-spring-boot-samples/pom.xml index 3172d609d8a..b83b51ba012 100644 --- a/hapi-fhir-spring-boot/hapi-fhir-spring-boot-samples/pom.xml +++ b/hapi-fhir-spring-boot/hapi-fhir-spring-boot-samples/pom.xml @@ -5,7 +5,7 @@ ca.uhn.hapi.fhir hapi-fhir-spring-boot - 7.3.7-SNAPSHOT + 7.3.8-SNAPSHOT diff --git a/hapi-fhir-spring-boot/hapi-fhir-spring-boot-starter/pom.xml b/hapi-fhir-spring-boot/hapi-fhir-spring-boot-starter/pom.xml index ff62090b5d1..9c63563684f 100644 --- a/hapi-fhir-spring-boot/hapi-fhir-spring-boot-starter/pom.xml +++ b/hapi-fhir-spring-boot/hapi-fhir-spring-boot-starter/pom.xml @@ -5,7 +5,7 @@ ca.uhn.hapi.fhir hapi-deployable-pom - 7.3.7-SNAPSHOT + 7.3.8-SNAPSHOT ../../hapi-deployable-pom/pom.xml diff --git a/hapi-fhir-spring-boot/pom.xml b/hapi-fhir-spring-boot/pom.xml index afb870730a1..aa18dfb90ff 100644 --- a/hapi-fhir-spring-boot/pom.xml +++ b/hapi-fhir-spring-boot/pom.xml @@ -5,7 +5,7 @@ ca.uhn.hapi.fhir hapi-fhir - 7.3.7-SNAPSHOT + 7.3.8-SNAPSHOT ../pom.xml diff --git a/hapi-fhir-sql-migrate/pom.xml b/hapi-fhir-sql-migrate/pom.xml index ce8c9c06fe6..a7cc58ab8fe 100644 --- a/hapi-fhir-sql-migrate/pom.xml +++ b/hapi-fhir-sql-migrate/pom.xml @@ -5,7 +5,7 @@ ca.uhn.hapi.fhir hapi-deployable-pom - 7.3.7-SNAPSHOT + 7.3.8-SNAPSHOT ../hapi-deployable-pom/pom.xml diff --git a/hapi-fhir-sql-migrate/src/main/java/ca/uhn/fhir/jpa/migrate/HapiMigrator.java b/hapi-fhir-sql-migrate/src/main/java/ca/uhn/fhir/jpa/migrate/HapiMigrator.java index dd8afdce598..17ee41c8032 100644 --- a/hapi-fhir-sql-migrate/src/main/java/ca/uhn/fhir/jpa/migrate/HapiMigrator.java +++ b/hapi-fhir-sql-migrate/src/main/java/ca/uhn/fhir/jpa/migrate/HapiMigrator.java @@ -23,6 +23,7 @@ import ca.uhn.fhir.i18n.Msg; import ca.uhn.fhir.jpa.migrate.dao.HapiMigrationDao; import ca.uhn.fhir.jpa.migrate.taskdef.BaseTask; import ca.uhn.fhir.jpa.migrate.taskdef.InitializeSchemaTask; +import ca.uhn.fhir.jpa.migrate.tasks.api.TaskFlagEnum; import ca.uhn.fhir.system.HapiSystemProperties; import ca.uhn.fhir.util.StopWatch; import com.google.common.annotations.VisibleForTesting; @@ -44,6 +45,7 @@ public class HapiMigrator { private static final Logger ourLog = LoggerFactory.getLogger(HapiMigrator.class); private final MigrationTaskList myTaskList = new MigrationTaskList(); private boolean myDryRun; + private boolean myRunHeavyweightSkippableTasks; private boolean myNoColumnShrink; private final DriverTypeEnum myDriverType; private final DataSource myDataSource; @@ -69,6 +71,24 @@ public class HapiMigrator { myDryRun = theDryRun; } + /** + * Should we run the tasks marked with {@link ca.uhn.fhir.jpa.migrate.tasks.api.TaskFlagEnum#HEAVYWEIGHT_SKIP_BY_DEFAULT} + * + * @since 7.4.0 + */ + public boolean isRunHeavyweightSkippableTasks() { + return myRunHeavyweightSkippableTasks; + } + + /** + * Should we run the tasks marked with {@link ca.uhn.fhir.jpa.migrate.tasks.api.TaskFlagEnum#HEAVYWEIGHT_SKIP_BY_DEFAULT} + * + * @since 7.4.0 + */ + public void setRunHeavyweightSkippableTasks(boolean theRunHeavyweightSkippableTasks) { + myRunHeavyweightSkippableTasks = theRunHeavyweightSkippableTasks; + } + public boolean isNoColumnShrink() { return myNoColumnShrink; } @@ -131,14 +151,27 @@ public class HapiMigrator { try (DriverTypeEnum.ConnectionProperties connectionProperties = getDriverType().newConnectionProperties(getDataSource())) { - newTaskList.forEach(next -> { + if (!isRunHeavyweightSkippableTasks()) { + newTaskList.removeIf(BaseTask::isHeavyweightSkippableTask); + } + + boolean initializedSchema = false; + for (BaseTask next : newTaskList) { + if (initializedSchema && !next.hasFlag(TaskFlagEnum.RUN_DURING_SCHEMA_INITIALIZATION)) { + ourLog.info("Skipping task {} because schema is being initialized", next.getMigrationVersion()); + recordTaskAsCompletedIfNotDryRun(next, 0L, true); + continue; + } + next.setDriverType(getDriverType()); next.setDryRun(isDryRun()); next.setNoColumnShrink(isNoColumnShrink()); next.setConnectionProperties(connectionProperties); executeTask(next, retval); - }); + + initializedSchema |= next.initializedSchema(); + } } } catch (Exception e) { ourLog.error("Migration failed", e); @@ -167,13 +200,13 @@ public class HapiMigrator { } preExecute(theTask); theTask.execute(); - postExecute(theTask, sw, true); + recordTaskAsCompletedIfNotDryRun(theTask, sw.getMillis(), true); theMigrationResult.changes += theTask.getChangesCount(); theMigrationResult.executedStatements.addAll(theTask.getExecutedStatements()); theMigrationResult.succeededTasks.add(theTask); } catch (SQLException | HapiMigrationException e) { theMigrationResult.failedTasks.add(theTask); - postExecute(theTask, sw, false); + recordTaskAsCompletedIfNotDryRun(theTask, sw.getMillis(), false); String description = theTask.getDescription(); if (isBlank(description)) { description = theTask.getClass().getSimpleName(); @@ -187,9 +220,9 @@ public class HapiMigrator { myCallbacks.forEach(action -> action.preExecution(theTask)); } - private void postExecute(BaseTask theNext, StopWatch theStopWatch, boolean theSuccess) { + private void recordTaskAsCompletedIfNotDryRun(BaseTask theNext, long theExecutionMillis, boolean theSuccess) { if (!theNext.isDryRun()) { - myHapiMigrationStorageSvc.saveTask(theNext, Math.toIntExact(theStopWatch.getMillis()), theSuccess); + myHapiMigrationStorageSvc.saveTask(theNext, Math.toIntExact(theExecutionMillis), theSuccess); } } @@ -211,7 +244,7 @@ public class HapiMigrator { } public void setCallbacks(@Nonnull List theCallbacks) { - Validate.notNull(theCallbacks); + Validate.notNull(theCallbacks, "theCallbacks must not be null"); myCallbacks = theCallbacks; } diff --git a/hapi-fhir-sql-migrate/src/main/java/ca/uhn/fhir/jpa/migrate/MigrationTaskList.java b/hapi-fhir-sql-migrate/src/main/java/ca/uhn/fhir/jpa/migrate/MigrationTaskList.java index 668b51095c9..17a56f5a48b 100644 --- a/hapi-fhir-sql-migrate/src/main/java/ca/uhn/fhir/jpa/migrate/MigrationTaskList.java +++ b/hapi-fhir-sql-migrate/src/main/java/ca/uhn/fhir/jpa/migrate/MigrationTaskList.java @@ -29,6 +29,7 @@ import java.util.Iterator; import java.util.List; import java.util.Set; import java.util.function.Consumer; +import java.util.function.Predicate; import java.util.stream.Collectors; public class MigrationTaskList implements Iterable { @@ -95,4 +96,12 @@ public class MigrationTaskList implements Iterable { .reduce((first, second) -> second) .orElse(null); } + + public void removeIf(Predicate theFilter) { + myTasks.removeIf(theFilter); + } + + public BaseTask[] toTaskArray() { + return myTasks.toArray(new BaseTask[0]); + } } diff --git a/hapi-fhir-sql-migrate/src/main/java/ca/uhn/fhir/jpa/migrate/MigrationTaskSkipper.java b/hapi-fhir-sql-migrate/src/main/java/ca/uhn/fhir/jpa/migrate/MigrationTaskSkipper.java index 0e74e504380..6de5b05fe3d 100644 --- a/hapi-fhir-sql-migrate/src/main/java/ca/uhn/fhir/jpa/migrate/MigrationTaskSkipper.java +++ b/hapi-fhir-sql-migrate/src/main/java/ca/uhn/fhir/jpa/migrate/MigrationTaskSkipper.java @@ -20,6 +20,7 @@ package ca.uhn.fhir.jpa.migrate; import ca.uhn.fhir.jpa.migrate.taskdef.BaseTask; +import ca.uhn.fhir.jpa.migrate.tasks.api.TaskFlagEnum; import org.apache.commons.lang3.StringUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -49,7 +50,7 @@ public class MigrationTaskSkipper { for (BaseTask task : theTasks) { if (skippedVersionSet.contains(task.getMigrationVersion())) { ourLog.info("Will skip {}: {}", task.getMigrationVersion(), task.getDescription()); - task.setDoNothing(true); + task.addFlag(TaskFlagEnum.DO_NOTHING); } } } diff --git a/hapi-fhir-sql-migrate/src/main/java/ca/uhn/fhir/jpa/migrate/taskdef/AddColumnTask.java b/hapi-fhir-sql-migrate/src/main/java/ca/uhn/fhir/jpa/migrate/taskdef/AddColumnTask.java index d1e6ed52064..933b81d3e8a 100644 --- a/hapi-fhir-sql-migrate/src/main/java/ca/uhn/fhir/jpa/migrate/taskdef/AddColumnTask.java +++ b/hapi-fhir-sql-migrate/src/main/java/ca/uhn/fhir/jpa/migrate/taskdef/AddColumnTask.java @@ -35,12 +35,6 @@ public class AddColumnTask extends BaseTableColumnTypeTask { return new AddColumnTask(null, null, ColumnNameCase.ALL_LOWER, theColumnDriverMappingOverrides); } - public AddColumnTask() { - this(null, null); - setDryRun(true); - myCheckForExistingTables = false; - } - public AddColumnTask(String theProductVersion, String theSchemaVersion) { super(theProductVersion, theSchemaVersion); } @@ -84,6 +78,7 @@ public class AddColumnTask extends BaseTableColumnTypeTask { break; case DERBY_EMBEDDED: case POSTGRES_9_4: + case COCKROACHDB_21_1: sql = "alter table " + getTableName() + " add column " + getColumnName() + " " + typeStatement; break; case MSSQL_2012: diff --git a/hapi-fhir-sql-migrate/src/main/java/ca/uhn/fhir/jpa/migrate/taskdef/AddTableByColumnTask.java b/hapi-fhir-sql-migrate/src/main/java/ca/uhn/fhir/jpa/migrate/taskdef/AddTableByColumnTask.java index abf38c1672a..969a05b21c0 100644 --- a/hapi-fhir-sql-migrate/src/main/java/ca/uhn/fhir/jpa/migrate/taskdef/AddTableByColumnTask.java +++ b/hapi-fhir-sql-migrate/src/main/java/ca/uhn/fhir/jpa/migrate/taskdef/AddTableByColumnTask.java @@ -56,7 +56,7 @@ public class AddTableByColumnTask extends BaseTableTask { this(theProductVersion, theSchemaVersion, null); } - private AddTableByColumnTask( + public AddTableByColumnTask( String theProductVersion, String theSchemaVersion, Comparator theColumnSortingRules) { super(theProductVersion, theSchemaVersion); myColumnSortingRules = theColumnSortingRules; diff --git a/hapi-fhir-sql-migrate/src/main/java/ca/uhn/fhir/jpa/migrate/taskdef/ArbitrarySqlTask.java b/hapi-fhir-sql-migrate/src/main/java/ca/uhn/fhir/jpa/migrate/taskdef/ArbitrarySqlTask.java index e3b9459fe06..41dedf315a7 100644 --- a/hapi-fhir-sql-migrate/src/main/java/ca/uhn/fhir/jpa/migrate/taskdef/ArbitrarySqlTask.java +++ b/hapi-fhir-sql-migrate/src/main/java/ca/uhn/fhir/jpa/migrate/taskdef/ArbitrarySqlTask.java @@ -46,6 +46,9 @@ public class ArbitrarySqlTask extends BaseTask { private String myExecuteOnlyIfTableExists; private List myConditionalOnExistenceOf = new ArrayList<>(); + /** + * Constructor + */ public ArbitrarySqlTask(VersionEnum theRelease, String theVersion, String theTableName, String theDescription) { super(theRelease.toString(), theVersion); myTableName = theTableName; diff --git a/hapi-fhir-sql-migrate/src/main/java/ca/uhn/fhir/jpa/migrate/taskdef/BaseTask.java b/hapi-fhir-sql-migrate/src/main/java/ca/uhn/fhir/jpa/migrate/taskdef/BaseTask.java index 50b7859a0a9..f652fa0a938 100644 --- a/hapi-fhir-sql-migrate/src/main/java/ca/uhn/fhir/jpa/migrate/taskdef/BaseTask.java +++ b/hapi-fhir-sql-migrate/src/main/java/ca/uhn/fhir/jpa/migrate/taskdef/BaseTask.java @@ -22,10 +22,14 @@ package ca.uhn.fhir.jpa.migrate.taskdef; import ca.uhn.fhir.i18n.Msg; import ca.uhn.fhir.jpa.migrate.DriverTypeEnum; import ca.uhn.fhir.jpa.migrate.HapiMigrationException; +import ca.uhn.fhir.jpa.migrate.tasks.api.TaskFlagEnum; import ca.uhn.fhir.system.HapiSystemProperties; +import jakarta.annotation.Nonnull; import org.apache.commons.lang3.Validate; import org.apache.commons.lang3.builder.EqualsBuilder; import org.apache.commons.lang3.builder.HashCodeBuilder; +import org.apache.commons.lang3.builder.ToStringBuilder; +import org.apache.commons.lang3.builder.ToStringStyle; import org.flywaydb.core.api.MigrationVersion; import org.intellij.lang.annotations.Language; import org.slf4j.Logger; @@ -38,6 +42,7 @@ import java.sql.SQLException; import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; +import java.util.EnumSet; import java.util.HashSet; import java.util.List; import java.util.Set; @@ -51,11 +56,42 @@ public abstract class BaseTask { private static final Pattern versionPattern = Pattern.compile(MIGRATION_VERSION_PATTERN); private final String myProductVersion; private final String mySchemaVersion; + private final List myPreconditions = new ArrayList<>(); + private final EnumSet myFlags = EnumSet.noneOf(TaskFlagEnum.class); + private final List myExecutedStatements = new ArrayList<>(); + /** + * Whether to check for existing tables + * before generating SQL + */ + protected boolean myCheckForExistingTables = true; + /** + * Whether to generate the SQL in a 'readable format' + */ + protected boolean myPrettyPrint = false; + private DriverTypeEnum.ConnectionProperties myConnectionProperties; private DriverTypeEnum myDriverType; private String myDescription; private Integer myChangesCount = 0; private boolean myDryRun; + private boolean myTransactional = true; + private Set myOnlyAppliesToPlatforms = new HashSet<>(); + private boolean myNoColumnShrink; + + protected BaseTask(String theProductVersion, String theSchemaVersion) { + myProductVersion = theProductVersion; + mySchemaVersion = theSchemaVersion; + } + + /** + * Adds a flag if it's not already present, otherwise this call is ignored. + * + * @param theFlag The flag, must not be null + */ + public BaseTask addFlag(@Nonnull TaskFlagEnum theFlag) { + myFlags.add(theFlag); + return this; + } /** * Some migrations can not be run in a transaction. @@ -65,48 +101,12 @@ public abstract class BaseTask { myTransactional = theTransactional; } - private boolean myTransactional = true; - private boolean myDoNothing; - private List myExecutedStatements = new ArrayList<>(); - private Set myOnlyAppliesToPlatforms = new HashSet<>(); - private boolean myNoColumnShrink; - private boolean myFailureAllowed; - private boolean myRunDuringSchemaInitialization; - /** - * Whether or not to check for existing tables - * before generating SQL - */ - protected boolean myCheckForExistingTables = true; - - /** - * Whether or not to generate the SQL in a 'readable format' - */ - protected boolean myPrettyPrint = false; - - protected BaseTask(String theProductVersion, String theSchemaVersion) { - myProductVersion = theProductVersion; - mySchemaVersion = theSchemaVersion; - } - - public boolean isRunDuringSchemaInitialization() { - return myRunDuringSchemaInitialization; - } - public void setPrettyPrint(boolean thePrettyPrint) { myPrettyPrint = thePrettyPrint; } - /** - * Should this task run even if we're doing the very first initialization of an empty schema. By - * default we skip most tasks during that pass, since they just take up time and the - * schema should be fully initialized by the {@link InitializeSchemaTask} - */ - public void setRunDuringSchemaInitialization(boolean theRunDuringSchemaInitialization) { - myRunDuringSchemaInitialization = theRunDuringSchemaInitialization; - } - public void setOnlyAppliesToPlatforms(Set theOnlyAppliesToPlatforms) { - Validate.notNull(theOnlyAppliesToPlatforms); + Validate.notNull(theOnlyAppliesToPlatforms, "theOnlyAppliesToPlatforms must not be null"); myOnlyAppliesToPlatforms = theOnlyAppliesToPlatforms; } @@ -188,7 +188,7 @@ public abstract class BaseTask { private Integer doExecuteSqlList(List theSqlStatements) { int changesCount = 0; - for (String nextSql : theSqlStatements) { + for (@Language("SQL") String nextSql : theSqlStatements) { changesCount += doExecuteSql(nextSql); } @@ -206,7 +206,7 @@ public abstract class BaseTask { } return changesCount; } catch (DataAccessException e) { - if (myFailureAllowed) { + if (myFlags.contains(TaskFlagEnum.FAILURE_ALLOWED)) { ourLog.info("Task {} did not exit successfully, but task is allowed to fail", getMigrationVersion()); ourLog.debug("Error was: {}", e.getMessage(), e); return 0; @@ -219,7 +219,7 @@ public abstract class BaseTask { protected void captureExecutedStatement( String theTableName, @Language("SQL") String theSql, Object... theArguments) { - myExecutedStatements.add(new ExecutedStatement(theTableName, theSql, theArguments)); + myExecutedStatements.add(new ExecutedStatement(mySchemaVersion, theTableName, theSql, theArguments)); } public DriverTypeEnum.ConnectionProperties getConnectionProperties() { @@ -250,10 +250,8 @@ public abstract class BaseTask { return getConnectionProperties().newJdbcTemplate(); } - private final List myPreconditions = new ArrayList<>(); - public void execute() throws SQLException { - if (myDoNothing) { + if (myFlags.contains(TaskFlagEnum.DO_NOTHING)) { ourLog.info("Skipping stubbed task: {}", getDescription()); return; } @@ -278,14 +276,6 @@ public abstract class BaseTask { protected abstract void doExecute() throws SQLException; - protected boolean isFailureAllowed() { - return myFailureAllowed; - } - - public void setFailureAllowed(boolean theFailureAllowed) { - myFailureAllowed = theFailureAllowed; - } - public String getMigrationVersion() { String releasePart = myProductVersion; if (releasePart.startsWith("V")) { @@ -296,6 +286,7 @@ public abstract class BaseTask { return migrationVersion.getVersion(); } + @SuppressWarnings("StringConcatenationArgumentToLogCall") protected void logInfo(Logger theLog, String theFormattedMessage, Object... theArguments) { theLog.info(getMigrationVersion() + ": " + theFormattedMessage, theArguments); } @@ -308,23 +299,6 @@ public abstract class BaseTask { } } - public void doNothing() { - setDoNothing(true); - } - - public void failureAllowed() { - setFailureAllowed(true); - } - - public boolean isDoNothing() { - return myDoNothing; - } - - public BaseTask setDoNothing(boolean theDoNothing) { - myDoNothing = theDoNothing; - return this; - } - public void addPrecondition(ExecuteTaskPrecondition thePrecondition) { myPreconditions.add(thePrecondition); } @@ -343,7 +317,6 @@ public abstract class BaseTask { if (theObject == null || getClass().equals(theObject.getClass()) == false) { return false; } - @SuppressWarnings("unchecked") BaseTask otherObject = (BaseTask) theObject; EqualsBuilder b = new EqualsBuilder(); @@ -357,17 +330,35 @@ public abstract class BaseTask { return false; } + public boolean isDoNothing() { + return myFlags.contains(TaskFlagEnum.DO_NOTHING); + } + + public boolean isHeavyweightSkippableTask() { + return myFlags.contains(TaskFlagEnum.HEAVYWEIGHT_SKIP_BY_DEFAULT); + } + + public boolean hasFlag(TaskFlagEnum theFlag) { + return myFlags.contains(theFlag); + } + public static class ExecutedStatement { private final String mySql; private final List myArguments; private final String myTableName; + private final String mySchemaVersion; - public ExecutedStatement(String theDescription, String theSql, Object[] theArguments) { + public ExecutedStatement(String theSchemaVersion, String theDescription, String theSql, Object[] theArguments) { + mySchemaVersion = theSchemaVersion; myTableName = theDescription; mySql = theSql; myArguments = theArguments != null ? Arrays.asList(theArguments) : Collections.emptyList(); } + public String getSchemaVersion() { + return mySchemaVersion; + } + public String getTableName() { return myTableName; } @@ -379,5 +370,14 @@ public abstract class BaseTask { public List getArguments() { return myArguments; } + + @Override + public String toString() { + return new ToStringBuilder(this, ToStringStyle.SHORT_PREFIX_STYLE) + .append("tableName", myTableName) + .append("sql", mySql) + .append("arguments", myArguments) + .toString(); + } } } diff --git a/hapi-fhir-sql-migrate/src/main/java/ca/uhn/fhir/jpa/migrate/taskdef/ForceIdMigrationFixTask.java b/hapi-fhir-sql-migrate/src/main/java/ca/uhn/fhir/jpa/migrate/taskdef/ForceIdMigrationFixTask.java index 7f29d3ba38b..07607a90845 100644 --- a/hapi-fhir-sql-migrate/src/main/java/ca/uhn/fhir/jpa/migrate/taskdef/ForceIdMigrationFixTask.java +++ b/hapi-fhir-sql-migrate/src/main/java/ca/uhn/fhir/jpa/migrate/taskdef/ForceIdMigrationFixTask.java @@ -113,6 +113,13 @@ public class ForceIdMigrationFixTask extends BaseTask { switch (getDriverType()) { case MSSQL_2012: return " where (fhir_id is null or DATALENGTH(fhir_id) > LEN(fhir_id)) "; + case H2_EMBEDDED: + case DERBY_EMBEDDED: + case MARIADB_10_1: + case MYSQL_5_7: + case POSTGRES_9_4: + case ORACLE_12C: + case COCKROACHDB_21_1: default: return " where (fhir_id is null or fhir_id <> trim(fhir_id)) "; } diff --git a/hapi-fhir-sql-migrate/src/main/java/ca/uhn/fhir/jpa/migrate/taskdef/InitializeSchemaTask.java b/hapi-fhir-sql-migrate/src/main/java/ca/uhn/fhir/jpa/migrate/taskdef/InitializeSchemaTask.java index a2eeb639bf9..9fe4aa996ce 100644 --- a/hapi-fhir-sql-migrate/src/main/java/ca/uhn/fhir/jpa/migrate/taskdef/InitializeSchemaTask.java +++ b/hapi-fhir-sql-migrate/src/main/java/ca/uhn/fhir/jpa/migrate/taskdef/InitializeSchemaTask.java @@ -22,6 +22,7 @@ package ca.uhn.fhir.jpa.migrate.taskdef; import ca.uhn.fhir.jpa.migrate.DriverTypeEnum; import ca.uhn.fhir.jpa.migrate.JdbcUtils; import ca.uhn.fhir.jpa.migrate.tasks.api.ISchemaInitializationProvider; +import ca.uhn.fhir.jpa.migrate.tasks.api.TaskFlagEnum; import org.apache.commons.lang3.builder.EqualsBuilder; import org.apache.commons.lang3.builder.HashCodeBuilder; import org.slf4j.Logger; @@ -44,11 +45,7 @@ public class InitializeSchemaTask extends BaseTask { super(theProductVersion, theSchemaVersion); mySchemaInitializationProvider = theSchemaInitializationProvider; setDescription(DESCRIPTION_PREFIX + mySchemaInitializationProvider.getSchemaDescription()); - } - - @Override - public boolean isRunDuringSchemaInitialization() { - return true; + addFlag(TaskFlagEnum.RUN_DURING_SCHEMA_INITIALIZATION); } @Override @@ -102,12 +99,12 @@ public class InitializeSchemaTask extends BaseTask { @Override protected void generateEquals(EqualsBuilder theBuilder, BaseTask theOtherObject) { InitializeSchemaTask otherObject = (InitializeSchemaTask) theOtherObject; - theBuilder.append(mySchemaInitializationProvider, otherObject.mySchemaInitializationProvider); + theBuilder.append(getSchemaInitializationProvider(), otherObject.getSchemaInitializationProvider()); } @Override protected void generateHashCode(HashCodeBuilder theBuilder) { - theBuilder.append(mySchemaInitializationProvider); + theBuilder.append(getSchemaInitializationProvider()); } public ISchemaInitializationProvider getSchemaInitializationProvider() { diff --git a/hapi-fhir-sql-migrate/src/main/java/ca/uhn/fhir/jpa/migrate/taskdef/ModifyColumnTask.java b/hapi-fhir-sql-migrate/src/main/java/ca/uhn/fhir/jpa/migrate/taskdef/ModifyColumnTask.java index 70dc3d2c0d1..574ac66ae8a 100644 --- a/hapi-fhir-sql-migrate/src/main/java/ca/uhn/fhir/jpa/migrate/taskdef/ModifyColumnTask.java +++ b/hapi-fhir-sql-migrate/src/main/java/ca/uhn/fhir/jpa/migrate/taskdef/ModifyColumnTask.java @@ -21,6 +21,7 @@ package ca.uhn.fhir.jpa.migrate.taskdef; import ca.uhn.fhir.i18n.Msg; import ca.uhn.fhir.jpa.migrate.JdbcUtils; +import ca.uhn.fhir.jpa.migrate.tasks.api.TaskFlagEnum; import ca.uhn.fhir.rest.server.exceptions.InternalErrorException; import org.intellij.lang.annotations.Language; import org.slf4j.Logger; @@ -159,8 +160,8 @@ public class ModifyColumnTask extends BaseTableColumnTypeTask { throw new IllegalStateException(Msg.code(67) + "Dont know how to handle " + getDriverType()); } - if (!isFailureAllowed() && isShrinkOnly) { - setFailureAllowed(true); + if (isShrinkOnly) { + addFlag(TaskFlagEnum.FAILURE_ALLOWED); } logInfo(ourLog, "Updating column {} on table {} to type {}", getColumnName(), getTableName(), type); diff --git a/hapi-fhir-sql-migrate/src/main/java/ca/uhn/fhir/jpa/migrate/taskdef/RenameTableTask.java b/hapi-fhir-sql-migrate/src/main/java/ca/uhn/fhir/jpa/migrate/taskdef/RenameTableTask.java index 4b90ea91ac3..e9007755ce0 100644 --- a/hapi-fhir-sql-migrate/src/main/java/ca/uhn/fhir/jpa/migrate/taskdef/RenameTableTask.java +++ b/hapi-fhir-sql-migrate/src/main/java/ca/uhn/fhir/jpa/migrate/taskdef/RenameTableTask.java @@ -99,6 +99,7 @@ public class RenameTableTask extends BaseTableTask { return retVal; } + @Override protected void generateHashCode(HashCodeBuilder theBuilder) { super.generateHashCode(theBuilder); theBuilder.append(myOldTableName); diff --git a/hapi-fhir-sql-migrate/src/main/java/ca/uhn/fhir/jpa/migrate/tasks/SchemaInitializationProvider.java b/hapi-fhir-sql-migrate/src/main/java/ca/uhn/fhir/jpa/migrate/tasks/SchemaInitializationProvider.java index 083e90ab9b1..5dad63f92d8 100644 --- a/hapi-fhir-sql-migrate/src/main/java/ca/uhn/fhir/jpa/migrate/tasks/SchemaInitializationProvider.java +++ b/hapi-fhir-sql-migrate/src/main/java/ca/uhn/fhir/jpa/migrate/tasks/SchemaInitializationProvider.java @@ -23,6 +23,7 @@ import ca.uhn.fhir.context.ConfigurationException; import ca.uhn.fhir.i18n.Msg; import ca.uhn.fhir.jpa.migrate.DriverTypeEnum; import ca.uhn.fhir.jpa.migrate.tasks.api.ISchemaInitializationProvider; +import ca.uhn.fhir.util.ClasspathUtil; import com.google.common.annotations.VisibleForTesting; import com.google.common.base.Charsets; import jakarta.annotation.Nonnull; @@ -71,11 +72,7 @@ public class SchemaInitializationProvider implements ISchemaInitializationProvid String initScript = mySchemaFileClassPath + "/" + getInitScript(theDriverType); try { - InputStream sqlFileInputStream = SchemaInitializationProvider.class.getResourceAsStream(initScript); - if (sqlFileInputStream == null) { - throw new ConfigurationException( - Msg.code(49) + "Schema initialization script " + initScript + " not found on classpath"); - } + InputStream sqlFileInputStream = ClasspathUtil.loadResourceAsStream(initScript); // Assumes no escaped semicolons... String sqlString = IOUtils.toString(sqlFileInputStream, Charsets.UTF_8); parseSqlFileIntoIndividualStatements(theDriverType, retval, sqlString); @@ -91,7 +88,7 @@ public class SchemaInitializationProvider implements ISchemaInitializationProvid String sqlString = theSqlString.replaceAll("--.*", ""); String sqlStringNoComments = preProcessSqlString(theDriverType, sqlString); - String[] statements = sqlStringNoComments.split("\\;"); + String[] statements = sqlStringNoComments.split(";"); for (String statement : statements) { String cleanedStatement = preProcessSqlStatement(theDriverType, statement); if (!isBlank(cleanedStatement)) { diff --git a/hapi-fhir-sql-migrate/src/main/java/ca/uhn/fhir/jpa/migrate/tasks/api/Builder.java b/hapi-fhir-sql-migrate/src/main/java/ca/uhn/fhir/jpa/migrate/tasks/api/Builder.java index 53741f65e5a..7639e486a8d 100644 --- a/hapi-fhir-sql-migrate/src/main/java/ca/uhn/fhir/jpa/migrate/tasks/api/Builder.java +++ b/hapi-fhir-sql-migrate/src/main/java/ca/uhn/fhir/jpa/migrate/tasks/api/Builder.java @@ -85,18 +85,17 @@ public class Builder { } public BuilderCompleteTask executeRawSql(String theVersion, @Language("SQL") String theSql) { - ExecuteRawSqlTask task = executeRawSqlOptional(false, theVersion, theSql); + ExecuteRawSqlTask task = executeRawSqlOptional(theVersion, theSql); return new BuilderCompleteTask(task); } public void executeRawSqlStub(String theVersion, @Language("SQL") String theSql) { - executeRawSqlOptional(true, theVersion, theSql); + BuilderCompleteTask task = executeRawSql(theVersion, theSql); + task.withFlag(TaskFlagEnum.DO_NOTHING); } - private ExecuteRawSqlTask executeRawSqlOptional( - boolean theDoNothing, String theVersion, @Language("SQL") String theSql) { + private ExecuteRawSqlTask executeRawSqlOptional(String theVersion, @Language("SQL") String theSql) { ExecuteRawSqlTask task = new ExecuteRawSqlTask(myRelease, theVersion).addSql(theSql); - task.setDoNothing(theDoNothing); mySink.addTask(task); return task; } @@ -172,10 +171,10 @@ public class Builder { addTask(task); } - public DropIdGeneratorTask dropIdGenerator(String theVersion, String theIdGeneratorName) { + public BuilderCompleteTask dropIdGenerator(String theVersion, String theIdGeneratorName) { DropIdGeneratorTask task = new DropIdGeneratorTask(myRelease, theVersion, theIdGeneratorName); addTask(task); - return task; + return new BuilderCompleteTask(task); } public void addNop(String theVersion) { @@ -199,7 +198,7 @@ public class Builder { } public BuilderCompleteTask dropIndex(String theVersion, String theIndexName) { - BaseTask task = dropIndexOptional(false, theVersion, theIndexName); + BaseTask task = dropIndexOptional(theVersion, theIndexName); return new BuilderCompleteTask(task); } @@ -207,20 +206,20 @@ public class Builder { * Drop index without taking write lock on PG, Oracle, MSSQL. */ public BuilderCompleteTask dropIndexOnline(String theVersion, String theIndexName) { - DropIndexTask task = dropIndexOptional(false, theVersion, theIndexName); + DropIndexTask task = dropIndexOptional(theVersion, theIndexName); task.setOnline(true); return new BuilderCompleteTask(task); } public void dropIndexStub(String theVersion, String theIndexName) { - dropIndexOptional(true, theVersion, theIndexName); + DropIndexTask task = dropIndexOptional(theVersion, theIndexName); + task.addFlag(TaskFlagEnum.DO_NOTHING); } - private DropIndexTask dropIndexOptional(boolean theDoNothing, String theVersion, String theIndexName) { + private DropIndexTask dropIndexOptional(String theVersion, String theIndexName) { DropIndexTask task = new DropIndexTask(myRelease, theVersion); task.setIndexName(theIndexName); task.setTableName(myTableName); - task.setDoNothing(theDoNothing); addTask(task); return task; } @@ -230,24 +229,24 @@ public class Builder { */ @Deprecated public void renameIndex(String theVersion, String theOldIndexName, String theNewIndexName) { - renameIndexOptional(false, theVersion, theOldIndexName, theNewIndexName); + renameIndexOptional(theVersion, theOldIndexName, theNewIndexName); } /** * @deprecated Do not rename indexes - It is too hard to figure out what happened if something goes wrong */ public void renameIndexStub(String theVersion, String theOldIndexName, String theNewIndexName) { - renameIndexOptional(true, theVersion, theOldIndexName, theNewIndexName); + RenameIndexTask task = renameIndexOptional(theVersion, theOldIndexName, theNewIndexName); + task.addFlag(TaskFlagEnum.DO_NOTHING); } - private void renameIndexOptional( - boolean theDoNothing, String theVersion, String theOldIndexName, String theNewIndexName) { + private RenameIndexTask renameIndexOptional(String theVersion, String theOldIndexName, String theNewIndexName) { RenameIndexTask task = new RenameIndexTask(myRelease, theVersion); task.setOldIndexName(theOldIndexName); task.setNewIndexName(theNewIndexName); task.setTableName(myTableName); - task.setDoNothing(theDoNothing); addTask(task); + return task; } public void dropThisTable(String theVersion) { @@ -388,27 +387,22 @@ public class Builder { } public void withColumnsStub(String... theColumnNames) { - withColumnsOptional(true, theColumnNames); + BuilderCompleteTask task = withColumns(theColumnNames); + task.withFlag(TaskFlagEnum.DO_NOTHING); } public BuilderCompleteTask withColumns(String... theColumnNames) { - BaseTask task = withColumnsOptional(false, theColumnNames); - return new BuilderCompleteTask(task); - } - - private AddIndexTask withColumnsOptional(boolean theDoNothing, String... theColumnNames) { AddIndexTask task = new AddIndexTask(myRelease, myVersion); task.setTableName(myTableName); task.setIndexName(myIndexName); task.setUnique(myUnique); task.setColumns(theColumnNames); - task.setDoNothing(theDoNothing); task.setOnline(myOnline); if (myIncludeColumns != null) { task.setIncludeColumns(myIncludeColumns); } addTask(task); - return task; + return new BuilderCompleteTask(task); } public BuilderAddIndexUnique includeColumns(String... theIncludeColumns) { @@ -453,18 +447,17 @@ public class Builder { public class BuilderModifyColumnWithNameAndNullable { private final String myVersion; private final boolean myNullable; - private boolean myFailureAllowed; public BuilderModifyColumnWithNameAndNullable(String theVersion, boolean theNullable) { myVersion = theVersion; myNullable = theNullable; } - public void withType(ColumnTypeEnum theColumnType) { - withType(theColumnType, null); + public BuilderCompleteTask withType(ColumnTypeEnum theColumnType) { + return withType(theColumnType, null); } - public void withType(ColumnTypeEnum theColumnType, Integer theLength) { + public BuilderCompleteTask withType(ColumnTypeEnum theColumnType, Integer theLength) { if (theColumnType == ColumnTypeEnum.STRING) { if (theLength == null || theLength == 0) { throw new IllegalArgumentException( @@ -478,6 +471,7 @@ public class Builder { } ModifyColumnTask task = new ModifyColumnTask(myRelease, myVersion); + task.setColumnName(myColumnName); task.setTableName(myTableName); if (theLength != null) { @@ -485,13 +479,8 @@ public class Builder { } task.setNullable(myNullable); task.setColumnType(theColumnType); - task.setFailureAllowed(myFailureAllowed); addTask(task); - } - - public BuilderModifyColumnWithNameAndNullable failureAllowed() { - myFailureAllowed = true; - return this; + return new BuilderCompleteTask(task); } } } @@ -596,12 +585,12 @@ public class Builder { } public BuilderCompleteTask failureAllowed() { - myTask.setFailureAllowed(true); + myTask.addFlag(TaskFlagEnum.FAILURE_ALLOWED); return this; } public BuilderCompleteTask doNothing() { - myTask.setDoNothing(true); + myTask.addFlag(TaskFlagEnum.DO_NOTHING); return this; } @@ -646,7 +635,7 @@ public class Builder { } public BuilderCompleteTask runEvenDuringSchemaInitialization() { - myTask.setRunDuringSchemaInitialization(true); + myTask.addFlag(TaskFlagEnum.RUN_DURING_SCHEMA_INITIALIZATION); return this; } @@ -654,6 +643,16 @@ public class Builder { myTask.setTransactional(theFlag); return this; } + + public BuilderCompleteTask heavyweightSkipByDefault() { + myTask.addFlag(TaskFlagEnum.HEAVYWEIGHT_SKIP_BY_DEFAULT); + return this; + } + + public BuilderCompleteTask withFlag(TaskFlagEnum theFlag) { + myTask.addFlag(theFlag); + return this; + } } public class BuilderAddTableRawSql { @@ -707,9 +706,8 @@ public class Builder { } } - public BuilderAddTableByColumns failureAllowed() { - myTask.setFailureAllowed(true); - return this; + public BuilderCompleteTask withFlags() { + return new BuilderCompleteTask(myTask); } } diff --git a/hapi-fhir-sql-migrate/src/main/java/ca/uhn/fhir/jpa/migrate/tasks/api/TaskFlagEnum.java b/hapi-fhir-sql-migrate/src/main/java/ca/uhn/fhir/jpa/migrate/tasks/api/TaskFlagEnum.java new file mode 100644 index 00000000000..73041b1a053 --- /dev/null +++ b/hapi-fhir-sql-migrate/src/main/java/ca/uhn/fhir/jpa/migrate/tasks/api/TaskFlagEnum.java @@ -0,0 +1,65 @@ +/*- + * #%L + * HAPI FHIR Server - SQL Migration + * %% + * Copyright (C) 2014 - 2024 Smile CDR, Inc. + * %% + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * #L% + */ +package ca.uhn.fhir.jpa.migrate.tasks.api; + +import ca.uhn.fhir.jpa.migrate.taskdef.InitializeSchemaTask; + +public enum TaskFlagEnum { + + /** + * This task should be skipped by default on live system migrations. Use this flag + * in cases where the benefit of applying a migration doesn't justify the effort, and + * only use this flag in cases where skipping the task will not leave the system in + * an unusable state. + *

+ * This flag is intended for situations where a change to the schema would be useful + * to add to the default initialized schema, but would not be worth the effort of + * applying in-place to large databases due to the outsized effort it would take. + *

+ *

+ * USE THIS FLAG WITH CAUTION: It means that the migrator unit tests will execute the + * task, but live migrations probably won't. So think long and hard before you set it! + *

+ */ + HEAVYWEIGHT_SKIP_BY_DEFAULT, + + /** + * Should this task run even if we're doing the very first initialization of an empty schema. By + * default, we skip most tasks during that pass, since they just take up time and the + * schema should be fully initialized by the {@link InitializeSchemaTask} + */ + RUN_DURING_SCHEMA_INITIALIZATION, + + /** + * This task is allowed to fail, and failure won't abort the migration. Upon any + * detected failures, the task will still be recorded as having been successfully + * complected. A failure in this context means that the SQL statement(s) being + * executed returned a non-successful response, or timed out. + */ + FAILURE_ALLOWED, + + /** + * This task should not actually do anything, and will always be recorded as + * a success. Use this flag for tasks that are no longer needed, e.g. because + * they are superseded by later tasks or because they turned out to be a bad + * idea. + */ + DO_NOTHING +} diff --git a/hapi-fhir-sql-migrate/src/test/java/ca/uhn/fhir/jpa/migrate/HapiMigrationStorageSvcTest.java b/hapi-fhir-sql-migrate/src/test/java/ca/uhn/fhir/jpa/migrate/HapiMigrationStorageSvcTest.java index ca5175d585d..e0c919a952f 100644 --- a/hapi-fhir-sql-migrate/src/test/java/ca/uhn/fhir/jpa/migrate/HapiMigrationStorageSvcTest.java +++ b/hapi-fhir-sql-migrate/src/test/java/ca/uhn/fhir/jpa/migrate/HapiMigrationStorageSvcTest.java @@ -128,7 +128,7 @@ class HapiMigrationStorageSvcTest extends BaseMigrationTest { return taskList; } - public Builder forVersion(MigrationTaskList theTaskList) { + public static Builder forVersion(MigrationTaskList theTaskList) { BaseMigrationTasks.IAcceptsTasks sink = theTask -> { theTask.validate(); theTaskList.add(theTask); diff --git a/hapi-fhir-sql-migrate/src/test/java/ca/uhn/fhir/jpa/migrate/HapiMigratorIT.java b/hapi-fhir-sql-migrate/src/test/java/ca/uhn/fhir/jpa/migrate/HapiMigratorIT.java index cf0a125698d..252df0cfd05 100644 --- a/hapi-fhir-sql-migrate/src/test/java/ca/uhn/fhir/jpa/migrate/HapiMigratorIT.java +++ b/hapi-fhir-sql-migrate/src/test/java/ca/uhn/fhir/jpa/migrate/HapiMigratorIT.java @@ -3,7 +3,11 @@ package ca.uhn.fhir.jpa.migrate; import ca.uhn.fhir.interceptor.api.HookParams; import ca.uhn.fhir.jpa.migrate.dao.HapiMigrationDao; import ca.uhn.fhir.jpa.migrate.taskdef.BaseTask; +import ca.uhn.fhir.jpa.migrate.taskdef.ColumnTypeEnum; import ca.uhn.fhir.jpa.migrate.taskdef.NopTask; +import ca.uhn.fhir.jpa.migrate.tasks.SchemaInitializationProvider; +import ca.uhn.fhir.jpa.migrate.tasks.api.Builder; +import ca.uhn.fhir.jpa.migrate.tasks.api.TaskFlagEnum; import ca.uhn.test.concurrency.IPointcutLatch; import ca.uhn.test.concurrency.PointcutLatch; import jakarta.annotation.Nonnull; @@ -13,6 +17,8 @@ import org.apache.commons.lang3.builder.HashCodeBuilder; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.ValueSource; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.jdbc.core.JdbcTemplate; @@ -23,12 +29,15 @@ import java.util.concurrent.ExecutionException; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.Future; +import java.util.stream.Collectors; import static org.assertj.core.api.Assertions.assertThat; import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotNull; import static org.junit.jupiter.api.Assertions.assertTrue; import static org.junit.jupiter.api.Assertions.fail; +@SuppressWarnings("SqlDialectInspection") class HapiMigratorIT { private static final Logger ourLog = LoggerFactory.getLogger(HapiMigratorIT.class); private static final String MIGRATION_TABLENAME = "TEST_MIGRATOR_TABLE"; @@ -42,6 +51,7 @@ class HapiMigratorIT { HapiMigrator migrator = buildMigrator(); migrator.createMigrationTableIfRequired(); Integer count = myJdbcTemplate.queryForObject("SELECT COUNT(*) FROM " + MIGRATION_TABLENAME, Integer.class); + assertNotNull(count); assertTrue(count > 0); HapiMigrationDao migrationDao = new HapiMigrationDao(myDataSource, DriverTypeEnum.H2_EMBEDDED, MIGRATION_TABLENAME); myMigrationStorageSvc = new HapiMigrationStorageSvc(migrationDao); @@ -56,6 +66,62 @@ class HapiMigratorIT { System.clearProperty(HapiMigrationLock.CLEAR_LOCK_TABLE_WITH_DESCRIPTION); } + @Test + public void testInitializeSchema() { + SchemaInitializationProvider schemaInitProvider = new SchemaInitializationProvider( + "A schema", + "/hapi-migrator-it-init-schema", + "HFJ_RES_REINDEX_JOB", + true + ); + + MigrationTaskList taskList = new MigrationTaskList(); + Builder version = HapiMigrationStorageSvcTest.forVersion(taskList); + + version.initializeSchema("1", schemaInitProvider); + + Builder.BuilderAddTableByColumns nonSchemaInit = version.addTableByColumns("2", "NON_SCHEMA_INIT", "PID"); + nonSchemaInit.addColumn("PID").nonNullable().type(ColumnTypeEnum.LONG); + + Builder.BuilderAddTableByColumns schemaInit = version.addTableByColumns("3", "SCHEMA_INIT", "PID"); + schemaInit.addColumn("PID").nonNullable().type(ColumnTypeEnum.LONG); + schemaInit.withFlags().runEvenDuringSchemaInitialization(); + + HapiMigrator migrator; + MigrationResult outcome; + + /* + * Run the migrator for the first time. This should execute 2 tasks: the initial + * schema initialization, and the task set to run even during initialization. Task + * 2 should not run. + */ + migrator = buildMigrator(taskList.toTaskArray()); + outcome = migrator.migrate(); + assertThat(toTaskVersionList(outcome)).as(toTaskStatementDescriptions(outcome)).containsExactly("1", "3"); + + /* + * Run again - Nothing should happen since we've already finished the migration + */ + migrator = buildMigrator(taskList.toTaskArray()); + outcome = migrator.migrate(); + assertThat(toTaskVersionList(outcome)).as(toTaskStatementDescriptions(outcome)).isEmpty(); + + /* + * Add another pair of tasks - Both should run + */ + Builder.BuilderAddTableByColumns nonSchemaInit2 = version.addTableByColumns("4", "NON_SCHEMA_INIT_2", "PID"); + nonSchemaInit2.addColumn("PID").nonNullable().type(ColumnTypeEnum.LONG); + + Builder.BuilderAddTableByColumns schemaInit2 = version.addTableByColumns("5", "SCHEMA_INIT_2", "PID"); + schemaInit2.addColumn("PID").nonNullable().type(ColumnTypeEnum.LONG); + schemaInit2.withFlags().runEvenDuringSchemaInitialization(); + + migrator = buildMigrator(taskList.toTaskArray()); + outcome = migrator.migrate(); + assertThat(toTaskVersionList(outcome)).as(toTaskStatementDescriptions(outcome)).containsExactly("4", "5"); + + } + @Test void test_onecall_noblock() throws InterruptedException, ExecutionException { @@ -65,7 +131,7 @@ class HapiMigratorIT { HapiMigrator migrator = buildMigrator(latchMigrationTask); latchMigrationTask.setExpectedCount(1); - Future future = executor.submit(() -> migrator.migrate()); + Future future = executor.submit(migrator::migrate); latchMigrationTask.awaitExpected(); latchMigrationTask.release("1"); @@ -91,7 +157,7 @@ class HapiMigratorIT { // is released, the first one will have already run so there will be nothing to do latchMigrationTask1.setExpectedCount(1); - Future future1 = executor.submit(() -> migrator1.migrate()); + Future future1 = executor.submit(migrator1::migrate); latchMigrationTask1.awaitExpected(); // We wait until the first migration is in the middle of executing the migration task before we start the second one @@ -101,7 +167,7 @@ class HapiMigratorIT { latchMigrationTask1.release("1"); latchMigrationTask2.setExpectedCount(1); - Future future2 = executor.submit(() -> migrator2.migrate()); + Future future2 = executor.submit(migrator2::migrate); latchMigrationTask2.awaitExpected(); // This second call shouldn't be necessary, but it will help the test fail faster with a clearer error @@ -127,7 +193,7 @@ class HapiMigratorIT { { latchMigrationTask.setExpectedCount(1); - Future future = executor.submit(() -> migrator.migrate()); + Future future = executor.submit(migrator::migrate); latchMigrationTask.awaitExpected(); assertEquals(1, countLockRecords()); latchMigrationTask.release("1"); @@ -138,7 +204,7 @@ class HapiMigratorIT { } { - Future future = executor.submit(() -> migrator.migrate()); + Future future = executor.submit(migrator::migrate); MigrationResult result = future.get(); assertEquals(0, countLockRecords()); @@ -147,7 +213,6 @@ class HapiMigratorIT { } - @Test void test_oldLockFails_block() { HapiMigrationLock.setMaxRetryAttempts(0); @@ -176,7 +241,31 @@ class HapiMigratorIT { assertThat(result.succeededTasks).hasSize(1); } + @ParameterizedTest + @ValueSource(booleans = {true, false}) + void test_HeavyweightSkippable(boolean theShouldRun) throws InterruptedException, ExecutionException { + ExecutorService executor = Executors.newSingleThreadExecutor(); + LatchMigrationTask latchMigrationTask = new LatchMigrationTask("only", "1"); + latchMigrationTask.addFlag(TaskFlagEnum.HEAVYWEIGHT_SKIP_BY_DEFAULT); + + HapiMigrator migrator = buildMigrator(latchMigrationTask); + int expectedInvocations = 0; + if (theShouldRun) { + migrator.setRunHeavyweightSkippableTasks(true); + expectedInvocations = 1; + } + latchMigrationTask.setExpectedCount(expectedInvocations); + + Future future = executor.submit(migrator::migrate); + latchMigrationTask.awaitExpected(); + latchMigrationTask.release("1"); + + MigrationResult result = future.get(); + assertThat(result.succeededTasks).hasSize(expectedInvocations); + } + + @SuppressWarnings("DataFlowIssue") private int countLockRecords() { return myJdbcTemplate.queryForObject("SELECT COUNT(*) FROM " + MIGRATION_TABLENAME + " WHERE \"installed_rank\" = " + HapiMigrationLock.LOCK_PID, Integer.class); } @@ -195,8 +284,15 @@ class HapiMigratorIT { return new HapiMigrator(MIGRATION_TABLENAME, myDataSource, DriverTypeEnum.H2_EMBEDDED); } + private static @Nonnull String toTaskStatementDescriptions(MigrationResult outcome) { + return "Statements:\n * " + outcome.executedStatements.stream().map(BaseTask.ExecutedStatement::toString).collect(Collectors.joining("\n * ")); + } - private class LatchMigrationTask extends BaseTask implements IPointcutLatch { + private static @Nonnull List toTaskVersionList(MigrationResult outcome) { + return outcome.executedStatements.stream().map(BaseTask.ExecutedStatement::getSchemaVersion).toList(); + } + + private static class LatchMigrationTask extends BaseTask implements IPointcutLatch { private final PointcutLatch myLatch; private final PointcutLatch myWaitLatch; diff --git a/hapi-fhir-sql-migrate/src/test/java/ca/uhn/fhir/jpa/migrate/taskdef/AddTableByColumnTaskTest.java b/hapi-fhir-sql-migrate/src/test/java/ca/uhn/fhir/jpa/migrate/taskdef/AddTableByColumnTaskTest.java index d9944f10f08..91f39e556af 100644 --- a/hapi-fhir-sql-migrate/src/test/java/ca/uhn/fhir/jpa/migrate/taskdef/AddTableByColumnTaskTest.java +++ b/hapi-fhir-sql-migrate/src/test/java/ca/uhn/fhir/jpa/migrate/taskdef/AddTableByColumnTaskTest.java @@ -108,7 +108,7 @@ public class AddTableByColumnTaskTest extends BaseTest { return theTask1.getColumnName().compareTo(theTask2.getColumnName()); }; - final AddTableByColumnTask addTableByColumnTask = new AddTableByColumnTask(comparator); + final AddTableByColumnTask addTableByColumnTask = new AddTableByColumnTask("1", "1", comparator); addTableByColumnTask.setTableName(tableName); addTableByColumnTask.setDriverType(driverType); addTableByColumnTask.setPkColumns(Collections.singletonList(columnNameId)); diff --git a/hapi-fhir-sql-migrate/src/test/java/ca/uhn/fhir/jpa/migrate/taskdef/ModifyColumnTest.java b/hapi-fhir-sql-migrate/src/test/java/ca/uhn/fhir/jpa/migrate/taskdef/ModifyColumnTest.java index 13ca3290722..c6cdce4098e 100644 --- a/hapi-fhir-sql-migrate/src/test/java/ca/uhn/fhir/jpa/migrate/taskdef/ModifyColumnTest.java +++ b/hapi-fhir-sql-migrate/src/test/java/ca/uhn/fhir/jpa/migrate/taskdef/ModifyColumnTest.java @@ -3,6 +3,7 @@ package ca.uhn.fhir.jpa.migrate.taskdef; import ca.uhn.fhir.jpa.migrate.DriverTypeEnum; import ca.uhn.fhir.jpa.migrate.HapiMigrationException; import ca.uhn.fhir.jpa.migrate.JdbcUtils; +import ca.uhn.fhir.jpa.migrate.tasks.api.TaskFlagEnum; import jakarta.annotation.Nonnull; import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.MethodSource; @@ -17,6 +18,7 @@ import static org.junit.jupiter.api.Assertions.assertTrue; import static org.junit.jupiter.api.Assertions.fail; +@SuppressWarnings("SqlDialectInspection") public class ModifyColumnTest extends BaseTest { @ParameterizedTest(name = "{index}: {0}") @@ -232,14 +234,11 @@ public class ModifyColumnTest extends BaseTest { @SuppressWarnings("EnumSwitchStatementWhichMissesCases") @Nonnull private JdbcUtils.ColumnType getLongColumnType(Supplier theTestDatabaseDetails) { - switch (theTestDatabaseDetails.get().getDriverType()) { - case H2_EMBEDDED: - return new JdbcUtils.ColumnType(ColumnTypeEnum.LONG, 64); - case DERBY_EMBEDDED: - return new JdbcUtils.ColumnType(ColumnTypeEnum.LONG, 19); - default: - throw new UnsupportedOperationException(); - } + return switch (theTestDatabaseDetails.get().getDriverType()) { + case H2_EMBEDDED -> new JdbcUtils.ColumnType(ColumnTypeEnum.LONG, 64); + case DERBY_EMBEDDED -> new JdbcUtils.ColumnType(ColumnTypeEnum.LONG, 19); + default -> throw new UnsupportedOperationException(); + }; } @ParameterizedTest(name = "{index}: {0}") @@ -270,11 +269,11 @@ public class ModifyColumnTest extends BaseTest { executeSql("insert into SOMETABLE (TEXTCOL) values ('HELLO')"); ModifyColumnTask task = new ModifyColumnTask("1", "1"); + task.addFlag(TaskFlagEnum.FAILURE_ALLOWED); task.setTableName("SOMETABLE"); task.setColumnName("TEXTCOL"); task.setColumnType(ColumnTypeEnum.LONG); task.setNullable(true); - task.setFailureAllowed(true); getMigrator().addTask(task); getMigrator().migrate(); diff --git a/hapi-fhir-sql-migrate/src/test/resources/hapi-migrator-it-init-schema/h2.sql b/hapi-fhir-sql-migrate/src/test/resources/hapi-migrator-it-init-schema/h2.sql new file mode 100644 index 00000000000..1105f9aa68f --- /dev/null +++ b/hapi-fhir-sql-migrate/src/test/resources/hapi-migrator-it-init-schema/h2.sql @@ -0,0 +1,6 @@ + +create table HFJ_RES_REINDEX_JOB ( + PID bigint not null, + RES_TYPE varchar(100), + primary key (PID) +); diff --git a/hapi-fhir-storage-batch2-jobs/pom.xml b/hapi-fhir-storage-batch2-jobs/pom.xml index 951683d6335..f1113e6dca9 100644 --- a/hapi-fhir-storage-batch2-jobs/pom.xml +++ b/hapi-fhir-storage-batch2-jobs/pom.xml @@ -5,7 +5,7 @@ ca.uhn.hapi.fhir hapi-deployable-pom - 7.3.7-SNAPSHOT + 7.3.8-SNAPSHOT ../hapi-deployable-pom/pom.xml diff --git a/hapi-fhir-storage-batch2-test-utilities/pom.xml b/hapi-fhir-storage-batch2-test-utilities/pom.xml index 499a9c799f5..0ef577ce1d2 100644 --- a/hapi-fhir-storage-batch2-test-utilities/pom.xml +++ b/hapi-fhir-storage-batch2-test-utilities/pom.xml @@ -7,7 +7,7 @@ ca.uhn.hapi.fhir hapi-deployable-pom - 7.3.7-SNAPSHOT + 7.3.8-SNAPSHOT ../hapi-deployable-pom/pom.xml diff --git a/hapi-fhir-storage-batch2/pom.xml b/hapi-fhir-storage-batch2/pom.xml index b9445f9c590..d91e6d799af 100644 --- a/hapi-fhir-storage-batch2/pom.xml +++ b/hapi-fhir-storage-batch2/pom.xml @@ -7,7 +7,7 @@ ca.uhn.hapi.fhir hapi-deployable-pom - 7.3.7-SNAPSHOT + 7.3.8-SNAPSHOT ../hapi-deployable-pom/pom.xml diff --git a/hapi-fhir-storage-cr/pom.xml b/hapi-fhir-storage-cr/pom.xml index 8c717536d95..a31d5be77fe 100644 --- a/hapi-fhir-storage-cr/pom.xml +++ b/hapi-fhir-storage-cr/pom.xml @@ -7,7 +7,7 @@ ca.uhn.hapi.fhir hapi-deployable-pom - 7.3.7-SNAPSHOT + 7.3.8-SNAPSHOT ../hapi-deployable-pom/pom.xml diff --git a/hapi-fhir-storage-mdm/pom.xml b/hapi-fhir-storage-mdm/pom.xml index ebd4cf3cf89..56ae8033745 100644 --- a/hapi-fhir-storage-mdm/pom.xml +++ b/hapi-fhir-storage-mdm/pom.xml @@ -5,7 +5,7 @@ ca.uhn.hapi.fhir hapi-deployable-pom - 7.3.7-SNAPSHOT + 7.3.8-SNAPSHOT ../hapi-deployable-pom/pom.xml diff --git a/hapi-fhir-storage-test-utilities/pom.xml b/hapi-fhir-storage-test-utilities/pom.xml index b67d78bb04d..3fe9e63c42f 100644 --- a/hapi-fhir-storage-test-utilities/pom.xml +++ b/hapi-fhir-storage-test-utilities/pom.xml @@ -5,7 +5,7 @@ ca.uhn.hapi.fhir hapi-deployable-pom - 7.3.7-SNAPSHOT + 7.3.8-SNAPSHOT ../hapi-deployable-pom/pom.xml diff --git a/hapi-fhir-storage/pom.xml b/hapi-fhir-storage/pom.xml index 5aa1daab056..5374ed9f355 100644 --- a/hapi-fhir-storage/pom.xml +++ b/hapi-fhir-storage/pom.xml @@ -5,7 +5,7 @@ ca.uhn.hapi.fhir hapi-deployable-pom - 7.3.7-SNAPSHOT + 7.3.8-SNAPSHOT ../hapi-deployable-pom/pom.xml diff --git a/hapi-fhir-structures-dstu2.1/pom.xml b/hapi-fhir-structures-dstu2.1/pom.xml index d83f98ec648..ad0198154b0 100644 --- a/hapi-fhir-structures-dstu2.1/pom.xml +++ b/hapi-fhir-structures-dstu2.1/pom.xml @@ -5,7 +5,7 @@ ca.uhn.hapi.fhir hapi-deployable-pom - 7.3.7-SNAPSHOT + 7.3.8-SNAPSHOT ../hapi-deployable-pom/pom.xml diff --git a/hapi-fhir-structures-dstu2/pom.xml b/hapi-fhir-structures-dstu2/pom.xml index 5d8654e87ba..400c371bdb2 100644 --- a/hapi-fhir-structures-dstu2/pom.xml +++ b/hapi-fhir-structures-dstu2/pom.xml @@ -4,7 +4,7 @@ ca.uhn.hapi.fhir hapi-deployable-pom - 7.3.7-SNAPSHOT + 7.3.8-SNAPSHOT ../hapi-deployable-pom/pom.xml diff --git a/hapi-fhir-structures-dstu3/pom.xml b/hapi-fhir-structures-dstu3/pom.xml index 11a5d0e063a..daf1d9ab7b7 100644 --- a/hapi-fhir-structures-dstu3/pom.xml +++ b/hapi-fhir-structures-dstu3/pom.xml @@ -5,7 +5,7 @@ ca.uhn.hapi.fhir hapi-deployable-pom - 7.3.7-SNAPSHOT + 7.3.8-SNAPSHOT ../hapi-deployable-pom/pom.xml diff --git a/hapi-fhir-structures-hl7org-dstu2/pom.xml b/hapi-fhir-structures-hl7org-dstu2/pom.xml index 1d849cbeba1..95115731b44 100644 --- a/hapi-fhir-structures-hl7org-dstu2/pom.xml +++ b/hapi-fhir-structures-hl7org-dstu2/pom.xml @@ -5,7 +5,7 @@ ca.uhn.hapi.fhir hapi-deployable-pom - 7.3.7-SNAPSHOT + 7.3.8-SNAPSHOT ../hapi-deployable-pom/pom.xml diff --git a/hapi-fhir-structures-r4/pom.xml b/hapi-fhir-structures-r4/pom.xml index 4a90cf8be3d..c5622f8473c 100644 --- a/hapi-fhir-structures-r4/pom.xml +++ b/hapi-fhir-structures-r4/pom.xml @@ -5,7 +5,7 @@ ca.uhn.hapi.fhir hapi-deployable-pom - 7.3.7-SNAPSHOT + 7.3.8-SNAPSHOT ../hapi-deployable-pom/pom.xml diff --git a/hapi-fhir-structures-r4b/pom.xml b/hapi-fhir-structures-r4b/pom.xml index cca72114d9c..9d400c1fa0c 100644 --- a/hapi-fhir-structures-r4b/pom.xml +++ b/hapi-fhir-structures-r4b/pom.xml @@ -5,7 +5,7 @@ ca.uhn.hapi.fhir hapi-deployable-pom - 7.3.7-SNAPSHOT + 7.3.8-SNAPSHOT ../hapi-deployable-pom/pom.xml diff --git a/hapi-fhir-structures-r5/pom.xml b/hapi-fhir-structures-r5/pom.xml index 99f5b18a411..168f2c2c4e7 100644 --- a/hapi-fhir-structures-r5/pom.xml +++ b/hapi-fhir-structures-r5/pom.xml @@ -5,7 +5,7 @@ ca.uhn.hapi.fhir hapi-deployable-pom - 7.3.7-SNAPSHOT + 7.3.8-SNAPSHOT ../hapi-deployable-pom/pom.xml diff --git a/hapi-fhir-test-utilities/pom.xml b/hapi-fhir-test-utilities/pom.xml index bcc6eda773a..365d17d1ba9 100644 --- a/hapi-fhir-test-utilities/pom.xml +++ b/hapi-fhir-test-utilities/pom.xml @@ -5,7 +5,7 @@ ca.uhn.hapi.fhir hapi-deployable-pom - 7.3.7-SNAPSHOT + 7.3.8-SNAPSHOT ../hapi-deployable-pom/pom.xml diff --git a/hapi-fhir-test-utilities/src/main/java/ca/uhn/fhir/test/utilities/jpa/JpaModelScannerAndVerifier.java b/hapi-fhir-test-utilities/src/main/java/ca/uhn/fhir/test/utilities/jpa/JpaModelScannerAndVerifier.java index 224d2c42371..a9f4e2588aa 100644 --- a/hapi-fhir-test-utilities/src/main/java/ca/uhn/fhir/test/utilities/jpa/JpaModelScannerAndVerifier.java +++ b/hapi-fhir-test-utilities/src/main/java/ca/uhn/fhir/test/utilities/jpa/JpaModelScannerAndVerifier.java @@ -187,7 +187,8 @@ public class JpaModelScannerAndVerifier { scan(nextField, theNames, theIsSuperClass, isView); Id id = nextField.getAnnotation(Id.class); - if (id != null) { + boolean isId = id != null; + if (isId) { Validate.isTrue(!foundId, "Multiple fields annotated with @Id"); foundId = true; @@ -241,12 +242,24 @@ public class JpaModelScannerAndVerifier { int columnLength = 16; String columnName = null; + boolean nullable = false; if (hasColumn) { - columnName = nextField.getAnnotation(Column.class).name(); - columnLength = nextField.getAnnotation(Column.class).length(); + Column column = nextField.getAnnotation(Column.class); + columnName = column.name(); + columnLength = column.length(); + nullable = column.nullable(); } if (hasJoinColumn) { - columnName = nextField.getAnnotation(JoinColumn.class).name(); + JoinColumn joinColumn = nextField.getAnnotation(JoinColumn.class); + columnName = joinColumn.name(); + nullable = joinColumn.nullable(); + } + if (isId) { + nullable = false; + } + + if (nullable && !isView) { + Validate.isTrue(!nextField.getType().isPrimitive(), "Field [%s] has a nullable primitive type: %s", nextField.getName(), nextField.getType()); } if (columnName != null) { diff --git a/hapi-fhir-testpage-overlay/pom.xml b/hapi-fhir-testpage-overlay/pom.xml index 7834ca35c4a..22081db8948 100644 --- a/hapi-fhir-testpage-overlay/pom.xml +++ b/hapi-fhir-testpage-overlay/pom.xml @@ -5,7 +5,7 @@ ca.uhn.hapi.fhir hapi-fhir - 7.3.7-SNAPSHOT + 7.3.8-SNAPSHOT ../pom.xml diff --git a/hapi-fhir-validation-resources-dstu2.1/pom.xml b/hapi-fhir-validation-resources-dstu2.1/pom.xml index 0790e96f1df..8cf8d41169b 100644 --- a/hapi-fhir-validation-resources-dstu2.1/pom.xml +++ b/hapi-fhir-validation-resources-dstu2.1/pom.xml @@ -4,7 +4,7 @@ ca.uhn.hapi.fhir hapi-deployable-pom - 7.3.7-SNAPSHOT + 7.3.8-SNAPSHOT ../hapi-deployable-pom/pom.xml diff --git a/hapi-fhir-validation-resources-dstu2/pom.xml b/hapi-fhir-validation-resources-dstu2/pom.xml index 71f137c80ba..05c35297aed 100644 --- a/hapi-fhir-validation-resources-dstu2/pom.xml +++ b/hapi-fhir-validation-resources-dstu2/pom.xml @@ -4,7 +4,7 @@ ca.uhn.hapi.fhir hapi-deployable-pom - 7.3.7-SNAPSHOT + 7.3.8-SNAPSHOT ../hapi-deployable-pom/pom.xml diff --git a/hapi-fhir-validation-resources-dstu3/pom.xml b/hapi-fhir-validation-resources-dstu3/pom.xml index 1d0819a3af0..ace33e92751 100644 --- a/hapi-fhir-validation-resources-dstu3/pom.xml +++ b/hapi-fhir-validation-resources-dstu3/pom.xml @@ -4,7 +4,7 @@ ca.uhn.hapi.fhir hapi-deployable-pom - 7.3.7-SNAPSHOT + 7.3.8-SNAPSHOT ../hapi-deployable-pom/pom.xml diff --git a/hapi-fhir-validation-resources-r4/pom.xml b/hapi-fhir-validation-resources-r4/pom.xml index b81fd93c621..2b43d5b4223 100644 --- a/hapi-fhir-validation-resources-r4/pom.xml +++ b/hapi-fhir-validation-resources-r4/pom.xml @@ -4,7 +4,7 @@ ca.uhn.hapi.fhir hapi-deployable-pom - 7.3.7-SNAPSHOT + 7.3.8-SNAPSHOT ../hapi-deployable-pom/pom.xml diff --git a/hapi-fhir-validation-resources-r4b/pom.xml b/hapi-fhir-validation-resources-r4b/pom.xml index f0b2c9fe7b1..256726e6a6e 100644 --- a/hapi-fhir-validation-resources-r4b/pom.xml +++ b/hapi-fhir-validation-resources-r4b/pom.xml @@ -4,7 +4,7 @@ ca.uhn.hapi.fhir hapi-deployable-pom - 7.3.7-SNAPSHOT + 7.3.8-SNAPSHOT ../hapi-deployable-pom/pom.xml diff --git a/hapi-fhir-validation-resources-r5/pom.xml b/hapi-fhir-validation-resources-r5/pom.xml index 61828536241..89d01c817e5 100644 --- a/hapi-fhir-validation-resources-r5/pom.xml +++ b/hapi-fhir-validation-resources-r5/pom.xml @@ -4,7 +4,7 @@ ca.uhn.hapi.fhir hapi-deployable-pom - 7.3.7-SNAPSHOT + 7.3.8-SNAPSHOT ../hapi-deployable-pom/pom.xml diff --git a/hapi-fhir-validation/pom.xml b/hapi-fhir-validation/pom.xml index 91a608190b8..867ac8fab61 100644 --- a/hapi-fhir-validation/pom.xml +++ b/hapi-fhir-validation/pom.xml @@ -5,7 +5,7 @@ ca.uhn.hapi.fhir hapi-deployable-pom - 7.3.7-SNAPSHOT + 7.3.8-SNAPSHOT ../hapi-deployable-pom/pom.xml diff --git a/hapi-tinder-plugin/pom.xml b/hapi-tinder-plugin/pom.xml index 454a4537616..5be411f6d6d 100644 --- a/hapi-tinder-plugin/pom.xml +++ b/hapi-tinder-plugin/pom.xml @@ -5,7 +5,7 @@ ca.uhn.hapi.fhir hapi-fhir - 7.3.7-SNAPSHOT + 7.3.8-SNAPSHOT ../pom.xml diff --git a/hapi-tinder-test/pom.xml b/hapi-tinder-test/pom.xml index d1f52593bf5..73cb280486c 100644 --- a/hapi-tinder-test/pom.xml +++ b/hapi-tinder-test/pom.xml @@ -4,7 +4,7 @@ ca.uhn.hapi.fhir hapi-fhir - 7.3.7-SNAPSHOT + 7.3.8-SNAPSHOT ../pom.xml diff --git a/pom.xml b/pom.xml index 915a1c75af2..60eabdcfcaf 100644 --- a/pom.xml +++ b/pom.xml @@ -8,7 +8,7 @@ ca.uhn.hapi.fhir hapi-fhir pom - 7.3.7-SNAPSHOT + 7.3.8-SNAPSHOT HAPI-FHIR An open-source implementation of the FHIR specification in Java. diff --git a/tests/hapi-fhir-base-test-jaxrsserver-kotlin/pom.xml b/tests/hapi-fhir-base-test-jaxrsserver-kotlin/pom.xml index 99ebdb57ef7..e190bc932e2 100644 --- a/tests/hapi-fhir-base-test-jaxrsserver-kotlin/pom.xml +++ b/tests/hapi-fhir-base-test-jaxrsserver-kotlin/pom.xml @@ -7,7 +7,7 @@ ca.uhn.hapi.fhir hapi-fhir - 7.3.7-SNAPSHOT + 7.3.8-SNAPSHOT ../../pom.xml diff --git a/tests/hapi-fhir-base-test-mindeps-client/pom.xml b/tests/hapi-fhir-base-test-mindeps-client/pom.xml index c7a989f642b..87b69d1ce57 100644 --- a/tests/hapi-fhir-base-test-mindeps-client/pom.xml +++ b/tests/hapi-fhir-base-test-mindeps-client/pom.xml @@ -4,7 +4,7 @@ ca.uhn.hapi.fhir hapi-fhir - 7.3.7-SNAPSHOT + 7.3.8-SNAPSHOT ../../pom.xml diff --git a/tests/hapi-fhir-base-test-mindeps-server/pom.xml b/tests/hapi-fhir-base-test-mindeps-server/pom.xml index da5b9aadeeb..223943c80a0 100644 --- a/tests/hapi-fhir-base-test-mindeps-server/pom.xml +++ b/tests/hapi-fhir-base-test-mindeps-server/pom.xml @@ -5,7 +5,7 @@ ca.uhn.hapi.fhir hapi-fhir - 7.3.7-SNAPSHOT + 7.3.8-SNAPSHOT ../../pom.xml