diff --git a/hapi-deployable-pom/pom.xml b/hapi-deployable-pom/pom.xml
index bc427b6b7bc8..26b28e3c3e60 100644
--- a/hapi-deployable-pom/pom.xml
+++ b/hapi-deployable-pom/pom.xml
@@ -228,6 +228,7 @@
process-sources
+ true
false
apache_v2
true
diff --git a/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/7_2_0/5748-avoid-lob-usage-in-batch2-and-search.yaml b/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/7_2_0/5748-avoid-lob-usage-in-batch2-and-search.yaml
new file mode 100644
index 000000000000..802c32091b88
--- /dev/null
+++ b/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/7_2_0/5748-avoid-lob-usage-in-batch2-and-search.yaml
@@ -0,0 +1,9 @@
+---
+type: perf
+issue: 5748
+backport: 6.8.6
+title: "In the JPA server, several database columns related to Batch2 jobs and searching
+ have been reworked so that they no will longer use LOB datatypes going forward. This
+ is a significant advantage on Postgresql databases as it removes a significant use
+ of the inefficient `pg_largeobject` table, and should yield performance boosts for
+ MSSQL as well."
diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/data/IBatch2JobInstanceRepository.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/data/IBatch2JobInstanceRepository.java
index ee575c644785..18b8250472f0 100644
--- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/data/IBatch2JobInstanceRepository.java
+++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/data/IBatch2JobInstanceRepository.java
@@ -55,14 +55,15 @@ int updateInstanceStatusIfIn(
int updateWorkChunksPurgedTrue(@Param("id") String theInstanceId);
@Query(
- "SELECT b from Batch2JobInstanceEntity b WHERE b.myDefinitionId = :defId AND b.myParamsJson = :params AND b.myStatus IN( :stats )")
+ "SELECT b from Batch2JobInstanceEntity b WHERE b.myDefinitionId = :defId AND (b.myParamsJson = :params OR b.myParamsJsonVc = :params) AND b.myStatus IN( :stats )")
List findInstancesByJobIdParamsAndStatus(
@Param("defId") String theDefinitionId,
@Param("params") String theParams,
@Param("stats") Set theStatus,
Pageable thePageable);
- @Query("SELECT b from Batch2JobInstanceEntity b WHERE b.myDefinitionId = :defId AND b.myParamsJson = :params")
+ @Query(
+ "SELECT b from Batch2JobInstanceEntity b WHERE b.myDefinitionId = :defId AND (b.myParamsJson = :params OR b.myParamsJsonVc = :params)")
List findInstancesByJobIdAndParams(
@Param("defId") String theDefinitionId, @Param("params") String theParams, Pageable thePageable);
diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/data/IBatch2WorkChunkRepository.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/data/IBatch2WorkChunkRepository.java
index 8e6fea0339dd..1ce326cefd65 100644
--- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/data/IBatch2WorkChunkRepository.java
+++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/data/IBatch2WorkChunkRepository.java
@@ -65,7 +65,7 @@ Stream fetchChunksForStep(
@Modifying
@Query("UPDATE Batch2WorkChunkEntity e SET e.myStatus = :status, e.myEndTime = :et, "
- + "e.myRecordsProcessed = :rp, e.myErrorCount = e.myErrorCount + :errorRetries, e.mySerializedData = null, "
+ + "e.myRecordsProcessed = :rp, e.myErrorCount = e.myErrorCount + :errorRetries, e.mySerializedData = null, e.mySerializedDataVc = null, "
+ "e.myWarningMessage = :warningMessage WHERE e.myId = :id")
void updateChunkStatusAndClearDataForEndSuccess(
@Param("id") String theChunkId,
@@ -77,7 +77,7 @@ void updateChunkStatusAndClearDataForEndSuccess(
@Modifying
@Query(
- "UPDATE Batch2WorkChunkEntity e SET e.myStatus = :status, e.myEndTime = :et, e.mySerializedData = null, e.myErrorMessage = :em WHERE e.myId IN(:ids)")
+ "UPDATE Batch2WorkChunkEntity e SET e.myStatus = :status, e.myEndTime = :et, e.mySerializedData = null, e.mySerializedDataVc = null, e.myErrorMessage = :em WHERE e.myId IN(:ids)")
void updateAllChunksForInstanceStatusClearDataAndSetError(
@Param("ids") List theChunkIds,
@Param("et") Date theEndTime,
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 dd9205a8f306..889ec2aa95a1 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
@@ -21,6 +21,7 @@
import ca.uhn.fhir.batch2.model.JobDefinition;
import ca.uhn.fhir.batch2.model.StatusEnum;
+import ca.uhn.fhir.jpa.model.util.JpaConstants;
import org.apache.commons.lang3.builder.ToStringBuilder;
import org.apache.commons.lang3.builder.ToStringStyle;
@@ -43,6 +44,7 @@
import static ca.uhn.fhir.batch2.model.JobDefinition.ID_MAX_LENGTH;
import static ca.uhn.fhir.jpa.entity.Batch2WorkChunkEntity.ERROR_MSG_MAX_LENGTH;
import static ca.uhn.fhir.jpa.entity.Batch2WorkChunkEntity.WARNING_MSG_MAX_LENGTH;
+import static ca.uhn.fhir.jpa.model.entity.ResourceHistoryTable.RES_TEXT_VC_MAX_LENGTH;
import static org.apache.commons.lang3.StringUtils.left;
@Entity
@@ -93,13 +95,18 @@ public class Batch2JobInstanceEntity implements Serializable {
@Column(name = "FAST_TRACKING", nullable = true)
private Boolean myFastTracking;
+ // TODO: VC column added in 7.2.0 - Remove non-VC column later
@Column(name = "PARAMS_JSON", length = PARAMS_JSON_MAX_LENGTH, nullable = true)
private String myParamsJson;
- @Lob
+ @Lob // TODO: VC column added in 7.2.0 - Remove non-VC column later
@Column(name = "PARAMS_JSON_LOB", nullable = true)
private String myParamsJsonLob;
+ @Column(name = "PARAMS_JSON_VC", nullable = true, length = RES_TEXT_VC_MAX_LENGTH)
+ @org.hibernate.annotations.Type(type = JpaConstants.ORG_HIBERNATE_TYPE_TEXT_TYPE)
+ private String myParamsJsonVc;
+
@Column(name = "CMB_RECS_PROCESSED", nullable = true)
private Integer myCombinedRecordsProcessed;
@@ -134,11 +141,15 @@ public class Batch2JobInstanceEntity implements Serializable {
* Any output from the job can be held in this column
* Even serialized json
*/
- @Lob
+ @Lob // TODO: VC column added in 7.2.0 - Remove non-VC column later
@Basic(fetch = FetchType.LAZY)
@Column(name = "REPORT", nullable = true, length = Integer.MAX_VALUE - 1)
private String myReport;
+ @Column(name = "REPORT_VC", nullable = true, length = RES_TEXT_VC_MAX_LENGTH)
+ @org.hibernate.annotations.Type(type = JpaConstants.ORG_HIBERNATE_TYPE_TEXT_TYPE)
+ private String myReportVc;
+
public String getCurrentGatedStepId() {
return myCurrentGatedStepId;
}
@@ -252,6 +263,9 @@ public void setStatus(StatusEnum theStatus) {
}
public String getParams() {
+ if (myParamsJsonVc != null) {
+ return myParamsJsonVc;
+ }
if (myParamsJsonLob != null) {
return myParamsJsonLob;
}
@@ -259,13 +273,9 @@ public String getParams() {
}
public void setParams(String theParams) {
+ myParamsJsonVc = theParams;
myParamsJsonLob = null;
myParamsJson = null;
- if (theParams != null && theParams.length() > PARAMS_JSON_MAX_LENGTH) {
- myParamsJsonLob = theParams;
- } else {
- myParamsJson = theParams;
- }
}
public boolean getWorkChunksPurged() {
@@ -301,11 +311,12 @@ public void setEstimatedTimeRemaining(String theEstimatedTimeRemaining) {
}
public String getReport() {
- return myReport;
+ return myReportVc != null ? myReportVc : myReport;
}
public void setReport(String theReport) {
- myReport = theReport;
+ myReportVc = theReport;
+ myReport = null;
}
public String getWarningMessages() {
@@ -336,7 +347,7 @@ public String toString() {
.append("progress", myProgress)
.append("errorMessage", myErrorMessage)
.append("estimatedTimeRemaining", myEstimatedTimeRemaining)
- .append("report", myReport)
+ .append("report", getReport())
.append("warningMessages", myWarningMessages)
.toString();
}
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 e65c8814faa7..338c6a883a9b 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
@@ -20,6 +20,7 @@
package ca.uhn.fhir.jpa.entity;
import ca.uhn.fhir.batch2.model.WorkChunkStatusEnum;
+import ca.uhn.fhir.jpa.model.util.JpaConstants;
import org.apache.commons.lang3.builder.ToStringBuilder;
import org.apache.commons.lang3.builder.ToStringStyle;
@@ -44,6 +45,7 @@
import static ca.uhn.fhir.batch2.model.JobDefinition.ID_MAX_LENGTH;
import static ca.uhn.fhir.jpa.entity.Batch2JobInstanceEntity.STATUS_MAX_LENGTH;
+import static ca.uhn.fhir.jpa.model.entity.ResourceHistoryTable.RES_TEXT_VC_MAX_LENGTH;
import static org.apache.commons.lang3.StringUtils.left;
@Entity
@@ -92,11 +94,15 @@ public class Batch2WorkChunkEntity implements Serializable {
@Column(name = "TGT_STEP_ID", length = ID_MAX_LENGTH, nullable = false)
private String myTargetStepId;
- @Lob
+ @Lob // TODO: VC column added in 7.2.0 - Remove non-VC column later
@Basic(fetch = FetchType.LAZY)
@Column(name = "CHUNK_DATA", nullable = true, length = Integer.MAX_VALUE - 1)
private String mySerializedData;
+ @Column(name = "CHUNK_DATA_VC", nullable = true, length = RES_TEXT_VC_MAX_LENGTH)
+ @org.hibernate.annotations.Type(type = JpaConstants.ORG_HIBERNATE_TYPE_TEXT_TYPE)
+ private String mySerializedDataVc;
+
@Column(name = "STAT", length = STATUS_MAX_LENGTH, nullable = false)
@Enumerated(EnumType.STRING)
private WorkChunkStatusEnum myStatus;
@@ -263,11 +269,12 @@ public void setTargetStepId(String theTargetStepId) {
}
public String getSerializedData() {
- return mySerializedData;
+ return mySerializedDataVc != null ? mySerializedDataVc : mySerializedData;
}
public void setSerializedData(String theSerializedData) {
- mySerializedData = theSerializedData;
+ mySerializedData = null;
+ mySerializedDataVc = theSerializedData;
}
public WorkChunkStatusEnum getStatus() {
@@ -309,7 +316,7 @@ public String toString() {
.append("updateTime", myUpdateTime)
.append("recordsProcessed", myRecordsProcessed)
.append("targetStepId", myTargetStepId)
- .append("serializedData", mySerializedData)
+ .append("serializedData", getSerializedData())
.append("status", myStatus)
.append("errorMessage", myErrorMessage)
.append("warningMessage", myWarningMessage)
diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/entity/BulkImportJobFileEntity.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/entity/BulkImportJobFileEntity.java
index 11a2998989f1..7beb524caab3 100644
--- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/entity/BulkImportJobFileEntity.java
+++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/entity/BulkImportJobFileEntity.java
@@ -20,6 +20,7 @@
package ca.uhn.fhir.jpa.entity;
import ca.uhn.fhir.jpa.bulk.imprt.model.BulkImportJobFileJson;
+import ca.uhn.fhir.jpa.model.util.JpaConstants;
import java.io.Serializable;
import java.nio.charset.StandardCharsets;
@@ -36,6 +37,7 @@
import javax.persistence.SequenceGenerator;
import javax.persistence.Table;
+import static ca.uhn.fhir.jpa.model.entity.ResourceHistoryTable.RES_TEXT_VC_MAX_LENGTH;
import static org.apache.commons.lang3.StringUtils.left;
@Entity
@@ -66,10 +68,14 @@ public class BulkImportJobFileEntity implements Serializable {
@Column(name = "FILE_DESCRIPTION", nullable = true, length = MAX_DESCRIPTION_LENGTH)
private String myFileDescription;
- @Lob
- @Column(name = "JOB_CONTENTS", nullable = false)
+ @Lob // TODO: VC column added in 7.2.0 - Remove non-VC column later
+ @Column(name = "JOB_CONTENTS", nullable = true)
private byte[] myContents;
+ @Column(name = "JOB_CONTENTS_VC", nullable = true, length = RES_TEXT_VC_MAX_LENGTH)
+ @org.hibernate.annotations.Type(type = JpaConstants.ORG_HIBERNATE_TYPE_TEXT_TYPE)
+ private String myContentsVc;
+
@Column(name = "TENANT_NAME", nullable = true, length = PartitionEntity.MAX_NAME_LENGTH)
private String myTenantName;
@@ -98,11 +104,16 @@ public void setFileSequence(int theFileSequence) {
}
public String getContents() {
- return new String(myContents, StandardCharsets.UTF_8);
+ if (myContentsVc != null) {
+ return myContentsVc;
+ } else {
+ return new String(myContents, StandardCharsets.UTF_8);
+ }
}
public void setContents(String theContents) {
- myContents = theContents.getBytes(StandardCharsets.UTF_8);
+ myContentsVc = theContents;
+ myContents = null;
}
public BulkImportJobFileJson toJson() {
diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/entity/Search.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/entity/Search.java
index 7da0b2e22481..9f923b4d570a 100644
--- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/entity/Search.java
+++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/entity/Search.java
@@ -21,6 +21,7 @@
import ca.uhn.fhir.interceptor.model.RequestPartitionId;
import ca.uhn.fhir.jpa.model.search.SearchStatusEnum;
+import ca.uhn.fhir.jpa.model.util.JpaConstants;
import ca.uhn.fhir.jpa.searchparam.SearchParameterMap;
import ca.uhn.fhir.model.api.Include;
import ca.uhn.fhir.rest.param.DateRangeParam;
@@ -30,6 +31,7 @@
import org.apache.commons.lang3.SerializationUtils;
import org.apache.commons.lang3.builder.ToStringBuilder;
import org.hibernate.annotations.OptimisticLock;
+import org.hibernate.annotations.Type;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -64,6 +66,7 @@
import javax.persistence.UniqueConstraint;
import javax.persistence.Version;
+import static ca.uhn.fhir.jpa.model.entity.ResourceHistoryTable.RES_TEXT_VC_MAX_LENGTH;
import static org.apache.commons.lang3.StringUtils.left;
@Entity
@@ -139,14 +142,22 @@ public class Search implements ICachedSearchDetails, Serializable {
@Column(name = "RESOURCE_TYPE", length = 200, nullable = true)
private String myResourceType;
+
/**
* Note that this field may have the request partition IDs prepended to it
*/
- @Lob()
+ @Lob // TODO: VC column added in 7.2.0 - Remove non-VC column later
@Basic(fetch = FetchType.LAZY)
@Column(name = "SEARCH_QUERY_STRING", nullable = true, updatable = false, length = MAX_SEARCH_QUERY_STRING)
private String mySearchQueryString;
+ /**
+ * Note that this field may have the request partition IDs prepended to it
+ */
+ @Column(name = "SEARCH_QUERY_STRING_VC", nullable = true, length = RES_TEXT_VC_MAX_LENGTH)
+ @org.hibernate.annotations.Type(type = JpaConstants.ORG_HIBERNATE_TYPE_TEXT_TYPE)
+ private String mySearchQueryStringVc;
+
@Column(name = "SEARCH_QUERY_STRING_HASH", nullable = true, updatable = false)
private Integer mySearchQueryStringHash;
@@ -169,10 +180,14 @@ public class Search implements ICachedSearchDetails, Serializable {
@Column(name = "OPTLOCK_VERSION", nullable = true)
private Integer myVersion;
- @Lob
+ @Lob // TODO: VC column added in 7.2.0 - Remove non-VC column later
@Column(name = "SEARCH_PARAM_MAP", nullable = true)
private byte[] mySearchParameterMap;
+ @Column(name = "SEARCH_PARAM_MAP_BIN", nullable = true, length = JpaConstants.ORG_HIBERNATE_TYPE_BINARY_TYPE_LENGTH)
+ @Type(type = JpaConstants.ORG_HIBERNATE_TYPE_BINARY_TYPE)
+ private byte[] mySearchParameterMapBin;
+
@Transient
private transient SearchParameterMap mySearchParameterMapTransient;
@@ -347,7 +362,7 @@ public void setResourceType(String theResourceType) {
* Note that this field may have the request partition IDs prepended to it
*/
public String getSearchQueryString() {
- return mySearchQueryString;
+ return mySearchQueryStringVc != null ? mySearchQueryStringVc : mySearchQueryString;
}
public void setSearchQueryString(String theSearchQueryString, RequestPartitionId theRequestPartitionId) {
@@ -359,12 +374,13 @@ public void setSearchQueryString(String theSearchQueryString, RequestPartitionId
// We want this field to always have a wide distribution of values in order
// to avoid optimizers avoiding using it if it has lots of nulls, so in the
// case of null, just put a value that will never be hit
- mySearchQueryString = UUID.randomUUID().toString();
+ mySearchQueryStringVc = UUID.randomUUID().toString();
} else {
- mySearchQueryString = searchQueryString;
+ mySearchQueryStringVc = searchQueryString;
}
- mySearchQueryStringHash = mySearchQueryString.hashCode();
+ mySearchQueryString = null;
+ mySearchQueryStringHash = mySearchQueryStringVc.hashCode();
}
public SearchTypeEnum getSearchType() {
@@ -463,8 +479,12 @@ public Optional getSearchParameterMap() {
return Optional.of(mySearchParameterMapTransient);
}
SearchParameterMap searchParameterMap = null;
- if (mySearchParameterMap != null) {
- searchParameterMap = SerializationUtils.deserialize(mySearchParameterMap);
+ byte[] searchParameterMapSerialized = mySearchParameterMapBin;
+ if (searchParameterMapSerialized == null) {
+ searchParameterMapSerialized = mySearchParameterMap;
+ }
+ if (searchParameterMapSerialized != null) {
+ searchParameterMap = SerializationUtils.deserialize(searchParameterMapSerialized);
mySearchParameterMapTransient = searchParameterMap;
}
return Optional.ofNullable(searchParameterMap);
@@ -472,7 +492,8 @@ public Optional getSearchParameterMap() {
public void setSearchParameterMap(SearchParameterMap theSearchParameterMap) {
mySearchParameterMapTransient = theSearchParameterMap;
- mySearchParameterMap = SerializationUtils.serialize(theSearchParameterMap);
+ mySearchParameterMapBin = SerializationUtils.serialize(theSearchParameterMap);
+ mySearchParameterMap = null;
}
@Override
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 9d2734a3217a..1909668f093d 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
@@ -93,6 +93,45 @@ public HapiFhirJpaMigrationTasks(Set theFlags) {
init640_after_20230126();
init660();
init680();
+ init680_Part2();
+ }
+
+ private void init680_Part2() {
+ Builder version = forVersion(VersionEnum.V6_8_0);
+
+ // Add additional LOB migration columns
+ version.onTable("BT2_JOB_INSTANCE")
+ .addColumn("20240227.1", "REPORT_VC")
+ .nullable()
+ .type(ColumnTypeEnum.TEXT);
+ version.onTable("BT2_JOB_INSTANCE")
+ .addColumn("20240227.2", "PARAMS_JSON_VC")
+ .nullable()
+ .type(ColumnTypeEnum.TEXT);
+
+ version.onTable("BT2_WORK_CHUNK")
+ .addColumn("20240227.3", "CHUNK_DATA_VC")
+ .nullable()
+ .type(ColumnTypeEnum.TEXT);
+
+ version.onTable("HFJ_SEARCH")
+ .addColumn("20240227.4", "SEARCH_QUERY_STRING_VC")
+ .nullable()
+ .type(ColumnTypeEnum.TEXT);
+ version.onTable("HFJ_SEARCH")
+ .addColumn("20240227.5", "SEARCH_PARAM_MAP_BIN")
+ .nullable()
+ .type(ColumnTypeEnum.BINARY);
+
+ version.onTable("HFJ_BLK_IMPORT_JOBFILE")
+ .addColumn("20240227.6", "JOB_CONTENTS_VC")
+ .nullable()
+ .type(ColumnTypeEnum.TEXT);
+
+ version.onTable("HFJ_BLK_IMPORT_JOBFILE")
+ .modifyColumn("20240227.7", "JOB_CONTENTS")
+ .nullable()
+ .withType(ColumnTypeEnum.BLOB);
}
protected void init680() {
diff --git a/hapi-fhir-jpaserver-model/src/main/java/ca/uhn/fhir/jpa/model/util/JpaConstants.java b/hapi-fhir-jpaserver-model/src/main/java/ca/uhn/fhir/jpa/model/util/JpaConstants.java
index b0fb197f4c43..b164ddbc268d 100644
--- a/hapi-fhir-jpaserver-model/src/main/java/ca/uhn/fhir/jpa/model/util/JpaConstants.java
+++ b/hapi-fhir-jpaserver-model/src/main/java/ca/uhn/fhir/jpa/model/util/JpaConstants.java
@@ -301,6 +301,18 @@ public class JpaConstants {
public static final String SUMMARY_OPERATION_URL = "http://hl7.org/fhir/uv/ips/OperationDefinition/summary";
public static final String ORG_HIBERNATE_TYPE_TEXT_TYPE = "org.hibernate.type.TextType";
+ /**
+ * Columns annotated with this type must use a max length of {@link #ORG_HIBERNATE_TYPE_BINARY_TYPE_LENGTH}
+ */
+ public static final String ORG_HIBERNATE_TYPE_BINARY_TYPE = "org.hibernate.type.BinaryType";
+ /**
+ * This length is chosen because it's the max length supported by H2 - It is long enough
+ * that all the real databases replace it with an unlimited length type anyhow.
+ *
+ * @see #ORG_HIBERNATE_TYPE_BINARY_TYPE
+ */
+ public static final int ORG_HIBERNATE_TYPE_BINARY_TYPE_LENGTH = 1000000000;
+
public static final String BULK_META_EXTENSION_EXPORT_IDENTIFIER =
"https://hapifhir.org/NamingSystem/bulk-export-identifier";
public static final String BULK_META_EXTENSION_JOB_ID = "https://hapifhir.org/NamingSystem/bulk-export-job-id";
diff --git a/hapi-fhir-sql-migrate/src/main/java/ca/uhn/fhir/jpa/migrate/JdbcUtils.java b/hapi-fhir-sql-migrate/src/main/java/ca/uhn/fhir/jpa/migrate/JdbcUtils.java
index b9863105de6c..d7ccc696bd19 100644
--- a/hapi-fhir-sql-migrate/src/main/java/ca/uhn/fhir/jpa/migrate/JdbcUtils.java
+++ b/hapi-fhir-sql-migrate/src/main/java/ca/uhn/fhir/jpa/migrate/JdbcUtils.java
@@ -205,14 +205,7 @@ public static ColumnType getColumnType(
case Types.BLOB:
return new ColumnType(ColumnTypeEnum.BLOB, length);
case Types.LONGVARBINARY:
- if (DriverTypeEnum.MYSQL_5_7.equals(theConnectionProperties.getDriverType())) {
- // See git
- return new ColumnType(ColumnTypeEnum.BLOB, length);
- } else {
- throw new IllegalArgumentException(
- Msg.code(32) + "Don't know how to handle datatype " + dataType
- + " for column " + theColumnName + " on table " + theTableName);
- }
+ return new ColumnType(ColumnTypeEnum.BINARY, length);
case Types.VARBINARY:
if (DriverTypeEnum.MSSQL_2012.equals(theConnectionProperties.getDriverType())) {
// MS SQLServer seems to be mapping BLOB to VARBINARY under the covers, so we need
diff --git a/hapi-fhir-sql-migrate/src/main/java/ca/uhn/fhir/jpa/migrate/taskdef/ColumnTypeEnum.java b/hapi-fhir-sql-migrate/src/main/java/ca/uhn/fhir/jpa/migrate/taskdef/ColumnTypeEnum.java
index d78223f77736..ab779d3e66ac 100644
--- a/hapi-fhir-sql-migrate/src/main/java/ca/uhn/fhir/jpa/migrate/taskdef/ColumnTypeEnum.java
+++ b/hapi-fhir-sql-migrate/src/main/java/ca/uhn/fhir/jpa/migrate/taskdef/ColumnTypeEnum.java
@@ -31,5 +31,8 @@ public enum ColumnTypeEnum {
BLOB,
CLOB,
DOUBLE,
- TEXT;
+ /** Long inline text */
+ TEXT,
+ /** Long inline binary */
+ BINARY;
}
diff --git a/hapi-fhir-sql-migrate/src/main/java/ca/uhn/fhir/jpa/migrate/taskdef/ColumnTypeToDriverTypeToSqlType.java b/hapi-fhir-sql-migrate/src/main/java/ca/uhn/fhir/jpa/migrate/taskdef/ColumnTypeToDriverTypeToSqlType.java
index 03b222718cd2..9b359cf6608e 100644
--- a/hapi-fhir-sql-migrate/src/main/java/ca/uhn/fhir/jpa/migrate/taskdef/ColumnTypeToDriverTypeToSqlType.java
+++ b/hapi-fhir-sql-migrate/src/main/java/ca/uhn/fhir/jpa/migrate/taskdef/ColumnTypeToDriverTypeToSqlType.java
@@ -127,9 +127,17 @@ private ColumnTypeToDriverTypeToSqlType() {}
setColumnType(ColumnTypeEnum.TEXT, DriverTypeEnum.DERBY_EMBEDDED, "long varchar");
setColumnType(ColumnTypeEnum.TEXT, DriverTypeEnum.MARIADB_10_1, "longtext");
setColumnType(ColumnTypeEnum.TEXT, DriverTypeEnum.MYSQL_5_7, "longtext");
- setColumnType(ColumnTypeEnum.TEXT, DriverTypeEnum.ORACLE_12C, "long");
+ setColumnType(ColumnTypeEnum.TEXT, DriverTypeEnum.ORACLE_12C, "clob");
setColumnType(ColumnTypeEnum.TEXT, DriverTypeEnum.POSTGRES_9_4, "text");
setColumnType(ColumnTypeEnum.TEXT, DriverTypeEnum.MSSQL_2012, "varchar(MAX)");
+
+ setColumnType(ColumnTypeEnum.BINARY, DriverTypeEnum.H2_EMBEDDED, "binary(1000000000)");
+ setColumnType(ColumnTypeEnum.BINARY, DriverTypeEnum.DERBY_EMBEDDED, "varchar(1000000000) for bit data");
+ setColumnType(ColumnTypeEnum.BINARY, DriverTypeEnum.MARIADB_10_1, "longblob");
+ setColumnType(ColumnTypeEnum.BINARY, DriverTypeEnum.MYSQL_5_7, "longblob");
+ setColumnType(ColumnTypeEnum.BINARY, DriverTypeEnum.ORACLE_12C, "blob");
+ setColumnType(ColumnTypeEnum.BINARY, DriverTypeEnum.POSTGRES_9_4, "bytea");
+ setColumnType(ColumnTypeEnum.BINARY, DriverTypeEnum.MSSQL_2012, "varbinary(MAX)");
}
public static Map> getColumnTypeToDriverTypeToSqlType() {
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 7cb486079d87..b28216172c3e 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
@@ -133,9 +133,10 @@ public void doExecute() throws SQLException {
}
break;
case ORACLE_12C:
- String oracleNullableStmt = !alreadyCorrectNullable ? notNull : "";
- sql = "alter table " + getTableName() + " modify ( " + getColumnName() + " " + type + oracleNullableStmt
- + " )";
+ String oracleNullableStmt = alreadyCorrectNullable ? "" : notNull;
+ String oracleTypeStmt = alreadyOfCorrectType ? "" : type;
+ sql = "alter table " + getTableName() + " modify ( " + getColumnName() + " " + oracleTypeStmt + " "
+ + oracleNullableStmt + " )";
break;
case MSSQL_2012:
sql = "alter table " + getTableName() + " alter column " + getColumnName() + " " + type + notNull;
diff --git a/pom.xml b/pom.xml
index b66a3fbac915..236024752234 100644
--- a/pom.xml
+++ b/pom.xml
@@ -2438,6 +2438,7 @@
true
false
UTF-8
+ true