Allow reading from multiple partitions (#2198)
* Partitioning rework * Work on partition improvements * Partition updates * Work on partitiong * Test fixes * Add docs * Add changelog * Resolve FIXME * Test fixes * Test fixes * Test fixes * Compile fix * Fix compile error * Test fix * Test fixes
This commit is contained in:
parent
283834ed1d
commit
c309737166
|
@ -470,9 +470,6 @@ public class FhirContext {
|
|||
|
||||
/**
|
||||
* Returns the name of a given resource class.
|
||||
*
|
||||
* @param theResourceType
|
||||
* @return
|
||||
*/
|
||||
public String getResourceType(final Class<? extends IBaseResource> theResourceType) {
|
||||
return getResourceDefinition(theResourceType).getName();
|
||||
|
@ -603,7 +600,7 @@ public class FhirContext {
|
|||
/**
|
||||
* Set the restful client factory
|
||||
*
|
||||
* @param theRestfulClientFactory
|
||||
* @param theRestfulClientFactory The new client factory (must not be null)
|
||||
*/
|
||||
public void setRestfulClientFactory(final IRestfulClientFactory theRestfulClientFactory) {
|
||||
Validate.notNull(theRestfulClientFactory, "theRestfulClientFactory must not be null");
|
||||
|
|
|
@ -20,12 +20,23 @@ package ca.uhn.fhir.interceptor.model;
|
|||
* #L%
|
||||
*/
|
||||
|
||||
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 javax.annotation.Nonnull;
|
||||
import javax.annotation.Nullable;
|
||||
import java.time.LocalDate;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import static org.apache.commons.lang3.ObjectUtils.defaultIfNull;
|
||||
|
||||
/**
|
||||
* @since 5.0.0
|
||||
|
@ -35,15 +46,25 @@ public class RequestPartitionId {
|
|||
private static final RequestPartitionId ALL_PARTITIONS = new RequestPartitionId();
|
||||
private final LocalDate myPartitionDate;
|
||||
private final boolean myAllPartitions;
|
||||
private final Integer myPartitionId;
|
||||
private final String myPartitionName;
|
||||
private final List<Integer> myPartitionIds;
|
||||
private final List<String> myPartitionNames;
|
||||
|
||||
/**
|
||||
* Constructor for a single partition
|
||||
*/
|
||||
private RequestPartitionId(@Nullable String thePartitionName, @Nullable Integer thePartitionId, @Nullable LocalDate thePartitionDate) {
|
||||
myPartitionId = thePartitionId;
|
||||
myPartitionName = thePartitionName;
|
||||
myPartitionIds = toListOrNull(thePartitionId);
|
||||
myPartitionNames = toListOrNull(thePartitionName);
|
||||
myPartitionDate = thePartitionDate;
|
||||
myAllPartitions = false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructor for a multiple partition
|
||||
*/
|
||||
private RequestPartitionId(@Nullable List<String> thePartitionName, @Nullable List<Integer> thePartitionId, @Nullable LocalDate thePartitionDate) {
|
||||
myPartitionIds = toListOrNull(thePartitionId);
|
||||
myPartitionNames = toListOrNull(thePartitionName);
|
||||
myPartitionDate = thePartitionDate;
|
||||
myAllPartitions = false;
|
||||
}
|
||||
|
@ -54,8 +75,8 @@ public class RequestPartitionId {
|
|||
private RequestPartitionId() {
|
||||
super();
|
||||
myPartitionDate = null;
|
||||
myPartitionName = null;
|
||||
myPartitionId = null;
|
||||
myPartitionNames = null;
|
||||
myPartitionIds = null;
|
||||
myAllPartitions = true;
|
||||
}
|
||||
|
||||
|
@ -69,28 +90,26 @@ public class RequestPartitionId {
|
|||
}
|
||||
|
||||
@Nullable
|
||||
public String getPartitionName() {
|
||||
return myPartitionName;
|
||||
public List<String> getPartitionNames() {
|
||||
return myPartitionNames;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public Integer getPartitionId() {
|
||||
return myPartitionId;
|
||||
@Nonnull
|
||||
public List<Integer> getPartitionIds() {
|
||||
Validate.notNull(myPartitionIds, "Partition IDs have not been set");
|
||||
return myPartitionIds;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "RequestPartitionId[id=" + getPartitionId() + ", name=" + getPartitionName() + "]";
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the partition ID (numeric) as a string, or the string "null"
|
||||
*/
|
||||
public String getPartitionIdStringOrNullString() {
|
||||
if (myPartitionId == null) {
|
||||
return "null";
|
||||
ToStringBuilder b = new ToStringBuilder(this, ToStringStyle.SHORT_PREFIX_STYLE);
|
||||
if (hasPartitionIds()) {
|
||||
b.append("ids", getPartitionIds());
|
||||
}
|
||||
return myPartitionId.toString();
|
||||
if (hasPartitionNames()) {
|
||||
b.append("names", getPartitionNames());
|
||||
}
|
||||
return b.build();
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -108,8 +127,8 @@ public class RequestPartitionId {
|
|||
return new EqualsBuilder()
|
||||
.append(myAllPartitions, that.myAllPartitions)
|
||||
.append(myPartitionDate, that.myPartitionDate)
|
||||
.append(myPartitionId, that.myPartitionId)
|
||||
.append(myPartitionName, that.myPartitionName)
|
||||
.append(myPartitionIds, that.myPartitionIds)
|
||||
.append(myPartitionNames, that.myPartitionNames)
|
||||
.isEquals();
|
||||
}
|
||||
|
||||
|
@ -118,11 +137,82 @@ public class RequestPartitionId {
|
|||
return new HashCodeBuilder(17, 37)
|
||||
.append(myPartitionDate)
|
||||
.append(myAllPartitions)
|
||||
.append(myPartitionId)
|
||||
.append(myPartitionName)
|
||||
.append(myPartitionIds)
|
||||
.append(myPartitionNames)
|
||||
.toHashCode();
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public Integer getFirstPartitionIdOrNull() {
|
||||
if (myPartitionIds != null) {
|
||||
return myPartitionIds.get(0);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public String getFirstPartitionNameOrNull() {
|
||||
if (myPartitionNames != null) {
|
||||
return myPartitionNames.get(0);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if this request partition contains only one partition ID and it is the DEFAULT partition ID (null)
|
||||
*/
|
||||
public boolean isDefaultPartition() {
|
||||
return getPartitionIds().size() == 1 && getPartitionIds().get(0) == null;
|
||||
}
|
||||
|
||||
public boolean hasPartitionId(Integer thePartitionId) {
|
||||
Validate.notNull(myPartitionIds, "Partition IDs not set");
|
||||
return myPartitionIds.contains(thePartitionId);
|
||||
}
|
||||
|
||||
public boolean hasPartitionIds() {
|
||||
return myPartitionIds != null;
|
||||
}
|
||||
|
||||
public boolean hasPartitionNames() {
|
||||
return myPartitionNames != null;
|
||||
}
|
||||
|
||||
public boolean hasDefaultPartitionId() {
|
||||
return getPartitionIds().contains(null);
|
||||
}
|
||||
|
||||
public List<Integer> getPartitionIdsWithoutDefault() {
|
||||
return getPartitionIds().stream().filter(t -> t != null).collect(Collectors.toList());
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private static <T> List<T> toListOrNull(@Nullable Collection<T> theList) {
|
||||
if (theList != null) {
|
||||
if (theList.size() == 1) {
|
||||
return Collections.singletonList(theList.iterator().next());
|
||||
}
|
||||
return Collections.unmodifiableList(new ArrayList<>(theList));
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private static <T> List<T> toListOrNull(@Nullable T theObject) {
|
||||
if (theObject != null) {
|
||||
return Collections.singletonList(theObject);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@SafeVarargs
|
||||
@Nullable
|
||||
private static <T> List<T> toListOrNull(@Nullable T... theObject) {
|
||||
if (theObject != null) {
|
||||
return Arrays.asList(theObject);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@Nonnull
|
||||
public static RequestPartitionId allPartitions() {
|
||||
return ALL_PARTITIONS;
|
||||
|
@ -130,17 +220,27 @@ public class RequestPartitionId {
|
|||
|
||||
@Nonnull
|
||||
public static RequestPartitionId defaultPartition() {
|
||||
return fromPartitionId(null);
|
||||
return fromPartitionIds(Collections.singletonList(null));
|
||||
}
|
||||
|
||||
@Nonnull
|
||||
public static RequestPartitionId fromPartitionId(@Nullable Integer thePartitionId) {
|
||||
return fromPartitionId(thePartitionId, null);
|
||||
return fromPartitionIds(Collections.singletonList(thePartitionId));
|
||||
}
|
||||
|
||||
@Nonnull
|
||||
public static RequestPartitionId fromPartitionId(@Nullable Integer thePartitionId, @Nullable LocalDate thePartitionDate) {
|
||||
return new RequestPartitionId(null, thePartitionId, thePartitionDate);
|
||||
return new RequestPartitionId(null, Collections.singletonList(thePartitionId), thePartitionDate);
|
||||
}
|
||||
|
||||
@Nonnull
|
||||
public static RequestPartitionId fromPartitionIds(@Nonnull Collection<Integer> thePartitionIds) {
|
||||
return new RequestPartitionId(null, toListOrNull(thePartitionIds), null);
|
||||
}
|
||||
|
||||
@Nonnull
|
||||
public static RequestPartitionId fromPartitionIds(Integer... thePartitionIds) {
|
||||
return new RequestPartitionId(null, toListOrNull(thePartitionIds), null);
|
||||
}
|
||||
|
||||
@Nonnull
|
||||
|
@ -153,6 +253,16 @@ public class RequestPartitionId {
|
|||
return new RequestPartitionId(thePartitionName, null, thePartitionDate);
|
||||
}
|
||||
|
||||
@Nonnull
|
||||
public static RequestPartitionId fromPartitionNames(@Nullable List<String> thePartitionNames) {
|
||||
return new RequestPartitionId(toListOrNull(thePartitionNames), null, null);
|
||||
}
|
||||
|
||||
@Nonnull
|
||||
public static RequestPartitionId fromPartitionNames(String... thePartitionNames) {
|
||||
return new RequestPartitionId(toListOrNull(thePartitionNames), null, null);
|
||||
}
|
||||
|
||||
@Nonnull
|
||||
public static RequestPartitionId fromPartitionIdAndName(@Nullable Integer thePartitionId, @Nullable String thePartitionName) {
|
||||
return new RequestPartitionId(thePartitionName, thePartitionId, null);
|
||||
|
@ -163,13 +273,25 @@ public class RequestPartitionId {
|
|||
return new RequestPartitionId(thePartitionName, thePartitionId, thePartitionDate);
|
||||
}
|
||||
|
||||
@Nonnull
|
||||
public static RequestPartitionId forPartitionIdsAndNames(List<String> thePartitionNames, List<Integer> thePartitionIds, LocalDate thePartitionDate) {
|
||||
return new RequestPartitionId(thePartitionNames, thePartitionIds, thePartitionDate);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a string representation suitable for use as a cache key. Null aware.
|
||||
* <p>
|
||||
* Returns the partition IDs (numeric) as a joined string with a space between, using the string "null" for any null values
|
||||
*/
|
||||
public static String stringifyForKey(RequestPartitionId theRequestPartitionId) {
|
||||
String retVal = "(null)";
|
||||
if (theRequestPartitionId != null) {
|
||||
retVal = theRequestPartitionId.getPartitionIdStringOrNullString();
|
||||
public static String stringifyForKey(@Nonnull RequestPartitionId theRequestPartitionId) {
|
||||
String retVal = "(all partitions)";
|
||||
if (!theRequestPartitionId.isAllPartitions()) {
|
||||
assert theRequestPartitionId.hasPartitionIds();
|
||||
retVal = theRequestPartitionId
|
||||
.getPartitionIds()
|
||||
.stream()
|
||||
.map(t -> defaultIfNull(t, "null").toString())
|
||||
.collect(Collectors.joining(" "));
|
||||
}
|
||||
return retVal;
|
||||
}
|
||||
|
|
|
@ -171,12 +171,10 @@ ca.uhn.fhir.jpa.dao.index.IdHelperService.nonUniqueForcedId=Non-unique ID specif
|
|||
|
||||
ca.uhn.fhir.jpa.partition.PartitionLookupSvcImpl.noIdSupplied=No Partition ID supplied
|
||||
ca.uhn.fhir.jpa.partition.PartitionLookupSvcImpl.missingPartitionIdOrName=Partition must have an ID and a Name
|
||||
ca.uhn.fhir.jpa.partition.PartitionLookupSvcImpl.cantCreatePartition0=Can not create a partition with ID 0 (this is a reserved value)
|
||||
ca.uhn.fhir.jpa.partition.PartitionLookupSvcImpl.unknownPartitionId=No partition exists with ID {0}
|
||||
ca.uhn.fhir.jpa.partition.PartitionLookupSvcImpl.invalidName=Partition name "{0}" is not valid
|
||||
ca.uhn.fhir.jpa.partition.PartitionLookupSvcImpl.cantCreateDuplicatePartitionName=Partition name "{0}" is already defined
|
||||
ca.uhn.fhir.jpa.partition.PartitionLookupSvcImpl.cantDeleteDefaultPartition=Can not delete default partition
|
||||
ca.uhn.fhir.jpa.partition.PartitionLookupSvcImpl.cantRenameDefaultPartition=Can not rename default partition
|
||||
ca.uhn.fhir.jpa.partition.PartitionLookupSvcImpl.cantCreateDefaultPartition=Can not create partition with name "DEFAULT"
|
||||
|
||||
ca.uhn.fhir.rest.server.interceptor.partition.RequestTenantPartitionInterceptor.unknownTenantName=Unknown tenant: {0}
|
||||
|
||||
|
|
|
@ -0,0 +1,5 @@
|
|||
---
|
||||
type: add
|
||||
issue: 2198
|
||||
title: "It is now possible for read operations (read/history/search/etc) in a partitioned server to read across more than one
|
||||
partition if the partitioning interceptor indicates multiple partitions."
|
|
@ -43,10 +43,23 @@ When a resource is **updated**, the partition ID and date from the previous vers
|
|||
|
||||
When a **read operation** is being performed (e.g. a read, search, history, etc.), a separate [interceptor hook](#partition-interceptors) is invoked in order to determine whether the operation should target a specific partition. The outcome of this hook determines how the partitioning manifests itself to the end user:
|
||||
|
||||
* The system can be configured to operate as a **multitenant** solution by configuring the partition interceptor to scope all read operations to read data only from the partition that request has access to.```
|
||||
* The system can be configured to operate as a **multitenant** solution by configuring the partition interceptor to scope all read operations to read data only from the partition that request has access to.
|
||||
* The system can be configured to operate with logical segments by configuring the partition interceptor to scope read operations to access all partitions.
|
||||
|
||||
|
||||
# Partitioning and Resource IDs
|
||||
|
||||
In a partitioned repository, it is important to understand that only a single pool of resource IDs exists. In other words, only one resource with the ID `Patient/1` can exist across all partitions, and it must be in a single partition.
|
||||
|
||||
This fact can have security implications:
|
||||
|
||||
* A client might be blocked from creating `Patient/ABC` in the partition they have access to because this ID is already in use in another partition.
|
||||
|
||||
* In a server using the default configuration of SEQUENTIAL_NUMERIC [Server ID Strategy](/hapi-fhir/apidocs/hapi-fhir-jpaserver-api/ca/uhn/fhir/jpa/api/config/DaoConfig.html#setResourceServerIdStrategy(ca.uhn.fhir.jpa.api.config.DaoConfig.IdStrategyEnum)) a client may be able to infer the IDs of resources in other partitions based on the ID they were assigned.
|
||||
|
||||
These considerations can be addressed by using UUID Server ID Strategy, and disallowing client-assigned IDs.
|
||||
|
||||
|
||||
# Partition Interceptors
|
||||
|
||||
In order to implement partitioning, an interceptor must be registered against the interceptor registry (either the REST Server registry, or the JPA Server registry will work).
|
||||
|
@ -67,6 +80,9 @@ The criteria for determining the partition will depend on your use case. For exa
|
|||
|
||||
A hook against the [`Pointcut.STORAGE_PARTITION_IDENTIFY_READ`](/hapi-fhir/apidocs/hapi-fhir-base/ca/uhn/fhir/interceptor/api/Pointcut.html#STORAGE_PARTITION_IDENTIFY_READ) pointcut must be registered, and this hook method will be invoked every time a resource is created in order to determine the partition to assign the resource to.
|
||||
|
||||
<cdr:fail cdr:afterVersion="5.4.0"/>
|
||||
As of HAPI FHIR 5.3.0, the *Identify Partition for Read* hook method may return multiple partition names or IDs. If more than one partition is identified, the server will search in all identified partitions.
|
||||
|
||||
## Examples
|
||||
|
||||
See [Partition Interceptor Examples](./partition_interceptor_examples.html) for various samples of how partitioning interceptors can be set up.
|
||||
|
|
|
@ -37,6 +37,7 @@ import ca.uhn.fhir.jpa.model.entity.BaseHasResource;
|
|||
import ca.uhn.fhir.jpa.model.entity.BaseTag;
|
||||
import ca.uhn.fhir.jpa.model.entity.ForcedId;
|
||||
import ca.uhn.fhir.jpa.model.entity.IBaseResourceEntity;
|
||||
import ca.uhn.fhir.jpa.model.entity.PartitionablePartitionId;
|
||||
import ca.uhn.fhir.jpa.model.entity.ResourceEncodingEnum;
|
||||
import ca.uhn.fhir.jpa.model.entity.ResourceHistoryProvenanceEntity;
|
||||
import ca.uhn.fhir.jpa.model.entity.ResourceHistoryTable;
|
||||
|
@ -962,7 +963,7 @@ public abstract class BaseHapiFhirDao<T extends IBaseResource> extends BaseStora
|
|||
|
||||
// 7. Add partition information
|
||||
if (myPartitionSettings.isPartitioningEnabled()) {
|
||||
RequestPartitionId partitionId = theEntity.getPartitionId();
|
||||
PartitionablePartitionId partitionId = theEntity.getPartitionId();
|
||||
if (partitionId != null && partitionId.getPartitionId() != null) {
|
||||
PartitionEntity persistedPartition = myPartitionLookupSvc.getPartitionById(partitionId.getPartitionId());
|
||||
retVal.setUserData(Constants.RESOURCE_PARTITION_ID, persistedPartition.toRequestPartitionId());
|
||||
|
|
|
@ -1102,19 +1102,17 @@ public abstract class BaseHapiFhirResourceDao<T extends IBaseResource> extends B
|
|||
|
||||
// Verify that the resource is for the correct partition
|
||||
if (!requestPartitionId.isAllPartitions()) {
|
||||
if (requestPartitionId.getPartitionId() == null) {
|
||||
if (entity.getPartitionId().getPartitionId() != null) {
|
||||
ourLog.debug("Performing a read for PartitionId={} but entity has partition: {}", requestPartitionId, entity.getPartitionId());
|
||||
entity = null;
|
||||
}
|
||||
} else if (entity.getPartitionId().getPartitionId() != null) {
|
||||
if (!requestPartitionId.getPartitionId().equals(entity.getPartitionId().getPartitionId())) {
|
||||
if (entity.getPartitionId() != null && entity.getPartitionId().getPartitionId() != null) {
|
||||
if (!requestPartitionId.hasPartitionId(entity.getPartitionId().getPartitionId())) {
|
||||
ourLog.debug("Performing a read for PartitionId={} but entity has partition: {}", requestPartitionId, entity.getPartitionId());
|
||||
entity = null;
|
||||
}
|
||||
} else {
|
||||
ourLog.debug("Performing a read for PartitionId=null but entity has partition: {}", entity.getPartitionId());
|
||||
entity = null;
|
||||
// Entity Partition ID is null
|
||||
if (!requestPartitionId.hasPartitionId(null)) {
|
||||
ourLog.debug("Performing a read for PartitionId=null but entity has partition: {}", entity.getPartitionId());
|
||||
entity = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1145,6 +1143,7 @@ public abstract class BaseHapiFhirResourceDao<T extends IBaseResource> extends B
|
|||
}
|
||||
}
|
||||
|
||||
Validate.notNull(entity);
|
||||
validateResourceType(entity);
|
||||
|
||||
if (theCheckForForcedId) {
|
||||
|
|
|
@ -142,10 +142,15 @@ public class HistoryBuilder {
|
|||
List<Predicate> predicates = new ArrayList<>();
|
||||
|
||||
if (!thePartitionId.isAllPartitions()) {
|
||||
if (thePartitionId.getPartitionId() != null) {
|
||||
predicates.add(theCriteriaBuilder.equal(theFrom.get("myPartitionIdValue").as(Integer.class), thePartitionId.getPartitionId()));
|
||||
} else {
|
||||
if (thePartitionId.isDefaultPartition()) {
|
||||
predicates.add(theCriteriaBuilder.isNull(theFrom.get("myPartitionIdValue").as(Integer.class)));
|
||||
} else if (thePartitionId.hasDefaultPartitionId()) {
|
||||
predicates.add(theCriteriaBuilder.or(
|
||||
theCriteriaBuilder.isNull(theFrom.get("myPartitionIdValue").as(Integer.class)),
|
||||
theFrom.get("myPartitionIdValue").as(Integer.class).in(thePartitionId.getPartitionIdsWithoutDefault())
|
||||
));
|
||||
} else {
|
||||
predicates.add(theFrom.get("myPartitionIdValue").as(Integer.class).in(thePartitionId.getPartitionIds()));
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -42,6 +42,7 @@ import ca.uhn.fhir.jpa.entity.ResourceSearchView;
|
|||
import ca.uhn.fhir.jpa.interceptor.JpaPreResourceAccessDetails;
|
||||
import ca.uhn.fhir.jpa.model.config.PartitionSettings;
|
||||
import ca.uhn.fhir.jpa.model.entity.BaseResourceIndexedSearchParam;
|
||||
import ca.uhn.fhir.jpa.model.entity.PartitionablePartitionId;
|
||||
import ca.uhn.fhir.jpa.model.entity.ResourceIndexedCompositeStringUnique;
|
||||
import ca.uhn.fhir.jpa.model.entity.ResourceLink;
|
||||
import ca.uhn.fhir.jpa.model.entity.ResourceTable;
|
||||
|
@ -953,7 +954,7 @@ public class LegacySearchBuilder implements ISearchBuilder {
|
|||
From<?, ResourceIndexedCompositeStringUnique> join = myQueryStack.createJoin(SearchBuilderJoinEnum.COMPOSITE_UNIQUE, null);
|
||||
|
||||
if (!theRequestPartitionId.isAllPartitions()) {
|
||||
Integer partitionId = theRequestPartitionId.getPartitionId();
|
||||
Integer partitionId = theRequestPartitionId.getFirstPartitionIdOrNull();
|
||||
Predicate predicate = myCriteriaBuilder.equal(join.get("myPartitionIdValue").as(Integer.class), partitionId);
|
||||
myQueryStack.addPredicate(predicate);
|
||||
}
|
||||
|
|
|
@ -44,8 +44,11 @@ public interface IForcedIdDao extends JpaRepository<ForcedId, Long> {
|
|||
@Query("SELECT f.myResourcePid FROM ForcedId f WHERE myPartitionId.myPartitionId IS NULL AND myResourceType = :resource_type AND myForcedId = :forced_id")
|
||||
Optional<Long> findByPartitionIdNullAndTypeAndForcedId(@Param("resource_type") String theResourceType, @Param("forced_id") String theForcedId);
|
||||
|
||||
@Query("SELECT f.myResourcePid FROM ForcedId f WHERE myPartitionId.myPartitionId = :partition_id AND myResourceType = :resource_type AND myForcedId = :forced_id")
|
||||
Optional<Long> findByPartitionIdAndTypeAndForcedId(@Param("partition_id") Integer thePartitionId, @Param("resource_type") String theResourceType, @Param("forced_id") String theForcedId);
|
||||
@Query("SELECT f.myResourcePid FROM ForcedId f WHERE myPartitionId.myPartitionId IN :partition_id AND myResourceType = :resource_type AND myForcedId = :forced_id")
|
||||
Optional<Long> findByPartitionIdAndTypeAndForcedId(@Param("partition_id") Collection<Integer> thePartitionId, @Param("resource_type") String theResourceType, @Param("forced_id") String theForcedId);
|
||||
|
||||
@Query("SELECT f.myResourcePid FROM ForcedId f WHERE (myPartitionId.myPartitionId IN :partition_id OR myPartitionId.myPartitionId IS NULL) AND myResourceType = :resource_type AND myForcedId = :forced_id")
|
||||
Optional<Long> findByPartitionIdOrNullAndTypeAndForcedId(@Param("partition_id") Collection<Integer> thePartitionId, @Param("resource_type") String theResourceType, @Param("forced_id") String theForcedId);
|
||||
|
||||
@Query("SELECT f FROM ForcedId f WHERE f.myResourcePid = :resource_pid")
|
||||
Optional<ForcedId> findByResourcePid(@Param("resource_pid") Long theResourcePid);
|
||||
|
@ -65,8 +68,15 @@ public interface IForcedIdDao extends JpaRepository<ForcedId, Long> {
|
|||
* This method returns a Collection where each row is an element in the collection. Each element in the collection
|
||||
* is an object array, where the order matters (the array represents columns returned by the query). Be careful if you change this query in any way.
|
||||
*/
|
||||
@Query("SELECT f.myForcedId, f.myResourcePid FROM ForcedId f WHERE myPartitionIdValue = :partition_id AND myResourceType = :resource_type AND myForcedId IN ( :forced_id )")
|
||||
Collection<Object[]> findByTypeAndForcedIdInPartition(@Param("resource_type") String theResourceType, @Param("forced_id") Collection<String> theForcedId, @Param("partition_id") Integer thePartitionId);
|
||||
@Query("SELECT f.myForcedId, f.myResourcePid FROM ForcedId f WHERE myPartitionIdValue IN ( :partition_id ) AND myResourceType = :resource_type AND myForcedId IN ( :forced_id )")
|
||||
Collection<Object[]> findByTypeAndForcedIdInPartitionIds(@Param("resource_type") String theResourceType, @Param("forced_id") Collection<String> theForcedId, @Param("partition_id") Collection<Integer> thePartitionId);
|
||||
|
||||
/**
|
||||
* This method returns a Collection where each row is an element in the collection. Each element in the collection
|
||||
* is an object array, where the order matters (the array represents columns returned by the query). Be careful if you change this query in any way.
|
||||
*/
|
||||
@Query("SELECT f.myForcedId, f.myResourcePid FROM ForcedId f WHERE (myPartitionIdValue IS NULL OR myPartitionIdValue IN ( :partition_id )) AND myResourceType = :resource_type AND myForcedId IN ( :forced_id )")
|
||||
Collection<Object[]> findByTypeAndForcedIdInPartitionIdsOrNullPartition(@Param("resource_type") String theResourceType, @Param("forced_id") Collection<String> theForcedId, @Param("partition_id") Collection<Integer> thePartitionId);
|
||||
|
||||
/**
|
||||
* This method returns a Collection where each row is an element in the collection. Each element in the collection
|
||||
|
@ -110,8 +120,8 @@ public interface IForcedIdDao extends JpaRepository<ForcedId, Long> {
|
|||
" f.myResourceType, f.myResourcePid, f.myForcedId, t.myDeleted " +
|
||||
"FROM ForcedId f " +
|
||||
"JOIN ResourceTable t ON t.myId = f.myResourcePid " +
|
||||
"WHERE f.myResourceType = :resource_type AND f.myForcedId IN ( :forced_id ) AND f.myPartitionIdValue = :partition_id")
|
||||
Collection<Object[]> findAndResolveByForcedIdWithNoTypeInPartition(@Param("resource_type") String theResourceType, @Param("forced_id") Collection<String> theForcedIds, @Param("partition_id") Integer thePartitionId);
|
||||
"WHERE f.myResourceType = :resource_type AND f.myForcedId IN ( :forced_id ) AND f.myPartitionIdValue IN :partition_id")
|
||||
Collection<Object[]> findAndResolveByForcedIdWithNoTypeInPartition(@Param("resource_type") String theResourceType, @Param("forced_id") Collection<String> theForcedIds, @Param("partition_id") Collection<Integer> thePartitionId);
|
||||
|
||||
|
||||
/**
|
||||
|
@ -127,4 +137,15 @@ public interface IForcedIdDao extends JpaRepository<ForcedId, Long> {
|
|||
Collection<Object[]> findAndResolveByForcedIdWithNoTypeInPartitionNull(@Param("resource_type") String theResourceType, @Param("forced_id") Collection<String> theForcedIds);
|
||||
|
||||
|
||||
/**
|
||||
* This method returns a Collection where each row is an element in the collection. Each element in the collection
|
||||
* is an object array, where the order matters (the array represents columns returned by the query). Be careful if you change this query in any way.
|
||||
*/
|
||||
@Query("" +
|
||||
"SELECT " +
|
||||
" f.myResourceType, f.myResourcePid, f.myForcedId, t.myDeleted " +
|
||||
"FROM ForcedId f " +
|
||||
"JOIN ResourceTable t ON t.myId = f.myResourcePid " +
|
||||
"WHERE f.myResourceType = :resource_type AND f.myForcedId IN ( :forced_id ) AND (f.myPartitionIdValue IS NULL OR f.myPartitionIdValue IN :partition_id)")
|
||||
Collection<Object[]> findAndResolveByForcedIdWithNoTypeInPartitionIdOrNullPartitionId(@Param("resource_type") String theNextResourceType, @Param("forced_id") Collection<String> theNextIds, @Param("forced_id") List<Integer> thePartitionIdsWithoutDefault);
|
||||
}
|
||||
|
|
|
@ -12,7 +12,6 @@ import java.util.Collection;
|
|||
import java.util.Date;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Optional;
|
||||
|
||||
/*
|
||||
* #%L
|
||||
|
@ -65,12 +64,31 @@ public interface IResourceTableDao extends JpaRepository<ResourceTable, Long> {
|
|||
@Query("DELETE FROM ResourceTable t WHERE t.myId = :pid")
|
||||
void deleteByPid(@Param("pid") Long theId);
|
||||
|
||||
/**
|
||||
* This method returns a Collection where each row is an element in the collection. Each element in the collection
|
||||
* is an object array, where the order matters (the array represents columns returned by the query). Be careful if you change this query in any way.
|
||||
*/
|
||||
@Query("SELECT t.myResourceType, t.myId, t.myDeleted FROM ResourceTable t WHERE t.myId IN (:pid)")
|
||||
Collection<Object[]> findLookupFieldsByResourcePid(@Param("pid") List<Long> thePids);
|
||||
|
||||
@Query("SELECT t.myResourceType, t.myId, t.myDeleted FROM ResourceTable t WHERE t.myId IN (:pid) AND t.myPartitionIdValue = :partition_id")
|
||||
Collection<Object[]> findLookupFieldsByResourcePidInPartition(@Param("pid") List<Long> thePids, @Param("partition_id") Integer thePartitionId);
|
||||
/**
|
||||
* This method returns a Collection where each row is an element in the collection. Each element in the collection
|
||||
* is an object array, where the order matters (the array represents columns returned by the query). Be careful if you change this query in any way.
|
||||
*/
|
||||
@Query("SELECT t.myResourceType, t.myId, t.myDeleted FROM ResourceTable t WHERE t.myId IN (:pid) AND t.myPartitionIdValue IN :partition_id")
|
||||
Collection<Object[]> findLookupFieldsByResourcePidInPartitionIds(@Param("pid") List<Long> thePids, @Param("partition_id") Collection<Integer> thePartitionId);
|
||||
|
||||
/**
|
||||
* This method returns a Collection where each row is an element in the collection. Each element in the collection
|
||||
* is an object array, where the order matters (the array represents columns returned by the query). Be careful if you change this query in any way.
|
||||
*/
|
||||
@Query("SELECT t.myResourceType, t.myId, t.myDeleted FROM ResourceTable t WHERE t.myId IN (:pid) AND (t.myPartitionIdValue IS NULL OR t.myPartitionIdValue IN :partition_id)")
|
||||
Collection<Object[]> findLookupFieldsByResourcePidInPartitionIdsOrNullPartition(@Param("pid") List<Long> thePids, @Param("partition_id") Collection<Integer> thePartitionId);
|
||||
|
||||
/**
|
||||
* This method returns a Collection where each row is an element in the collection. Each element in the collection
|
||||
* is an object array, where the order matters (the array represents columns returned by the query). Be careful if you change this query in any way.
|
||||
*/
|
||||
@Query("SELECT t.myResourceType, t.myId, t.myDeleted FROM ResourceTable t WHERE t.myId IN (:pid) AND t.myPartitionIdValue IS NULL")
|
||||
Collection<Object[]> findLookupFieldsByResourcePidInPartitionNull(@Param("pid") List<Long> thePids);
|
||||
}
|
||||
|
|
|
@ -40,11 +40,10 @@ public class EmpiLinkDeleteSvc {
|
|||
|
||||
/**
|
||||
* Delete all EmpiLink records with any reference to this resource. (Used by Expunge.)
|
||||
* @param theResource
|
||||
* @return the number of records deleted
|
||||
*/
|
||||
public int deleteWithAnyReferenceTo(IBaseResource theResource) {
|
||||
Long pid = myIdHelperService.getPidOrThrowException(theResource.getIdElement(), null);
|
||||
Long pid = myIdHelperService.getPidOrThrowException(theResource.getIdElement());
|
||||
int removed = myEmpiLinkDao.deleteWithAnyReferenceToPid(pid);
|
||||
if (removed > 0) {
|
||||
ourLog.info("Removed {} EMPI links with references to {}", removed, theResource.getIdElement().toVersionless());
|
||||
|
@ -53,7 +52,7 @@ public class EmpiLinkDeleteSvc {
|
|||
}
|
||||
|
||||
public int deleteNonRedirectWithWithAnyReferenceTo(IBaseResource theResource) {
|
||||
Long pid = myIdHelperService.getPidOrThrowException(theResource.getIdElement(), null);
|
||||
Long pid = myIdHelperService.getPidOrThrowException(theResource.getIdElement());
|
||||
int removed = myEmpiLinkDao.deleteWithAnyReferenceToPidAndMatchResultNot(pid, EmpiMatchResultEnum.REDIRECT);
|
||||
if (removed > 0) {
|
||||
ourLog.info("Removed {} non-redirect EMPI links with references to {}", removed, theResource.getIdElement().toVersionless());
|
||||
|
|
|
@ -68,7 +68,7 @@ public class DaoResourceLinkResolver implements IResourceLinkResolver {
|
|||
IResourceLookup resolvedResource;
|
||||
String idPart = theSourceResourceId.getIdPart();
|
||||
try {
|
||||
resolvedResource = myIdHelperService.resolveResourceIdentity(theRequestPartitionId, theResourceType, idPart, theRequest);
|
||||
resolvedResource = myIdHelperService.resolveResourceIdentity(theRequestPartitionId, theResourceType, idPart);
|
||||
ourLog.trace("Translated {}/{} to resource PID {}", theType, idPart, resolvedResource);
|
||||
} catch (ResourceNotFoundException e) {
|
||||
|
||||
|
|
|
@ -21,7 +21,6 @@ package ca.uhn.fhir.jpa.dao.index;
|
|||
*/
|
||||
|
||||
import ca.uhn.fhir.context.FhirContext;
|
||||
import ca.uhn.fhir.interceptor.api.IInterceptorBroadcaster;
|
||||
import ca.uhn.fhir.interceptor.model.RequestPartitionId;
|
||||
import ca.uhn.fhir.jpa.api.config.DaoConfig;
|
||||
import ca.uhn.fhir.jpa.dao.data.IForcedIdDao;
|
||||
|
@ -33,7 +32,6 @@ import ca.uhn.fhir.jpa.model.entity.ResourceTable;
|
|||
import ca.uhn.fhir.jpa.util.MemoryCacheService;
|
||||
import ca.uhn.fhir.jpa.util.QueryChunker;
|
||||
import ca.uhn.fhir.model.primitive.IdDt;
|
||||
import ca.uhn.fhir.rest.api.server.RequestDetails;
|
||||
import ca.uhn.fhir.rest.api.server.storage.ResourcePersistentId;
|
||||
import ca.uhn.fhir.rest.server.exceptions.PreconditionFailedException;
|
||||
import ca.uhn.fhir.rest.server.exceptions.ResourceNotFoundException;
|
||||
|
@ -44,10 +42,7 @@ import org.apache.commons.lang3.Validate;
|
|||
import org.hl7.fhir.instance.model.api.IAnyResource;
|
||||
import org.hl7.fhir.instance.model.api.IBaseResource;
|
||||
import org.hl7.fhir.instance.model.api.IIdType;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.dao.IncorrectResultSizeDataAccessException;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import javax.annotation.Nonnull;
|
||||
|
@ -62,7 +57,6 @@ import java.util.List;
|
|||
import java.util.Map;
|
||||
import java.util.Optional;
|
||||
import java.util.Set;
|
||||
import java.util.function.Function;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import static org.apache.commons.lang3.StringUtils.isBlank;
|
||||
|
@ -87,7 +81,6 @@ import static org.apache.commons.lang3.StringUtils.isNotBlank;
|
|||
*/
|
||||
@Service
|
||||
public class IdHelperService {
|
||||
private static final Logger ourLog = LoggerFactory.getLogger(IdHelperService.class);
|
||||
private static final String RESOURCE_PID = "RESOURCE_PID";
|
||||
|
||||
@Autowired
|
||||
|
@ -97,8 +90,6 @@ public class IdHelperService {
|
|||
@Autowired
|
||||
private DaoConfig myDaoConfig;
|
||||
@Autowired
|
||||
private IInterceptorBroadcaster myInterceptorBroadcaster;
|
||||
@Autowired
|
||||
private FhirContext myFhirCtx;
|
||||
@Autowired
|
||||
private MemoryCacheService myMemoryCacheService;
|
||||
|
@ -114,14 +105,25 @@ public class IdHelperService {
|
|||
* @throws ResourceNotFoundException If the ID can not be found
|
||||
*/
|
||||
@Nonnull
|
||||
public IResourceLookup resolveResourceIdentity(@Nonnull RequestPartitionId theRequestPartitionId, String theResourceType, String theResourceId, RequestDetails theRequestDetails) throws ResourceNotFoundException {
|
||||
public IResourceLookup resolveResourceIdentity(@Nonnull RequestPartitionId theRequestPartitionId, String theResourceType, String theResourceId) throws ResourceNotFoundException {
|
||||
// We only pass 1 input in so only 0..1 will come back
|
||||
IdDt id = new IdDt(theResourceType, theResourceId);
|
||||
Collection<IResourceLookup> matches = translateForcedIdToPids(theRequestPartitionId, theRequestDetails, Collections.singletonList(id));
|
||||
assert matches.size() <= 1;
|
||||
Collection<IResourceLookup> matches = translateForcedIdToPids(theRequestPartitionId, Collections.singletonList(id));
|
||||
|
||||
if (matches.isEmpty()) {
|
||||
throw new ResourceNotFoundException(id);
|
||||
}
|
||||
|
||||
if (matches.size() > 1) {
|
||||
/*
|
||||
* This means that:
|
||||
* 1. There are two resources with the exact same resource type and forced id
|
||||
* 2. The unique constraint on this column-pair has been dropped
|
||||
*/
|
||||
String msg = myFhirCtx.getLocalizer().getMessage(IdHelperService.class, "nonUniqueForcedId");
|
||||
throw new PreconditionFailedException(msg);
|
||||
}
|
||||
|
||||
return matches.iterator().next();
|
||||
}
|
||||
|
||||
|
@ -137,10 +139,10 @@ public class IdHelperService {
|
|||
Long retVal;
|
||||
if (myDaoConfig.getResourceClientIdStrategy() == DaoConfig.ClientIdStrategyEnum.ANY || !isValidPid(theId)) {
|
||||
if (myDaoConfig.isDeleteEnabled()) {
|
||||
retVal = resolveResourceIdentity(theRequestPartitionId, theResourceType, theId);
|
||||
retVal = resolveResourceIdentity(theRequestPartitionId, theResourceType, theId).getResourceId();
|
||||
} else {
|
||||
String key = RequestPartitionId.stringifyForKey(theRequestPartitionId) + "/" + theResourceType + "/" + theId;
|
||||
retVal = myMemoryCacheService.get(MemoryCacheService.CacheEnum.PERSISTENT_ID, key, t -> resolveResourceIdentity(theRequestPartitionId, theResourceType, theId));
|
||||
retVal = myMemoryCacheService.get(MemoryCacheService.CacheEnum.PERSISTENT_ID, key, t -> resolveResourceIdentity(theRequestPartitionId, theResourceType, theId).getResourceId());
|
||||
}
|
||||
|
||||
} else {
|
||||
|
@ -187,9 +189,10 @@ public class IdHelperService {
|
|||
|
||||
} else {
|
||||
|
||||
String partitionIdStringForKey = RequestPartitionId.stringifyForKey(theRequestPartitionId);
|
||||
for (Iterator<String> idIterator = nextIds.iterator(); idIterator.hasNext(); ) {
|
||||
String nextId = idIterator.next();
|
||||
String key = RequestPartitionId.stringifyForKey(theRequestPartitionId) + "/" + nextResourceType + "/" + nextId;
|
||||
String key = partitionIdStringForKey + "/" + nextResourceType + "/" + nextId;
|
||||
Long nextCachedPid = myMemoryCacheService.getIfPresent(MemoryCacheService.CacheEnum.PERSISTENT_ID, key);
|
||||
if (nextCachedPid != null) {
|
||||
idIterator.remove();
|
||||
|
@ -203,10 +206,12 @@ public class IdHelperService {
|
|||
if (theRequestPartitionId.isAllPartitions()) {
|
||||
views = myForcedIdDao.findByTypeAndForcedId(nextResourceType, nextIds);
|
||||
} else {
|
||||
if (theRequestPartitionId.getPartitionId() != null) {
|
||||
views = myForcedIdDao.findByTypeAndForcedIdInPartition(nextResourceType, nextIds, theRequestPartitionId.getPartitionId());
|
||||
} else {
|
||||
if (theRequestPartitionId.isDefaultPartition()) {
|
||||
views = myForcedIdDao.findByTypeAndForcedIdInPartitionNull(nextResourceType, nextIds);
|
||||
} else if (theRequestPartitionId.hasDefaultPartitionId()) {
|
||||
views = myForcedIdDao.findByTypeAndForcedIdInPartitionIdsOrNullPartition(nextResourceType, nextIds, theRequestPartitionId.getPartitionIds());
|
||||
} else {
|
||||
views = myForcedIdDao.findByTypeAndForcedIdInPartitionIds(nextResourceType, nextIds, theRequestPartitionId.getPartitionIds());
|
||||
}
|
||||
}
|
||||
for (Object[] nextView : views) {
|
||||
|
@ -214,7 +219,7 @@ public class IdHelperService {
|
|||
Long pid = (Long) nextView[1];
|
||||
retVal.add(new ResourcePersistentId(pid));
|
||||
|
||||
String key = RequestPartitionId.stringifyForKey(theRequestPartitionId) + "/" + nextResourceType + "/" + forcedId;
|
||||
String key = partitionIdStringForKey + "/" + nextResourceType + "/" + forcedId;
|
||||
myMemoryCacheService.put(MemoryCacheService.CacheEnum.PERSISTENT_ID, key, pid);
|
||||
}
|
||||
}
|
||||
|
@ -261,35 +266,7 @@ public class IdHelperService {
|
|||
return typeToIds;
|
||||
}
|
||||
|
||||
private Long resolveResourceIdentity(@Nonnull RequestPartitionId theRequestPartitionId, @Nonnull String theResourceType, @Nonnull String theId) {
|
||||
Optional<Long> pid;
|
||||
if (theRequestPartitionId.isAllPartitions()) {
|
||||
try {
|
||||
pid = myForcedIdDao.findByTypeAndForcedId(theResourceType, theId);
|
||||
} catch (IncorrectResultSizeDataAccessException e) {
|
||||
/*
|
||||
* This means that:
|
||||
* 1. There are two resources with the exact same resource type and forced id
|
||||
* 2. The unique constraint on this column-pair has been dropped
|
||||
*/
|
||||
String msg = myFhirCtx.getLocalizer().getMessage(IdHelperService.class, "nonUniqueForcedId");
|
||||
throw new PreconditionFailedException(msg);
|
||||
}
|
||||
} else {
|
||||
if (theRequestPartitionId.getPartitionId() == null) {
|
||||
pid = myForcedIdDao.findByPartitionIdNullAndTypeAndForcedId(theResourceType, theId);
|
||||
} else {
|
||||
pid = myForcedIdDao.findByPartitionIdAndTypeAndForcedId(theRequestPartitionId.getPartitionId(), theResourceType, theId);
|
||||
}
|
||||
}
|
||||
|
||||
if (!pid.isPresent()) {
|
||||
throw new ResourceNotFoundException(new IdDt(theResourceType, theId));
|
||||
}
|
||||
return pid.get();
|
||||
}
|
||||
|
||||
private Collection<IResourceLookup> translateForcedIdToPids(@Nonnull RequestPartitionId theRequestPartitionId, RequestDetails theRequest, Collection<IIdType> theId) {
|
||||
private Collection<IResourceLookup> translateForcedIdToPids(@Nonnull RequestPartitionId theRequestPartitionId, Collection<IIdType> theId) {
|
||||
theId.forEach(id -> Validate.isTrue(id.hasIdPart()));
|
||||
|
||||
if (theId.isEmpty()) {
|
||||
|
@ -333,10 +310,12 @@ public class IdHelperService {
|
|||
if (theRequestPartitionId.isAllPartitions()) {
|
||||
views = myForcedIdDao.findAndResolveByForcedIdWithNoType(nextResourceType, nextIds);
|
||||
} else {
|
||||
if (theRequestPartitionId.getPartitionId() != null) {
|
||||
views = myForcedIdDao.findAndResolveByForcedIdWithNoTypeInPartition(nextResourceType, nextIds, theRequestPartitionId.getPartitionId());
|
||||
} else {
|
||||
if (theRequestPartitionId.isDefaultPartition()) {
|
||||
views = myForcedIdDao.findAndResolveByForcedIdWithNoTypeInPartitionNull(nextResourceType, nextIds);
|
||||
} else if (theRequestPartitionId.hasDefaultPartitionId()) {
|
||||
views = myForcedIdDao.findAndResolveByForcedIdWithNoTypeInPartitionIdOrNullPartitionId(nextResourceType, nextIds, theRequestPartitionId.getPartitionIdsWithoutDefault());
|
||||
} else {
|
||||
views = myForcedIdDao.findAndResolveByForcedIdWithNoTypeInPartition(nextResourceType, nextIds, theRequestPartitionId.getPartitionIds());
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -379,10 +358,12 @@ public class IdHelperService {
|
|||
if (theRequestPartitionId.isAllPartitions()) {
|
||||
lookup = myResourceTableDao.findLookupFieldsByResourcePid(thePidsToResolve);
|
||||
} else {
|
||||
if (theRequestPartitionId.getPartitionId() != null) {
|
||||
lookup = myResourceTableDao.findLookupFieldsByResourcePidInPartition(thePidsToResolve, theRequestPartitionId.getPartitionId());
|
||||
} else {
|
||||
if (theRequestPartitionId.isDefaultPartition()) {
|
||||
lookup = myResourceTableDao.findLookupFieldsByResourcePidInPartitionNull(thePidsToResolve);
|
||||
} else if (theRequestPartitionId.hasDefaultPartitionId()) {
|
||||
lookup = myResourceTableDao.findLookupFieldsByResourcePidInPartitionIdsOrNullPartition(thePidsToResolve, theRequestPartitionId.getPartitionIdsWithoutDefault());
|
||||
} else {
|
||||
lookup = myResourceTableDao.findLookupFieldsByResourcePidInPartitionIds(thePidsToResolve, theRequestPartitionId.getPartitionIds());
|
||||
}
|
||||
}
|
||||
lookup
|
||||
|
@ -448,23 +429,14 @@ public class IdHelperService {
|
|||
|
||||
@Nonnull
|
||||
public Long getPidOrThrowException(IIdType theId) {
|
||||
return getPidOrThrowException(theId, null);
|
||||
}
|
||||
|
||||
@Nonnull
|
||||
public Long getPidOrThrowException(IAnyResource theResource) {
|
||||
return (Long) theResource.getUserData(RESOURCE_PID);
|
||||
}
|
||||
|
||||
@Nonnull
|
||||
public Long getPidOrThrowException(IIdType theId, RequestDetails theRequestDetails) {
|
||||
List<IIdType> ids = Collections.singletonList(theId);
|
||||
List<ResourcePersistentId> resourcePersistentIds = this.resolveResourcePersistentIdsWithCache(RequestPartitionId.allPartitions(), ids);
|
||||
return resourcePersistentIds.get(0).getIdAsLong();
|
||||
}
|
||||
|
||||
public Map<Long, IIdType> getPidToIdMap(Collection<IIdType> theIds, RequestDetails theRequestDetails) {
|
||||
return theIds.stream().collect(Collectors.toMap(this::getPidOrThrowException, Function.identity()));
|
||||
@Nonnull
|
||||
public Long getPidOrThrowException(IAnyResource theResource) {
|
||||
return (Long) theResource.getUserData(RESOURCE_PID);
|
||||
}
|
||||
|
||||
public IIdType resourceIdFromPidOrThrowException(Long thePid) {
|
||||
|
|
|
@ -25,6 +25,7 @@ import ca.uhn.fhir.context.RuntimeResourceDefinition;
|
|||
import ca.uhn.fhir.context.RuntimeSearchParam;
|
||||
import ca.uhn.fhir.interceptor.model.RequestPartitionId;
|
||||
import ca.uhn.fhir.jpa.api.config.DaoConfig;
|
||||
import ca.uhn.fhir.jpa.model.entity.PartitionablePartitionId;
|
||||
import ca.uhn.fhir.rest.api.server.storage.TransactionDetails;
|
||||
import ca.uhn.fhir.jpa.dao.BaseHapiFhirDao;
|
||||
import ca.uhn.fhir.jpa.dao.MatchResourceUrlService;
|
||||
|
@ -99,7 +100,7 @@ public class SearchParamWithInlineReferencesExtractor {
|
|||
|
||||
RequestPartitionId partitionId;
|
||||
if (myPartitionSettings.isPartitioningEnabled()) {
|
||||
partitionId = theEntity.getPartitionId();
|
||||
partitionId = PartitionablePartitionId.toRequestPartitionId(theEntity.getPartitionId());
|
||||
} else {
|
||||
partitionId = RequestPartitionId.allPartitions();
|
||||
}
|
||||
|
|
|
@ -92,10 +92,10 @@ abstract class BasePredicateBuilder {
|
|||
|
||||
void addPredicateParamMissingForNonReference(String theResourceName, String theParamName, boolean theMissing, From<?, ? extends BaseResourceIndexedSearchParam> theJoin, RequestPartitionId theRequestPartitionId) {
|
||||
if (!theRequestPartitionId.isAllPartitions()) {
|
||||
if (theRequestPartitionId.getPartitionId() != null) {
|
||||
myQueryStack.addPredicate(myCriteriaBuilder.equal(theJoin.get("myPartitionIdValue"), theRequestPartitionId.getPartitionId()));
|
||||
} else {
|
||||
if (theRequestPartitionId.isDefaultPartition()) {
|
||||
myQueryStack.addPredicate(myCriteriaBuilder.isNull(theJoin.get("myPartitionIdValue")));
|
||||
} else {
|
||||
myQueryStack.addPredicate(theJoin.get("myPartitionIdValue").in(theRequestPartitionId.getPartitionIds()));
|
||||
}
|
||||
}
|
||||
myQueryStack.addPredicateWithImplicitTypeSelection(myCriteriaBuilder.equal(theJoin.get("myResourceType"), theResourceName));
|
||||
|
@ -184,12 +184,11 @@ abstract class BasePredicateBuilder {
|
|||
|
||||
void addPartitionIdPredicate(RequestPartitionId theRequestPartitionId, From<?, ? extends BasePartitionable> theJoin, List<Predicate> theCodePredicates) {
|
||||
if (!theRequestPartitionId.isAllPartitions()) {
|
||||
Integer partitionId = theRequestPartitionId.getPartitionId();
|
||||
Predicate partitionPredicate;
|
||||
if (partitionId != null) {
|
||||
partitionPredicate = myCriteriaBuilder.equal(theJoin.get("myPartitionIdValue").as(Integer.class), partitionId);
|
||||
} else {
|
||||
if (theRequestPartitionId.isDefaultPartition()) {
|
||||
partitionPredicate = myCriteriaBuilder.isNull(theJoin.get("myPartitionIdValue").as(Integer.class));
|
||||
} else {
|
||||
partitionPredicate = theJoin.get("myPartitionIdValue").as(Integer.class).in(theRequestPartitionId.getPartitionIds());
|
||||
}
|
||||
myQueryStack.addPredicate(partitionPredicate);
|
||||
}
|
||||
|
|
|
@ -151,7 +151,7 @@ class PredicateBuilderToken extends BasePredicateBuilder implements IPredicateBu
|
|||
theBuilder,
|
||||
theFrom,
|
||||
null,
|
||||
theRequestPartitionId);
|
||||
theRequestPartitionId);
|
||||
}
|
||||
|
||||
private Collection<Predicate> createPredicateToken(Collection<IQueryParameterType> theParameters,
|
||||
|
|
|
@ -63,8 +63,8 @@ class QueryRootEntryResourceTable extends QueryRootEntry {
|
|||
}
|
||||
addPredicate(myCriteriaBuilder.isNull(getRoot().get("myDeleted")));
|
||||
if (!myRequestPartitionId.isAllPartitions()) {
|
||||
if (myRequestPartitionId.getPartitionId() != null) {
|
||||
addPredicate(myCriteriaBuilder.equal(getRoot().get("myPartitionIdValue").as(Integer.class), myRequestPartitionId.getPartitionId()));
|
||||
if (!myRequestPartitionId.isDefaultPartition()) {
|
||||
addPredicate(getRoot().get("myPartitionIdValue").as(Integer.class).in(myRequestPartitionId.getPartitionIds()));
|
||||
} else {
|
||||
addPredicate(myCriteriaBuilder.isNull(getRoot().get("myPartitionIdValue").as(Integer.class)));
|
||||
}
|
||||
|
|
|
@ -24,6 +24,7 @@ import ca.uhn.fhir.context.FhirVersionEnum;
|
|||
import ca.uhn.fhir.interceptor.model.RequestPartitionId;
|
||||
import ca.uhn.fhir.jpa.model.entity.ForcedId;
|
||||
import ca.uhn.fhir.jpa.model.entity.IBaseResourceEntity;
|
||||
import ca.uhn.fhir.jpa.model.entity.PartitionablePartitionId;
|
||||
import ca.uhn.fhir.jpa.model.entity.ResourceEncodingEnum;
|
||||
import ca.uhn.fhir.jpa.model.entity.ResourceHistoryProvenanceEntity;
|
||||
import ca.uhn.fhir.model.primitive.IdDt;
|
||||
|
@ -32,6 +33,7 @@ import ca.uhn.fhir.rest.api.Constants;
|
|||
import org.hibernate.annotations.Immutable;
|
||||
import org.hibernate.annotations.Subselect;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
import javax.persistence.Column;
|
||||
import javax.persistence.Entity;
|
||||
import javax.persistence.EnumType;
|
||||
|
@ -199,8 +201,13 @@ public class ResourceSearchView implements IBaseResourceEntity, Serializable {
|
|||
}
|
||||
|
||||
@Override
|
||||
public RequestPartitionId getPartitionId() {
|
||||
return RequestPartitionId.fromPartitionId(myPartitionId);
|
||||
@Nullable
|
||||
public PartitionablePartitionId getPartitionId() {
|
||||
if (myPartitionId != null) {
|
||||
return new PartitionablePartitionId(myPartitionId, null);
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
public byte[] getResource() {
|
||||
|
|
|
@ -23,6 +23,8 @@ package ca.uhn.fhir.jpa.partition;
|
|||
import ca.uhn.fhir.jpa.entity.PartitionEntity;
|
||||
import ca.uhn.fhir.rest.server.exceptions.ResourceNotFoundException;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
|
||||
public interface IPartitionLookupSvc {
|
||||
|
||||
/**
|
||||
|
@ -33,6 +35,7 @@ public interface IPartitionLookupSvc {
|
|||
/**
|
||||
* @throws ResourceNotFoundException If the name is not known
|
||||
*/
|
||||
@Nullable
|
||||
PartitionEntity getPartitionByName(String theName) throws ResourceNotFoundException;
|
||||
|
||||
/**
|
||||
|
|
|
@ -23,6 +23,7 @@ package ca.uhn.fhir.jpa.partition;
|
|||
import ca.uhn.fhir.context.FhirContext;
|
||||
import ca.uhn.fhir.jpa.dao.data.IPartitionDao;
|
||||
import ca.uhn.fhir.jpa.entity.PartitionEntity;
|
||||
import ca.uhn.fhir.jpa.model.util.JpaConstants;
|
||||
import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException;
|
||||
import ca.uhn.fhir.rest.server.exceptions.ResourceNotFoundException;
|
||||
import com.github.benmanes.caffeine.cache.CacheLoader;
|
||||
|
@ -47,9 +48,6 @@ import static org.apache.commons.lang3.StringUtils.isBlank;
|
|||
|
||||
public class PartitionLookupSvcImpl implements IPartitionLookupSvc {
|
||||
|
||||
public static final int DEFAULT_PERSISTED_PARTITION_ID = 0;
|
||||
public static final String DEFAULT_PERSISTED_PARTITION_NAME = "DEFAULT";
|
||||
private static final String DEFAULT_PERSISTED_PARTITION_DESC = "Default partition";
|
||||
private static final Pattern PARTITION_NAME_VALID_PATTERN = Pattern.compile("[a-zA-Z0-9_-]+");
|
||||
private static final Logger ourLog = LoggerFactory.getLogger(PartitionLookupSvcImpl.class);
|
||||
|
||||
|
@ -76,23 +74,14 @@ public class PartitionLookupSvcImpl implements IPartitionLookupSvc {
|
|||
.expireAfterWrite(1, TimeUnit.MINUTES)
|
||||
.build(new IdToPartitionCacheLoader());
|
||||
myTxTemplate = new TransactionTemplate(myTxManager);
|
||||
|
||||
// Create default partition definition if it doesn't already exist
|
||||
myTxTemplate.executeWithoutResult(t -> {
|
||||
if (myPartitionDao.findById(DEFAULT_PERSISTED_PARTITION_ID).isPresent() == false) {
|
||||
ourLog.info("Creating default partition definition");
|
||||
PartitionEntity partitionEntity = new PartitionEntity();
|
||||
partitionEntity.setId(DEFAULT_PERSISTED_PARTITION_ID);
|
||||
partitionEntity.setName(DEFAULT_PERSISTED_PARTITION_NAME);
|
||||
partitionEntity.setDescription(DEFAULT_PERSISTED_PARTITION_DESC);
|
||||
myPartitionDao.save(partitionEntity);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public PartitionEntity getPartitionByName(String theName) {
|
||||
Validate.notBlank(theName, "The name must not be null or blank");
|
||||
if (JpaConstants.DEFAULT_PARTITION_NAME.equals(theName)) {
|
||||
return null;
|
||||
}
|
||||
return myNameToPartitionCache.get(theName);
|
||||
}
|
||||
|
||||
|
@ -114,11 +103,6 @@ public class PartitionLookupSvcImpl implements IPartitionLookupSvc {
|
|||
validateHaveValidPartitionIdAndName(thePartition);
|
||||
validatePartitionNameDoesntAlreadyExist(thePartition.getName());
|
||||
|
||||
if (thePartition.getId() == DEFAULT_PERSISTED_PARTITION_ID) {
|
||||
String msg = myFhirCtx.getLocalizer().getMessage(PartitionLookupSvcImpl.class, "cantCreatePartition0");
|
||||
throw new InvalidRequestException(msg);
|
||||
}
|
||||
|
||||
ourLog.info("Creating new partition with ID {} and Name {}", thePartition.getId(), thePartition.getName());
|
||||
|
||||
myPartitionDao.save(thePartition);
|
||||
|
@ -141,13 +125,6 @@ public class PartitionLookupSvcImpl implements IPartitionLookupSvc {
|
|||
validatePartitionNameDoesntAlreadyExist(thePartition.getName());
|
||||
}
|
||||
|
||||
if (DEFAULT_PERSISTED_PARTITION_ID == thePartition.getId()) {
|
||||
if (!DEFAULT_PERSISTED_PARTITION_NAME.equals(thePartition.getName())) {
|
||||
String msg = myFhirCtx.getLocalizer().getMessageSanitized(PartitionLookupSvcImpl.class, "cantRenameDefaultPartition");
|
||||
throw new InvalidRequestException(msg);
|
||||
}
|
||||
}
|
||||
|
||||
existingPartition.setName(thePartition.getName());
|
||||
existingPartition.setDescription(thePartition.getDescription());
|
||||
myPartitionDao.save(existingPartition);
|
||||
|
@ -160,11 +137,6 @@ public class PartitionLookupSvcImpl implements IPartitionLookupSvc {
|
|||
public void deletePartition(Integer thePartitionId) {
|
||||
validatePartitionIdSupplied(myFhirCtx, thePartitionId);
|
||||
|
||||
if (DEFAULT_PERSISTED_PARTITION_ID == thePartitionId) {
|
||||
String msg = myFhirCtx.getLocalizer().getMessageSanitized(PartitionLookupSvcImpl.class, "cantDeleteDefaultPartition");
|
||||
throw new InvalidRequestException(msg);
|
||||
}
|
||||
|
||||
Optional<PartitionEntity> partition = myPartitionDao.findById(thePartitionId);
|
||||
if (!partition.isPresent()) {
|
||||
String msg = myFhirCtx.getLocalizer().getMessageSanitized(PartitionLookupSvcImpl.class, "unknownPartitionId", thePartitionId);
|
||||
|
@ -189,6 +161,11 @@ public class PartitionLookupSvcImpl implements IPartitionLookupSvc {
|
|||
throw new InvalidRequestException(msg);
|
||||
}
|
||||
|
||||
if (thePartition.getName().equals(JpaConstants.DEFAULT_PARTITION_NAME)) {
|
||||
String msg = myFhirCtx.getLocalizer().getMessageSanitized(PartitionLookupSvcImpl.class, "cantCreateDefaultPartition");
|
||||
throw new InvalidRequestException(msg);
|
||||
}
|
||||
|
||||
if (!PARTITION_NAME_VALID_PATTERN.matcher(thePartition.getName()).matches()) {
|
||||
String msg = myFhirCtx.getLocalizer().getMessageSanitized(PartitionLookupSvcImpl.class, "invalidName", thePartition.getName());
|
||||
throw new InvalidRequestException(msg);
|
||||
|
|
|
@ -27,9 +27,9 @@ import ca.uhn.fhir.interceptor.api.Pointcut;
|
|||
import ca.uhn.fhir.interceptor.model.RequestPartitionId;
|
||||
import ca.uhn.fhir.jpa.entity.PartitionEntity;
|
||||
import ca.uhn.fhir.jpa.model.config.PartitionSettings;
|
||||
import ca.uhn.fhir.jpa.model.util.JpaConstants;
|
||||
import ca.uhn.fhir.rest.api.server.RequestDetails;
|
||||
import ca.uhn.fhir.rest.server.exceptions.InternalErrorException;
|
||||
import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException;
|
||||
import ca.uhn.fhir.rest.server.exceptions.ResourceNotFoundException;
|
||||
import ca.uhn.fhir.rest.server.exceptions.UnprocessableEntityException;
|
||||
import ca.uhn.fhir.rest.server.servlet.ServletRequestDetails;
|
||||
|
@ -39,7 +39,10 @@ import org.springframework.beans.factory.annotation.Autowired;
|
|||
|
||||
import javax.annotation.Nonnull;
|
||||
import javax.annotation.Nullable;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
|
||||
import static ca.uhn.fhir.jpa.util.JpaInterceptorBroadcaster.doCallHooks;
|
||||
import static ca.uhn.fhir.jpa.util.JpaInterceptorBroadcaster.doCallHooksAndReturnObject;
|
||||
|
@ -102,9 +105,9 @@ public class RequestPartitionHelperSvc implements IRequestPartitionHelperSvc {
|
|||
requestPartitionId = null;
|
||||
}
|
||||
|
||||
validatePartition(requestPartitionId, theResourceType, Pointcut.STORAGE_PARTITION_IDENTIFY_READ);
|
||||
validateRequestPartitionNotNull(requestPartitionId, Pointcut.STORAGE_PARTITION_IDENTIFY_READ);
|
||||
|
||||
return normalizeAndNotifyHooks(requestPartitionId, theRequest);
|
||||
return validateNormalizeAndNotifyHooksForRead(requestPartitionId, theRequest);
|
||||
}
|
||||
|
||||
return RequestPartitionId.allPartitions();
|
||||
|
@ -132,48 +135,29 @@ public class RequestPartitionHelperSvc implements IRequestPartitionHelperSvc {
|
|||
requestPartitionId = (RequestPartitionId) doCallHooksAndReturnObject(myInterceptorBroadcaster, theRequest, Pointcut.STORAGE_PARTITION_IDENTIFY_CREATE, params);
|
||||
|
||||
String resourceName = myFhirContext.getResourceType(theResource);
|
||||
validatePartition(requestPartitionId, resourceName, Pointcut.STORAGE_PARTITION_IDENTIFY_CREATE);
|
||||
validateSinglePartitionForCreate(requestPartitionId, resourceName, Pointcut.STORAGE_PARTITION_IDENTIFY_CREATE);
|
||||
|
||||
return normalizeAndNotifyHooks(requestPartitionId, theRequest);
|
||||
return validateNormalizeAndNotifyHooksForRead(requestPartitionId, theRequest);
|
||||
}
|
||||
|
||||
return RequestPartitionId.allPartitions();
|
||||
}
|
||||
|
||||
/**
|
||||
* If the partition only has a name but not an ID, this method resolves the ID
|
||||
* If the partition only has a name but not an ID, this method resolves the ID.
|
||||
* <p>
|
||||
* If the partition has an ID but not a name, the name is resolved.
|
||||
* <p>
|
||||
* If the partition has both, they are validated to ensure that they correspond.
|
||||
*/
|
||||
@Nonnull
|
||||
private RequestPartitionId normalizeAndNotifyHooks(@Nonnull RequestPartitionId theRequestPartitionId, RequestDetails theRequest) {
|
||||
private RequestPartitionId validateNormalizeAndNotifyHooksForRead(@Nonnull RequestPartitionId theRequestPartitionId, RequestDetails theRequest) {
|
||||
RequestPartitionId retVal = theRequestPartitionId;
|
||||
|
||||
if (retVal.getPartitionName() != null) {
|
||||
|
||||
PartitionEntity partition;
|
||||
try {
|
||||
partition = myPartitionConfigSvc.getPartitionByName(retVal.getPartitionName());
|
||||
} catch (IllegalArgumentException e) {
|
||||
String msg = myFhirContext.getLocalizer().getMessage(RequestPartitionHelperSvc.class, "unknownPartitionName", retVal.getPartitionName());
|
||||
throw new ResourceNotFoundException(msg);
|
||||
}
|
||||
|
||||
if (retVal.getPartitionId() != null) {
|
||||
Validate.isTrue(retVal.getPartitionId().equals(partition.getId()), "Partition name %s does not match ID %n", retVal.getPartitionName(), retVal.getPartitionId());
|
||||
} else {
|
||||
retVal = RequestPartitionId.forPartitionIdAndName(partition.getId(), retVal.getPartitionName(), retVal.getPartitionDate());
|
||||
}
|
||||
|
||||
} else if (retVal.getPartitionId() != null) {
|
||||
|
||||
PartitionEntity partition;
|
||||
try {
|
||||
partition = myPartitionConfigSvc.getPartitionById(retVal.getPartitionId());
|
||||
} catch (IllegalArgumentException e) {
|
||||
String msg = myFhirContext.getLocalizer().getMessage(RequestPartitionHelperSvc.class, "unknownPartitionId", retVal.getPartitionId());
|
||||
throw new ResourceNotFoundException(msg);
|
||||
}
|
||||
retVal = RequestPartitionId.forPartitionIdAndName(partition.getId(), partition.getName(), retVal.getPartitionDate());
|
||||
|
||||
if (retVal.getPartitionNames() != null) {
|
||||
retVal = validateAndNormalizePartitionNames(retVal);
|
||||
} else if (retVal.hasPartitionIds()) {
|
||||
retVal = validateAndNormalizePartitionIds(retVal);
|
||||
}
|
||||
|
||||
// Note: It's still possible that the partition only has a date but no name/id
|
||||
|
@ -188,27 +172,117 @@ public class RequestPartitionHelperSvc implements IRequestPartitionHelperSvc {
|
|||
|
||||
}
|
||||
|
||||
private void validatePartition(RequestPartitionId theRequestPartitionId, @Nonnull String theResourceName, Pointcut thePointcut) {
|
||||
if (theRequestPartitionId == null) {
|
||||
throw new InternalErrorException("No interceptor provided a value for pointcut: " + thePointcut);
|
||||
private RequestPartitionId validateAndNormalizePartitionIds(RequestPartitionId theRequestPartitionId) {
|
||||
List<String> names = null;
|
||||
for (int i = 0; i < theRequestPartitionId.getPartitionIds().size(); i++) {
|
||||
|
||||
PartitionEntity partition;
|
||||
Integer id = theRequestPartitionId.getPartitionIds().get(i);
|
||||
if (id == null) {
|
||||
partition = null;
|
||||
} else {
|
||||
try {
|
||||
partition = myPartitionConfigSvc.getPartitionById(id);
|
||||
} catch (IllegalArgumentException e) {
|
||||
String msg = myFhirContext.getLocalizer().getMessage(RequestPartitionHelperSvc.class, "unknownPartitionId", theRequestPartitionId.getPartitionIds().get(i));
|
||||
throw new ResourceNotFoundException(msg);
|
||||
}
|
||||
}
|
||||
|
||||
if (theRequestPartitionId.getPartitionNames() != null) {
|
||||
if (partition == null) {
|
||||
Validate.isTrue(theRequestPartitionId.getPartitionIds().get(i) == null, "Partition %s must not have an ID", JpaConstants.DEFAULT_PARTITION_NAME);
|
||||
} else {
|
||||
Validate.isTrue(Objects.equals(theRequestPartitionId.getPartitionIds().get(i), partition.getId()), "Partition name %s does not match ID %n", theRequestPartitionId.getPartitionNames().get(i), theRequestPartitionId.getPartitionIds().get(i));
|
||||
}
|
||||
} else {
|
||||
if (names == null) {
|
||||
names = new ArrayList<>();
|
||||
}
|
||||
if (partition != null) {
|
||||
names.add(partition.getName());
|
||||
} else {
|
||||
names.add(null);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
if (theRequestPartitionId.getPartitionId() != null) {
|
||||
if (names != null) {
|
||||
return RequestPartitionId.forPartitionIdsAndNames(names, theRequestPartitionId.getPartitionIds(), theRequestPartitionId.getPartitionDate());
|
||||
}
|
||||
|
||||
return theRequestPartitionId;
|
||||
}
|
||||
|
||||
private RequestPartitionId validateAndNormalizePartitionNames(RequestPartitionId theRequestPartitionId) {
|
||||
List<Integer> ids = null;
|
||||
for (int i = 0; i < theRequestPartitionId.getPartitionNames().size(); i++) {
|
||||
|
||||
PartitionEntity partition;
|
||||
try {
|
||||
partition = myPartitionConfigSvc.getPartitionByName(theRequestPartitionId.getPartitionNames().get(i));
|
||||
} catch (IllegalArgumentException e) {
|
||||
String msg = myFhirContext.getLocalizer().getMessage(RequestPartitionHelperSvc.class, "unknownPartitionName", theRequestPartitionId.getPartitionNames().get(i));
|
||||
throw new ResourceNotFoundException(msg);
|
||||
}
|
||||
|
||||
if (theRequestPartitionId.hasPartitionIds()) {
|
||||
if (partition == null) {
|
||||
Validate.isTrue(theRequestPartitionId.getPartitionIds().get(i) == null, "Partition %s must not have an ID", JpaConstants.DEFAULT_PARTITION_NAME);
|
||||
} else {
|
||||
Validate.isTrue(Objects.equals(theRequestPartitionId.getPartitionIds().get(i), partition.getId()), "Partition name %s does not match ID %n", theRequestPartitionId.getPartitionNames().get(i), theRequestPartitionId.getPartitionIds().get(i));
|
||||
}
|
||||
} else {
|
||||
if (ids == null) {
|
||||
ids = new ArrayList<>();
|
||||
}
|
||||
if (partition != null) {
|
||||
ids.add(partition.getId());
|
||||
} else {
|
||||
ids.add(null);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
if (ids != null) {
|
||||
return RequestPartitionId.forPartitionIdsAndNames(theRequestPartitionId.getPartitionNames(), ids, theRequestPartitionId.getPartitionDate());
|
||||
}
|
||||
|
||||
return theRequestPartitionId;
|
||||
}
|
||||
|
||||
private void validateSinglePartitionForCreate(RequestPartitionId theRequestPartitionId, @Nonnull String theResourceName, Pointcut thePointcut) {
|
||||
validateRequestPartitionNotNull(theRequestPartitionId, thePointcut);
|
||||
|
||||
if (theRequestPartitionId.hasPartitionIds()) {
|
||||
validateSinglePartitionIdOrNameForCreate(theRequestPartitionId.getPartitionIds());
|
||||
}
|
||||
validateSinglePartitionIdOrNameForCreate(theRequestPartitionId.getPartitionNames());
|
||||
|
||||
// Make sure we're not using one of the conformance resources in a non-default partition
|
||||
if ((theRequestPartitionId.hasPartitionIds() && !theRequestPartitionId.getPartitionIds().contains(null)) ||
|
||||
(theRequestPartitionId.hasPartitionNames() && !theRequestPartitionId.getPartitionNames().contains(JpaConstants.DEFAULT_PARTITION_NAME))) {
|
||||
|
||||
// Make sure we're not using one of the conformance resources in a non-default partition
|
||||
if (myPartitioningBlacklist.contains(theResourceName)) {
|
||||
String msg = myFhirContext.getLocalizer().getMessageSanitized(RequestPartitionHelperSvc.class, "blacklistedResourceTypeForPartitioning", theResourceName);
|
||||
throw new UnprocessableEntityException(msg);
|
||||
}
|
||||
|
||||
// Make sure the partition exists
|
||||
try {
|
||||
myPartitionConfigSvc.getPartitionById(theRequestPartitionId.getPartitionId());
|
||||
} catch (IllegalArgumentException e) {
|
||||
String msg = myFhirContext.getLocalizer().getMessageSanitized(RequestPartitionHelperSvc.class, "unknownPartitionId", theRequestPartitionId.getPartitionId());
|
||||
throw new InvalidRequestException(msg);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private void validateRequestPartitionNotNull(RequestPartitionId theTheRequestPartitionId, Pointcut theThePointcut) {
|
||||
if (theTheRequestPartitionId == null) {
|
||||
throw new InternalErrorException("No interceptor provided a value for pointcut: " + theThePointcut);
|
||||
}
|
||||
}
|
||||
|
||||
private void validateSinglePartitionIdOrNameForCreate(@Nullable List<?> thePartitionIds) {
|
||||
if (thePartitionIds != null && thePartitionIds.size() != 1) {
|
||||
throw new InternalErrorException("RequestPartitionId must contain a single partition for create operations, found: " + thePartitionIds);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -24,6 +24,7 @@ import ca.uhn.fhir.interceptor.model.RequestPartitionId;
|
|||
import ca.uhn.fhir.jpa.search.builder.sql.SearchQueryBuilder;
|
||||
import com.healthmarketscience.sqlbuilder.BinaryCondition;
|
||||
import com.healthmarketscience.sqlbuilder.Condition;
|
||||
import com.healthmarketscience.sqlbuilder.InCondition;
|
||||
import com.healthmarketscience.sqlbuilder.NotCondition;
|
||||
import com.healthmarketscience.sqlbuilder.UnaryCondition;
|
||||
import com.healthmarketscience.sqlbuilder.dbspec.basic.DbColumn;
|
||||
|
@ -35,6 +36,7 @@ import java.util.List;
|
|||
|
||||
import static ca.uhn.fhir.jpa.search.builder.QueryStack.toAndPredicate;
|
||||
import static ca.uhn.fhir.jpa.search.builder.QueryStack.toEqualToOrInPredicate;
|
||||
import static ca.uhn.fhir.jpa.search.builder.QueryStack.toOrPredicate;
|
||||
|
||||
public abstract class BaseJoiningPredicateBuilder extends BasePredicateBuilder {
|
||||
|
||||
|
@ -70,12 +72,16 @@ public abstract class BaseJoiningPredicateBuilder extends BasePredicateBuilder {
|
|||
public Condition createPartitionIdPredicate(RequestPartitionId theRequestPartitionId) {
|
||||
if (theRequestPartitionId != null && !theRequestPartitionId.isAllPartitions()) {
|
||||
Condition condition;
|
||||
Integer partitionId = theRequestPartitionId.getPartitionId();
|
||||
if (partitionId != null) {
|
||||
Object placeholder = generatePlaceholder(partitionId);
|
||||
condition = BinaryCondition.equalTo(getPartitionIdColumn(), placeholder);
|
||||
} else {
|
||||
if (theRequestPartitionId.isDefaultPartition()) {
|
||||
condition = UnaryCondition.isNull(getPartitionIdColumn());
|
||||
} else if (theRequestPartitionId.hasDefaultPartitionId()) {
|
||||
List<String> placeholders = generatePlaceholders(theRequestPartitionId.getPartitionIdsWithoutDefault());
|
||||
UnaryCondition partitionNullPredicate = UnaryCondition.isNull(getPartitionIdColumn());
|
||||
InCondition partitionIdsPredicate = new InCondition(getPartitionIdColumn(), placeholders);
|
||||
condition = toOrPredicate(partitionNullPredicate, partitionIdsPredicate);
|
||||
} else {
|
||||
List<String> placeholders = generatePlaceholders(theRequestPartitionId.getPartitionIds());
|
||||
condition = new InCondition(getPartitionIdColumn(), placeholders);
|
||||
}
|
||||
return condition;
|
||||
} else {
|
||||
|
|
|
@ -22,6 +22,7 @@ import ca.uhn.fhir.jpa.config.TestR4Config;
|
|||
import ca.uhn.fhir.jpa.dao.BaseJpaTest;
|
||||
import ca.uhn.fhir.jpa.dao.IFulltextSearchSvc;
|
||||
import ca.uhn.fhir.jpa.dao.data.IForcedIdDao;
|
||||
import ca.uhn.fhir.jpa.dao.data.IPartitionDao;
|
||||
import ca.uhn.fhir.jpa.dao.data.IResourceHistoryTableDao;
|
||||
import ca.uhn.fhir.jpa.dao.data.IResourceHistoryTagDao;
|
||||
import ca.uhn.fhir.jpa.dao.data.IResourceIndexedCompositeStringUniqueDao;
|
||||
|
@ -185,6 +186,8 @@ public abstract class BaseJpaR4Test extends BaseJpaTest implements ITestDataBuil
|
|||
@Autowired
|
||||
protected IPartitionLookupSvc myPartitionConfigSvc;
|
||||
@Autowired
|
||||
protected IPartitionDao myPartitionDao;
|
||||
@Autowired
|
||||
protected ITermReadSvc myHapiTerminologySvc;
|
||||
@Autowired
|
||||
protected CachingValidationSupport myCachingValidationSupport;
|
||||
|
|
|
@ -10,6 +10,7 @@ import ca.uhn.fhir.jpa.api.config.DaoConfig;
|
|||
import ca.uhn.fhir.jpa.entity.PartitionEntity;
|
||||
import ca.uhn.fhir.jpa.model.config.PartitionSettings;
|
||||
import ca.uhn.fhir.jpa.model.entity.ForcedId;
|
||||
import ca.uhn.fhir.jpa.model.entity.PartitionablePartitionId;
|
||||
import ca.uhn.fhir.jpa.model.entity.ResourceHistoryTable;
|
||||
import ca.uhn.fhir.jpa.model.entity.ResourceHistoryTag;
|
||||
import ca.uhn.fhir.jpa.model.entity.ResourceIndexedCompositeStringUnique;
|
||||
|
@ -19,6 +20,7 @@ import ca.uhn.fhir.jpa.model.entity.ResourceLink;
|
|||
import ca.uhn.fhir.jpa.model.entity.ResourceTable;
|
||||
import ca.uhn.fhir.jpa.model.entity.ResourceTag;
|
||||
import ca.uhn.fhir.jpa.model.entity.SearchParamPresent;
|
||||
import ca.uhn.fhir.jpa.model.util.JpaConstants;
|
||||
import ca.uhn.fhir.jpa.partition.IPartitionLookupSvc;
|
||||
import ca.uhn.fhir.jpa.searchparam.SearchParameterMap;
|
||||
import ca.uhn.fhir.jpa.util.SqlQuery;
|
||||
|
@ -35,6 +37,7 @@ import ca.uhn.fhir.rest.param.ReferenceParam;
|
|||
import ca.uhn.fhir.rest.param.StringParam;
|
||||
import ca.uhn.fhir.rest.param.TokenParam;
|
||||
import ca.uhn.fhir.rest.param.TokenParamModifier;
|
||||
import ca.uhn.fhir.rest.server.exceptions.InternalErrorException;
|
||||
import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException;
|
||||
import ca.uhn.fhir.rest.server.exceptions.PreconditionFailedException;
|
||||
import ca.uhn.fhir.rest.server.exceptions.ResourceNotFoundException;
|
||||
|
@ -74,6 +77,7 @@ import java.util.stream.Collectors;
|
|||
import static ca.uhn.fhir.jpa.util.TestUtil.sleepAtLeast;
|
||||
import static org.apache.commons.lang3.StringUtils.countMatches;
|
||||
import static org.hamcrest.MatcherAssert.assertThat;
|
||||
import static org.hamcrest.Matchers.contains;
|
||||
import static org.hamcrest.Matchers.containsString;
|
||||
import static org.hamcrest.Matchers.matchesPattern;
|
||||
import static org.hamcrest.Matchers.startsWith;
|
||||
|
@ -90,8 +94,11 @@ import static org.mockito.Mockito.when;
|
|||
@SuppressWarnings("unchecked")
|
||||
public class PartitioningSqlR4Test extends BaseJpaR4SystemTest {
|
||||
|
||||
static final String PARTITION_1 = "PART-1";
|
||||
static final String PARTITION_2 = "PART-2";
|
||||
static final String PARTITION_3 = "PART-3";
|
||||
static final String PARTITION_4 = "PART-4";
|
||||
private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(PartitioningSqlR4Test.class);
|
||||
|
||||
private MyReadWriteInterceptor myPartitionInterceptor;
|
||||
private LocalDate myPartitionDate;
|
||||
private LocalDate myPartitionDate2;
|
||||
|
@ -142,11 +149,17 @@ public class PartitioningSqlR4Test extends BaseJpaR4SystemTest {
|
|||
myPartitionInterceptor = new MyReadWriteInterceptor();
|
||||
myInterceptorRegistry.registerInterceptor(myPartitionInterceptor);
|
||||
|
||||
myPartitionConfigSvc.createPartition(new PartitionEntity().setId(1).setName("PART-1"));
|
||||
myPartitionConfigSvc.createPartition(new PartitionEntity().setId(2).setName("PART-2"));
|
||||
myPartitionConfigSvc.createPartition(new PartitionEntity().setId(3).setName("PART-3"));
|
||||
myPartitionConfigSvc.createPartition(new PartitionEntity().setId(1).setName(PARTITION_1));
|
||||
myPartitionConfigSvc.createPartition(new PartitionEntity().setId(2).setName(PARTITION_2));
|
||||
myPartitionConfigSvc.createPartition(new PartitionEntity().setId(3).setName(PARTITION_3));
|
||||
myPartitionConfigSvc.createPartition(new PartitionEntity().setId(4).setName(PARTITION_4));
|
||||
|
||||
myDaoConfig.setIndexMissingFields(DaoConfig.IndexEnabledEnum.ENABLED);
|
||||
|
||||
// Ensure the partition names are resolved
|
||||
myPartitionInterceptor.addReadPartition(RequestPartitionId.fromPartitionNames(JpaConstants.DEFAULT_PARTITION_NAME, PARTITION_1, PARTITION_2, PARTITION_3, PARTITION_4));
|
||||
myPatientDao.search(new SearchParameterMap().setLoadSynchronous(true));
|
||||
|
||||
}
|
||||
|
||||
@Test
|
||||
|
@ -162,7 +175,7 @@ public class PartitioningSqlR4Test extends BaseJpaR4SystemTest {
|
|||
|
||||
runInTransaction(() -> {
|
||||
ResourceTable resourceTable = myResourceTableDao.findById(id).orElseThrow(IllegalArgumentException::new);
|
||||
assertEquals(RequestPartitionId.defaultPartition(), resourceTable.getPartitionId());
|
||||
assertEquals(null, resourceTable.getPartitionId());
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -341,8 +354,10 @@ public class PartitioningSqlR4Test extends BaseJpaR4SystemTest {
|
|||
runInTransaction(() -> {
|
||||
// HFJ_RESOURCE
|
||||
ResourceTable resourceTable = myResourceTableDao.findById(id).orElseThrow(IllegalArgumentException::new);
|
||||
assertNull(resourceTable.getPartitionId().getPartitionId());
|
||||
assertEquals(myPartitionDate, resourceTable.getPartitionId().getPartitionDate());
|
||||
PartitionablePartitionId partitionId = resourceTable.getPartitionId();
|
||||
assertNotNull(partitionId);
|
||||
assertNull(partitionId.getPartitionId());
|
||||
assertEquals(myPartitionDate, partitionId.getPartitionDate());
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -393,7 +408,7 @@ public class PartitioningSqlR4Test extends BaseJpaR4SystemTest {
|
|||
|
||||
runInTransaction(() -> {
|
||||
ResourceTable resourceTable = myResourceTableDao.findById(patientId).orElseThrow(IllegalArgumentException::new);
|
||||
assertEquals(RequestPartitionId.defaultPartition(), resourceTable.getPartitionId());
|
||||
assertEquals(null, resourceTable.getPartitionId());
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -615,8 +630,8 @@ public class PartitioningSqlR4Test extends BaseJpaR4SystemTest {
|
|||
// HFJ_FORCED_ID
|
||||
List<ForcedId> forcedIds = myForcedIdDao.findAll();
|
||||
assertEquals(2, forcedIds.size());
|
||||
assertEquals(null, forcedIds.get(0).getPartitionId().getPartitionId());
|
||||
assertEquals(null, forcedIds.get(1).getPartitionId().getPartitionId());
|
||||
assertEquals(null, forcedIds.get(0).getPartitionId());
|
||||
assertEquals(null, forcedIds.get(1).getPartitionId());
|
||||
});
|
||||
|
||||
}
|
||||
|
@ -883,6 +898,114 @@ public class PartitioningSqlR4Test extends BaseJpaR4SystemTest {
|
|||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testRead_PidId_MultiplePartitionNames() {
|
||||
IIdType patientIdNull = createPatient(withPartition(null), withActiveTrue());
|
||||
IIdType patientId1 = createPatient(withPartition(1), withActiveTrue());
|
||||
createPatient(withPartition(2), withActiveTrue());
|
||||
IIdType patientId3 = createPatient(withPartition(3), withActiveTrue());
|
||||
|
||||
// Two partitions - Found
|
||||
{
|
||||
myCaptureQueriesListener.clear();
|
||||
myPartitionInterceptor.addReadPartition(RequestPartitionId.fromPartitionNames(PARTITION_1, PARTITION_2));
|
||||
IdType gotId1 = myPatientDao.read(patientId1, mySrd).getIdElement().toUnqualifiedVersionless();
|
||||
assertEquals(patientId1, gotId1);
|
||||
|
||||
// Only the read columns should be used, but no selectors on partition ID
|
||||
String searchSql = myCaptureQueriesListener.getSelectQueriesForCurrentThread().get(0).getSql(true, true);
|
||||
assertEquals(2, StringUtils.countMatches(searchSql, "PARTITION_ID as "), searchSql);
|
||||
assertEquals(2, StringUtils.countMatches(searchSql, "PARTITION_ID"), searchSql);
|
||||
}
|
||||
|
||||
// Two partitions including default - Found
|
||||
{
|
||||
myCaptureQueriesListener.clear();
|
||||
myPartitionInterceptor.addReadPartition(RequestPartitionId.fromPartitionNames(PARTITION_1, JpaConstants.DEFAULT_PARTITION_NAME));
|
||||
IdType gotId1 = myPatientDao.read(patientIdNull, mySrd).getIdElement().toUnqualifiedVersionless();
|
||||
assertEquals(patientIdNull, gotId1);
|
||||
|
||||
// Only the read columns should be used, but no selectors on partition ID
|
||||
String searchSql = myCaptureQueriesListener.getSelectQueriesForCurrentThread().get(0).getSql(true, true);
|
||||
assertEquals(2, StringUtils.countMatches(searchSql, "PARTITION_ID as "), searchSql);
|
||||
assertEquals(2, StringUtils.countMatches(searchSql, "PARTITION_ID"), searchSql);
|
||||
}
|
||||
|
||||
// Two partitions - Not Found
|
||||
{
|
||||
myPartitionInterceptor.addReadPartition(RequestPartitionId.fromPartitionNames(PARTITION_1, PARTITION_2));
|
||||
try {
|
||||
myPatientDao.read(patientId3, mySrd);
|
||||
fail();
|
||||
} catch (ResourceNotFoundException e) {
|
||||
// good
|
||||
}
|
||||
|
||||
myPartitionInterceptor.addReadPartition(RequestPartitionId.fromPartitionNames(PARTITION_1, PARTITION_2));
|
||||
try {
|
||||
myPatientDao.read(patientIdNull, mySrd);
|
||||
fail();
|
||||
} catch (ResourceNotFoundException e) {
|
||||
// good
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testRead_PidId_MultiplePartitionIds() {
|
||||
IIdType patientIdNull = createPatient(withPartition(null), withActiveTrue());
|
||||
IIdType patientId1 = createPatient(withPartition(1), withActiveTrue());
|
||||
createPatient(withPartition(2), withActiveTrue());
|
||||
IIdType patientId3 = createPatient(withPartition(3), withActiveTrue());
|
||||
|
||||
// Two partitions - Found
|
||||
{
|
||||
myCaptureQueriesListener.clear();
|
||||
myPartitionInterceptor.addReadPartition(RequestPartitionId.fromPartitionIds(1, 2));
|
||||
IdType gotId1 = myPatientDao.read(patientId1, mySrd).getIdElement().toUnqualifiedVersionless();
|
||||
assertEquals(patientId1, gotId1);
|
||||
|
||||
// Only the read columns should be used, but no selectors on partition ID
|
||||
String searchSql = myCaptureQueriesListener.getSelectQueriesForCurrentThread().get(0).getSql(true, true);
|
||||
assertEquals(2, StringUtils.countMatches(searchSql, "PARTITION_ID as "), searchSql);
|
||||
assertEquals(2, StringUtils.countMatches(searchSql, "PARTITION_ID"), searchSql);
|
||||
}
|
||||
|
||||
// Two partitions including default - Found
|
||||
{
|
||||
myCaptureQueriesListener.clear();
|
||||
myPartitionInterceptor.addReadPartition(RequestPartitionId.fromPartitionIds(1, null));
|
||||
IdType gotId1 = myPatientDao.read(patientIdNull, mySrd).getIdElement().toUnqualifiedVersionless();
|
||||
assertEquals(patientIdNull, gotId1);
|
||||
|
||||
// Only the read columns should be used, but no selectors on partition ID
|
||||
String searchSql = myCaptureQueriesListener.getSelectQueriesForCurrentThread().get(0).getSql(true, true);
|
||||
assertEquals(2, StringUtils.countMatches(searchSql, "PARTITION_ID as "), searchSql);
|
||||
assertEquals(2, StringUtils.countMatches(searchSql, "PARTITION_ID"), searchSql);
|
||||
}
|
||||
|
||||
// Two partitions - Not Found
|
||||
{
|
||||
myPartitionInterceptor.addReadPartition(RequestPartitionId.fromPartitionNames(PARTITION_1, PARTITION_2));
|
||||
try {
|
||||
myPatientDao.read(patientId3, mySrd);
|
||||
fail();
|
||||
} catch (ResourceNotFoundException e) {
|
||||
// good
|
||||
}
|
||||
|
||||
myPartitionInterceptor.addReadPartition(RequestPartitionId.fromPartitionNames(PARTITION_1, PARTITION_2));
|
||||
try {
|
||||
myPatientDao.read(patientIdNull, mySrd);
|
||||
fail();
|
||||
} catch (ResourceNotFoundException e) {
|
||||
// good
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testRead_PidId_DefaultPartition() {
|
||||
IIdType patientIdNull = createPatient(withPartition(null), withActiveTrue());
|
||||
|
@ -1030,7 +1153,7 @@ public class PartitioningSqlR4Test extends BaseJpaR4SystemTest {
|
|||
map.setLoadSynchronous(true);
|
||||
IBundleProvider results = myPatientDao.search(map);
|
||||
List<IIdType> ids = toUnqualifiedVersionlessIds(results);
|
||||
assertThat(ids, Matchers.contains(patientIdNull, patientId1, patientId2));
|
||||
assertThat(ids, contains(patientIdNull, patientId1, patientId2));
|
||||
|
||||
String searchSql = myCaptureQueriesListener.getSelectQueriesForCurrentThread().get(0).getSql(true, true);
|
||||
ourLog.info("Search SQL:\n{}", searchSql);
|
||||
|
@ -1047,7 +1170,7 @@ public class PartitioningSqlR4Test extends BaseJpaR4SystemTest {
|
|||
map.setLoadSynchronous(true);
|
||||
IBundleProvider results = myPatientDao.search(map);
|
||||
List<IIdType> ids = toUnqualifiedVersionlessIds(results);
|
||||
assertThat(ids, Matchers.contains(patientIdNull, patientId1, patientId2));
|
||||
assertThat(ids, contains(patientIdNull, patientId1, patientId2));
|
||||
|
||||
String searchSql = myCaptureQueriesListener.getSelectQueriesForCurrentThread().get(0).getSql(true, true);
|
||||
ourLog.info("Search SQL:\n{}", searchSql);
|
||||
|
@ -1072,11 +1195,11 @@ public class PartitioningSqlR4Test extends BaseJpaR4SystemTest {
|
|||
map.setLoadSynchronous(true);
|
||||
IBundleProvider results = myPatientDao.search(map);
|
||||
List<IIdType> ids = toUnqualifiedVersionlessIds(results);
|
||||
assertThat(ids, Matchers.contains(patientId1));
|
||||
assertThat(ids, contains(patientId1));
|
||||
|
||||
String searchSql = myCaptureQueriesListener.getSelectQueriesForCurrentThread().get(0).getSql(true, true);
|
||||
ourLog.info("Search SQL:\n{}", searchSql);
|
||||
assertEquals(1, StringUtils.countMatches(searchSql, "t0.PARTITION_ID = '1'"), searchSql);
|
||||
ourLog.info("Search SQL:\n{}", myCaptureQueriesListener.getSelectQueriesForCurrentThread().get(0).getSql(true, true));
|
||||
String searchSql = myCaptureQueriesListener.getSelectQueriesForCurrentThread().get(0).getSql(true, false);
|
||||
assertEquals(1, StringUtils.countMatches(searchSql, "t0.PARTITION_ID IN ('1')"), searchSql);
|
||||
assertEquals(1, StringUtils.countMatches(searchSql, "SP_MISSING = 'true'"), searchSql);
|
||||
}
|
||||
|
||||
|
@ -1089,11 +1212,11 @@ public class PartitioningSqlR4Test extends BaseJpaR4SystemTest {
|
|||
map.setLoadSynchronous(true);
|
||||
IBundleProvider results = myPatientDao.search(map);
|
||||
List<IIdType> ids = toUnqualifiedVersionlessIds(results);
|
||||
assertThat(ids, Matchers.contains(patientId1));
|
||||
assertThat(ids, contains(patientId1));
|
||||
|
||||
String searchSql = myCaptureQueriesListener.getSelectQueriesForCurrentThread().get(0).getSql(true, true);
|
||||
ourLog.info("Search SQL:\n{}", searchSql);
|
||||
assertEquals(1, StringUtils.countMatches(searchSql, "t0.PARTITION_ID = '1'"));
|
||||
ourLog.info("Search SQL:\n{}", myCaptureQueriesListener.getSelectQueriesForCurrentThread().get(0).getSql(true, true));
|
||||
String searchSql = myCaptureQueriesListener.getSelectQueriesForCurrentThread().get(0).getSql(true, false);
|
||||
assertEquals(1, StringUtils.countMatches(searchSql, "t0.PARTITION_ID IN ('1')"));
|
||||
assertEquals(1, StringUtils.countMatches(searchSql, "SP_MISSING = 'false'"));
|
||||
}
|
||||
}
|
||||
|
@ -1113,7 +1236,7 @@ public class PartitioningSqlR4Test extends BaseJpaR4SystemTest {
|
|||
map.setLoadSynchronous(true);
|
||||
IBundleProvider results = myPatientDao.search(map);
|
||||
List<IIdType> ids = toUnqualifiedVersionlessIds(results);
|
||||
assertThat(ids, Matchers.contains(patientIdNull));
|
||||
assertThat(ids, contains(patientIdNull));
|
||||
|
||||
String searchSql = myCaptureQueriesListener.getSelectQueriesForCurrentThread().get(0).getSql(true, true);
|
||||
ourLog.info("Search SQL:\n{}", searchSql);
|
||||
|
@ -1130,7 +1253,7 @@ public class PartitioningSqlR4Test extends BaseJpaR4SystemTest {
|
|||
map.setLoadSynchronous(true);
|
||||
IBundleProvider results = myPatientDao.search(map);
|
||||
List<IIdType> ids = toUnqualifiedVersionlessIds(results);
|
||||
assertThat(ids, Matchers.contains(patientIdNull));
|
||||
assertThat(ids, contains(patientIdNull));
|
||||
|
||||
String searchSql = myCaptureQueriesListener.getSelectQueriesForCurrentThread().get(0).getSql(true, true);
|
||||
ourLog.info("Search SQL:\n{}", searchSql);
|
||||
|
@ -1156,7 +1279,7 @@ public class PartitioningSqlR4Test extends BaseJpaR4SystemTest {
|
|||
map.setLoadSynchronous(true);
|
||||
IBundleProvider results = myPatientDao.search(map);
|
||||
List<IIdType> ids = toUnqualifiedVersionlessIds(results);
|
||||
assertThat(ids, Matchers.contains(patientIdNull, patientId1, patientId2));
|
||||
assertThat(ids, contains(patientIdNull, patientId1, patientId2));
|
||||
|
||||
String searchSql = myCaptureQueriesListener.getSelectQueriesForCurrentThread().get(0).getSql(true, true);
|
||||
ourLog.info("Search SQL:\n{}", searchSql);
|
||||
|
@ -1183,12 +1306,12 @@ public class PartitioningSqlR4Test extends BaseJpaR4SystemTest {
|
|||
map.setLoadSynchronous(true);
|
||||
IBundleProvider results = myPatientDao.search(map);
|
||||
List<IIdType> ids = toUnqualifiedVersionlessIds(results);
|
||||
assertThat(ids, Matchers.contains(patientId1));
|
||||
assertThat(ids, contains(patientId1));
|
||||
|
||||
String searchSql = myCaptureQueriesListener.getSelectQueriesForCurrentThread().get(0).getSql(true, true);
|
||||
ourLog.info("Search SQL:\n{}", searchSql);
|
||||
ourLog.info("Search SQL:\n{}", myCaptureQueriesListener.getSelectQueriesForCurrentThread().get(0).getSql(true, true));
|
||||
String searchSql = myCaptureQueriesListener.getSelectQueriesForCurrentThread().get(0).getSql(true, false);
|
||||
assertEquals(1, StringUtils.countMatches(searchSql, "PARTITION_ID"), searchSql);
|
||||
assertEquals(1, StringUtils.countMatches(searchSql, "t0.PARTITION_ID = '1'"), searchSql);
|
||||
assertEquals(1, StringUtils.countMatches(searchSql, "t0.PARTITION_ID IN ('1')"), searchSql);
|
||||
assertEquals(1, StringUtils.countMatches(searchSql, "HFJ_RES_PARAM_PRESENT"), searchSql);
|
||||
assertEquals(1, StringUtils.countMatches(searchSql, "HASH_PRESENCE = '-3438137196820602023'"), searchSql);
|
||||
}
|
||||
|
@ -1211,12 +1334,12 @@ public class PartitioningSqlR4Test extends BaseJpaR4SystemTest {
|
|||
map.setLoadSynchronous(true);
|
||||
IBundleProvider results = myPatientDao.search(map);
|
||||
List<IIdType> ids = toUnqualifiedVersionlessIds(results);
|
||||
assertThat(ids, Matchers.contains(patientId1));
|
||||
assertThat(ids, contains(patientId1));
|
||||
|
||||
String searchSql = myCaptureQueriesListener.getSelectQueriesForCurrentThread().get(0).getSql(true, true);
|
||||
ourLog.info("Search SQL:\n{}", searchSql);
|
||||
ourLog.info("Search SQL:\n{}", myCaptureQueriesListener.getSelectQueriesForCurrentThread().get(0).getSql(true, true));
|
||||
String searchSql = myCaptureQueriesListener.getSelectQueriesForCurrentThread().get(0).getSql(true, false);
|
||||
assertEquals(1, StringUtils.countMatches(searchSql, "PARTITION_ID"), searchSql);
|
||||
assertEquals(1, StringUtils.countMatches(searchSql, "t0.PARTITION_ID = '1'"), searchSql);
|
||||
assertEquals(1, StringUtils.countMatches(searchSql, "t0.PARTITION_ID IN ('1')"), searchSql);
|
||||
assertEquals(1, StringUtils.countMatches(searchSql, "HFJ_RES_PARAM_PRESENT"), searchSql);
|
||||
assertEquals(1, StringUtils.countMatches(searchSql, "HASH_PRESENCE = '1919227773735728687'"), searchSql);
|
||||
}
|
||||
|
@ -1237,7 +1360,7 @@ public class PartitioningSqlR4Test extends BaseJpaR4SystemTest {
|
|||
map.setLoadSynchronous(true);
|
||||
IBundleProvider results = myPatientDao.search(map);
|
||||
List<IIdType> ids = toUnqualifiedVersionlessIds(results);
|
||||
assertThat(ids, Matchers.contains(patientIdDefault));
|
||||
assertThat(ids, contains(patientIdDefault));
|
||||
|
||||
String searchSql = myCaptureQueriesListener.getSelectQueriesForCurrentThread().get(0).getSql(true, true);
|
||||
ourLog.info("Search SQL:\n{}", searchSql);
|
||||
|
@ -1262,7 +1385,7 @@ public class PartitioningSqlR4Test extends BaseJpaR4SystemTest {
|
|||
map.setLoadSynchronous(true);
|
||||
IBundleProvider results = myPatientDao.search(map);
|
||||
List<IIdType> ids = toUnqualifiedVersionlessIds(results);
|
||||
assertThat(ids, Matchers.contains(patientIdNull, patientId1, patientId2));
|
||||
assertThat(ids, contains(patientIdNull, patientId1, patientId2));
|
||||
|
||||
String searchSql = myCaptureQueriesListener.getSelectQueriesForCurrentThread().get(0).getSql(true, true);
|
||||
ourLog.info("Search SQL:\n{}", searchSql);
|
||||
|
@ -1282,13 +1405,56 @@ public class PartitioningSqlR4Test extends BaseJpaR4SystemTest {
|
|||
map.setLoadSynchronous(true);
|
||||
IBundleProvider results = myPatientDao.search(map);
|
||||
List<IIdType> ids = toUnqualifiedVersionlessIds(results);
|
||||
assertThat(ids, Matchers.contains(patientId1));
|
||||
assertThat(ids, contains(patientId1));
|
||||
|
||||
String searchSql = myCaptureQueriesListener.getSelectQueriesForCurrentThread().get(0).getSql(true, true);
|
||||
ourLog.info("Search SQL:\n{}", searchSql);
|
||||
assertEquals(1, StringUtils.countMatches(searchSql, "PARTITION_ID"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSearch_NoParams_SearchMultiplePartitionsByName_NoDefault() {
|
||||
createPatient(withPartition(null), withActiveTrue());
|
||||
IIdType patientId1 = createPatient(withPartition(1), withActiveTrue());
|
||||
IIdType patientId2 = createPatient(withPartition(2), withActiveTrue());
|
||||
createPatient(withPartition(3), withActiveTrue());
|
||||
|
||||
addReadPartitions(PARTITION_1, PARTITION_2);
|
||||
|
||||
myCaptureQueriesListener.clear();
|
||||
SearchParameterMap map = new SearchParameterMap();
|
||||
map.setLoadSynchronous(true);
|
||||
IBundleProvider results = myPatientDao.search(map);
|
||||
List<IIdType> ids = toUnqualifiedVersionlessIds(results);
|
||||
assertThat(ids, Matchers.contains(patientId1, patientId2));
|
||||
|
||||
ourLog.info("Search SQL:\n{}", myCaptureQueriesListener.getSelectQueriesForCurrentThread().get(0).getSql(true, true));
|
||||
String searchSql = myCaptureQueriesListener.getSelectQueriesForCurrentThread().get(0).getSql(true, false);
|
||||
assertThat(searchSql, containsString("PARTITION_ID IN ('1','2')"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSearch_NoParams_SearchMultiplePartitionsByName_WithDefault() {
|
||||
IIdType patientIdNull = createPatient(withPartition(null), withActiveTrue());
|
||||
createPatient(withPartition(1), withActiveTrue());
|
||||
IIdType patientId2 = createPatient(withPartition(2), withActiveTrue());
|
||||
createPatient(withPartition(3), withActiveTrue());
|
||||
|
||||
addReadPartitions(JpaConstants.DEFAULT_PARTITION_NAME, PARTITION_2);
|
||||
|
||||
myCaptureQueriesListener.clear();
|
||||
SearchParameterMap map = new SearchParameterMap();
|
||||
map.setLoadSynchronous(true);
|
||||
IBundleProvider results = myPatientDao.search(map);
|
||||
List<IIdType> ids = toUnqualifiedVersionlessIds(results);
|
||||
assertThat(ids.toString(), ids, Matchers.containsInAnyOrder(patientIdNull, patientId2));
|
||||
|
||||
ourLog.info("Search SQL:\n{}", myCaptureQueriesListener.getSelectQueriesForCurrentThread().get(0).getSql(true, true));
|
||||
String sql = myCaptureQueriesListener.getSelectQueriesForCurrentThread().get(0).getSql(true, false);
|
||||
assertThat(sql, sql, containsString("PARTITION_ID IN ('2')"));
|
||||
assertThat(sql, sql, containsString("PARTITION_ID IS NULL"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSearch_DateParam_SearchAllPartitions() {
|
||||
myPartitionSettings.setIncludePartitionInSearchHashes(false);
|
||||
|
@ -1309,7 +1475,7 @@ public class PartitioningSqlR4Test extends BaseJpaR4SystemTest {
|
|||
map.setLoadSynchronous(true);
|
||||
IBundleProvider results = myPatientDao.search(map);
|
||||
List<IIdType> ids = toUnqualifiedVersionlessIds(results);
|
||||
assertThat(ids, Matchers.contains(patientIdNull, patientId1, patientId2));
|
||||
assertThat(ids, contains(patientIdNull, patientId1, patientId2));
|
||||
|
||||
String searchSql = myCaptureQueriesListener.getSelectQueriesForCurrentThread().get(0).getSql(true, true);
|
||||
ourLog.info("Search SQL:\n{}", searchSql);
|
||||
|
@ -1325,7 +1491,7 @@ public class PartitioningSqlR4Test extends BaseJpaR4SystemTest {
|
|||
map.setLoadSynchronous(true);
|
||||
results = myPatientDao.search(map);
|
||||
ids = toUnqualifiedVersionlessIds(results);
|
||||
assertThat(ids, Matchers.contains(patientIdNull, patientId1, patientId2));
|
||||
assertThat(ids, contains(patientIdNull, patientId1, patientId2));
|
||||
|
||||
searchSql = myCaptureQueriesListener.getSelectQueriesForCurrentThread().get(0).getSql(true, true);
|
||||
ourLog.info("Search SQL:\n{}", searchSql);
|
||||
|
@ -1341,7 +1507,7 @@ public class PartitioningSqlR4Test extends BaseJpaR4SystemTest {
|
|||
map.setLoadSynchronous(true);
|
||||
results = myPatientDao.search(map);
|
||||
ids = toUnqualifiedVersionlessIds(results);
|
||||
assertThat(ids, Matchers.contains(patientIdNull, patientId1, patientId2));
|
||||
assertThat(ids, contains(patientIdNull, patientId1, patientId2));
|
||||
|
||||
searchSql = myCaptureQueriesListener.getSelectQueriesForCurrentThread().get(0).getSql(true, true);
|
||||
ourLog.info("Search SQL:\n{}", searchSql);
|
||||
|
@ -1357,7 +1523,7 @@ public class PartitioningSqlR4Test extends BaseJpaR4SystemTest {
|
|||
map.setLoadSynchronous(true);
|
||||
results = myPatientDao.search(map);
|
||||
ids = toUnqualifiedVersionlessIds(results);
|
||||
assertThat(ids, Matchers.contains(patientIdNull, patientId1, patientId2));
|
||||
assertThat(ids, contains(patientIdNull, patientId1, patientId2));
|
||||
|
||||
searchSql = myCaptureQueriesListener.getSelectQueriesForCurrentThread().get(0).getSql(true, true);
|
||||
ourLog.info("Search SQL:\n{}", searchSql);
|
||||
|
@ -1371,9 +1537,9 @@ public class PartitioningSqlR4Test extends BaseJpaR4SystemTest {
|
|||
public void testSearch_DateParam_SearchSpecificPartitions() {
|
||||
myPartitionSettings.setIncludePartitionInSearchHashes(false);
|
||||
|
||||
IIdType patientIdNull = createPatient(withPartition(null), withBirthdate("2020-04-20"));
|
||||
createPatient(withPartition(null), withBirthdate("2020-04-20"));
|
||||
IIdType patientId1 = createPatient(withPartition(1), withBirthdate("2020-04-20"));
|
||||
IIdType patientId2 = createPatient(withPartition(2), withBirthdate("2020-04-20"));
|
||||
createPatient(withPartition(2), withBirthdate("2020-04-20"));
|
||||
createPatient(withPartition(null), withBirthdate("2021-04-20"));
|
||||
createPatient(withPartition(1), withBirthdate("2021-04-20"));
|
||||
createPatient(withPartition(2), withBirthdate("2021-04-20"));
|
||||
|
@ -1390,7 +1556,7 @@ public class PartitioningSqlR4Test extends BaseJpaR4SystemTest {
|
|||
IBundleProvider results = myPatientDao.search(map);
|
||||
myCaptureQueriesListener.logSelectQueriesForCurrentThread();
|
||||
List<IIdType> ids = toUnqualifiedVersionlessIds(results);
|
||||
assertThat(ids, Matchers.contains(patientId1));
|
||||
assertThat(ids, contains(patientId1));
|
||||
|
||||
String searchSql = myCaptureQueriesListener.getSelectQueriesForCurrentThread().get(0).getSql(true, true);
|
||||
ourLog.info("Search SQL:\n{}", searchSql);
|
||||
|
@ -1406,7 +1572,7 @@ public class PartitioningSqlR4Test extends BaseJpaR4SystemTest {
|
|||
map.setLoadSynchronous(true);
|
||||
results = myPatientDao.search(map);
|
||||
ids = toUnqualifiedVersionlessIds(results);
|
||||
assertThat(ids, Matchers.contains(patientId1));
|
||||
assertThat(ids, contains(patientId1));
|
||||
|
||||
searchSql = myCaptureQueriesListener.getSelectQueriesForCurrentThread().get(0).getSql(true, true);
|
||||
ourLog.info("Search SQL:\n{}", searchSql);
|
||||
|
@ -1422,7 +1588,7 @@ public class PartitioningSqlR4Test extends BaseJpaR4SystemTest {
|
|||
map.setLoadSynchronous(true);
|
||||
results = myPatientDao.search(map);
|
||||
ids = toUnqualifiedVersionlessIds(results);
|
||||
assertThat(ids, Matchers.contains(patientId1));
|
||||
assertThat(ids, contains(patientId1));
|
||||
|
||||
searchSql = myCaptureQueriesListener.getSelectQueriesForCurrentThread().get(0).getSql(true, true);
|
||||
ourLog.info("Search SQL:\n{}", searchSql);
|
||||
|
@ -1438,7 +1604,7 @@ public class PartitioningSqlR4Test extends BaseJpaR4SystemTest {
|
|||
map.setLoadSynchronous(true);
|
||||
results = myPatientDao.search(map);
|
||||
ids = toUnqualifiedVersionlessIds(results);
|
||||
assertThat(ids, Matchers.contains(patientId1));
|
||||
assertThat(ids, contains(patientId1));
|
||||
|
||||
searchSql = myCaptureQueriesListener.getSelectQueriesForCurrentThread().get(0).getSql(true, true);
|
||||
ourLog.info("Search SQL:\n{}", searchSql);
|
||||
|
@ -1453,8 +1619,8 @@ public class PartitioningSqlR4Test extends BaseJpaR4SystemTest {
|
|||
myPartitionSettings.setIncludePartitionInSearchHashes(false);
|
||||
|
||||
IIdType patientIdNull = createPatient(withPartition(null), withBirthdate("2020-04-20"));
|
||||
IIdType patientId1 = createPatient(withPartition(1), withBirthdate("2020-04-20"));
|
||||
IIdType patientId2 = createPatient(withPartition(2), withBirthdate("2020-04-20"));
|
||||
createPatient(withPartition(1), withBirthdate("2020-04-20"));
|
||||
createPatient(withPartition(2), withBirthdate("2020-04-20"));
|
||||
createPatient(withPartition(null), withBirthdate("2021-04-20"));
|
||||
createPatient(withPartition(1), withBirthdate("2021-04-20"));
|
||||
createPatient(withPartition(2), withBirthdate("2021-04-20"));
|
||||
|
@ -1468,7 +1634,7 @@ public class PartitioningSqlR4Test extends BaseJpaR4SystemTest {
|
|||
map.setLoadSynchronous(true);
|
||||
IBundleProvider results = myPatientDao.search(map);
|
||||
List<IIdType> ids = toUnqualifiedVersionlessIds(results);
|
||||
assertThat(ids, Matchers.contains(patientIdNull));
|
||||
assertThat(ids, contains(patientIdNull));
|
||||
|
||||
String searchSql = myCaptureQueriesListener.getSelectQueriesForCurrentThread().get(0).getSql(true, true);
|
||||
ourLog.info("Search SQL:\n{}", searchSql);
|
||||
|
@ -1484,7 +1650,7 @@ public class PartitioningSqlR4Test extends BaseJpaR4SystemTest {
|
|||
map.setLoadSynchronous(true);
|
||||
results = myPatientDao.search(map);
|
||||
ids = toUnqualifiedVersionlessIds(results);
|
||||
assertThat(ids, Matchers.contains(patientIdNull));
|
||||
assertThat(ids, contains(patientIdNull));
|
||||
|
||||
searchSql = myCaptureQueriesListener.getSelectQueriesForCurrentThread().get(0).getSql(true, true);
|
||||
ourLog.info("Search SQL:\n{}", searchSql);
|
||||
|
@ -1500,7 +1666,7 @@ public class PartitioningSqlR4Test extends BaseJpaR4SystemTest {
|
|||
map.setLoadSynchronous(true);
|
||||
results = myPatientDao.search(map);
|
||||
ids = toUnqualifiedVersionlessIds(results);
|
||||
assertThat(ids, Matchers.contains(patientIdNull));
|
||||
assertThat(ids, contains(patientIdNull));
|
||||
|
||||
searchSql = myCaptureQueriesListener.getSelectQueriesForCurrentThread().get(0).getSql(true, true);
|
||||
ourLog.info("Search SQL:\n{}", searchSql);
|
||||
|
@ -1516,7 +1682,7 @@ public class PartitioningSqlR4Test extends BaseJpaR4SystemTest {
|
|||
map.setLoadSynchronous(true);
|
||||
results = myPatientDao.search(map);
|
||||
ids = toUnqualifiedVersionlessIds(results);
|
||||
assertThat(ids, Matchers.contains(patientIdNull));
|
||||
assertThat(ids, contains(patientIdNull));
|
||||
|
||||
searchSql = myCaptureQueriesListener.getSelectQueriesForCurrentThread().get(0).getSql(true, true);
|
||||
ourLog.info("Search SQL:\n{}", searchSql);
|
||||
|
@ -1581,7 +1747,7 @@ public class PartitioningSqlR4Test extends BaseJpaR4SystemTest {
|
|||
map.setLoadSynchronous(true);
|
||||
IBundleProvider results = myPatientDao.search(map);
|
||||
List<IIdType> ids = toUnqualifiedVersionlessIds(results);
|
||||
assertThat(ids, Matchers.contains(patientIdNull, patientId1, patientId2));
|
||||
assertThat(ids, contains(patientIdNull, patientId1, patientId2));
|
||||
|
||||
String searchSql = myCaptureQueriesListener.getSelectQueriesForCurrentThread().get(0).getSql(true, true);
|
||||
ourLog.info("Search SQL:\n{}", searchSql);
|
||||
|
@ -1603,7 +1769,7 @@ public class PartitioningSqlR4Test extends BaseJpaR4SystemTest {
|
|||
map.setLoadSynchronous(true);
|
||||
IBundleProvider results = myPatientDao.search(map);
|
||||
List<IIdType> ids = toUnqualifiedVersionlessIds(results);
|
||||
assertThat(ids, Matchers.contains(patientIdNull));
|
||||
assertThat(ids, contains(patientIdNull));
|
||||
|
||||
String searchSql = myCaptureQueriesListener.getSelectQueriesForCurrentThread().get(0).getSql(true, true);
|
||||
ourLog.info("Search SQL:\n{}", searchSql);
|
||||
|
@ -1628,7 +1794,7 @@ public class PartitioningSqlR4Test extends BaseJpaR4SystemTest {
|
|||
IBundleProvider results = myPatientDao.search(map);
|
||||
List<IIdType> ids = toUnqualifiedVersionlessIds(results);
|
||||
myCaptureQueriesListener.logSelectQueriesForCurrentThread();
|
||||
assertThat(ids, Matchers.contains(patientId1));
|
||||
assertThat(ids, contains(patientId1));
|
||||
|
||||
String searchSql = myCaptureQueriesListener.getSelectQueriesForCurrentThread().get(0).getSql(true, true);
|
||||
ourLog.info("Search SQL:\n{}", searchSql);
|
||||
|
@ -1636,6 +1802,73 @@ public class PartitioningSqlR4Test extends BaseJpaR4SystemTest {
|
|||
assertEquals(1, StringUtils.countMatches(searchSql, "SP_VALUE_NORMALIZED"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSearch_StringParam_SearchMultiplePartitions() {
|
||||
IIdType patientIdNull = createPatient(withPartition(null), withFamily("FAMILY"));
|
||||
IIdType patientId1 = createPatient(withPartition(1), withFamily("FAMILY"));
|
||||
IIdType patientId2 = createPatient(withPartition(2), withFamily("FAMILY"));
|
||||
createPatient(withPartition(3), withFamily("FAMILY"));
|
||||
|
||||
createPatient(withPartition(null), withFamily("BLAH"));
|
||||
createPatient(withPartition(1), withFamily("BLAH"));
|
||||
createPatient(withPartition(2), withFamily("BLAH"));
|
||||
createPatient(withPartition(3), withFamily("BLAH"));
|
||||
|
||||
|
||||
SearchParameterMap map = new SearchParameterMap();
|
||||
map.add(Patient.SP_FAMILY, new StringParam("FAMILY"));
|
||||
map.setLoadSynchronous(true);
|
||||
|
||||
// Match two partitions
|
||||
{
|
||||
addReadPartition(1, 2);
|
||||
|
||||
myCaptureQueriesListener.clear();
|
||||
IBundleProvider results = myPatientDao.search(map);
|
||||
List<IIdType> ids = toUnqualifiedVersionlessIds(results);
|
||||
assertThat(ids.toString(), ids, Matchers.containsInAnyOrder(patientId1, patientId2));
|
||||
|
||||
ourLog.info("Search SQL:\n{}", myCaptureQueriesListener.getSelectQueriesForCurrentThread().get(0).getSql(true, true));
|
||||
String searchSql = myCaptureQueriesListener.getSelectQueriesForCurrentThread().get(0).getSql(true, false);
|
||||
assertThat(searchSql, containsString("PARTITION_ID IN ('1','2')"));
|
||||
assertEquals(1, StringUtils.countMatches(searchSql, "PARTITION_ID"));
|
||||
}
|
||||
|
||||
// Match two partitions including null
|
||||
{
|
||||
addReadPartition(1, null);
|
||||
|
||||
myCaptureQueriesListener.clear();
|
||||
IBundleProvider results = myPatientDao.search(map);
|
||||
List<IIdType> ids = toUnqualifiedVersionlessIds(results);
|
||||
myCaptureQueriesListener.logSelectQueriesForCurrentThread();
|
||||
assertThat(ids.toString(), ids, Matchers.containsInAnyOrder(patientId1, patientIdNull));
|
||||
|
||||
ourLog.info("Search SQL:\n{}", myCaptureQueriesListener.getSelectQueriesForCurrentThread().get(0).getSql(true, true));
|
||||
String searchSql = myCaptureQueriesListener.getSelectQueriesForCurrentThread().get(0).getSql(true, false);
|
||||
assertThat(searchSql, containsString("PARTITION_ID IS NULL"));
|
||||
assertThat(searchSql, containsString("PARTITION_ID IN ('1')"));
|
||||
assertEquals(2, StringUtils.countMatches(searchSql, "PARTITION_ID"));
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSearch_StringParam_SearchMultiplePartitions_IncludePartitionInHashes() {
|
||||
myPartitionSettings.setIncludePartitionInSearchHashes(true);
|
||||
|
||||
SearchParameterMap map = new SearchParameterMap();
|
||||
map.add(Patient.SP_FAMILY, new StringParam("FAMILY"));
|
||||
map.setLoadSynchronous(true);
|
||||
|
||||
addReadPartition(1, 2);
|
||||
try {
|
||||
myPatientDao.search(map);
|
||||
fail();
|
||||
} catch (InternalErrorException e) {
|
||||
assertEquals("Can not search multiple partitions when partitions are included in search hashes", e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSearch_StringParam_SearchAllPartitions_IncludePartitionInHashes() {
|
||||
myPartitionSettings.setIncludePartitionInSearchHashes(true);
|
||||
|
@ -1671,7 +1904,7 @@ public class PartitioningSqlR4Test extends BaseJpaR4SystemTest {
|
|||
map.setLoadSynchronous(true);
|
||||
IBundleProvider results = myPatientDao.search(map);
|
||||
List<IIdType> ids = toUnqualifiedVersionlessIds(results);
|
||||
assertThat(ids, Matchers.contains(patientIdNull));
|
||||
assertThat(ids, contains(patientIdNull));
|
||||
|
||||
String searchSql = myCaptureQueriesListener.getSelectQueriesForCurrentThread().get(0).getSql(true, true);
|
||||
ourLog.info("Search SQL:\n{}", searchSql);
|
||||
|
@ -1698,7 +1931,7 @@ public class PartitioningSqlR4Test extends BaseJpaR4SystemTest {
|
|||
IBundleProvider results = myPatientDao.search(map);
|
||||
List<IIdType> ids = toUnqualifiedVersionlessIds(results);
|
||||
myCaptureQueriesListener.logSelectQueriesForCurrentThread();
|
||||
assertThat(ids, Matchers.contains(patientId1));
|
||||
assertThat(ids, contains(patientId1));
|
||||
|
||||
String searchSql = myCaptureQueriesListener.getSelectQueriesForCurrentThread().get(0).getSql(true, true);
|
||||
ourLog.info("Search SQL:\n{}", searchSql);
|
||||
|
@ -1722,7 +1955,7 @@ public class PartitioningSqlR4Test extends BaseJpaR4SystemTest {
|
|||
map.setLoadSynchronous(true);
|
||||
IBundleProvider results = myPatientDao.search(map);
|
||||
List<IIdType> ids = toUnqualifiedVersionlessIds(results);
|
||||
assertThat(ids, Matchers.contains(patientIdNull, patientId1, patientId2));
|
||||
assertThat(ids, contains(patientIdNull, patientId1, patientId2));
|
||||
|
||||
String searchSql = myCaptureQueriesListener.getSelectQueriesForCurrentThread().get(0).getSql(true, true);
|
||||
ourLog.info("Search SQL:\n{}", searchSql);
|
||||
|
@ -1739,7 +1972,7 @@ public class PartitioningSqlR4Test extends BaseJpaR4SystemTest {
|
|||
map.setLoadSynchronous(true);
|
||||
results = myPatientDao.search(map);
|
||||
ids = toUnqualifiedVersionlessIds(results);
|
||||
assertThat(ids, Matchers.contains(patientIdNull, patientId1));
|
||||
assertThat(ids, contains(patientIdNull, patientId1));
|
||||
|
||||
searchSql = myCaptureQueriesListener.getSelectQueriesForCurrentThread().get(0).getSql(true, true);
|
||||
ourLog.info("Search SQL:\n{}", searchSql);
|
||||
|
@ -1771,7 +2004,7 @@ public class PartitioningSqlR4Test extends BaseJpaR4SystemTest {
|
|||
assertEquals(1, StringUtils.countMatches(searchSql, "PARTITION_ID IS NULL"));
|
||||
assertEquals(1, StringUtils.countMatches(searchSql, "TAG_SYSTEM = 'http://system'"));
|
||||
|
||||
assertThat(ids.toString(), ids, Matchers.contains(patientIdNull));
|
||||
assertThat(ids.toString(), ids, contains(patientIdNull));
|
||||
}
|
||||
|
||||
@Test
|
||||
|
@ -1791,7 +2024,7 @@ public class PartitioningSqlR4Test extends BaseJpaR4SystemTest {
|
|||
map.setLoadSynchronous(true);
|
||||
IBundleProvider results = myPatientDao.search(map);
|
||||
List<IIdType> ids = toUnqualifiedVersionlessIds(results);
|
||||
assertThat(ids, Matchers.contains(patientId1));
|
||||
assertThat(ids, contains(patientId1));
|
||||
|
||||
String searchSql = myCaptureQueriesListener.getSelectQueriesForCurrentThread().get(0).getSql(true, true);
|
||||
ourLog.info("Search SQL:\n{}", searchSql);
|
||||
|
@ -1813,7 +2046,7 @@ public class PartitioningSqlR4Test extends BaseJpaR4SystemTest {
|
|||
map.setLoadSynchronous(true);
|
||||
IBundleProvider results = myPatientDao.search(map);
|
||||
List<IIdType> ids = toUnqualifiedVersionlessIds(results);
|
||||
assertThat(ids, Matchers.contains(patientIdNull, patientId1, patientId2));
|
||||
assertThat(ids, contains(patientIdNull, patientId1, patientId2));
|
||||
|
||||
String searchSql = myCaptureQueriesListener.getSelectQueriesForCurrentThread().get(0).getSql(true, true);
|
||||
ourLog.info("Search SQL:\n{}", searchSql);
|
||||
|
@ -1837,7 +2070,7 @@ public class PartitioningSqlR4Test extends BaseJpaR4SystemTest {
|
|||
IBundleProvider results = myPatientDao.search(map);
|
||||
List<IIdType> ids = toUnqualifiedVersionlessIds(results);
|
||||
myCaptureQueriesListener.logSelectQueriesForCurrentThread(0);
|
||||
assertThat(ids, Matchers.contains(patientId1));
|
||||
assertThat(ids, contains(patientId1));
|
||||
|
||||
String searchSql = myCaptureQueriesListener.getSelectQueriesForCurrentThread().get(0).getSql(true, true);
|
||||
ourLog.info("Search SQL:\n{}", searchSql);
|
||||
|
@ -1864,7 +2097,7 @@ public class PartitioningSqlR4Test extends BaseJpaR4SystemTest {
|
|||
map.setLoadSynchronous(true);
|
||||
IBundleProvider results = myPatientDao.search(map);
|
||||
List<IIdType> ids = toUnqualifiedVersionlessIds(results);
|
||||
assertThat(ids, Matchers.contains(patientIdNull, patientId1, patientId2));
|
||||
assertThat(ids, contains(patientIdNull, patientId1, patientId2));
|
||||
|
||||
String searchSql = myCaptureQueriesListener.getSelectQueriesForCurrentThread().get(0).getSql(true, true);
|
||||
ourLog.info("Search SQL:\n{}", searchSql);
|
||||
|
@ -1889,7 +2122,7 @@ public class PartitioningSqlR4Test extends BaseJpaR4SystemTest {
|
|||
map.setLoadSynchronous(true);
|
||||
IBundleProvider results = myPatientDao.search(map);
|
||||
List<IIdType> ids = toUnqualifiedVersionlessIds(results);
|
||||
assertThat(ids, Matchers.contains(patientId1));
|
||||
assertThat(ids, contains(patientId1));
|
||||
|
||||
String searchSql = myCaptureQueriesListener.getSelectQueriesForCurrentThread().get(0).getSql(true, true);
|
||||
ourLog.info("Search SQL:\n{}", searchSql);
|
||||
|
@ -1912,7 +2145,7 @@ public class PartitioningSqlR4Test extends BaseJpaR4SystemTest {
|
|||
IBundleProvider results = myPatientDao.search(map);
|
||||
List<IIdType> ids = toUnqualifiedVersionlessIds(results);
|
||||
myCaptureQueriesListener.logSelectQueriesForCurrentThread();
|
||||
assertThat(ids, Matchers.contains(id));
|
||||
assertThat(ids, contains(id));
|
||||
|
||||
String searchSql = myCaptureQueriesListener.getSelectQueriesForCurrentThread().get(0).getSql(true, true);
|
||||
ourLog.info("Search SQL:\n{}", searchSql);
|
||||
|
@ -1935,7 +2168,7 @@ public class PartitioningSqlR4Test extends BaseJpaR4SystemTest {
|
|||
IBundleProvider results = myPatientDao.search(map);
|
||||
List<IIdType> ids = toUnqualifiedVersionlessIds(results);
|
||||
myCaptureQueriesListener.logSelectQueriesForCurrentThread();
|
||||
assertThat(ids, Matchers.contains(id));
|
||||
assertThat(ids, contains(id));
|
||||
|
||||
String searchSql = myCaptureQueriesListener.getSelectQueriesForCurrentThread().get(0).getSql(true, true);
|
||||
ourLog.info("Search SQL:\n{}", searchSql);
|
||||
|
@ -1970,11 +2203,11 @@ public class PartitioningSqlR4Test extends BaseJpaR4SystemTest {
|
|||
IBundleProvider results = myObservationDao.search(map);
|
||||
List<IIdType> ids = toUnqualifiedVersionlessIds(results);
|
||||
myCaptureQueriesListener.logSelectQueriesForCurrentThread();
|
||||
assertThat(ids, Matchers.contains(observationId));
|
||||
assertThat(ids, contains(observationId));
|
||||
|
||||
ourLog.info("Search SQL:\n{}", myCaptureQueriesListener.getSelectQueriesForCurrentThread().get(0).getSql(true, true));
|
||||
String searchSql = myCaptureQueriesListener.getSelectQueriesForCurrentThread().get(0).getSql(true, false);
|
||||
ourLog.info("Search SQL:\n{}", searchSql);
|
||||
assertEquals(1, StringUtils.countMatches(searchSql, "t0.PARTITION_ID = '1'"), searchSql);
|
||||
assertEquals(1, StringUtils.countMatches(searchSql, "t0.PARTITION_ID IN ('1')"), searchSql);
|
||||
assertEquals(1, StringUtils.countMatches(searchSql, "t0.SRC_PATH = 'Observation.subject'"), searchSql);
|
||||
assertEquals(1, StringUtils.countMatches(searchSql, "t0.TARGET_RESOURCE_ID = '" + patientId.getIdPartAsLong() + "'"), searchSql);
|
||||
assertEquals(1, StringUtils.countMatches(searchSql, "PARTITION_ID"), searchSql);
|
||||
|
@ -2000,14 +2233,14 @@ public class PartitioningSqlR4Test extends BaseJpaR4SystemTest {
|
|||
IIdType observationId = createObservation(withPartition(null), withSubject(patientId));
|
||||
|
||||
addReadDefaultPartition();
|
||||
;
|
||||
|
||||
myCaptureQueriesListener.clear();
|
||||
SearchParameterMap map = new SearchParameterMap();
|
||||
map.add(Observation.SP_SUBJECT, new ReferenceParam(patientId));
|
||||
map.setLoadSynchronous(true);
|
||||
IBundleProvider results = myObservationDao.search(map);
|
||||
List<IIdType> ids = toUnqualifiedVersionlessIds(results);
|
||||
assertThat(ids, Matchers.contains(observationId));
|
||||
assertThat(ids, contains(observationId));
|
||||
|
||||
String searchSql = myCaptureQueriesListener.getSelectQueriesForCurrentThread().get(0).getSql(true, false);
|
||||
ourLog.info("Search SQL:\n{}", searchSql);
|
||||
|
@ -2044,11 +2277,11 @@ public class PartitioningSqlR4Test extends BaseJpaR4SystemTest {
|
|||
IBundleProvider results = myObservationDao.search(map);
|
||||
List<IIdType> ids = toUnqualifiedVersionlessIds(results);
|
||||
myCaptureQueriesListener.logSelectQueriesForCurrentThread();
|
||||
assertThat(ids, Matchers.contains(observationId));
|
||||
assertThat(ids, contains(observationId));
|
||||
|
||||
String searchSql = myCaptureQueriesListener.getSelectQueriesForCurrentThread().get(0).getSql(true, true);
|
||||
ourLog.info("Search SQL:\n{}", searchSql);
|
||||
assertEquals(1, StringUtils.countMatches(searchSql, "forcedid0_.PARTITION_ID='1'"), searchSql);
|
||||
ourLog.info("Search SQL:\n{}", myCaptureQueriesListener.getSelectQueriesForCurrentThread().get(0).getSql(true, true));
|
||||
String searchSql = myCaptureQueriesListener.getSelectQueriesForCurrentThread().get(0).getSql(true, false);
|
||||
assertEquals(1, StringUtils.countMatches(searchSql, "forcedid0_.PARTITION_ID in ('1')"), searchSql);
|
||||
assertEquals(1, StringUtils.countMatches(searchSql, "and forcedid0_.RESOURCE_TYPE='Patient'"), searchSql);
|
||||
assertEquals(1, StringUtils.countMatches(searchSql, "PARTITION_ID"), searchSql);
|
||||
|
||||
|
@ -2080,10 +2313,10 @@ public class PartitioningSqlR4Test extends BaseJpaR4SystemTest {
|
|||
map.setLoadSynchronous(true);
|
||||
IBundleProvider results = myObservationDao.search(map);
|
||||
List<IIdType> ids = toUnqualifiedVersionlessIds(results);
|
||||
assertThat(ids, Matchers.contains(observationId));
|
||||
|
||||
String searchSql = myCaptureQueriesListener.getSelectQueriesForCurrentThread().get(0).getSql(true, true);
|
||||
ourLog.info("Search SQL:\n{}", searchSql);
|
||||
assertThat(ids, contains(observationId)); // FIXME: move up
|
||||
assertEquals(1, StringUtils.countMatches(searchSql, "forcedid0_.PARTITION_ID is null"), searchSql);
|
||||
assertEquals(1, StringUtils.countMatches(searchSql, "forcedid0_.RESOURCE_TYPE='Patient'"), searchSql);
|
||||
assertEquals(1, StringUtils.countMatches(searchSql, "PARTITION_ID"), searchSql);
|
||||
|
@ -2129,7 +2362,7 @@ public class PartitioningSqlR4Test extends BaseJpaR4SystemTest {
|
|||
IBundleProvider results = myPatientDao.history(id, null, null, mySrd);
|
||||
assertEquals(2, results.sizeOrThrowNpe());
|
||||
List<String> ids = toUnqualifiedIdValues(results);
|
||||
assertThat(ids, Matchers.contains(id.withVersion("2").getValue(), id.withVersion("1").getValue()));
|
||||
assertThat(ids, contains(id.withVersion("2").getValue(), id.withVersion("1").getValue()));
|
||||
|
||||
assertEquals(4, myCaptureQueriesListener.getSelectQueriesForCurrentThread().size());
|
||||
|
||||
|
@ -2192,7 +2425,7 @@ public class PartitioningSqlR4Test extends BaseJpaR4SystemTest {
|
|||
IBundleProvider results = myPatientDao.history(id, null, null, mySrd);
|
||||
assertEquals(2, results.sizeOrThrowNpe());
|
||||
List<String> ids = toUnqualifiedIdValues(results);
|
||||
assertThat(ids, Matchers.contains(id.withVersion("2").getValue(), id.withVersion("1").getValue()));
|
||||
assertThat(ids, contains(id.withVersion("2").getValue(), id.withVersion("1").getValue()));
|
||||
|
||||
assertEquals(4, myCaptureQueriesListener.getSelectQueriesForCurrentThread().size());
|
||||
|
||||
|
@ -2233,7 +2466,7 @@ public class PartitioningSqlR4Test extends BaseJpaR4SystemTest {
|
|||
IBundleProvider results = myPatientDao.history(id, null, null, mySrd);
|
||||
assertEquals(2, results.sizeOrThrowNpe());
|
||||
List<String> ids = toUnqualifiedIdValues(results);
|
||||
assertThat(ids, Matchers.contains(id.withVersion("2").getValue(), id.withVersion("1").getValue()));
|
||||
assertThat(ids, contains(id.withVersion("2").getValue(), id.withVersion("1").getValue()));
|
||||
}
|
||||
|
||||
@Test
|
||||
|
@ -2262,26 +2495,27 @@ public class PartitioningSqlR4Test extends BaseJpaR4SystemTest {
|
|||
IBundleProvider results = mySystemDao.history(null, null, mySrd);
|
||||
assertEquals(2, results.sizeOrThrowNpe());
|
||||
List<String> ids = toUnqualifiedIdValues(results);
|
||||
assertThat(ids, Matchers.contains(id1B.withVersion("1").getValue(), id1A.withVersion("1").getValue()));
|
||||
assertThat(ids, contains(id1B.withVersion("1").getValue(), id1A.withVersion("1").getValue()));
|
||||
|
||||
assertEquals(3, myCaptureQueriesListener.getSelectQueriesForCurrentThread().size());
|
||||
|
||||
// Count
|
||||
String searchSql = myCaptureQueriesListener.getSelectQueriesForCurrentThread().get(0).getSql(true, true);
|
||||
ourLog.info("SQL:{}", searchSql);
|
||||
assertEquals(1, countMatches(searchSql, "count("));
|
||||
assertEquals(1, countMatches(searchSql, "PARTITION_ID='1'"));
|
||||
ourLog.info("SQL:{}", myCaptureQueriesListener.getSelectQueriesForCurrentThread().get(0).getSql(true, true));
|
||||
String sql = myCaptureQueriesListener.getSelectQueriesForCurrentThread().get(0).getSql(true, false).toUpperCase();
|
||||
assertEquals(1, countMatches(sql, "COUNT("), sql);
|
||||
assertEquals(1, countMatches(sql, "PARTITION_ID IN ('1')"), sql);
|
||||
|
||||
// Fetch history
|
||||
searchSql = myCaptureQueriesListener.getSelectQueriesForCurrentThread().get(1).getSql(true, true);
|
||||
ourLog.info("SQL:{}", searchSql);
|
||||
assertEquals(1, countMatches(searchSql, "PARTITION_ID='1'"));
|
||||
sql = myCaptureQueriesListener.getSelectQueriesForCurrentThread().get(1).getSql(true, false).toUpperCase();
|
||||
ourLog.info("SQL:{}", sql);
|
||||
assertEquals(1, countMatches(sql, "PARTITION_ID IN ('1')"), sql);
|
||||
|
||||
// Fetch history resource
|
||||
searchSql = myCaptureQueriesListener.getSelectQueriesForCurrentThread().get(2).getSql(true, true);
|
||||
ourLog.info("SQL:{}", searchSql);
|
||||
assertEquals(0, countMatches(searchSql, "PARTITION_ID="), searchSql.replace(" ", "").toUpperCase());
|
||||
assertEquals(0, countMatches(searchSql, "PARTITION_IDIN"), searchSql.replace(" ", "").toUpperCase());
|
||||
sql = myCaptureQueriesListener.getSelectQueriesForCurrentThread().get(2).getSql(true, false);
|
||||
sql = sql.replace(" ", "").toUpperCase();
|
||||
ourLog.info("SQL:{}", sql);
|
||||
assertEquals(0, countMatches(sql, "PARTITION_ID="), sql);
|
||||
assertEquals(0, countMatches(sql, "PARTITION_IDIN"), sql);
|
||||
}
|
||||
|
||||
@Test
|
||||
|
@ -2299,7 +2533,7 @@ public class PartitioningSqlR4Test extends BaseJpaR4SystemTest {
|
|||
IBundleProvider results = mySystemDao.history(null, null, mySrd);
|
||||
assertEquals(2, results.sizeOrThrowNpe());
|
||||
List<String> ids = toUnqualifiedIdValues(results);
|
||||
assertThat(ids, Matchers.contains(id1B.withVersion("1").getValue(), id1A.withVersion("1").getValue()));
|
||||
assertThat(ids, contains(id1B.withVersion("1").getValue(), id1A.withVersion("1").getValue()));
|
||||
|
||||
assertEquals(3, myCaptureQueriesListener.getSelectQueriesForCurrentThread().size());
|
||||
|
||||
|
@ -2320,6 +2554,42 @@ public class PartitioningSqlR4Test extends BaseJpaR4SystemTest {
|
|||
assertEquals(0, countMatches(searchSql, "PARTITION_IDIN"), searchSql.replace(" ", "").toUpperCase());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testHistory_Server_MultiplePartitions() {
|
||||
String idNull1 = createPatient(withPartition(null), withBirthdate("2020-01-01")).toUnqualifiedVersionless().getValue();
|
||||
sleepAtLeast(10);
|
||||
String idNull2 = createPatient(withPartition(null), withBirthdate("2020-01-01")).toUnqualifiedVersionless().getValue();
|
||||
sleepAtLeast(10);
|
||||
String id21 = createPatient(withPartition(2), withBirthdate("2020-01-01")).toUnqualifiedVersionless().getValue();
|
||||
sleepAtLeast(10);
|
||||
String id31 = createPatient(withPartition(3), withBirthdate("2020-01-01")).toUnqualifiedVersionless().getValue();
|
||||
sleepAtLeast(10);
|
||||
String id22 = createPatient(withPartition(2), withBirthdate("2020-01-01")).toUnqualifiedVersionless().getValue();
|
||||
sleepAtLeast(10);
|
||||
String id32 = createPatient(withPartition(3), withBirthdate("2020-01-01")).toUnqualifiedVersionless().getValue();
|
||||
|
||||
// Multiple Partitions
|
||||
{
|
||||
addReadPartition(2, null);
|
||||
myCaptureQueriesListener.clear();
|
||||
IBundleProvider results = mySystemDao.history(null, null, mySrd);
|
||||
assertEquals(4, results.sizeOrThrowNpe());
|
||||
List<String> ids = toUnqualifiedVersionlessIdValues(results);
|
||||
assertThat(ids, contains(id22, id21, idNull2, idNull1));
|
||||
}
|
||||
|
||||
// Multiple Partitions With Null
|
||||
{
|
||||
addReadPartition(2, 3);
|
||||
myCaptureQueriesListener.clear();
|
||||
IBundleProvider results = mySystemDao.history(null, null, mySrd);
|
||||
assertEquals(4, results.sizeOrThrowNpe());
|
||||
List<String> ids = toUnqualifiedVersionlessIdValues(results);
|
||||
assertThat(ids, contains(id32, id22, id31, id21));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testHistory_Type_AllPartitions() {
|
||||
addReadAllPartitions();
|
||||
|
@ -2346,25 +2616,25 @@ public class PartitioningSqlR4Test extends BaseJpaR4SystemTest {
|
|||
IBundleProvider results = myPatientDao.history(null, null, mySrd);
|
||||
assertEquals(2, results.sizeOrThrowNpe());
|
||||
List<String> ids = toUnqualifiedIdValues(results);
|
||||
assertThat(ids, Matchers.contains(id1B.withVersion("1").getValue(), id1A.withVersion("1").getValue()));
|
||||
assertThat(ids, contains(id1B.withVersion("1").getValue(), id1A.withVersion("1").getValue()));
|
||||
|
||||
assertEquals(3, myCaptureQueriesListener.getSelectQueriesForCurrentThread().size());
|
||||
|
||||
// Count
|
||||
String sql = myCaptureQueriesListener.getSelectQueriesForCurrentThread().get(0).getSql(true, true);
|
||||
String sql = myCaptureQueriesListener.getSelectQueriesForCurrentThread().get(0).getSql(true, false).toUpperCase();
|
||||
ourLog.info("SQL:{}", sql);
|
||||
assertEquals(1, countMatches(sql, "count("));
|
||||
assertEquals(1, countMatches(sql, "PARTITION_ID='1'"));
|
||||
assertEquals(1, countMatches(sql, "COUNT("), sql);
|
||||
assertEquals(1, countMatches(sql, "PARTITION_ID IN ('1')"), sql);
|
||||
|
||||
// Fetch history resources
|
||||
sql = myCaptureQueriesListener.getSelectQueriesForCurrentThread().get(1).getSql(true, true);
|
||||
sql = myCaptureQueriesListener.getSelectQueriesForCurrentThread().get(1).getSql(true, false).toUpperCase();
|
||||
ourLog.info("SQL:{}", sql);
|
||||
assertEquals(1, countMatches(sql, "PARTITION_ID='1'"));
|
||||
assertEquals(1, countMatches(sql, "PARTITION_ID IN ('1')"), sql);
|
||||
|
||||
// Resolve forced ID
|
||||
sql = myCaptureQueriesListener.getSelectQueriesForCurrentThread().get(2).getSql(true, true);
|
||||
sql = myCaptureQueriesListener.getSelectQueriesForCurrentThread().get(2).getSql(true, false).toUpperCase();
|
||||
ourLog.info("SQL:{}", sql);
|
||||
assertEquals(0, countMatches(sql, "PARTITION_ID='1'"));
|
||||
assertEquals(0, countMatches(sql, "PARTITION_ID IN ('1')"), sql);
|
||||
}
|
||||
|
||||
|
||||
|
@ -2383,7 +2653,7 @@ public class PartitioningSqlR4Test extends BaseJpaR4SystemTest {
|
|||
IBundleProvider results = myPatientDao.history(null, null, mySrd);
|
||||
assertEquals(2, results.sizeOrThrowNpe());
|
||||
List<String> ids = toUnqualifiedIdValues(results);
|
||||
assertThat(ids, Matchers.contains(id1B.withVersion("1").getValue(), id1A.withVersion("1").getValue()));
|
||||
assertThat(ids, contains(id1B.withVersion("1").getValue(), id1A.withVersion("1").getValue()));
|
||||
|
||||
myCaptureQueriesListener.logSelectQueriesForCurrentThread();
|
||||
assertEquals(3, myCaptureQueriesListener.getSelectQueriesForCurrentThread().size());
|
||||
|
@ -2414,8 +2684,8 @@ public class PartitioningSqlR4Test extends BaseJpaR4SystemTest {
|
|||
verify(interceptor, times(1)).invoke(eq(Pointcut.STORAGE_PARTITION_SELECTED), captor.capture());
|
||||
|
||||
RequestPartitionId partitionId = captor.getValue().get(RequestPartitionId.class);
|
||||
assertEquals(1, partitionId.getPartitionId().intValue());
|
||||
assertEquals("PART-1", partitionId.getPartitionName());
|
||||
assertEquals(1, partitionId.getPartitionIds().get(0).intValue());
|
||||
assertEquals("PART-1", partitionId.getPartitionNames().get(0));
|
||||
|
||||
} finally {
|
||||
myInterceptorRegistry.unregisterInterceptor(interceptor);
|
||||
|
@ -2471,9 +2741,15 @@ public class PartitioningSqlR4Test extends BaseJpaR4SystemTest {
|
|||
myPartitionInterceptor.addCreatePartition(requestPartitionId);
|
||||
}
|
||||
|
||||
private void addReadPartition(Integer thePartitionId) {
|
||||
private void addReadPartition(Integer... thePartitionId) {
|
||||
Validate.notNull(thePartitionId);
|
||||
myPartitionInterceptor.addReadPartition(RequestPartitionId.fromPartitionId(thePartitionId, null));
|
||||
myPartitionInterceptor.addReadPartition(RequestPartitionId.fromPartitionIds(thePartitionId));
|
||||
}
|
||||
|
||||
private void addReadPartitions(String... thePartitionNames) {
|
||||
Validate.notNull(thePartitionNames);
|
||||
Validate.isTrue(thePartitionNames.length > 0);
|
||||
myPartitionInterceptor.addReadPartition(RequestPartitionId.fromPartitionNames(thePartitionNames));
|
||||
}
|
||||
|
||||
private void addReadDefaultPartition() {
|
||||
|
|
|
@ -50,18 +50,6 @@ public class PartitionSettingsSvcImplTest extends BaseJpaR4Test {
|
|||
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testDeletePartition_TryToDeleteDefault() {
|
||||
|
||||
try {
|
||||
myPartitionConfigSvc.deletePartition(0);
|
||||
fail();
|
||||
} catch (InvalidRequestException e) {
|
||||
assertEquals("Can not delete default partition", e.getMessage());
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testUpdatePartition_TryToUseExistingName() {
|
||||
|
||||
|
@ -92,14 +80,14 @@ public class PartitionSettingsSvcImplTest extends BaseJpaR4Test {
|
|||
@Test
|
||||
public void testUpdatePartition_TryToRenameDefault() {
|
||||
PartitionEntity partition = new PartitionEntity();
|
||||
partition.setId(0);
|
||||
partition.setId(null);
|
||||
partition.setName("NAME123");
|
||||
partition.setDescription("A description");
|
||||
try {
|
||||
myPartitionConfigSvc.updatePartition(partition);
|
||||
fail();
|
||||
} catch (InvalidRequestException e) {
|
||||
assertEquals("Can not rename default partition", e.getMessage());
|
||||
assertEquals("Partition must have an ID and a Name", e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -141,21 +129,6 @@ public class PartitionSettingsSvcImplTest extends BaseJpaR4Test {
|
|||
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testCreatePartition_0Blocked() {
|
||||
PartitionEntity partition = new PartitionEntity();
|
||||
partition.setId(0);
|
||||
partition.setName("NAME123");
|
||||
partition.setDescription("A description");
|
||||
try {
|
||||
myPartitionConfigSvc.createPartition(partition);
|
||||
fail();
|
||||
} catch (InvalidRequestException e) {
|
||||
assertEquals("Can not create a partition with ID 0 (this is a reserved value)", e.getMessage());
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testUpdatePartition_UnknownPartitionBlocked() {
|
||||
PartitionEntity partition = new PartitionEntity();
|
||||
|
|
|
@ -27,7 +27,7 @@ import java.util.List;
|
|||
import java.util.function.Consumer;
|
||||
import java.util.function.Supplier;
|
||||
|
||||
import static ca.uhn.fhir.jpa.partition.PartitionLookupSvcImpl.DEFAULT_PERSISTED_PARTITION_NAME;
|
||||
import static ca.uhn.fhir.jpa.model.util.JpaConstants.DEFAULT_PARTITION_NAME;
|
||||
|
||||
public abstract class BaseMultitenantResourceProviderR4Test extends BaseResourceProviderR4Test implements ITestDataBuilder {
|
||||
|
||||
|
@ -88,7 +88,7 @@ public abstract class BaseMultitenantResourceProviderR4Test extends BaseResource
|
|||
|
||||
|
||||
private void createTenants() {
|
||||
myTenantClientInterceptor.setTenantId(DEFAULT_PERSISTED_PARTITION_NAME);
|
||||
myTenantClientInterceptor.setTenantId(DEFAULT_PARTITION_NAME);
|
||||
|
||||
myClient
|
||||
.operation()
|
||||
|
|
|
@ -3,6 +3,7 @@ package ca.uhn.fhir.jpa.provider.r4;
|
|||
import ca.uhn.fhir.context.support.IValidationSupport;
|
||||
import ca.uhn.fhir.jpa.api.dao.DaoRegistry;
|
||||
import ca.uhn.fhir.jpa.api.svc.ISearchCoordinatorSvc;
|
||||
import ca.uhn.fhir.jpa.dao.data.IPartitionDao;
|
||||
import ca.uhn.fhir.jpa.dao.r4.BaseJpaR4Test;
|
||||
import ca.uhn.fhir.jpa.provider.DiffProvider;
|
||||
import ca.uhn.fhir.jpa.provider.GraphQLProvider;
|
||||
|
@ -65,15 +66,15 @@ public abstract class BaseResourceProviderR4Test extends BaseJpaR4Test {
|
|||
protected static Server ourServer;
|
||||
private static DatabaseBackedPagingProvider ourPagingProvider;
|
||||
private static GenericWebApplicationContext ourWebApplicationContext;
|
||||
private static SubscriptionMatcherInterceptor ourSubscriptionMatcherInterceptor;
|
||||
protected IGenericClient myClient;
|
||||
@Autowired
|
||||
protected SubscriptionLoader mySubscriptionLoader;
|
||||
@Autowired
|
||||
protected DaoRegistry myDaoRegistry;
|
||||
@Autowired
|
||||
protected IPartitionDao myPartitionDao;
|
||||
ResourceCountCache myResourceCountsCache;
|
||||
private TerminologyUploaderProvider myTerminologyUploaderProvider;
|
||||
private boolean ourRestHookSubscriptionInterceptorRequested;
|
||||
|
||||
public BaseResourceProviderR4Test() {
|
||||
super();
|
||||
|
@ -163,7 +164,7 @@ public abstract class BaseResourceProviderR4Test extends BaseJpaR4Test {
|
|||
WebApplicationContext wac = WebApplicationContextUtils.getWebApplicationContext(subsServletHolder.getServlet().getServletConfig().getServletContext());
|
||||
myValidationSupport = wac.getBean(IValidationSupport.class);
|
||||
mySearchCoordinatorSvc = wac.getBean(ISearchCoordinatorSvc.class);
|
||||
ourSubscriptionMatcherInterceptor = wac.getBean(SubscriptionMatcherInterceptor.class);
|
||||
SubscriptionMatcherInterceptor ourSubscriptionMatcherInterceptor = wac.getBean(SubscriptionMatcherInterceptor.class);
|
||||
|
||||
confProvider.setSearchParamRegistry(ourSearchParamRegistry);
|
||||
|
||||
|
|
|
@ -1,5 +1,8 @@
|
|||
package ca.uhn.fhir.jpa.provider.r4;
|
||||
|
||||
import ca.uhn.fhir.jpa.entity.PartitionEntity;
|
||||
import ca.uhn.fhir.jpa.model.entity.ResourceTable;
|
||||
import ca.uhn.fhir.jpa.model.util.JpaConstants;
|
||||
import ca.uhn.fhir.rest.server.exceptions.ResourceNotFoundException;
|
||||
import ca.uhn.fhir.test.utilities.ITestDataBuilder;
|
||||
import org.hl7.fhir.instance.model.api.IIdType;
|
||||
|
@ -15,6 +18,8 @@ import java.util.Date;
|
|||
import static org.hamcrest.MatcherAssert.assertThat;
|
||||
import static org.hamcrest.Matchers.containsString;
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
import static org.junit.jupiter.api.Assertions.assertFalse;
|
||||
import static org.junit.jupiter.api.Assertions.assertNull;
|
||||
import static org.junit.jupiter.api.Assertions.assertTrue;
|
||||
import static org.junit.jupiter.api.Assertions.fail;
|
||||
|
||||
|
@ -31,19 +36,34 @@ public class MultitenantServerR4Test extends BaseMultitenantResourceProviderR4Te
|
|||
}
|
||||
|
||||
@Test
|
||||
public void testCreateAndRead() {
|
||||
public void testCreateAndRead_NamedTenant() {
|
||||
|
||||
// Create patients
|
||||
|
||||
IIdType idA = createPatient(withTenant(TENANT_A), withActiveTrue());
|
||||
createPatient(withTenant(TENANT_B), withActiveFalse());
|
||||
|
||||
runInTransaction(() -> {
|
||||
PartitionEntity partition = myPartitionDao.findForName(TENANT_A).orElseThrow(() -> new IllegalStateException());
|
||||
ResourceTable resourceTable = myResourceTableDao.findById(idA.getIdPartAsLong()).orElseThrow(() -> new IllegalStateException());
|
||||
assertEquals(partition.getId(), resourceTable.getPartitionId().getPartitionId());
|
||||
});
|
||||
|
||||
// Now read back
|
||||
|
||||
myTenantClientInterceptor.setTenantId(TENANT_A);
|
||||
Patient response = myClient.read().resource(Patient.class).withId(idA).execute();
|
||||
assertTrue(response.getActive());
|
||||
|
||||
// Update resource (should remain in correct partition)
|
||||
|
||||
createPatient(withActiveFalse(), withId(idA));
|
||||
|
||||
// Now read back
|
||||
|
||||
response = myClient.read().resource(Patient.class).withId(idA.withVersion("2")).execute();
|
||||
assertFalse(response.getActive());
|
||||
|
||||
myTenantClientInterceptor.setTenantId(TENANT_B);
|
||||
try {
|
||||
myClient.read().resource(Patient.class).withId(idA).execute();
|
||||
|
@ -53,6 +73,47 @@ public class MultitenantServerR4Test extends BaseMultitenantResourceProviderR4Te
|
|||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testCreateAndRead_DefaultTenant() {
|
||||
|
||||
// Create patients
|
||||
|
||||
IIdType idA = createPatient(withTenant(JpaConstants.DEFAULT_PARTITION_NAME), withActiveTrue());
|
||||
createPatient(withTenant(TENANT_B), withActiveFalse());
|
||||
|
||||
runInTransaction(() -> {
|
||||
ResourceTable resourceTable = myResourceTableDao.findById(idA.getIdPartAsLong()).orElseThrow(() -> new IllegalStateException());
|
||||
assertNull(resourceTable.getPartitionId());
|
||||
});
|
||||
|
||||
|
||||
// Now read back
|
||||
|
||||
myTenantClientInterceptor.setTenantId(JpaConstants.DEFAULT_PARTITION_NAME);
|
||||
Patient response = myClient.read().resource(Patient.class).withId(idA).execute();
|
||||
assertTrue(response.getActive());
|
||||
|
||||
// Update resource (should remain in correct partition)
|
||||
|
||||
createPatient(withActiveFalse(), withId(idA));
|
||||
|
||||
// Now read back
|
||||
|
||||
response = myClient.read().resource(Patient.class).withId(idA.withVersion("2")).execute();
|
||||
assertFalse(response.getActive());
|
||||
|
||||
// Try reading from wrong partition
|
||||
|
||||
myTenantClientInterceptor.setTenantId(TENANT_B);
|
||||
try {
|
||||
myClient.read().resource(Patient.class).withId(idA).execute();
|
||||
fail();
|
||||
} catch (ResourceNotFoundException e) {
|
||||
// good
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
public void testCreate_InvalidTenant() {
|
||||
|
||||
|
|
|
@ -20,6 +20,7 @@ package ca.uhn.fhir.jpa.migrate.tasks;
|
|||
* #L%
|
||||
*/
|
||||
|
||||
import ca.uhn.fhir.interceptor.model.RequestPartitionId;
|
||||
import ca.uhn.fhir.jpa.entity.EmpiLink;
|
||||
import ca.uhn.fhir.jpa.entity.TermCodeSystemVersion;
|
||||
import ca.uhn.fhir.jpa.entity.TermConceptMap;
|
||||
|
@ -748,7 +749,7 @@ public class HapiFhirJpaMigrationTasks extends BaseMigrationTasks<VersionEnum> {
|
|||
.withColumns("HASH_IDENTITY", "SP_LATITUDE", "SP_LONGITUDE");
|
||||
spidxCoords
|
||||
.addTask(new CalculateHashesTask(VersionEnum.V3_5_0, "20180903.5")
|
||||
.addCalculator("HASH_IDENTITY", t -> BaseResourceIndexedSearchParam.calculateHashIdentity(new PartitionSettings(), null, t.getResourceType(), t.getString("SP_NAME")))
|
||||
.addCalculator("HASH_IDENTITY", t -> BaseResourceIndexedSearchParam.calculateHashIdentity(new PartitionSettings(), RequestPartitionId.defaultPartition(), t.getResourceType(), t.getString("SP_NAME")))
|
||||
.setColumnName("HASH_IDENTITY")
|
||||
);
|
||||
}
|
||||
|
@ -771,7 +772,7 @@ public class HapiFhirJpaMigrationTasks extends BaseMigrationTasks<VersionEnum> {
|
|||
.dropIndex("20180903.9", "IDX_SP_DATE");
|
||||
spidxDate
|
||||
.addTask(new CalculateHashesTask(VersionEnum.V3_5_0, "20180903.10")
|
||||
.addCalculator("HASH_IDENTITY", t -> BaseResourceIndexedSearchParam.calculateHashIdentity(new PartitionSettings(), null, t.getResourceType(), t.getString("SP_NAME")))
|
||||
.addCalculator("HASH_IDENTITY", t -> BaseResourceIndexedSearchParam.calculateHashIdentity(new PartitionSettings(), RequestPartitionId.defaultPartition(), t.getResourceType(), t.getString("SP_NAME")))
|
||||
.setColumnName("HASH_IDENTITY")
|
||||
);
|
||||
}
|
||||
|
@ -792,7 +793,7 @@ public class HapiFhirJpaMigrationTasks extends BaseMigrationTasks<VersionEnum> {
|
|||
.withColumns("HASH_IDENTITY", "SP_VALUE");
|
||||
spidxNumber
|
||||
.addTask(new CalculateHashesTask(VersionEnum.V3_5_0, "20180903.14")
|
||||
.addCalculator("HASH_IDENTITY", t -> BaseResourceIndexedSearchParam.calculateHashIdentity(new PartitionSettings(), null, t.getResourceType(), t.getString("SP_NAME")))
|
||||
.addCalculator("HASH_IDENTITY", t -> BaseResourceIndexedSearchParam.calculateHashIdentity(new PartitionSettings(), RequestPartitionId.defaultPartition(), t.getResourceType(), t.getString("SP_NAME")))
|
||||
.setColumnName("HASH_IDENTITY")
|
||||
);
|
||||
}
|
||||
|
@ -829,9 +830,9 @@ public class HapiFhirJpaMigrationTasks extends BaseMigrationTasks<VersionEnum> {
|
|||
.withColumns("HASH_IDENTITY_SYS_UNITS", "SP_VALUE");
|
||||
spidxQuantity
|
||||
.addTask(new CalculateHashesTask(VersionEnum.V3_5_0, "20180903.22")
|
||||
.addCalculator("HASH_IDENTITY", t -> BaseResourceIndexedSearchParam.calculateHashIdentity(new PartitionSettings(), null, t.getResourceType(), t.getString("SP_NAME")))
|
||||
.addCalculator("HASH_IDENTITY_AND_UNITS", t -> ResourceIndexedSearchParamQuantity.calculateHashUnits(new PartitionSettings(), null, t.getResourceType(), t.getString("SP_NAME"), t.getString("SP_UNITS")))
|
||||
.addCalculator("HASH_IDENTITY_SYS_UNITS", t -> ResourceIndexedSearchParamQuantity.calculateHashSystemAndUnits(new PartitionSettings(), null, t.getResourceType(), t.getString("SP_NAME"), t.getString("SP_SYSTEM"), t.getString("SP_UNITS")))
|
||||
.addCalculator("HASH_IDENTITY", t -> BaseResourceIndexedSearchParam.calculateHashIdentity(new PartitionSettings(), RequestPartitionId.defaultPartition(), t.getResourceType(), t.getString("SP_NAME")))
|
||||
.addCalculator("HASH_IDENTITY_AND_UNITS", t -> ResourceIndexedSearchParamQuantity.calculateHashUnits(new PartitionSettings(), RequestPartitionId.defaultPartition(), t.getResourceType(), t.getString("SP_NAME"), t.getString("SP_UNITS")))
|
||||
.addCalculator("HASH_IDENTITY_SYS_UNITS", t -> ResourceIndexedSearchParamQuantity.calculateHashSystemAndUnits(new PartitionSettings(), RequestPartitionId.defaultPartition(), t.getResourceType(), t.getString("SP_NAME"), t.getString("SP_SYSTEM"), t.getString("SP_UNITS")))
|
||||
.setColumnName("HASH_IDENTITY")
|
||||
);
|
||||
}
|
||||
|
@ -861,8 +862,8 @@ public class HapiFhirJpaMigrationTasks extends BaseMigrationTasks<VersionEnum> {
|
|||
spidxString
|
||||
.addTask(new CalculateHashesTask(VersionEnum.V3_5_0, "20180903.28")
|
||||
.setColumnName("HASH_NORM_PREFIX")
|
||||
.addCalculator("HASH_NORM_PREFIX", t -> ResourceIndexedSearchParamString.calculateHashNormalized(new PartitionSettings(), null, new ModelConfig(), t.getResourceType(), t.getString("SP_NAME"), t.getString("SP_VALUE_NORMALIZED")))
|
||||
.addCalculator("HASH_EXACT", t -> ResourceIndexedSearchParamString.calculateHashExact(new PartitionSettings(), null, t.getResourceType(), t.getParamName(), t.getString("SP_VALUE_EXACT")))
|
||||
.addCalculator("HASH_NORM_PREFIX", t -> ResourceIndexedSearchParamString.calculateHashNormalized(new PartitionSettings(), RequestPartitionId.defaultPartition(), new ModelConfig(), t.getResourceType(), t.getString("SP_NAME"), t.getString("SP_VALUE_NORMALIZED")))
|
||||
.addCalculator("HASH_EXACT", t -> ResourceIndexedSearchParamString.calculateHashExact(new PartitionSettings(), (ca.uhn.fhir.jpa.model.entity.PartitionablePartitionId) null, t.getResourceType(), t.getParamName(), t.getString("SP_VALUE_EXACT")))
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -909,10 +910,10 @@ public class HapiFhirJpaMigrationTasks extends BaseMigrationTasks<VersionEnum> {
|
|||
spidxToken
|
||||
.addTask(new CalculateHashesTask(VersionEnum.V3_5_0, "20180903.39")
|
||||
.setColumnName("HASH_IDENTITY")
|
||||
.addCalculator("HASH_IDENTITY", t -> BaseResourceIndexedSearchParam.calculateHashIdentity(new PartitionSettings(), null, t.getResourceType(), t.getString("SP_NAME")))
|
||||
.addCalculator("HASH_SYS", t -> ResourceIndexedSearchParamToken.calculateHashSystem(new PartitionSettings(), null, t.getResourceType(), t.getParamName(), t.getString("SP_SYSTEM")))
|
||||
.addCalculator("HASH_SYS_AND_VALUE", t -> ResourceIndexedSearchParamToken.calculateHashSystemAndValue(new PartitionSettings(), null, t.getResourceType(), t.getParamName(), t.getString("SP_SYSTEM"), t.getString("SP_VALUE")))
|
||||
.addCalculator("HASH_VALUE", t -> ResourceIndexedSearchParamToken.calculateHashValue(new PartitionSettings(), null, t.getResourceType(), t.getParamName(), t.getString("SP_VALUE")))
|
||||
.addCalculator("HASH_IDENTITY", t -> BaseResourceIndexedSearchParam.calculateHashIdentity(new PartitionSettings(), RequestPartitionId.defaultPartition(), t.getResourceType(), t.getString("SP_NAME")))
|
||||
.addCalculator("HASH_SYS", t -> ResourceIndexedSearchParamToken.calculateHashSystem(new PartitionSettings(), RequestPartitionId.defaultPartition(), t.getResourceType(), t.getParamName(), t.getString("SP_SYSTEM")))
|
||||
.addCalculator("HASH_SYS_AND_VALUE", t -> ResourceIndexedSearchParamToken.calculateHashSystemAndValue(new PartitionSettings(), RequestPartitionId.defaultPartition(), t.getResourceType(), t.getParamName(), t.getString("SP_SYSTEM"), t.getString("SP_VALUE")))
|
||||
.addCalculator("HASH_VALUE", t -> ResourceIndexedSearchParamToken.calculateHashValue(new PartitionSettings(), RequestPartitionId.defaultPartition(), t.getResourceType(), t.getParamName(), t.getString("SP_VALUE")))
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -939,8 +940,8 @@ public class HapiFhirJpaMigrationTasks extends BaseMigrationTasks<VersionEnum> {
|
|||
spidxUri
|
||||
.addTask(new CalculateHashesTask(VersionEnum.V3_5_0, "20180903.44")
|
||||
.setColumnName("HASH_IDENTITY")
|
||||
.addCalculator("HASH_IDENTITY", t -> BaseResourceIndexedSearchParam.calculateHashIdentity(new PartitionSettings(), null, t.getResourceType(), t.getString("SP_NAME")))
|
||||
.addCalculator("HASH_URI", t -> ResourceIndexedSearchParamUri.calculateHashUri(new PartitionSettings(), null, t.getResourceType(), t.getString("SP_NAME"), t.getString("SP_URI")))
|
||||
.addCalculator("HASH_IDENTITY", t -> BaseResourceIndexedSearchParam.calculateHashIdentity(new PartitionSettings(), (RequestPartitionId)null, t.getResourceType(), t.getString("SP_NAME")))
|
||||
.addCalculator("HASH_URI", t -> ResourceIndexedSearchParamUri.calculateHashUri(new PartitionSettings(), (RequestPartitionId)null, t.getResourceType(), t.getString("SP_NAME"), t.getString("SP_URI")))
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -973,7 +974,7 @@ public class HapiFhirJpaMigrationTasks extends BaseMigrationTasks<VersionEnum> {
|
|||
Boolean present = columnToBoolean(t.get("SP_PRESENT"));
|
||||
String resType = (String) t.get("RES_TYPE");
|
||||
String paramName = (String) t.get("PARAM_NAME");
|
||||
Long hash = SearchParamPresent.calculateHashPresence(new PartitionSettings(), null, resType, paramName, present);
|
||||
Long hash = SearchParamPresent.calculateHashPresence(new PartitionSettings(), (RequestPartitionId)null, resType, paramName, present);
|
||||
consolidateSearchParamPresenceIndexesTask.executeSql("HFJ_RES_PARAM_PRESENT", "update HFJ_RES_PARAM_PRESENT set HASH_PRESENCE = ? where PID = ?", hash, pid);
|
||||
});
|
||||
version.addTask(consolidateSearchParamPresenceIndexesTask);
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
package ca.uhn.fhir.jpa.migrate.taskdef;
|
||||
|
||||
import ca.uhn.fhir.interceptor.model.RequestPartitionId;
|
||||
import ca.uhn.fhir.jpa.migrate.tasks.api.BaseMigrationTasks;
|
||||
import ca.uhn.fhir.jpa.model.config.PartitionSettings;
|
||||
import ca.uhn.fhir.jpa.model.entity.SearchParamPresent;
|
||||
|
@ -42,7 +43,7 @@ public class ArbitrarySqlTaskTest extends BaseTest {
|
|||
Boolean present = (Boolean) t.get("SP_PRESENT");
|
||||
String resType = (String) t.get("RES_TYPE");
|
||||
String paramName = (String) t.get("PARAM_NAME");
|
||||
Long hash = SearchParamPresent.calculateHashPresence(new PartitionSettings(), null, resType, paramName, present);
|
||||
Long hash = SearchParamPresent.calculateHashPresence(new PartitionSettings(), RequestPartitionId.defaultPartition(), resType, paramName, present);
|
||||
task.executeSql("HFJ_RES_PARAM_PRESENT", "update HFJ_RES_PARAM_PRESENT set HASH_PRESENT = ? where PID = ?", hash, pid);
|
||||
});
|
||||
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
package ca.uhn.fhir.jpa.migrate.taskdef;
|
||||
|
||||
import ca.uhn.fhir.interceptor.model.RequestPartitionId;
|
||||
import ca.uhn.fhir.jpa.model.config.PartitionSettings;
|
||||
import ca.uhn.fhir.jpa.model.entity.BaseResourceIndexedSearchParam;
|
||||
import ca.uhn.fhir.jpa.model.entity.ResourceIndexedSearchParamToken;
|
||||
|
@ -27,10 +28,10 @@ public class CalculateHashesTest extends BaseTest {
|
|||
CalculateHashesTask task = new CalculateHashesTask(VersionEnum.V3_5_0, "1");
|
||||
task.setTableName("HFJ_SPIDX_TOKEN");
|
||||
task.setColumnName("HASH_IDENTITY");
|
||||
task.addCalculator("HASH_IDENTITY", t -> BaseResourceIndexedSearchParam.calculateHashIdentity(new PartitionSettings(), null, t.getResourceType(), t.getString("SP_NAME")));
|
||||
task.addCalculator("HASH_SYS", t -> ResourceIndexedSearchParamToken.calculateHashSystem(new PartitionSettings(), null, t.getResourceType(), t.getParamName(), t.getString("SP_SYSTEM")));
|
||||
task.addCalculator("HASH_SYS_AND_VALUE", t -> ResourceIndexedSearchParamToken.calculateHashSystemAndValue(new PartitionSettings(), null, t.getResourceType(), t.getParamName(), t.getString("SP_SYSTEM"), t.getString("SP_VALUE")));
|
||||
task.addCalculator("HASH_VALUE", t -> ResourceIndexedSearchParamToken.calculateHashValue(new PartitionSettings(), null, t.getResourceType(), t.getParamName(), t.getString("SP_VALUE")));
|
||||
task.addCalculator("HASH_IDENTITY", t -> BaseResourceIndexedSearchParam.calculateHashIdentity(new PartitionSettings(), RequestPartitionId.defaultPartition(), t.getResourceType(), t.getString("SP_NAME")));
|
||||
task.addCalculator("HASH_SYS", t -> ResourceIndexedSearchParamToken.calculateHashSystem(new PartitionSettings(), RequestPartitionId.defaultPartition(), t.getResourceType(), t.getParamName(), t.getString("SP_SYSTEM")));
|
||||
task.addCalculator("HASH_SYS_AND_VALUE", t -> ResourceIndexedSearchParamToken.calculateHashSystemAndValue(new PartitionSettings(), RequestPartitionId.defaultPartition(), t.getResourceType(), t.getParamName(), t.getString("SP_SYSTEM"), t.getString("SP_VALUE")));
|
||||
task.addCalculator("HASH_VALUE", t -> ResourceIndexedSearchParamToken.calculateHashValue(new PartitionSettings(), RequestPartitionId.defaultPartition(), t.getResourceType(), t.getParamName(), t.getString("SP_VALUE")));
|
||||
task.setBatchSize(1);
|
||||
getMigrator().addTask(task);
|
||||
|
||||
|
@ -77,10 +78,10 @@ public class CalculateHashesTest extends BaseTest {
|
|||
CalculateHashesTask task = new CalculateHashesTask(VersionEnum.V3_5_0, "1");
|
||||
task.setTableName("HFJ_SPIDX_TOKEN");
|
||||
task.setColumnName("HASH_IDENTITY");
|
||||
task.addCalculator("HASH_IDENTITY", t -> BaseResourceIndexedSearchParam.calculateHashIdentity(new PartitionSettings(), null, t.getResourceType(), t.getString("SP_NAME")));
|
||||
task.addCalculator("HASH_SYS", t -> ResourceIndexedSearchParamToken.calculateHashSystem(new PartitionSettings(), null, t.getResourceType(), t.getParamName(), t.getString("SP_SYSTEM")));
|
||||
task.addCalculator("HASH_SYS_AND_VALUE", t -> ResourceIndexedSearchParamToken.calculateHashSystemAndValue(new PartitionSettings(), null, t.getResourceType(), t.getParamName(), t.getString("SP_SYSTEM"), t.getString("SP_VALUE")));
|
||||
task.addCalculator("HASH_VALUE", t -> ResourceIndexedSearchParamToken.calculateHashValue(new PartitionSettings(), null, t.getResourceType(), t.getParamName(), t.getString("SP_VALUE")));
|
||||
task.addCalculator("HASH_IDENTITY", t -> BaseResourceIndexedSearchParam.calculateHashIdentity(new PartitionSettings(), RequestPartitionId.defaultPartition(), t.getResourceType(), t.getString("SP_NAME")));
|
||||
task.addCalculator("HASH_SYS", t -> ResourceIndexedSearchParamToken.calculateHashSystem(new PartitionSettings(), RequestPartitionId.defaultPartition(), t.getResourceType(), t.getParamName(), t.getString("SP_SYSTEM")));
|
||||
task.addCalculator("HASH_SYS_AND_VALUE", t -> ResourceIndexedSearchParamToken.calculateHashSystemAndValue(new PartitionSettings(), RequestPartitionId.defaultPartition(), t.getResourceType(), t.getParamName(), t.getString("SP_SYSTEM"), t.getString("SP_VALUE")));
|
||||
task.addCalculator("HASH_VALUE", t -> ResourceIndexedSearchParamToken.calculateHashValue(new PartitionSettings(), RequestPartitionId.defaultPartition(), t.getResourceType(), t.getParamName(), t.getString("SP_VALUE")));
|
||||
task.setBatchSize(3);
|
||||
getMigrator().addTask(task);
|
||||
|
||||
|
|
|
@ -22,7 +22,6 @@ package ca.uhn.fhir.jpa.model.entity;
|
|||
|
||||
import ca.uhn.fhir.interceptor.model.RequestPartitionId;
|
||||
|
||||
import javax.annotation.Nonnull;
|
||||
import javax.annotation.Nullable;
|
||||
import javax.persistence.Column;
|
||||
import javax.persistence.Embedded;
|
||||
|
@ -42,18 +41,18 @@ public class BasePartitionable implements Serializable {
|
|||
@Column(name = PartitionablePartitionId.PARTITION_ID, insertable = false, updatable = false, nullable = true)
|
||||
private Integer myPartitionIdValue;
|
||||
|
||||
@Nonnull
|
||||
public RequestPartitionId getPartitionId() {
|
||||
if (myPartitionId != null) {
|
||||
return myPartitionId.toPartitionId();
|
||||
} else {
|
||||
return RequestPartitionId.defaultPartition();
|
||||
}
|
||||
@Nullable
|
||||
public PartitionablePartitionId getPartitionId() {
|
||||
return myPartitionId;
|
||||
}
|
||||
|
||||
public void setPartitionId(PartitionablePartitionId thePartitionId) {
|
||||
myPartitionId = thePartitionId;
|
||||
}
|
||||
|
||||
public void setPartitionId(@Nullable RequestPartitionId theRequestPartitionId) {
|
||||
if (theRequestPartitionId != null) {
|
||||
myPartitionId = new PartitionablePartitionId(theRequestPartitionId.getPartitionId(), theRequestPartitionId.getPartitionDate());
|
||||
myPartitionId = new PartitionablePartitionId(theRequestPartitionId.getFirstPartitionIdOrNull(), theRequestPartitionId.getPartitionDate());
|
||||
} else {
|
||||
myPartitionId = null;
|
||||
}
|
||||
|
|
|
@ -20,10 +20,12 @@ package ca.uhn.fhir.jpa.model.entity;
|
|||
* #L%
|
||||
*/
|
||||
|
||||
import ca.uhn.fhir.context.ConfigurationException;
|
||||
import ca.uhn.fhir.interceptor.model.RequestPartitionId;
|
||||
import ca.uhn.fhir.jpa.model.config.PartitionSettings;
|
||||
import ca.uhn.fhir.model.api.IQueryParameterType;
|
||||
import ca.uhn.fhir.rest.api.Constants;
|
||||
import ca.uhn.fhir.rest.server.exceptions.InternalErrorException;
|
||||
import ca.uhn.fhir.util.UrlUtil;
|
||||
import com.google.common.base.Charsets;
|
||||
import com.google.common.hash.HashCode;
|
||||
|
@ -179,6 +181,11 @@ public abstract class BaseResourceIndexedSearchParam extends BaseResourceIndex {
|
|||
return myModelConfig;
|
||||
}
|
||||
|
||||
public static long calculateHashIdentity(PartitionSettings thePartitionSettings, PartitionablePartitionId theRequestPartitionId, String theResourceType, String theParamName) {
|
||||
RequestPartitionId requestPartitionId = PartitionablePartitionId.toRequestPartitionId(theRequestPartitionId);
|
||||
return calculateHashIdentity(thePartitionSettings, requestPartitionId, theResourceType, theParamName);
|
||||
}
|
||||
|
||||
public static long calculateHashIdentity(PartitionSettings thePartitionSettings, RequestPartitionId theRequestPartitionId, String theResourceType, String theParamName) {
|
||||
return hash(thePartitionSettings, theRequestPartitionId, theResourceType, theParamName);
|
||||
}
|
||||
|
@ -190,8 +197,12 @@ public abstract class BaseResourceIndexedSearchParam extends BaseResourceIndex {
|
|||
Hasher hasher = HASH_FUNCTION.newHasher();
|
||||
|
||||
if (thePartitionSettings.isPartitioningEnabled() && thePartitionSettings.isIncludePartitionInSearchHashes() && theRequestPartitionId != null) {
|
||||
if (theRequestPartitionId.getPartitionId() != null) {
|
||||
hasher.putInt(theRequestPartitionId.getPartitionId());
|
||||
if (theRequestPartitionId.getPartitionIds().size() > 1) {
|
||||
throw new InternalErrorException("Can not search multiple partitions when partitions are included in search hashes");
|
||||
}
|
||||
Integer partitionId = theRequestPartitionId.getFirstPartitionIdOrNull();
|
||||
if (partitionId != null) {
|
||||
hasher.putInt(partitionId);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -25,6 +25,7 @@ import ca.uhn.fhir.interceptor.model.RequestPartitionId;
|
|||
import ca.uhn.fhir.model.primitive.IdDt;
|
||||
import ca.uhn.fhir.model.primitive.InstantDt;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
import java.util.Date;
|
||||
|
||||
public interface IBaseResourceEntity {
|
||||
|
@ -51,5 +52,6 @@ public interface IBaseResourceEntity {
|
|||
|
||||
boolean isHasTags();
|
||||
|
||||
RequestPartitionId getPartitionId();
|
||||
@Nullable
|
||||
PartitionablePartitionId getPartitionId();
|
||||
}
|
||||
|
|
|
@ -22,9 +22,11 @@ package ca.uhn.fhir.jpa.model.entity;
|
|||
|
||||
import ca.uhn.fhir.interceptor.model.RequestPartitionId;
|
||||
|
||||
import javax.annotation.Nonnull;
|
||||
import javax.annotation.Nullable;
|
||||
import javax.persistence.Column;
|
||||
import javax.persistence.Embeddable;
|
||||
import javax.validation.constraints.Null;
|
||||
import java.time.LocalDate;
|
||||
|
||||
@Embeddable
|
||||
|
@ -83,4 +85,13 @@ public class PartitionablePartitionId implements Cloneable {
|
|||
public RequestPartitionId toPartitionId() {
|
||||
return RequestPartitionId.fromPartitionId(getPartitionId(), getPartitionDate());
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public static RequestPartitionId toRequestPartitionId(@Nullable PartitionablePartitionId theRequestPartitionId) {
|
||||
if (theRequestPartitionId != null) {
|
||||
return theRequestPartitionId.toPartitionId();
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -104,4 +104,5 @@ public class ResourceHistoryProvenanceEntity extends BasePartitionable {
|
|||
return myId;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
|
|
@ -20,64 +20,53 @@ package ca.uhn.fhir.jpa.model.entity;
|
|||
* #L%
|
||||
*/
|
||||
|
||||
import ca.uhn.fhir.interceptor.model.RequestPartitionId;
|
||||
|
||||
import javax.persistence.*;
|
||||
import javax.persistence.Column;
|
||||
import javax.persistence.Embeddable;
|
||||
import javax.persistence.Entity;
|
||||
import javax.persistence.ForeignKey;
|
||||
import javax.persistence.GeneratedValue;
|
||||
import javax.persistence.GenerationType;
|
||||
import javax.persistence.Id;
|
||||
import javax.persistence.JoinColumn;
|
||||
import javax.persistence.ManyToOne;
|
||||
import javax.persistence.SequenceGenerator;
|
||||
import javax.persistence.Table;
|
||||
import javax.persistence.UniqueConstraint;
|
||||
import java.io.Serializable;
|
||||
|
||||
@Embeddable
|
||||
@Entity
|
||||
@Table(name = "HFJ_HISTORY_TAG", uniqueConstraints= {
|
||||
@UniqueConstraint(name="IDX_RESHISTTAG_TAGID", columnNames= {"RES_VER_PID","TAG_ID"})
|
||||
@Table(name = "HFJ_HISTORY_TAG", uniqueConstraints = {
|
||||
@UniqueConstraint(name = "IDX_RESHISTTAG_TAGID", columnNames = {"RES_VER_PID", "TAG_ID"})
|
||||
})
|
||||
public class ResourceHistoryTag extends BaseTag implements Serializable {
|
||||
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
|
||||
@SequenceGenerator(name = "SEQ_HISTORYTAG_ID", sequenceName = "SEQ_HISTORYTAG_ID")
|
||||
@GeneratedValue(strategy = GenerationType.AUTO, generator = "SEQ_HISTORYTAG_ID")
|
||||
@Id
|
||||
@Column(name = "PID")
|
||||
private Long myId;
|
||||
|
||||
|
||||
@ManyToOne()
|
||||
@JoinColumn(name="RES_VER_PID", referencedColumnName="PID", nullable=false, foreignKey=@ForeignKey(name="FK_HISTORYTAG_HISTORY"))
|
||||
@JoinColumn(name = "RES_VER_PID", referencedColumnName = "PID", nullable = false, foreignKey = @ForeignKey(name = "FK_HISTORYTAG_HISTORY"))
|
||||
private ResourceHistoryTable myResourceHistory;
|
||||
|
||||
@Column(name="RES_VER_PID", insertable = false, updatable = false, nullable = false)
|
||||
@Column(name = "RES_VER_PID", insertable = false, updatable = false, nullable = false)
|
||||
private Long myResourceHistoryPid;
|
||||
|
||||
@Column(name = "RES_TYPE", length = ResourceTable.RESTYPE_LEN, nullable=false)
|
||||
@Column(name = "RES_TYPE", length = ResourceTable.RESTYPE_LEN, nullable = false)
|
||||
private String myResourceType;
|
||||
|
||||
@Column(name="RES_ID", nullable=false)
|
||||
@Column(name = "RES_ID", nullable = false)
|
||||
private Long myResourceId;
|
||||
|
||||
public String getResourceType() {
|
||||
return myResourceType;
|
||||
}
|
||||
|
||||
|
||||
public void setResourceType(String theResourceType) {
|
||||
myResourceType = theResourceType;
|
||||
}
|
||||
|
||||
|
||||
public Long getResourceId() {
|
||||
return myResourceId;
|
||||
}
|
||||
|
||||
|
||||
public void setResourceId(Long theResourceId) {
|
||||
myResourceId = theResourceId;
|
||||
}
|
||||
|
||||
|
||||
public ResourceHistoryTag() {
|
||||
}
|
||||
|
||||
|
||||
public ResourceHistoryTag(ResourceHistoryTable theResourceHistoryTable, TagDefinition theTag, RequestPartitionId theRequestPartitionId) {
|
||||
|
||||
public ResourceHistoryTag(ResourceHistoryTable theResourceHistoryTable, TagDefinition theTag, PartitionablePartitionId theRequestPartitionId) {
|
||||
setTag(theTag);
|
||||
setResource(theResourceHistoryTable);
|
||||
setResourceId(theResourceHistoryTable.getResourceId());
|
||||
|
@ -85,6 +74,22 @@ public class ResourceHistoryTag extends BaseTag implements Serializable {
|
|||
setPartitionId(theRequestPartitionId);
|
||||
}
|
||||
|
||||
public String getResourceType() {
|
||||
return myResourceType;
|
||||
}
|
||||
|
||||
public void setResourceType(String theResourceType) {
|
||||
myResourceType = theResourceType;
|
||||
}
|
||||
|
||||
public Long getResourceId() {
|
||||
return myResourceId;
|
||||
}
|
||||
|
||||
public void setResourceId(Long theResourceId) {
|
||||
myResourceId = theResourceId;
|
||||
}
|
||||
|
||||
public ResourceHistoryTable getResourceHistory() {
|
||||
return myResourceHistory;
|
||||
}
|
||||
|
|
|
@ -283,10 +283,20 @@ public class ResourceIndexedSearchParamQuantity extends BaseResourceIndexedSearc
|
|||
return retval;
|
||||
}
|
||||
|
||||
public static long calculateHashSystemAndUnits(PartitionSettings thePartitionSettings, PartitionablePartitionId theRequestPartitionId, String theResourceType, String theParamName, String theSystem, String theUnits) {
|
||||
RequestPartitionId requestPartitionId = PartitionablePartitionId.toRequestPartitionId(theRequestPartitionId);
|
||||
return calculateHashSystemAndUnits(thePartitionSettings, requestPartitionId, theResourceType, theParamName, theSystem, theUnits);
|
||||
}
|
||||
|
||||
public static long calculateHashSystemAndUnits(PartitionSettings thePartitionSettings, RequestPartitionId theRequestPartitionId, String theResourceType, String theParamName, String theSystem, String theUnits) {
|
||||
return hash(thePartitionSettings, theRequestPartitionId, theResourceType, theParamName, theSystem, theUnits);
|
||||
}
|
||||
|
||||
public static long calculateHashUnits(PartitionSettings thePartitionSettings, PartitionablePartitionId theRequestPartitionId, String theResourceType, String theParamName, String theUnits) {
|
||||
RequestPartitionId requestPartitionId = PartitionablePartitionId.toRequestPartitionId(theRequestPartitionId);
|
||||
return calculateHashUnits(thePartitionSettings, requestPartitionId, theResourceType, theParamName, theUnits);
|
||||
}
|
||||
|
||||
public static long calculateHashUnits(PartitionSettings thePartitionSettings, RequestPartitionId theRequestPartitionId, String theResourceType, String theParamName, String theUnits) {
|
||||
return hash(thePartitionSettings, theRequestPartitionId, theResourceType, theParamName, theUnits);
|
||||
}
|
||||
|
|
|
@ -270,10 +270,20 @@ public class ResourceIndexedSearchParamString extends BaseResourceIndexedSearchP
|
|||
return defaultString(getValueNormalized()).startsWith(normalizedString);
|
||||
}
|
||||
|
||||
public static long calculateHashExact(PartitionSettings thePartitionSettings, PartitionablePartitionId theRequestPartitionId, String theResourceType, String theParamName, String theValueExact) {
|
||||
RequestPartitionId requestPartitionId = PartitionablePartitionId.toRequestPartitionId(theRequestPartitionId);
|
||||
return calculateHashExact(thePartitionSettings, requestPartitionId, theResourceType, theParamName, theValueExact);
|
||||
}
|
||||
|
||||
public static long calculateHashExact(PartitionSettings thePartitionSettings, RequestPartitionId theRequestPartitionId, String theResourceType, String theParamName, String theValueExact) {
|
||||
return hash(thePartitionSettings, theRequestPartitionId, theResourceType, theParamName, theValueExact);
|
||||
}
|
||||
|
||||
public static long calculateHashNormalized(PartitionSettings thePartitionSettings, PartitionablePartitionId theRequestPartitionId, ModelConfig theModelConfig, String theResourceType, String theParamName, String theValueNormalized) {
|
||||
RequestPartitionId requestPartitionId = PartitionablePartitionId.toRequestPartitionId(theRequestPartitionId);
|
||||
return calculateHashNormalized(thePartitionSettings, requestPartitionId, theModelConfig, theResourceType, theParamName, theValueNormalized);
|
||||
}
|
||||
|
||||
public static long calculateHashNormalized(PartitionSettings thePartitionSettings, RequestPartitionId theRequestPartitionId, ModelConfig theModelConfig, String theResourceType, String theParamName, String theValueNormalized) {
|
||||
/*
|
||||
* If we're not allowing contained searches, we'll add the first
|
||||
|
|
|
@ -286,14 +286,29 @@ public class ResourceIndexedSearchParamToken extends BaseResourceIndexedSearchPa
|
|||
return retVal;
|
||||
}
|
||||
|
||||
public static long calculateHashSystem(PartitionSettings thePartitionSettings, PartitionablePartitionId theRequestPartitionId, String theResourceType, String theParamName, String theSystem) {
|
||||
RequestPartitionId requestPartitionId = PartitionablePartitionId.toRequestPartitionId(theRequestPartitionId);
|
||||
return calculateHashSystem(thePartitionSettings, requestPartitionId, theResourceType, theParamName, theSystem);
|
||||
}
|
||||
|
||||
public static long calculateHashSystem(PartitionSettings thePartitionSettings, RequestPartitionId theRequestPartitionId, String theResourceType, String theParamName, String theSystem) {
|
||||
return hash(thePartitionSettings, theRequestPartitionId, theResourceType, theParamName, trim(theSystem));
|
||||
}
|
||||
|
||||
public static long calculateHashSystemAndValue(PartitionSettings thePartitionSettings, PartitionablePartitionId theRequestPartitionId, String theResourceType, String theParamName, String theSystem, String theValue) {
|
||||
RequestPartitionId requestPartitionId = PartitionablePartitionId.toRequestPartitionId(theRequestPartitionId);
|
||||
return calculateHashSystemAndValue(thePartitionSettings, requestPartitionId, theResourceType, theParamName, theSystem, theValue);
|
||||
}
|
||||
|
||||
public static long calculateHashSystemAndValue(PartitionSettings thePartitionSettings, RequestPartitionId theRequestPartitionId, String theResourceType, String theParamName, String theSystem, String theValue) {
|
||||
return hash(thePartitionSettings, theRequestPartitionId, theResourceType, theParamName, defaultString(trim(theSystem)), trim(theValue));
|
||||
}
|
||||
|
||||
public static long calculateHashValue(PartitionSettings thePartitionSettings, PartitionablePartitionId theRequestPartitionId, String theResourceType, String theParamName, String theValue) {
|
||||
RequestPartitionId requestPartitionId = PartitionablePartitionId.toRequestPartitionId(theRequestPartitionId);
|
||||
return calculateHashValue(thePartitionSettings, requestPartitionId, theResourceType, theParamName, theValue);
|
||||
}
|
||||
|
||||
public static long calculateHashValue(PartitionSettings thePartitionSettings, RequestPartitionId theRequestPartitionId, String theResourceType, String theParamName, String theValue) {
|
||||
String value = trim(theValue);
|
||||
return hash(thePartitionSettings, theRequestPartitionId, theResourceType, theParamName, value);
|
||||
|
|
|
@ -209,6 +209,11 @@ public class ResourceIndexedSearchParamUri extends BaseResourceIndexedSearchPara
|
|||
return defaultString(getUri()).equalsIgnoreCase(uri.getValueNotNull());
|
||||
}
|
||||
|
||||
public static long calculateHashUri(PartitionSettings thePartitionSettings, PartitionablePartitionId theRequestPartitionId, String theResourceType, String theParamName, String theUri) {
|
||||
RequestPartitionId requestPartitionId = PartitionablePartitionId.toRequestPartitionId(theRequestPartitionId);
|
||||
return calculateHashUri(thePartitionSettings, requestPartitionId, theResourceType, theParamName, theUri);
|
||||
}
|
||||
|
||||
public static long calculateHashUri(PartitionSettings thePartitionSettings, RequestPartitionId theRequestPartitionId, String theResourceType, String theParamName, String theUri) {
|
||||
return hash(thePartitionSettings, theRequestPartitionId, theResourceType, theParamName, theUri);
|
||||
}
|
||||
|
|
|
@ -20,13 +20,23 @@ package ca.uhn.fhir.jpa.model.entity;
|
|||
* #L%
|
||||
*/
|
||||
|
||||
import ca.uhn.fhir.interceptor.model.RequestPartitionId;
|
||||
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 javax.persistence.*;
|
||||
import javax.persistence.Column;
|
||||
import javax.persistence.Entity;
|
||||
import javax.persistence.FetchType;
|
||||
import javax.persistence.ForeignKey;
|
||||
import javax.persistence.GeneratedValue;
|
||||
import javax.persistence.GenerationType;
|
||||
import javax.persistence.Id;
|
||||
import javax.persistence.JoinColumn;
|
||||
import javax.persistence.ManyToOne;
|
||||
import javax.persistence.SequenceGenerator;
|
||||
import javax.persistence.Table;
|
||||
import javax.persistence.UniqueConstraint;
|
||||
|
||||
@Entity
|
||||
@Table(name = "HFJ_RES_TAG", uniqueConstraints = {
|
||||
|
@ -52,10 +62,17 @@ public class ResourceTag extends BaseTag {
|
|||
@Column(name = "RES_ID", insertable = false, updatable = false)
|
||||
private Long myResourceId;
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
*/
|
||||
public ResourceTag() {
|
||||
super();
|
||||
}
|
||||
|
||||
public ResourceTag(ResourceTable theResourceTable, TagDefinition theTag, RequestPartitionId theRequestPartitionId) {
|
||||
/**
|
||||
* Constructor
|
||||
*/
|
||||
public ResourceTag(ResourceTable theResourceTable, TagDefinition theTag, PartitionablePartitionId theRequestPartitionId) {
|
||||
setTag(theTag);
|
||||
setResource(theResourceTable);
|
||||
setResourceId(theResourceTable.getId());
|
||||
|
|
|
@ -126,6 +126,11 @@ public class SearchParamPresent extends BasePartitionable implements Serializabl
|
|||
myPartitionSettings = thePartitionSettings;
|
||||
}
|
||||
|
||||
public static long calculateHashPresence(PartitionSettings thePartitionSettings, PartitionablePartitionId theRequestPartitionId, String theResourceType, String theParamName, Boolean thePresent) {
|
||||
RequestPartitionId requestPartitionId = PartitionablePartitionId.toRequestPartitionId(theRequestPartitionId);
|
||||
return calculateHashPresence(thePartitionSettings, requestPartitionId, theResourceType, theParamName, thePresent);
|
||||
}
|
||||
|
||||
public static long calculateHashPresence(PartitionSettings thePartitionSettings, RequestPartitionId theRequestPartitionId, String theResourceType, String theParamName, Boolean thePresent) {
|
||||
String string = thePresent != null ? Boolean.toString(thePresent) : Boolean.toString(false);
|
||||
return BaseResourceIndexedSearchParam.hash(thePartitionSettings, theRequestPartitionId, theResourceType, theParamName, string);
|
||||
|
|
|
@ -204,6 +204,11 @@ public class JpaConstants {
|
|||
public static final String EXT_SEARCHPARAM_PHONETIC_ENCODER = "http://hapifhir.io/fhir/StructureDefinition/searchparameter-phonetic-encoder";
|
||||
public static final String VALUESET_FILTER_DISPLAY = "display";
|
||||
|
||||
/**
|
||||
* The name of the default partition
|
||||
*/
|
||||
public static final String DEFAULT_PARTITION_NAME = "DEFAULT";
|
||||
|
||||
/**
|
||||
* Non-instantiable
|
||||
*/
|
||||
|
|
|
@ -215,7 +215,8 @@ public class RuleBuilder implements IAuthRuleBuilder {
|
|||
if (theResource != null) {
|
||||
RequestPartitionId partitionId = (RequestPartitionId) theResource.getUserData(Constants.RESOURCE_PARTITION_ID);
|
||||
if (partitionId != null) {
|
||||
if (!myTenantIds.contains(partitionId.getPartitionName())) {
|
||||
String partitionNameOrNull = partitionId.getFirstPartitionNameOrNull();
|
||||
if (partitionNameOrNull == null || !myTenantIds.contains(partitionNameOrNull)) {
|
||||
return !myOutcome;
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue