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:
James Agnew 2020-11-30 17:59:52 -05:00 committed by GitHub
parent 283834ed1d
commit c309737166
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
46 changed files with 1076 additions and 441 deletions

View File

@ -470,9 +470,6 @@ public class FhirContext {
/** /**
* Returns the name of a given resource class. * Returns the name of a given resource class.
*
* @param theResourceType
* @return
*/ */
public String getResourceType(final Class<? extends IBaseResource> theResourceType) { public String getResourceType(final Class<? extends IBaseResource> theResourceType) {
return getResourceDefinition(theResourceType).getName(); return getResourceDefinition(theResourceType).getName();
@ -603,7 +600,7 @@ public class FhirContext {
/** /**
* Set the restful client factory * Set the restful client factory
* *
* @param theRestfulClientFactory * @param theRestfulClientFactory The new client factory (must not be null)
*/ */
public void setRestfulClientFactory(final IRestfulClientFactory theRestfulClientFactory) { public void setRestfulClientFactory(final IRestfulClientFactory theRestfulClientFactory) {
Validate.notNull(theRestfulClientFactory, "theRestfulClientFactory must not be null"); Validate.notNull(theRestfulClientFactory, "theRestfulClientFactory must not be null");

View File

@ -20,12 +20,23 @@ package ca.uhn.fhir.interceptor.model;
* #L% * #L%
*/ */
import org.apache.commons.lang3.Validate;
import org.apache.commons.lang3.builder.EqualsBuilder; import org.apache.commons.lang3.builder.EqualsBuilder;
import org.apache.commons.lang3.builder.HashCodeBuilder; 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.Nonnull;
import javax.annotation.Nullable; import javax.annotation.Nullable;
import java.time.LocalDate; 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 * @since 5.0.0
@ -35,15 +46,25 @@ public class RequestPartitionId {
private static final RequestPartitionId ALL_PARTITIONS = new RequestPartitionId(); private static final RequestPartitionId ALL_PARTITIONS = new RequestPartitionId();
private final LocalDate myPartitionDate; private final LocalDate myPartitionDate;
private final boolean myAllPartitions; private final boolean myAllPartitions;
private final Integer myPartitionId; private final List<Integer> myPartitionIds;
private final String myPartitionName; private final List<String> myPartitionNames;
/** /**
* Constructor for a single partition * Constructor for a single partition
*/ */
private RequestPartitionId(@Nullable String thePartitionName, @Nullable Integer thePartitionId, @Nullable LocalDate thePartitionDate) { private RequestPartitionId(@Nullable String thePartitionName, @Nullable Integer thePartitionId, @Nullable LocalDate thePartitionDate) {
myPartitionId = thePartitionId; myPartitionIds = toListOrNull(thePartitionId);
myPartitionName = thePartitionName; 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; myPartitionDate = thePartitionDate;
myAllPartitions = false; myAllPartitions = false;
} }
@ -54,8 +75,8 @@ public class RequestPartitionId {
private RequestPartitionId() { private RequestPartitionId() {
super(); super();
myPartitionDate = null; myPartitionDate = null;
myPartitionName = null; myPartitionNames = null;
myPartitionId = null; myPartitionIds = null;
myAllPartitions = true; myAllPartitions = true;
} }
@ -69,28 +90,26 @@ public class RequestPartitionId {
} }
@Nullable @Nullable
public String getPartitionName() { public List<String> getPartitionNames() {
return myPartitionName; return myPartitionNames;
} }
@Nullable @Nonnull
public Integer getPartitionId() { public List<Integer> getPartitionIds() {
return myPartitionId; Validate.notNull(myPartitionIds, "Partition IDs have not been set");
return myPartitionIds;
} }
@Override @Override
public String toString() { public String toString() {
return "RequestPartitionId[id=" + getPartitionId() + ", name=" + getPartitionName() + "]"; ToStringBuilder b = new ToStringBuilder(this, ToStringStyle.SHORT_PREFIX_STYLE);
} if (hasPartitionIds()) {
b.append("ids", getPartitionIds());
/**
* Returns the partition ID (numeric) as a string, or the string "null"
*/
public String getPartitionIdStringOrNullString() {
if (myPartitionId == null) {
return "null";
} }
return myPartitionId.toString(); if (hasPartitionNames()) {
b.append("names", getPartitionNames());
}
return b.build();
} }
@Override @Override
@ -108,8 +127,8 @@ public class RequestPartitionId {
return new EqualsBuilder() return new EqualsBuilder()
.append(myAllPartitions, that.myAllPartitions) .append(myAllPartitions, that.myAllPartitions)
.append(myPartitionDate, that.myPartitionDate) .append(myPartitionDate, that.myPartitionDate)
.append(myPartitionId, that.myPartitionId) .append(myPartitionIds, that.myPartitionIds)
.append(myPartitionName, that.myPartitionName) .append(myPartitionNames, that.myPartitionNames)
.isEquals(); .isEquals();
} }
@ -118,11 +137,82 @@ public class RequestPartitionId {
return new HashCodeBuilder(17, 37) return new HashCodeBuilder(17, 37)
.append(myPartitionDate) .append(myPartitionDate)
.append(myAllPartitions) .append(myAllPartitions)
.append(myPartitionId) .append(myPartitionIds)
.append(myPartitionName) .append(myPartitionNames)
.toHashCode(); .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 @Nonnull
public static RequestPartitionId allPartitions() { public static RequestPartitionId allPartitions() {
return ALL_PARTITIONS; return ALL_PARTITIONS;
@ -130,17 +220,27 @@ public class RequestPartitionId {
@Nonnull @Nonnull
public static RequestPartitionId defaultPartition() { public static RequestPartitionId defaultPartition() {
return fromPartitionId(null); return fromPartitionIds(Collections.singletonList(null));
} }
@Nonnull @Nonnull
public static RequestPartitionId fromPartitionId(@Nullable Integer thePartitionId) { public static RequestPartitionId fromPartitionId(@Nullable Integer thePartitionId) {
return fromPartitionId(thePartitionId, null); return fromPartitionIds(Collections.singletonList(thePartitionId));
} }
@Nonnull @Nonnull
public static RequestPartitionId fromPartitionId(@Nullable Integer thePartitionId, @Nullable LocalDate thePartitionDate) { 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 @Nonnull
@ -153,6 +253,16 @@ public class RequestPartitionId {
return new RequestPartitionId(thePartitionName, null, thePartitionDate); 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 @Nonnull
public static RequestPartitionId fromPartitionIdAndName(@Nullable Integer thePartitionId, @Nullable String thePartitionName) { public static RequestPartitionId fromPartitionIdAndName(@Nullable Integer thePartitionId, @Nullable String thePartitionName) {
return new RequestPartitionId(thePartitionName, thePartitionId, null); return new RequestPartitionId(thePartitionName, thePartitionId, null);
@ -163,13 +273,25 @@ public class RequestPartitionId {
return new RequestPartitionId(thePartitionName, thePartitionId, thePartitionDate); 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. * 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) { public static String stringifyForKey(@Nonnull RequestPartitionId theRequestPartitionId) {
String retVal = "(null)"; String retVal = "(all partitions)";
if (theRequestPartitionId != null) { if (!theRequestPartitionId.isAllPartitions()) {
retVal = theRequestPartitionId.getPartitionIdStringOrNullString(); assert theRequestPartitionId.hasPartitionIds();
retVal = theRequestPartitionId
.getPartitionIds()
.stream()
.map(t -> defaultIfNull(t, "null").toString())
.collect(Collectors.joining(" "));
} }
return retVal; return retVal;
} }

View File

@ -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.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.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.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.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.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.cantCreateDefaultPartition=Can not create partition with name "DEFAULT"
ca.uhn.fhir.jpa.partition.PartitionLookupSvcImpl.cantRenameDefaultPartition=Can not rename default partition
ca.uhn.fhir.rest.server.interceptor.partition.RequestTenantPartitionInterceptor.unknownTenantName=Unknown tenant: {0} ca.uhn.fhir.rest.server.interceptor.partition.RequestTenantPartitionInterceptor.unknownTenantName=Unknown tenant: {0}

View File

@ -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."

View File

@ -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: 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. * 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 # 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). 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. 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 ## Examples
See [Partition Interceptor Examples](./partition_interceptor_examples.html) for various samples of how partitioning interceptors can be set up. See [Partition Interceptor Examples](./partition_interceptor_examples.html) for various samples of how partitioning interceptors can be set up.

View File

@ -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.BaseTag;
import ca.uhn.fhir.jpa.model.entity.ForcedId; import ca.uhn.fhir.jpa.model.entity.ForcedId;
import ca.uhn.fhir.jpa.model.entity.IBaseResourceEntity; 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.ResourceEncodingEnum;
import ca.uhn.fhir.jpa.model.entity.ResourceHistoryProvenanceEntity; import ca.uhn.fhir.jpa.model.entity.ResourceHistoryProvenanceEntity;
import ca.uhn.fhir.jpa.model.entity.ResourceHistoryTable; 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 // 7. Add partition information
if (myPartitionSettings.isPartitioningEnabled()) { if (myPartitionSettings.isPartitioningEnabled()) {
RequestPartitionId partitionId = theEntity.getPartitionId(); PartitionablePartitionId partitionId = theEntity.getPartitionId();
if (partitionId != null && partitionId.getPartitionId() != null) { if (partitionId != null && partitionId.getPartitionId() != null) {
PartitionEntity persistedPartition = myPartitionLookupSvc.getPartitionById(partitionId.getPartitionId()); PartitionEntity persistedPartition = myPartitionLookupSvc.getPartitionById(partitionId.getPartitionId());
retVal.setUserData(Constants.RESOURCE_PARTITION_ID, persistedPartition.toRequestPartitionId()); retVal.setUserData(Constants.RESOURCE_PARTITION_ID, persistedPartition.toRequestPartitionId());

View File

@ -1102,19 +1102,17 @@ public abstract class BaseHapiFhirResourceDao<T extends IBaseResource> extends B
// Verify that the resource is for the correct partition // Verify that the resource is for the correct partition
if (!requestPartitionId.isAllPartitions()) { if (!requestPartitionId.isAllPartitions()) {
if (requestPartitionId.getPartitionId() == null) { if (entity.getPartitionId() != null && entity.getPartitionId().getPartitionId() != null) {
if (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 if (entity.getPartitionId().getPartitionId() != null) {
if (!requestPartitionId.getPartitionId().equals(entity.getPartitionId().getPartitionId())) {
ourLog.debug("Performing a read for PartitionId={} but entity has partition: {}", requestPartitionId, entity.getPartitionId()); ourLog.debug("Performing a read for PartitionId={} but entity has partition: {}", requestPartitionId, entity.getPartitionId());
entity = null; entity = null;
} }
} else { } else {
ourLog.debug("Performing a read for PartitionId=null but entity has partition: {}", entity.getPartitionId()); // Entity Partition ID is null
entity = 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); validateResourceType(entity);
if (theCheckForForcedId) { if (theCheckForForcedId) {

View File

@ -142,10 +142,15 @@ public class HistoryBuilder {
List<Predicate> predicates = new ArrayList<>(); List<Predicate> predicates = new ArrayList<>();
if (!thePartitionId.isAllPartitions()) { if (!thePartitionId.isAllPartitions()) {
if (thePartitionId.getPartitionId() != null) { if (thePartitionId.isDefaultPartition()) {
predicates.add(theCriteriaBuilder.equal(theFrom.get("myPartitionIdValue").as(Integer.class), thePartitionId.getPartitionId()));
} else {
predicates.add(theCriteriaBuilder.isNull(theFrom.get("myPartitionIdValue").as(Integer.class))); 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()));
} }
} }

View File

@ -42,6 +42,7 @@ import ca.uhn.fhir.jpa.entity.ResourceSearchView;
import ca.uhn.fhir.jpa.interceptor.JpaPreResourceAccessDetails; import ca.uhn.fhir.jpa.interceptor.JpaPreResourceAccessDetails;
import ca.uhn.fhir.jpa.model.config.PartitionSettings; import ca.uhn.fhir.jpa.model.config.PartitionSettings;
import ca.uhn.fhir.jpa.model.entity.BaseResourceIndexedSearchParam; 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.ResourceIndexedCompositeStringUnique;
import ca.uhn.fhir.jpa.model.entity.ResourceLink; import ca.uhn.fhir.jpa.model.entity.ResourceLink;
import ca.uhn.fhir.jpa.model.entity.ResourceTable; 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); From<?, ResourceIndexedCompositeStringUnique> join = myQueryStack.createJoin(SearchBuilderJoinEnum.COMPOSITE_UNIQUE, null);
if (!theRequestPartitionId.isAllPartitions()) { if (!theRequestPartitionId.isAllPartitions()) {
Integer partitionId = theRequestPartitionId.getPartitionId(); Integer partitionId = theRequestPartitionId.getFirstPartitionIdOrNull();
Predicate predicate = myCriteriaBuilder.equal(join.get("myPartitionIdValue").as(Integer.class), partitionId); Predicate predicate = myCriteriaBuilder.equal(join.get("myPartitionIdValue").as(Integer.class), partitionId);
myQueryStack.addPredicate(predicate); myQueryStack.addPredicate(predicate);
} }

View File

@ -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") @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); 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") @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") Integer thePartitionId, @Param("resource_type") String theResourceType, @Param("forced_id") String theForcedId); 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") @Query("SELECT f FROM ForcedId f WHERE f.myResourcePid = :resource_pid")
Optional<ForcedId> findByResourcePid(@Param("resource_pid") Long theResourcePid); 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 * 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. * 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 )") @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[]> findByTypeAndForcedIdInPartition(@Param("resource_type") String theResourceType, @Param("forced_id") Collection<String> theForcedId, @Param("partition_id") Integer thePartitionId); 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 * 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 " + " f.myResourceType, f.myResourcePid, f.myForcedId, t.myDeleted " +
"FROM ForcedId f " + "FROM ForcedId f " +
"JOIN ResourceTable t ON t.myId = f.myResourcePid " + "JOIN ResourceTable t ON t.myId = f.myResourcePid " +
"WHERE f.myResourceType = :resource_type AND f.myForcedId IN ( :forced_id ) AND f.myPartitionIdValue = :partition_id") "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") Integer thePartitionId); 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); 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);
} }

View File

@ -12,7 +12,6 @@ import java.util.Collection;
import java.util.Date; import java.util.Date;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.Optional;
/* /*
* #%L * #%L
@ -65,12 +64,31 @@ public interface IResourceTableDao extends JpaRepository<ResourceTable, Long> {
@Query("DELETE FROM ResourceTable t WHERE t.myId = :pid") @Query("DELETE FROM ResourceTable t WHERE t.myId = :pid")
void deleteByPid(@Param("pid") Long theId); 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)") @Query("SELECT t.myResourceType, t.myId, t.myDeleted FROM ResourceTable t WHERE t.myId IN (:pid)")
Collection<Object[]> findLookupFieldsByResourcePid(@Param("pid") List<Long> thePids); 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") @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); Collection<Object[]> findLookupFieldsByResourcePidInPartitionNull(@Param("pid") List<Long> thePids);
} }

View File

@ -40,11 +40,10 @@ public class EmpiLinkDeleteSvc {
/** /**
* Delete all EmpiLink records with any reference to this resource. (Used by Expunge.) * Delete all EmpiLink records with any reference to this resource. (Used by Expunge.)
* @param theResource
* @return the number of records deleted * @return the number of records deleted
*/ */
public int deleteWithAnyReferenceTo(IBaseResource theResource) { public int deleteWithAnyReferenceTo(IBaseResource theResource) {
Long pid = myIdHelperService.getPidOrThrowException(theResource.getIdElement(), null); Long pid = myIdHelperService.getPidOrThrowException(theResource.getIdElement());
int removed = myEmpiLinkDao.deleteWithAnyReferenceToPid(pid); int removed = myEmpiLinkDao.deleteWithAnyReferenceToPid(pid);
if (removed > 0) { if (removed > 0) {
ourLog.info("Removed {} EMPI links with references to {}", removed, theResource.getIdElement().toVersionless()); ourLog.info("Removed {} EMPI links with references to {}", removed, theResource.getIdElement().toVersionless());
@ -53,7 +52,7 @@ public class EmpiLinkDeleteSvc {
} }
public int deleteNonRedirectWithWithAnyReferenceTo(IBaseResource theResource) { 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); int removed = myEmpiLinkDao.deleteWithAnyReferenceToPidAndMatchResultNot(pid, EmpiMatchResultEnum.REDIRECT);
if (removed > 0) { if (removed > 0) {
ourLog.info("Removed {} non-redirect EMPI links with references to {}", removed, theResource.getIdElement().toVersionless()); ourLog.info("Removed {} non-redirect EMPI links with references to {}", removed, theResource.getIdElement().toVersionless());

View File

@ -68,7 +68,7 @@ public class DaoResourceLinkResolver implements IResourceLinkResolver {
IResourceLookup resolvedResource; IResourceLookup resolvedResource;
String idPart = theSourceResourceId.getIdPart(); String idPart = theSourceResourceId.getIdPart();
try { try {
resolvedResource = myIdHelperService.resolveResourceIdentity(theRequestPartitionId, theResourceType, idPart, theRequest); resolvedResource = myIdHelperService.resolveResourceIdentity(theRequestPartitionId, theResourceType, idPart);
ourLog.trace("Translated {}/{} to resource PID {}", theType, idPart, resolvedResource); ourLog.trace("Translated {}/{} to resource PID {}", theType, idPart, resolvedResource);
} catch (ResourceNotFoundException e) { } catch (ResourceNotFoundException e) {

View File

@ -21,7 +21,6 @@ package ca.uhn.fhir.jpa.dao.index;
*/ */
import ca.uhn.fhir.context.FhirContext; import ca.uhn.fhir.context.FhirContext;
import ca.uhn.fhir.interceptor.api.IInterceptorBroadcaster;
import ca.uhn.fhir.interceptor.model.RequestPartitionId; import ca.uhn.fhir.interceptor.model.RequestPartitionId;
import ca.uhn.fhir.jpa.api.config.DaoConfig; import ca.uhn.fhir.jpa.api.config.DaoConfig;
import ca.uhn.fhir.jpa.dao.data.IForcedIdDao; 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.MemoryCacheService;
import ca.uhn.fhir.jpa.util.QueryChunker; import ca.uhn.fhir.jpa.util.QueryChunker;
import ca.uhn.fhir.model.primitive.IdDt; 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.api.server.storage.ResourcePersistentId;
import ca.uhn.fhir.rest.server.exceptions.PreconditionFailedException; import ca.uhn.fhir.rest.server.exceptions.PreconditionFailedException;
import ca.uhn.fhir.rest.server.exceptions.ResourceNotFoundException; 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.IAnyResource;
import org.hl7.fhir.instance.model.api.IBaseResource; import org.hl7.fhir.instance.model.api.IBaseResource;
import org.hl7.fhir.instance.model.api.IIdType; 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.beans.factory.annotation.Autowired;
import org.springframework.dao.IncorrectResultSizeDataAccessException;
import org.springframework.stereotype.Service; import org.springframework.stereotype.Service;
import javax.annotation.Nonnull; import javax.annotation.Nonnull;
@ -62,7 +57,6 @@ import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.Optional; import java.util.Optional;
import java.util.Set; import java.util.Set;
import java.util.function.Function;
import java.util.stream.Collectors; import java.util.stream.Collectors;
import static org.apache.commons.lang3.StringUtils.isBlank; import static org.apache.commons.lang3.StringUtils.isBlank;
@ -87,7 +81,6 @@ import static org.apache.commons.lang3.StringUtils.isNotBlank;
*/ */
@Service @Service
public class IdHelperService { public class IdHelperService {
private static final Logger ourLog = LoggerFactory.getLogger(IdHelperService.class);
private static final String RESOURCE_PID = "RESOURCE_PID"; private static final String RESOURCE_PID = "RESOURCE_PID";
@Autowired @Autowired
@ -97,8 +90,6 @@ public class IdHelperService {
@Autowired @Autowired
private DaoConfig myDaoConfig; private DaoConfig myDaoConfig;
@Autowired @Autowired
private IInterceptorBroadcaster myInterceptorBroadcaster;
@Autowired
private FhirContext myFhirCtx; private FhirContext myFhirCtx;
@Autowired @Autowired
private MemoryCacheService myMemoryCacheService; private MemoryCacheService myMemoryCacheService;
@ -114,14 +105,25 @@ public class IdHelperService {
* @throws ResourceNotFoundException If the ID can not be found * @throws ResourceNotFoundException If the ID can not be found
*/ */
@Nonnull @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 // We only pass 1 input in so only 0..1 will come back
IdDt id = new IdDt(theResourceType, theResourceId); IdDt id = new IdDt(theResourceType, theResourceId);
Collection<IResourceLookup> matches = translateForcedIdToPids(theRequestPartitionId, theRequestDetails, Collections.singletonList(id)); Collection<IResourceLookup> matches = translateForcedIdToPids(theRequestPartitionId, Collections.singletonList(id));
assert matches.size() <= 1;
if (matches.isEmpty()) { if (matches.isEmpty()) {
throw new ResourceNotFoundException(id); 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(); return matches.iterator().next();
} }
@ -137,10 +139,10 @@ public class IdHelperService {
Long retVal; Long retVal;
if (myDaoConfig.getResourceClientIdStrategy() == DaoConfig.ClientIdStrategyEnum.ANY || !isValidPid(theId)) { if (myDaoConfig.getResourceClientIdStrategy() == DaoConfig.ClientIdStrategyEnum.ANY || !isValidPid(theId)) {
if (myDaoConfig.isDeleteEnabled()) { if (myDaoConfig.isDeleteEnabled()) {
retVal = resolveResourceIdentity(theRequestPartitionId, theResourceType, theId); retVal = resolveResourceIdentity(theRequestPartitionId, theResourceType, theId).getResourceId();
} else { } else {
String key = RequestPartitionId.stringifyForKey(theRequestPartitionId) + "/" + theResourceType + "/" + theId; 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 { } else {
@ -187,9 +189,10 @@ public class IdHelperService {
} else { } else {
String partitionIdStringForKey = RequestPartitionId.stringifyForKey(theRequestPartitionId);
for (Iterator<String> idIterator = nextIds.iterator(); idIterator.hasNext(); ) { for (Iterator<String> idIterator = nextIds.iterator(); idIterator.hasNext(); ) {
String nextId = idIterator.next(); 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); Long nextCachedPid = myMemoryCacheService.getIfPresent(MemoryCacheService.CacheEnum.PERSISTENT_ID, key);
if (nextCachedPid != null) { if (nextCachedPid != null) {
idIterator.remove(); idIterator.remove();
@ -203,10 +206,12 @@ public class IdHelperService {
if (theRequestPartitionId.isAllPartitions()) { if (theRequestPartitionId.isAllPartitions()) {
views = myForcedIdDao.findByTypeAndForcedId(nextResourceType, nextIds); views = myForcedIdDao.findByTypeAndForcedId(nextResourceType, nextIds);
} else { } else {
if (theRequestPartitionId.getPartitionId() != null) { if (theRequestPartitionId.isDefaultPartition()) {
views = myForcedIdDao.findByTypeAndForcedIdInPartition(nextResourceType, nextIds, theRequestPartitionId.getPartitionId());
} else {
views = myForcedIdDao.findByTypeAndForcedIdInPartitionNull(nextResourceType, nextIds); 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) { for (Object[] nextView : views) {
@ -214,7 +219,7 @@ public class IdHelperService {
Long pid = (Long) nextView[1]; Long pid = (Long) nextView[1];
retVal.add(new ResourcePersistentId(pid)); 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); myMemoryCacheService.put(MemoryCacheService.CacheEnum.PERSISTENT_ID, key, pid);
} }
} }
@ -261,35 +266,7 @@ public class IdHelperService {
return typeToIds; return typeToIds;
} }
private Long resolveResourceIdentity(@Nonnull RequestPartitionId theRequestPartitionId, @Nonnull String theResourceType, @Nonnull String theId) { private Collection<IResourceLookup> translateForcedIdToPids(@Nonnull RequestPartitionId theRequestPartitionId, Collection<IIdType> 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) {
theId.forEach(id -> Validate.isTrue(id.hasIdPart())); theId.forEach(id -> Validate.isTrue(id.hasIdPart()));
if (theId.isEmpty()) { if (theId.isEmpty()) {
@ -333,10 +310,12 @@ public class IdHelperService {
if (theRequestPartitionId.isAllPartitions()) { if (theRequestPartitionId.isAllPartitions()) {
views = myForcedIdDao.findAndResolveByForcedIdWithNoType(nextResourceType, nextIds); views = myForcedIdDao.findAndResolveByForcedIdWithNoType(nextResourceType, nextIds);
} else { } else {
if (theRequestPartitionId.getPartitionId() != null) { if (theRequestPartitionId.isDefaultPartition()) {
views = myForcedIdDao.findAndResolveByForcedIdWithNoTypeInPartition(nextResourceType, nextIds, theRequestPartitionId.getPartitionId());
} else {
views = myForcedIdDao.findAndResolveByForcedIdWithNoTypeInPartitionNull(nextResourceType, nextIds); 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()) { if (theRequestPartitionId.isAllPartitions()) {
lookup = myResourceTableDao.findLookupFieldsByResourcePid(thePidsToResolve); lookup = myResourceTableDao.findLookupFieldsByResourcePid(thePidsToResolve);
} else { } else {
if (theRequestPartitionId.getPartitionId() != null) { if (theRequestPartitionId.isDefaultPartition()) {
lookup = myResourceTableDao.findLookupFieldsByResourcePidInPartition(thePidsToResolve, theRequestPartitionId.getPartitionId());
} else {
lookup = myResourceTableDao.findLookupFieldsByResourcePidInPartitionNull(thePidsToResolve); lookup = myResourceTableDao.findLookupFieldsByResourcePidInPartitionNull(thePidsToResolve);
} else if (theRequestPartitionId.hasDefaultPartitionId()) {
lookup = myResourceTableDao.findLookupFieldsByResourcePidInPartitionIdsOrNullPartition(thePidsToResolve, theRequestPartitionId.getPartitionIdsWithoutDefault());
} else {
lookup = myResourceTableDao.findLookupFieldsByResourcePidInPartitionIds(thePidsToResolve, theRequestPartitionId.getPartitionIds());
} }
} }
lookup lookup
@ -448,23 +429,14 @@ public class IdHelperService {
@Nonnull @Nonnull
public Long getPidOrThrowException(IIdType theId) { 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<IIdType> ids = Collections.singletonList(theId);
List<ResourcePersistentId> resourcePersistentIds = this.resolveResourcePersistentIdsWithCache(RequestPartitionId.allPartitions(), ids); List<ResourcePersistentId> resourcePersistentIds = this.resolveResourcePersistentIdsWithCache(RequestPartitionId.allPartitions(), ids);
return resourcePersistentIds.get(0).getIdAsLong(); return resourcePersistentIds.get(0).getIdAsLong();
} }
public Map<Long, IIdType> getPidToIdMap(Collection<IIdType> theIds, RequestDetails theRequestDetails) { @Nonnull
return theIds.stream().collect(Collectors.toMap(this::getPidOrThrowException, Function.identity())); public Long getPidOrThrowException(IAnyResource theResource) {
return (Long) theResource.getUserData(RESOURCE_PID);
} }
public IIdType resourceIdFromPidOrThrowException(Long thePid) { public IIdType resourceIdFromPidOrThrowException(Long thePid) {

View File

@ -25,6 +25,7 @@ import ca.uhn.fhir.context.RuntimeResourceDefinition;
import ca.uhn.fhir.context.RuntimeSearchParam; import ca.uhn.fhir.context.RuntimeSearchParam;
import ca.uhn.fhir.interceptor.model.RequestPartitionId; import ca.uhn.fhir.interceptor.model.RequestPartitionId;
import ca.uhn.fhir.jpa.api.config.DaoConfig; 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.rest.api.server.storage.TransactionDetails;
import ca.uhn.fhir.jpa.dao.BaseHapiFhirDao; import ca.uhn.fhir.jpa.dao.BaseHapiFhirDao;
import ca.uhn.fhir.jpa.dao.MatchResourceUrlService; import ca.uhn.fhir.jpa.dao.MatchResourceUrlService;
@ -99,7 +100,7 @@ public class SearchParamWithInlineReferencesExtractor {
RequestPartitionId partitionId; RequestPartitionId partitionId;
if (myPartitionSettings.isPartitioningEnabled()) { if (myPartitionSettings.isPartitioningEnabled()) {
partitionId = theEntity.getPartitionId(); partitionId = PartitionablePartitionId.toRequestPartitionId(theEntity.getPartitionId());
} else { } else {
partitionId = RequestPartitionId.allPartitions(); partitionId = RequestPartitionId.allPartitions();
} }

View File

@ -92,10 +92,10 @@ abstract class BasePredicateBuilder {
void addPredicateParamMissingForNonReference(String theResourceName, String theParamName, boolean theMissing, From<?, ? extends BaseResourceIndexedSearchParam> theJoin, RequestPartitionId theRequestPartitionId) { void addPredicateParamMissingForNonReference(String theResourceName, String theParamName, boolean theMissing, From<?, ? extends BaseResourceIndexedSearchParam> theJoin, RequestPartitionId theRequestPartitionId) {
if (!theRequestPartitionId.isAllPartitions()) { if (!theRequestPartitionId.isAllPartitions()) {
if (theRequestPartitionId.getPartitionId() != null) { if (theRequestPartitionId.isDefaultPartition()) {
myQueryStack.addPredicate(myCriteriaBuilder.equal(theJoin.get("myPartitionIdValue"), theRequestPartitionId.getPartitionId()));
} else {
myQueryStack.addPredicate(myCriteriaBuilder.isNull(theJoin.get("myPartitionIdValue"))); myQueryStack.addPredicate(myCriteriaBuilder.isNull(theJoin.get("myPartitionIdValue")));
} else {
myQueryStack.addPredicate(theJoin.get("myPartitionIdValue").in(theRequestPartitionId.getPartitionIds()));
} }
} }
myQueryStack.addPredicateWithImplicitTypeSelection(myCriteriaBuilder.equal(theJoin.get("myResourceType"), theResourceName)); 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) { void addPartitionIdPredicate(RequestPartitionId theRequestPartitionId, From<?, ? extends BasePartitionable> theJoin, List<Predicate> theCodePredicates) {
if (!theRequestPartitionId.isAllPartitions()) { if (!theRequestPartitionId.isAllPartitions()) {
Integer partitionId = theRequestPartitionId.getPartitionId();
Predicate partitionPredicate; Predicate partitionPredicate;
if (partitionId != null) { if (theRequestPartitionId.isDefaultPartition()) {
partitionPredicate = myCriteriaBuilder.equal(theJoin.get("myPartitionIdValue").as(Integer.class), partitionId);
} else {
partitionPredicate = myCriteriaBuilder.isNull(theJoin.get("myPartitionIdValue").as(Integer.class)); partitionPredicate = myCriteriaBuilder.isNull(theJoin.get("myPartitionIdValue").as(Integer.class));
} else {
partitionPredicate = theJoin.get("myPartitionIdValue").as(Integer.class).in(theRequestPartitionId.getPartitionIds());
} }
myQueryStack.addPredicate(partitionPredicate); myQueryStack.addPredicate(partitionPredicate);
} }

View File

@ -151,7 +151,7 @@ class PredicateBuilderToken extends BasePredicateBuilder implements IPredicateBu
theBuilder, theBuilder,
theFrom, theFrom,
null, null,
theRequestPartitionId); theRequestPartitionId);
} }
private Collection<Predicate> createPredicateToken(Collection<IQueryParameterType> theParameters, private Collection<Predicate> createPredicateToken(Collection<IQueryParameterType> theParameters,

View File

@ -63,8 +63,8 @@ class QueryRootEntryResourceTable extends QueryRootEntry {
} }
addPredicate(myCriteriaBuilder.isNull(getRoot().get("myDeleted"))); addPredicate(myCriteriaBuilder.isNull(getRoot().get("myDeleted")));
if (!myRequestPartitionId.isAllPartitions()) { if (!myRequestPartitionId.isAllPartitions()) {
if (myRequestPartitionId.getPartitionId() != null) { if (!myRequestPartitionId.isDefaultPartition()) {
addPredicate(myCriteriaBuilder.equal(getRoot().get("myPartitionIdValue").as(Integer.class), myRequestPartitionId.getPartitionId())); addPredicate(getRoot().get("myPartitionIdValue").as(Integer.class).in(myRequestPartitionId.getPartitionIds()));
} else { } else {
addPredicate(myCriteriaBuilder.isNull(getRoot().get("myPartitionIdValue").as(Integer.class))); addPredicate(myCriteriaBuilder.isNull(getRoot().get("myPartitionIdValue").as(Integer.class)));
} }

View File

@ -24,6 +24,7 @@ import ca.uhn.fhir.context.FhirVersionEnum;
import ca.uhn.fhir.interceptor.model.RequestPartitionId; import ca.uhn.fhir.interceptor.model.RequestPartitionId;
import ca.uhn.fhir.jpa.model.entity.ForcedId; import ca.uhn.fhir.jpa.model.entity.ForcedId;
import ca.uhn.fhir.jpa.model.entity.IBaseResourceEntity; 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.ResourceEncodingEnum;
import ca.uhn.fhir.jpa.model.entity.ResourceHistoryProvenanceEntity; import ca.uhn.fhir.jpa.model.entity.ResourceHistoryProvenanceEntity;
import ca.uhn.fhir.model.primitive.IdDt; 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.Immutable;
import org.hibernate.annotations.Subselect; import org.hibernate.annotations.Subselect;
import javax.annotation.Nullable;
import javax.persistence.Column; import javax.persistence.Column;
import javax.persistence.Entity; import javax.persistence.Entity;
import javax.persistence.EnumType; import javax.persistence.EnumType;
@ -199,8 +201,13 @@ public class ResourceSearchView implements IBaseResourceEntity, Serializable {
} }
@Override @Override
public RequestPartitionId getPartitionId() { @Nullable
return RequestPartitionId.fromPartitionId(myPartitionId); public PartitionablePartitionId getPartitionId() {
if (myPartitionId != null) {
return new PartitionablePartitionId(myPartitionId, null);
} else {
return null;
}
} }
public byte[] getResource() { public byte[] getResource() {

View File

@ -23,6 +23,8 @@ package ca.uhn.fhir.jpa.partition;
import ca.uhn.fhir.jpa.entity.PartitionEntity; import ca.uhn.fhir.jpa.entity.PartitionEntity;
import ca.uhn.fhir.rest.server.exceptions.ResourceNotFoundException; import ca.uhn.fhir.rest.server.exceptions.ResourceNotFoundException;
import javax.annotation.Nullable;
public interface IPartitionLookupSvc { public interface IPartitionLookupSvc {
/** /**
@ -33,6 +35,7 @@ public interface IPartitionLookupSvc {
/** /**
* @throws ResourceNotFoundException If the name is not known * @throws ResourceNotFoundException If the name is not known
*/ */
@Nullable
PartitionEntity getPartitionByName(String theName) throws ResourceNotFoundException; PartitionEntity getPartitionByName(String theName) throws ResourceNotFoundException;
/** /**

View File

@ -23,6 +23,7 @@ package ca.uhn.fhir.jpa.partition;
import ca.uhn.fhir.context.FhirContext; import ca.uhn.fhir.context.FhirContext;
import ca.uhn.fhir.jpa.dao.data.IPartitionDao; import ca.uhn.fhir.jpa.dao.data.IPartitionDao;
import ca.uhn.fhir.jpa.entity.PartitionEntity; 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.InvalidRequestException;
import ca.uhn.fhir.rest.server.exceptions.ResourceNotFoundException; import ca.uhn.fhir.rest.server.exceptions.ResourceNotFoundException;
import com.github.benmanes.caffeine.cache.CacheLoader; 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 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 Pattern PARTITION_NAME_VALID_PATTERN = Pattern.compile("[a-zA-Z0-9_-]+");
private static final Logger ourLog = LoggerFactory.getLogger(PartitionLookupSvcImpl.class); private static final Logger ourLog = LoggerFactory.getLogger(PartitionLookupSvcImpl.class);
@ -76,23 +74,14 @@ public class PartitionLookupSvcImpl implements IPartitionLookupSvc {
.expireAfterWrite(1, TimeUnit.MINUTES) .expireAfterWrite(1, TimeUnit.MINUTES)
.build(new IdToPartitionCacheLoader()); .build(new IdToPartitionCacheLoader());
myTxTemplate = new TransactionTemplate(myTxManager); 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 @Override
public PartitionEntity getPartitionByName(String theName) { public PartitionEntity getPartitionByName(String theName) {
Validate.notBlank(theName, "The name must not be null or blank"); Validate.notBlank(theName, "The name must not be null or blank");
if (JpaConstants.DEFAULT_PARTITION_NAME.equals(theName)) {
return null;
}
return myNameToPartitionCache.get(theName); return myNameToPartitionCache.get(theName);
} }
@ -114,11 +103,6 @@ public class PartitionLookupSvcImpl implements IPartitionLookupSvc {
validateHaveValidPartitionIdAndName(thePartition); validateHaveValidPartitionIdAndName(thePartition);
validatePartitionNameDoesntAlreadyExist(thePartition.getName()); 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()); ourLog.info("Creating new partition with ID {} and Name {}", thePartition.getId(), thePartition.getName());
myPartitionDao.save(thePartition); myPartitionDao.save(thePartition);
@ -141,13 +125,6 @@ public class PartitionLookupSvcImpl implements IPartitionLookupSvc {
validatePartitionNameDoesntAlreadyExist(thePartition.getName()); 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.setName(thePartition.getName());
existingPartition.setDescription(thePartition.getDescription()); existingPartition.setDescription(thePartition.getDescription());
myPartitionDao.save(existingPartition); myPartitionDao.save(existingPartition);
@ -160,11 +137,6 @@ public class PartitionLookupSvcImpl implements IPartitionLookupSvc {
public void deletePartition(Integer thePartitionId) { public void deletePartition(Integer thePartitionId) {
validatePartitionIdSupplied(myFhirCtx, 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); Optional<PartitionEntity> partition = myPartitionDao.findById(thePartitionId);
if (!partition.isPresent()) { if (!partition.isPresent()) {
String msg = myFhirCtx.getLocalizer().getMessageSanitized(PartitionLookupSvcImpl.class, "unknownPartitionId", thePartitionId); String msg = myFhirCtx.getLocalizer().getMessageSanitized(PartitionLookupSvcImpl.class, "unknownPartitionId", thePartitionId);
@ -189,6 +161,11 @@ public class PartitionLookupSvcImpl implements IPartitionLookupSvc {
throw new InvalidRequestException(msg); 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()) { if (!PARTITION_NAME_VALID_PATTERN.matcher(thePartition.getName()).matches()) {
String msg = myFhirCtx.getLocalizer().getMessageSanitized(PartitionLookupSvcImpl.class, "invalidName", thePartition.getName()); String msg = myFhirCtx.getLocalizer().getMessageSanitized(PartitionLookupSvcImpl.class, "invalidName", thePartition.getName());
throw new InvalidRequestException(msg); throw new InvalidRequestException(msg);

View File

@ -27,9 +27,9 @@ import ca.uhn.fhir.interceptor.api.Pointcut;
import ca.uhn.fhir.interceptor.model.RequestPartitionId; import ca.uhn.fhir.interceptor.model.RequestPartitionId;
import ca.uhn.fhir.jpa.entity.PartitionEntity; import ca.uhn.fhir.jpa.entity.PartitionEntity;
import ca.uhn.fhir.jpa.model.config.PartitionSettings; 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.api.server.RequestDetails;
import ca.uhn.fhir.rest.server.exceptions.InternalErrorException; 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.ResourceNotFoundException;
import ca.uhn.fhir.rest.server.exceptions.UnprocessableEntityException; import ca.uhn.fhir.rest.server.exceptions.UnprocessableEntityException;
import ca.uhn.fhir.rest.server.servlet.ServletRequestDetails; 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.Nonnull;
import javax.annotation.Nullable; import javax.annotation.Nullable;
import java.util.ArrayList;
import java.util.HashSet; 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.doCallHooks;
import static ca.uhn.fhir.jpa.util.JpaInterceptorBroadcaster.doCallHooksAndReturnObject; import static ca.uhn.fhir.jpa.util.JpaInterceptorBroadcaster.doCallHooksAndReturnObject;
@ -102,9 +105,9 @@ public class RequestPartitionHelperSvc implements IRequestPartitionHelperSvc {
requestPartitionId = null; 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(); return RequestPartitionId.allPartitions();
@ -132,48 +135,29 @@ public class RequestPartitionHelperSvc implements IRequestPartitionHelperSvc {
requestPartitionId = (RequestPartitionId) doCallHooksAndReturnObject(myInterceptorBroadcaster, theRequest, Pointcut.STORAGE_PARTITION_IDENTIFY_CREATE, params); requestPartitionId = (RequestPartitionId) doCallHooksAndReturnObject(myInterceptorBroadcaster, theRequest, Pointcut.STORAGE_PARTITION_IDENTIFY_CREATE, params);
String resourceName = myFhirContext.getResourceType(theResource); 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(); 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 @Nonnull
private RequestPartitionId normalizeAndNotifyHooks(@Nonnull RequestPartitionId theRequestPartitionId, RequestDetails theRequest) { private RequestPartitionId validateNormalizeAndNotifyHooksForRead(@Nonnull RequestPartitionId theRequestPartitionId, RequestDetails theRequest) {
RequestPartitionId retVal = theRequestPartitionId; RequestPartitionId retVal = theRequestPartitionId;
if (retVal.getPartitionName() != null) { if (retVal.getPartitionNames() != null) {
retVal = validateAndNormalizePartitionNames(retVal);
PartitionEntity partition; } else if (retVal.hasPartitionIds()) {
try { retVal = validateAndNormalizePartitionIds(retVal);
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());
} }
// Note: It's still possible that the partition only has a date but no name/id // 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) { private RequestPartitionId validateAndNormalizePartitionIds(RequestPartitionId theRequestPartitionId) {
if (theRequestPartitionId == null) { List<String> names = null;
throw new InternalErrorException("No interceptor provided a value for pointcut: " + thePointcut); 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)) { if (myPartitioningBlacklist.contains(theResourceName)) {
String msg = myFhirContext.getLocalizer().getMessageSanitized(RequestPartitionHelperSvc.class, "blacklistedResourceTypeForPartitioning", theResourceName); String msg = myFhirContext.getLocalizer().getMessageSanitized(RequestPartitionHelperSvc.class, "blacklistedResourceTypeForPartitioning", theResourceName);
throw new UnprocessableEntityException(msg); 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);
} }
} }
} }

View File

@ -24,6 +24,7 @@ import ca.uhn.fhir.interceptor.model.RequestPartitionId;
import ca.uhn.fhir.jpa.search.builder.sql.SearchQueryBuilder; import ca.uhn.fhir.jpa.search.builder.sql.SearchQueryBuilder;
import com.healthmarketscience.sqlbuilder.BinaryCondition; import com.healthmarketscience.sqlbuilder.BinaryCondition;
import com.healthmarketscience.sqlbuilder.Condition; import com.healthmarketscience.sqlbuilder.Condition;
import com.healthmarketscience.sqlbuilder.InCondition;
import com.healthmarketscience.sqlbuilder.NotCondition; import com.healthmarketscience.sqlbuilder.NotCondition;
import com.healthmarketscience.sqlbuilder.UnaryCondition; import com.healthmarketscience.sqlbuilder.UnaryCondition;
import com.healthmarketscience.sqlbuilder.dbspec.basic.DbColumn; 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.toAndPredicate;
import static ca.uhn.fhir.jpa.search.builder.QueryStack.toEqualToOrInPredicate; 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 { public abstract class BaseJoiningPredicateBuilder extends BasePredicateBuilder {
@ -70,12 +72,16 @@ public abstract class BaseJoiningPredicateBuilder extends BasePredicateBuilder {
public Condition createPartitionIdPredicate(RequestPartitionId theRequestPartitionId) { public Condition createPartitionIdPredicate(RequestPartitionId theRequestPartitionId) {
if (theRequestPartitionId != null && !theRequestPartitionId.isAllPartitions()) { if (theRequestPartitionId != null && !theRequestPartitionId.isAllPartitions()) {
Condition condition; Condition condition;
Integer partitionId = theRequestPartitionId.getPartitionId(); if (theRequestPartitionId.isDefaultPartition()) {
if (partitionId != null) {
Object placeholder = generatePlaceholder(partitionId);
condition = BinaryCondition.equalTo(getPartitionIdColumn(), placeholder);
} else {
condition = UnaryCondition.isNull(getPartitionIdColumn()); 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; return condition;
} else { } else {

View File

@ -22,6 +22,7 @@ import ca.uhn.fhir.jpa.config.TestR4Config;
import ca.uhn.fhir.jpa.dao.BaseJpaTest; import ca.uhn.fhir.jpa.dao.BaseJpaTest;
import ca.uhn.fhir.jpa.dao.IFulltextSearchSvc; import ca.uhn.fhir.jpa.dao.IFulltextSearchSvc;
import ca.uhn.fhir.jpa.dao.data.IForcedIdDao; 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.IResourceHistoryTableDao;
import ca.uhn.fhir.jpa.dao.data.IResourceHistoryTagDao; import ca.uhn.fhir.jpa.dao.data.IResourceHistoryTagDao;
import ca.uhn.fhir.jpa.dao.data.IResourceIndexedCompositeStringUniqueDao; import ca.uhn.fhir.jpa.dao.data.IResourceIndexedCompositeStringUniqueDao;
@ -185,6 +186,8 @@ public abstract class BaseJpaR4Test extends BaseJpaTest implements ITestDataBuil
@Autowired @Autowired
protected IPartitionLookupSvc myPartitionConfigSvc; protected IPartitionLookupSvc myPartitionConfigSvc;
@Autowired @Autowired
protected IPartitionDao myPartitionDao;
@Autowired
protected ITermReadSvc myHapiTerminologySvc; protected ITermReadSvc myHapiTerminologySvc;
@Autowired @Autowired
protected CachingValidationSupport myCachingValidationSupport; protected CachingValidationSupport myCachingValidationSupport;

View File

@ -10,6 +10,7 @@ import ca.uhn.fhir.jpa.api.config.DaoConfig;
import ca.uhn.fhir.jpa.entity.PartitionEntity; import ca.uhn.fhir.jpa.entity.PartitionEntity;
import ca.uhn.fhir.jpa.model.config.PartitionSettings; import ca.uhn.fhir.jpa.model.config.PartitionSettings;
import ca.uhn.fhir.jpa.model.entity.ForcedId; 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.ResourceHistoryTable;
import ca.uhn.fhir.jpa.model.entity.ResourceHistoryTag; import ca.uhn.fhir.jpa.model.entity.ResourceHistoryTag;
import ca.uhn.fhir.jpa.model.entity.ResourceIndexedCompositeStringUnique; 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.ResourceTable;
import ca.uhn.fhir.jpa.model.entity.ResourceTag; import ca.uhn.fhir.jpa.model.entity.ResourceTag;
import ca.uhn.fhir.jpa.model.entity.SearchParamPresent; 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.partition.IPartitionLookupSvc;
import ca.uhn.fhir.jpa.searchparam.SearchParameterMap; import ca.uhn.fhir.jpa.searchparam.SearchParameterMap;
import ca.uhn.fhir.jpa.util.SqlQuery; 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.StringParam;
import ca.uhn.fhir.rest.param.TokenParam; import ca.uhn.fhir.rest.param.TokenParam;
import ca.uhn.fhir.rest.param.TokenParamModifier; 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.InvalidRequestException;
import ca.uhn.fhir.rest.server.exceptions.PreconditionFailedException; import ca.uhn.fhir.rest.server.exceptions.PreconditionFailedException;
import ca.uhn.fhir.rest.server.exceptions.ResourceNotFoundException; 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 ca.uhn.fhir.jpa.util.TestUtil.sleepAtLeast;
import static org.apache.commons.lang3.StringUtils.countMatches; import static org.apache.commons.lang3.StringUtils.countMatches;
import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.Matchers.contains;
import static org.hamcrest.Matchers.containsString; import static org.hamcrest.Matchers.containsString;
import static org.hamcrest.Matchers.matchesPattern; import static org.hamcrest.Matchers.matchesPattern;
import static org.hamcrest.Matchers.startsWith; import static org.hamcrest.Matchers.startsWith;
@ -90,8 +94,11 @@ import static org.mockito.Mockito.when;
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
public class PartitioningSqlR4Test extends BaseJpaR4SystemTest { 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 static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(PartitioningSqlR4Test.class);
private MyReadWriteInterceptor myPartitionInterceptor; private MyReadWriteInterceptor myPartitionInterceptor;
private LocalDate myPartitionDate; private LocalDate myPartitionDate;
private LocalDate myPartitionDate2; private LocalDate myPartitionDate2;
@ -142,11 +149,17 @@ public class PartitioningSqlR4Test extends BaseJpaR4SystemTest {
myPartitionInterceptor = new MyReadWriteInterceptor(); myPartitionInterceptor = new MyReadWriteInterceptor();
myInterceptorRegistry.registerInterceptor(myPartitionInterceptor); myInterceptorRegistry.registerInterceptor(myPartitionInterceptor);
myPartitionConfigSvc.createPartition(new PartitionEntity().setId(1).setName("PART-1")); myPartitionConfigSvc.createPartition(new PartitionEntity().setId(1).setName(PARTITION_1));
myPartitionConfigSvc.createPartition(new PartitionEntity().setId(2).setName("PART-2")); myPartitionConfigSvc.createPartition(new PartitionEntity().setId(2).setName(PARTITION_2));
myPartitionConfigSvc.createPartition(new PartitionEntity().setId(3).setName("PART-3")); myPartitionConfigSvc.createPartition(new PartitionEntity().setId(3).setName(PARTITION_3));
myPartitionConfigSvc.createPartition(new PartitionEntity().setId(4).setName(PARTITION_4));
myDaoConfig.setIndexMissingFields(DaoConfig.IndexEnabledEnum.ENABLED); 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 @Test
@ -162,7 +175,7 @@ public class PartitioningSqlR4Test extends BaseJpaR4SystemTest {
runInTransaction(() -> { runInTransaction(() -> {
ResourceTable resourceTable = myResourceTableDao.findById(id).orElseThrow(IllegalArgumentException::new); 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(() -> { runInTransaction(() -> {
// HFJ_RESOURCE // HFJ_RESOURCE
ResourceTable resourceTable = myResourceTableDao.findById(id).orElseThrow(IllegalArgumentException::new); ResourceTable resourceTable = myResourceTableDao.findById(id).orElseThrow(IllegalArgumentException::new);
assertNull(resourceTable.getPartitionId().getPartitionId()); PartitionablePartitionId partitionId = resourceTable.getPartitionId();
assertEquals(myPartitionDate, resourceTable.getPartitionId().getPartitionDate()); assertNotNull(partitionId);
assertNull(partitionId.getPartitionId());
assertEquals(myPartitionDate, partitionId.getPartitionDate());
}); });
} }
@ -393,7 +408,7 @@ public class PartitioningSqlR4Test extends BaseJpaR4SystemTest {
runInTransaction(() -> { runInTransaction(() -> {
ResourceTable resourceTable = myResourceTableDao.findById(patientId).orElseThrow(IllegalArgumentException::new); 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 // HFJ_FORCED_ID
List<ForcedId> forcedIds = myForcedIdDao.findAll(); List<ForcedId> forcedIds = myForcedIdDao.findAll();
assertEquals(2, forcedIds.size()); assertEquals(2, forcedIds.size());
assertEquals(null, forcedIds.get(0).getPartitionId().getPartitionId()); assertEquals(null, forcedIds.get(0).getPartitionId());
assertEquals(null, forcedIds.get(1).getPartitionId().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 @Test
public void testRead_PidId_DefaultPartition() { public void testRead_PidId_DefaultPartition() {
IIdType patientIdNull = createPatient(withPartition(null), withActiveTrue()); IIdType patientIdNull = createPatient(withPartition(null), withActiveTrue());
@ -1030,7 +1153,7 @@ public class PartitioningSqlR4Test extends BaseJpaR4SystemTest {
map.setLoadSynchronous(true); map.setLoadSynchronous(true);
IBundleProvider results = myPatientDao.search(map); IBundleProvider results = myPatientDao.search(map);
List<IIdType> ids = toUnqualifiedVersionlessIds(results); 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); String searchSql = myCaptureQueriesListener.getSelectQueriesForCurrentThread().get(0).getSql(true, true);
ourLog.info("Search SQL:\n{}", searchSql); ourLog.info("Search SQL:\n{}", searchSql);
@ -1047,7 +1170,7 @@ public class PartitioningSqlR4Test extends BaseJpaR4SystemTest {
map.setLoadSynchronous(true); map.setLoadSynchronous(true);
IBundleProvider results = myPatientDao.search(map); IBundleProvider results = myPatientDao.search(map);
List<IIdType> ids = toUnqualifiedVersionlessIds(results); 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); String searchSql = myCaptureQueriesListener.getSelectQueriesForCurrentThread().get(0).getSql(true, true);
ourLog.info("Search SQL:\n{}", searchSql); ourLog.info("Search SQL:\n{}", searchSql);
@ -1072,11 +1195,11 @@ public class PartitioningSqlR4Test extends BaseJpaR4SystemTest {
map.setLoadSynchronous(true); map.setLoadSynchronous(true);
IBundleProvider results = myPatientDao.search(map); IBundleProvider results = myPatientDao.search(map);
List<IIdType> ids = toUnqualifiedVersionlessIds(results); 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{}", myCaptureQueriesListener.getSelectQueriesForCurrentThread().get(0).getSql(true, true));
ourLog.info("Search SQL:\n{}", searchSql); String searchSql = myCaptureQueriesListener.getSelectQueriesForCurrentThread().get(0).getSql(true, false);
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, "SP_MISSING = 'true'"), searchSql); assertEquals(1, StringUtils.countMatches(searchSql, "SP_MISSING = 'true'"), searchSql);
} }
@ -1089,11 +1212,11 @@ public class PartitioningSqlR4Test extends BaseJpaR4SystemTest {
map.setLoadSynchronous(true); map.setLoadSynchronous(true);
IBundleProvider results = myPatientDao.search(map); IBundleProvider results = myPatientDao.search(map);
List<IIdType> ids = toUnqualifiedVersionlessIds(results); 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{}", myCaptureQueriesListener.getSelectQueriesForCurrentThread().get(0).getSql(true, true));
ourLog.info("Search SQL:\n{}", searchSql); String searchSql = myCaptureQueriesListener.getSelectQueriesForCurrentThread().get(0).getSql(true, false);
assertEquals(1, StringUtils.countMatches(searchSql, "t0.PARTITION_ID = '1'")); assertEquals(1, StringUtils.countMatches(searchSql, "t0.PARTITION_ID IN ('1')"));
assertEquals(1, StringUtils.countMatches(searchSql, "SP_MISSING = 'false'")); assertEquals(1, StringUtils.countMatches(searchSql, "SP_MISSING = 'false'"));
} }
} }
@ -1113,7 +1236,7 @@ public class PartitioningSqlR4Test extends BaseJpaR4SystemTest {
map.setLoadSynchronous(true); map.setLoadSynchronous(true);
IBundleProvider results = myPatientDao.search(map); IBundleProvider results = myPatientDao.search(map);
List<IIdType> ids = toUnqualifiedVersionlessIds(results); List<IIdType> ids = toUnqualifiedVersionlessIds(results);
assertThat(ids, Matchers.contains(patientIdNull)); assertThat(ids, contains(patientIdNull));
String searchSql = myCaptureQueriesListener.getSelectQueriesForCurrentThread().get(0).getSql(true, true); String searchSql = myCaptureQueriesListener.getSelectQueriesForCurrentThread().get(0).getSql(true, true);
ourLog.info("Search SQL:\n{}", searchSql); ourLog.info("Search SQL:\n{}", searchSql);
@ -1130,7 +1253,7 @@ public class PartitioningSqlR4Test extends BaseJpaR4SystemTest {
map.setLoadSynchronous(true); map.setLoadSynchronous(true);
IBundleProvider results = myPatientDao.search(map); IBundleProvider results = myPatientDao.search(map);
List<IIdType> ids = toUnqualifiedVersionlessIds(results); List<IIdType> ids = toUnqualifiedVersionlessIds(results);
assertThat(ids, Matchers.contains(patientIdNull)); assertThat(ids, contains(patientIdNull));
String searchSql = myCaptureQueriesListener.getSelectQueriesForCurrentThread().get(0).getSql(true, true); String searchSql = myCaptureQueriesListener.getSelectQueriesForCurrentThread().get(0).getSql(true, true);
ourLog.info("Search SQL:\n{}", searchSql); ourLog.info("Search SQL:\n{}", searchSql);
@ -1156,7 +1279,7 @@ public class PartitioningSqlR4Test extends BaseJpaR4SystemTest {
map.setLoadSynchronous(true); map.setLoadSynchronous(true);
IBundleProvider results = myPatientDao.search(map); IBundleProvider results = myPatientDao.search(map);
List<IIdType> ids = toUnqualifiedVersionlessIds(results); 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); String searchSql = myCaptureQueriesListener.getSelectQueriesForCurrentThread().get(0).getSql(true, true);
ourLog.info("Search SQL:\n{}", searchSql); ourLog.info("Search SQL:\n{}", searchSql);
@ -1183,12 +1306,12 @@ public class PartitioningSqlR4Test extends BaseJpaR4SystemTest {
map.setLoadSynchronous(true); map.setLoadSynchronous(true);
IBundleProvider results = myPatientDao.search(map); IBundleProvider results = myPatientDao.search(map);
List<IIdType> ids = toUnqualifiedVersionlessIds(results); 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{}", myCaptureQueriesListener.getSelectQueriesForCurrentThread().get(0).getSql(true, true));
ourLog.info("Search SQL:\n{}", searchSql); String searchSql = myCaptureQueriesListener.getSelectQueriesForCurrentThread().get(0).getSql(true, false);
assertEquals(1, StringUtils.countMatches(searchSql, "PARTITION_ID"), searchSql); 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, "HFJ_RES_PARAM_PRESENT"), searchSql);
assertEquals(1, StringUtils.countMatches(searchSql, "HASH_PRESENCE = '-3438137196820602023'"), searchSql); assertEquals(1, StringUtils.countMatches(searchSql, "HASH_PRESENCE = '-3438137196820602023'"), searchSql);
} }
@ -1211,12 +1334,12 @@ public class PartitioningSqlR4Test extends BaseJpaR4SystemTest {
map.setLoadSynchronous(true); map.setLoadSynchronous(true);
IBundleProvider results = myPatientDao.search(map); IBundleProvider results = myPatientDao.search(map);
List<IIdType> ids = toUnqualifiedVersionlessIds(results); 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{}", myCaptureQueriesListener.getSelectQueriesForCurrentThread().get(0).getSql(true, true));
ourLog.info("Search SQL:\n{}", searchSql); String searchSql = myCaptureQueriesListener.getSelectQueriesForCurrentThread().get(0).getSql(true, false);
assertEquals(1, StringUtils.countMatches(searchSql, "PARTITION_ID"), searchSql); 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, "HFJ_RES_PARAM_PRESENT"), searchSql);
assertEquals(1, StringUtils.countMatches(searchSql, "HASH_PRESENCE = '1919227773735728687'"), searchSql); assertEquals(1, StringUtils.countMatches(searchSql, "HASH_PRESENCE = '1919227773735728687'"), searchSql);
} }
@ -1237,7 +1360,7 @@ public class PartitioningSqlR4Test extends BaseJpaR4SystemTest {
map.setLoadSynchronous(true); map.setLoadSynchronous(true);
IBundleProvider results = myPatientDao.search(map); IBundleProvider results = myPatientDao.search(map);
List<IIdType> ids = toUnqualifiedVersionlessIds(results); List<IIdType> ids = toUnqualifiedVersionlessIds(results);
assertThat(ids, Matchers.contains(patientIdDefault)); assertThat(ids, contains(patientIdDefault));
String searchSql = myCaptureQueriesListener.getSelectQueriesForCurrentThread().get(0).getSql(true, true); String searchSql = myCaptureQueriesListener.getSelectQueriesForCurrentThread().get(0).getSql(true, true);
ourLog.info("Search SQL:\n{}", searchSql); ourLog.info("Search SQL:\n{}", searchSql);
@ -1262,7 +1385,7 @@ public class PartitioningSqlR4Test extends BaseJpaR4SystemTest {
map.setLoadSynchronous(true); map.setLoadSynchronous(true);
IBundleProvider results = myPatientDao.search(map); IBundleProvider results = myPatientDao.search(map);
List<IIdType> ids = toUnqualifiedVersionlessIds(results); 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); String searchSql = myCaptureQueriesListener.getSelectQueriesForCurrentThread().get(0).getSql(true, true);
ourLog.info("Search SQL:\n{}", searchSql); ourLog.info("Search SQL:\n{}", searchSql);
@ -1282,13 +1405,56 @@ public class PartitioningSqlR4Test extends BaseJpaR4SystemTest {
map.setLoadSynchronous(true); map.setLoadSynchronous(true);
IBundleProvider results = myPatientDao.search(map); IBundleProvider results = myPatientDao.search(map);
List<IIdType> ids = toUnqualifiedVersionlessIds(results); List<IIdType> ids = toUnqualifiedVersionlessIds(results);
assertThat(ids, Matchers.contains(patientId1)); assertThat(ids, contains(patientId1));
String searchSql = myCaptureQueriesListener.getSelectQueriesForCurrentThread().get(0).getSql(true, true); String searchSql = myCaptureQueriesListener.getSelectQueriesForCurrentThread().get(0).getSql(true, true);
ourLog.info("Search SQL:\n{}", searchSql); ourLog.info("Search SQL:\n{}", searchSql);
assertEquals(1, StringUtils.countMatches(searchSql, "PARTITION_ID")); 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 @Test
public void testSearch_DateParam_SearchAllPartitions() { public void testSearch_DateParam_SearchAllPartitions() {
myPartitionSettings.setIncludePartitionInSearchHashes(false); myPartitionSettings.setIncludePartitionInSearchHashes(false);
@ -1309,7 +1475,7 @@ public class PartitioningSqlR4Test extends BaseJpaR4SystemTest {
map.setLoadSynchronous(true); map.setLoadSynchronous(true);
IBundleProvider results = myPatientDao.search(map); IBundleProvider results = myPatientDao.search(map);
List<IIdType> ids = toUnqualifiedVersionlessIds(results); 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); String searchSql = myCaptureQueriesListener.getSelectQueriesForCurrentThread().get(0).getSql(true, true);
ourLog.info("Search SQL:\n{}", searchSql); ourLog.info("Search SQL:\n{}", searchSql);
@ -1325,7 +1491,7 @@ public class PartitioningSqlR4Test extends BaseJpaR4SystemTest {
map.setLoadSynchronous(true); map.setLoadSynchronous(true);
results = myPatientDao.search(map); results = myPatientDao.search(map);
ids = toUnqualifiedVersionlessIds(results); ids = toUnqualifiedVersionlessIds(results);
assertThat(ids, Matchers.contains(patientIdNull, patientId1, patientId2)); assertThat(ids, contains(patientIdNull, patientId1, patientId2));
searchSql = myCaptureQueriesListener.getSelectQueriesForCurrentThread().get(0).getSql(true, true); searchSql = myCaptureQueriesListener.getSelectQueriesForCurrentThread().get(0).getSql(true, true);
ourLog.info("Search SQL:\n{}", searchSql); ourLog.info("Search SQL:\n{}", searchSql);
@ -1341,7 +1507,7 @@ public class PartitioningSqlR4Test extends BaseJpaR4SystemTest {
map.setLoadSynchronous(true); map.setLoadSynchronous(true);
results = myPatientDao.search(map); results = myPatientDao.search(map);
ids = toUnqualifiedVersionlessIds(results); ids = toUnqualifiedVersionlessIds(results);
assertThat(ids, Matchers.contains(patientIdNull, patientId1, patientId2)); assertThat(ids, contains(patientIdNull, patientId1, patientId2));
searchSql = myCaptureQueriesListener.getSelectQueriesForCurrentThread().get(0).getSql(true, true); searchSql = myCaptureQueriesListener.getSelectQueriesForCurrentThread().get(0).getSql(true, true);
ourLog.info("Search SQL:\n{}", searchSql); ourLog.info("Search SQL:\n{}", searchSql);
@ -1357,7 +1523,7 @@ public class PartitioningSqlR4Test extends BaseJpaR4SystemTest {
map.setLoadSynchronous(true); map.setLoadSynchronous(true);
results = myPatientDao.search(map); results = myPatientDao.search(map);
ids = toUnqualifiedVersionlessIds(results); ids = toUnqualifiedVersionlessIds(results);
assertThat(ids, Matchers.contains(patientIdNull, patientId1, patientId2)); assertThat(ids, contains(patientIdNull, patientId1, patientId2));
searchSql = myCaptureQueriesListener.getSelectQueriesForCurrentThread().get(0).getSql(true, true); searchSql = myCaptureQueriesListener.getSelectQueriesForCurrentThread().get(0).getSql(true, true);
ourLog.info("Search SQL:\n{}", searchSql); ourLog.info("Search SQL:\n{}", searchSql);
@ -1371,9 +1537,9 @@ public class PartitioningSqlR4Test extends BaseJpaR4SystemTest {
public void testSearch_DateParam_SearchSpecificPartitions() { public void testSearch_DateParam_SearchSpecificPartitions() {
myPartitionSettings.setIncludePartitionInSearchHashes(false); 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 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(null), withBirthdate("2021-04-20"));
createPatient(withPartition(1), withBirthdate("2021-04-20")); createPatient(withPartition(1), withBirthdate("2021-04-20"));
createPatient(withPartition(2), 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); IBundleProvider results = myPatientDao.search(map);
myCaptureQueriesListener.logSelectQueriesForCurrentThread(); myCaptureQueriesListener.logSelectQueriesForCurrentThread();
List<IIdType> ids = toUnqualifiedVersionlessIds(results); List<IIdType> ids = toUnqualifiedVersionlessIds(results);
assertThat(ids, Matchers.contains(patientId1)); assertThat(ids, contains(patientId1));
String searchSql = myCaptureQueriesListener.getSelectQueriesForCurrentThread().get(0).getSql(true, true); String searchSql = myCaptureQueriesListener.getSelectQueriesForCurrentThread().get(0).getSql(true, true);
ourLog.info("Search SQL:\n{}", searchSql); ourLog.info("Search SQL:\n{}", searchSql);
@ -1406,7 +1572,7 @@ public class PartitioningSqlR4Test extends BaseJpaR4SystemTest {
map.setLoadSynchronous(true); map.setLoadSynchronous(true);
results = myPatientDao.search(map); results = myPatientDao.search(map);
ids = toUnqualifiedVersionlessIds(results); ids = toUnqualifiedVersionlessIds(results);
assertThat(ids, Matchers.contains(patientId1)); assertThat(ids, contains(patientId1));
searchSql = myCaptureQueriesListener.getSelectQueriesForCurrentThread().get(0).getSql(true, true); searchSql = myCaptureQueriesListener.getSelectQueriesForCurrentThread().get(0).getSql(true, true);
ourLog.info("Search SQL:\n{}", searchSql); ourLog.info("Search SQL:\n{}", searchSql);
@ -1422,7 +1588,7 @@ public class PartitioningSqlR4Test extends BaseJpaR4SystemTest {
map.setLoadSynchronous(true); map.setLoadSynchronous(true);
results = myPatientDao.search(map); results = myPatientDao.search(map);
ids = toUnqualifiedVersionlessIds(results); ids = toUnqualifiedVersionlessIds(results);
assertThat(ids, Matchers.contains(patientId1)); assertThat(ids, contains(patientId1));
searchSql = myCaptureQueriesListener.getSelectQueriesForCurrentThread().get(0).getSql(true, true); searchSql = myCaptureQueriesListener.getSelectQueriesForCurrentThread().get(0).getSql(true, true);
ourLog.info("Search SQL:\n{}", searchSql); ourLog.info("Search SQL:\n{}", searchSql);
@ -1438,7 +1604,7 @@ public class PartitioningSqlR4Test extends BaseJpaR4SystemTest {
map.setLoadSynchronous(true); map.setLoadSynchronous(true);
results = myPatientDao.search(map); results = myPatientDao.search(map);
ids = toUnqualifiedVersionlessIds(results); ids = toUnqualifiedVersionlessIds(results);
assertThat(ids, Matchers.contains(patientId1)); assertThat(ids, contains(patientId1));
searchSql = myCaptureQueriesListener.getSelectQueriesForCurrentThread().get(0).getSql(true, true); searchSql = myCaptureQueriesListener.getSelectQueriesForCurrentThread().get(0).getSql(true, true);
ourLog.info("Search SQL:\n{}", searchSql); ourLog.info("Search SQL:\n{}", searchSql);
@ -1453,8 +1619,8 @@ public class PartitioningSqlR4Test extends BaseJpaR4SystemTest {
myPartitionSettings.setIncludePartitionInSearchHashes(false); myPartitionSettings.setIncludePartitionInSearchHashes(false);
IIdType patientIdNull = createPatient(withPartition(null), withBirthdate("2020-04-20")); IIdType patientIdNull = createPatient(withPartition(null), withBirthdate("2020-04-20"));
IIdType patientId1 = createPatient(withPartition(1), withBirthdate("2020-04-20")); 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(null), withBirthdate("2021-04-20"));
createPatient(withPartition(1), withBirthdate("2021-04-20")); createPatient(withPartition(1), withBirthdate("2021-04-20"));
createPatient(withPartition(2), withBirthdate("2021-04-20")); createPatient(withPartition(2), withBirthdate("2021-04-20"));
@ -1468,7 +1634,7 @@ public class PartitioningSqlR4Test extends BaseJpaR4SystemTest {
map.setLoadSynchronous(true); map.setLoadSynchronous(true);
IBundleProvider results = myPatientDao.search(map); IBundleProvider results = myPatientDao.search(map);
List<IIdType> ids = toUnqualifiedVersionlessIds(results); List<IIdType> ids = toUnqualifiedVersionlessIds(results);
assertThat(ids, Matchers.contains(patientIdNull)); assertThat(ids, contains(patientIdNull));
String searchSql = myCaptureQueriesListener.getSelectQueriesForCurrentThread().get(0).getSql(true, true); String searchSql = myCaptureQueriesListener.getSelectQueriesForCurrentThread().get(0).getSql(true, true);
ourLog.info("Search SQL:\n{}", searchSql); ourLog.info("Search SQL:\n{}", searchSql);
@ -1484,7 +1650,7 @@ public class PartitioningSqlR4Test extends BaseJpaR4SystemTest {
map.setLoadSynchronous(true); map.setLoadSynchronous(true);
results = myPatientDao.search(map); results = myPatientDao.search(map);
ids = toUnqualifiedVersionlessIds(results); ids = toUnqualifiedVersionlessIds(results);
assertThat(ids, Matchers.contains(patientIdNull)); assertThat(ids, contains(patientIdNull));
searchSql = myCaptureQueriesListener.getSelectQueriesForCurrentThread().get(0).getSql(true, true); searchSql = myCaptureQueriesListener.getSelectQueriesForCurrentThread().get(0).getSql(true, true);
ourLog.info("Search SQL:\n{}", searchSql); ourLog.info("Search SQL:\n{}", searchSql);
@ -1500,7 +1666,7 @@ public class PartitioningSqlR4Test extends BaseJpaR4SystemTest {
map.setLoadSynchronous(true); map.setLoadSynchronous(true);
results = myPatientDao.search(map); results = myPatientDao.search(map);
ids = toUnqualifiedVersionlessIds(results); ids = toUnqualifiedVersionlessIds(results);
assertThat(ids, Matchers.contains(patientIdNull)); assertThat(ids, contains(patientIdNull));
searchSql = myCaptureQueriesListener.getSelectQueriesForCurrentThread().get(0).getSql(true, true); searchSql = myCaptureQueriesListener.getSelectQueriesForCurrentThread().get(0).getSql(true, true);
ourLog.info("Search SQL:\n{}", searchSql); ourLog.info("Search SQL:\n{}", searchSql);
@ -1516,7 +1682,7 @@ public class PartitioningSqlR4Test extends BaseJpaR4SystemTest {
map.setLoadSynchronous(true); map.setLoadSynchronous(true);
results = myPatientDao.search(map); results = myPatientDao.search(map);
ids = toUnqualifiedVersionlessIds(results); ids = toUnqualifiedVersionlessIds(results);
assertThat(ids, Matchers.contains(patientIdNull)); assertThat(ids, contains(patientIdNull));
searchSql = myCaptureQueriesListener.getSelectQueriesForCurrentThread().get(0).getSql(true, true); searchSql = myCaptureQueriesListener.getSelectQueriesForCurrentThread().get(0).getSql(true, true);
ourLog.info("Search SQL:\n{}", searchSql); ourLog.info("Search SQL:\n{}", searchSql);
@ -1581,7 +1747,7 @@ public class PartitioningSqlR4Test extends BaseJpaR4SystemTest {
map.setLoadSynchronous(true); map.setLoadSynchronous(true);
IBundleProvider results = myPatientDao.search(map); IBundleProvider results = myPatientDao.search(map);
List<IIdType> ids = toUnqualifiedVersionlessIds(results); 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); String searchSql = myCaptureQueriesListener.getSelectQueriesForCurrentThread().get(0).getSql(true, true);
ourLog.info("Search SQL:\n{}", searchSql); ourLog.info("Search SQL:\n{}", searchSql);
@ -1603,7 +1769,7 @@ public class PartitioningSqlR4Test extends BaseJpaR4SystemTest {
map.setLoadSynchronous(true); map.setLoadSynchronous(true);
IBundleProvider results = myPatientDao.search(map); IBundleProvider results = myPatientDao.search(map);
List<IIdType> ids = toUnqualifiedVersionlessIds(results); List<IIdType> ids = toUnqualifiedVersionlessIds(results);
assertThat(ids, Matchers.contains(patientIdNull)); assertThat(ids, contains(patientIdNull));
String searchSql = myCaptureQueriesListener.getSelectQueriesForCurrentThread().get(0).getSql(true, true); String searchSql = myCaptureQueriesListener.getSelectQueriesForCurrentThread().get(0).getSql(true, true);
ourLog.info("Search SQL:\n{}", searchSql); ourLog.info("Search SQL:\n{}", searchSql);
@ -1628,7 +1794,7 @@ public class PartitioningSqlR4Test extends BaseJpaR4SystemTest {
IBundleProvider results = myPatientDao.search(map); IBundleProvider results = myPatientDao.search(map);
List<IIdType> ids = toUnqualifiedVersionlessIds(results); List<IIdType> ids = toUnqualifiedVersionlessIds(results);
myCaptureQueriesListener.logSelectQueriesForCurrentThread(); myCaptureQueriesListener.logSelectQueriesForCurrentThread();
assertThat(ids, Matchers.contains(patientId1)); assertThat(ids, contains(patientId1));
String searchSql = myCaptureQueriesListener.getSelectQueriesForCurrentThread().get(0).getSql(true, true); String searchSql = myCaptureQueriesListener.getSelectQueriesForCurrentThread().get(0).getSql(true, true);
ourLog.info("Search SQL:\n{}", searchSql); ourLog.info("Search SQL:\n{}", searchSql);
@ -1636,6 +1802,73 @@ public class PartitioningSqlR4Test extends BaseJpaR4SystemTest {
assertEquals(1, StringUtils.countMatches(searchSql, "SP_VALUE_NORMALIZED")); 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 @Test
public void testSearch_StringParam_SearchAllPartitions_IncludePartitionInHashes() { public void testSearch_StringParam_SearchAllPartitions_IncludePartitionInHashes() {
myPartitionSettings.setIncludePartitionInSearchHashes(true); myPartitionSettings.setIncludePartitionInSearchHashes(true);
@ -1671,7 +1904,7 @@ public class PartitioningSqlR4Test extends BaseJpaR4SystemTest {
map.setLoadSynchronous(true); map.setLoadSynchronous(true);
IBundleProvider results = myPatientDao.search(map); IBundleProvider results = myPatientDao.search(map);
List<IIdType> ids = toUnqualifiedVersionlessIds(results); List<IIdType> ids = toUnqualifiedVersionlessIds(results);
assertThat(ids, Matchers.contains(patientIdNull)); assertThat(ids, contains(patientIdNull));
String searchSql = myCaptureQueriesListener.getSelectQueriesForCurrentThread().get(0).getSql(true, true); String searchSql = myCaptureQueriesListener.getSelectQueriesForCurrentThread().get(0).getSql(true, true);
ourLog.info("Search SQL:\n{}", searchSql); ourLog.info("Search SQL:\n{}", searchSql);
@ -1698,7 +1931,7 @@ public class PartitioningSqlR4Test extends BaseJpaR4SystemTest {
IBundleProvider results = myPatientDao.search(map); IBundleProvider results = myPatientDao.search(map);
List<IIdType> ids = toUnqualifiedVersionlessIds(results); List<IIdType> ids = toUnqualifiedVersionlessIds(results);
myCaptureQueriesListener.logSelectQueriesForCurrentThread(); myCaptureQueriesListener.logSelectQueriesForCurrentThread();
assertThat(ids, Matchers.contains(patientId1)); assertThat(ids, contains(patientId1));
String searchSql = myCaptureQueriesListener.getSelectQueriesForCurrentThread().get(0).getSql(true, true); String searchSql = myCaptureQueriesListener.getSelectQueriesForCurrentThread().get(0).getSql(true, true);
ourLog.info("Search SQL:\n{}", searchSql); ourLog.info("Search SQL:\n{}", searchSql);
@ -1722,7 +1955,7 @@ public class PartitioningSqlR4Test extends BaseJpaR4SystemTest {
map.setLoadSynchronous(true); map.setLoadSynchronous(true);
IBundleProvider results = myPatientDao.search(map); IBundleProvider results = myPatientDao.search(map);
List<IIdType> ids = toUnqualifiedVersionlessIds(results); 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); String searchSql = myCaptureQueriesListener.getSelectQueriesForCurrentThread().get(0).getSql(true, true);
ourLog.info("Search SQL:\n{}", searchSql); ourLog.info("Search SQL:\n{}", searchSql);
@ -1739,7 +1972,7 @@ public class PartitioningSqlR4Test extends BaseJpaR4SystemTest {
map.setLoadSynchronous(true); map.setLoadSynchronous(true);
results = myPatientDao.search(map); results = myPatientDao.search(map);
ids = toUnqualifiedVersionlessIds(results); ids = toUnqualifiedVersionlessIds(results);
assertThat(ids, Matchers.contains(patientIdNull, patientId1)); assertThat(ids, contains(patientIdNull, patientId1));
searchSql = myCaptureQueriesListener.getSelectQueriesForCurrentThread().get(0).getSql(true, true); searchSql = myCaptureQueriesListener.getSelectQueriesForCurrentThread().get(0).getSql(true, true);
ourLog.info("Search SQL:\n{}", searchSql); 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, "PARTITION_ID IS NULL"));
assertEquals(1, StringUtils.countMatches(searchSql, "TAG_SYSTEM = 'http://system'")); assertEquals(1, StringUtils.countMatches(searchSql, "TAG_SYSTEM = 'http://system'"));
assertThat(ids.toString(), ids, Matchers.contains(patientIdNull)); assertThat(ids.toString(), ids, contains(patientIdNull));
} }
@Test @Test
@ -1791,7 +2024,7 @@ public class PartitioningSqlR4Test extends BaseJpaR4SystemTest {
map.setLoadSynchronous(true); map.setLoadSynchronous(true);
IBundleProvider results = myPatientDao.search(map); IBundleProvider results = myPatientDao.search(map);
List<IIdType> ids = toUnqualifiedVersionlessIds(results); List<IIdType> ids = toUnqualifiedVersionlessIds(results);
assertThat(ids, Matchers.contains(patientId1)); assertThat(ids, contains(patientId1));
String searchSql = myCaptureQueriesListener.getSelectQueriesForCurrentThread().get(0).getSql(true, true); String searchSql = myCaptureQueriesListener.getSelectQueriesForCurrentThread().get(0).getSql(true, true);
ourLog.info("Search SQL:\n{}", searchSql); ourLog.info("Search SQL:\n{}", searchSql);
@ -1813,7 +2046,7 @@ public class PartitioningSqlR4Test extends BaseJpaR4SystemTest {
map.setLoadSynchronous(true); map.setLoadSynchronous(true);
IBundleProvider results = myPatientDao.search(map); IBundleProvider results = myPatientDao.search(map);
List<IIdType> ids = toUnqualifiedVersionlessIds(results); 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); String searchSql = myCaptureQueriesListener.getSelectQueriesForCurrentThread().get(0).getSql(true, true);
ourLog.info("Search SQL:\n{}", searchSql); ourLog.info("Search SQL:\n{}", searchSql);
@ -1837,7 +2070,7 @@ public class PartitioningSqlR4Test extends BaseJpaR4SystemTest {
IBundleProvider results = myPatientDao.search(map); IBundleProvider results = myPatientDao.search(map);
List<IIdType> ids = toUnqualifiedVersionlessIds(results); List<IIdType> ids = toUnqualifiedVersionlessIds(results);
myCaptureQueriesListener.logSelectQueriesForCurrentThread(0); myCaptureQueriesListener.logSelectQueriesForCurrentThread(0);
assertThat(ids, Matchers.contains(patientId1)); assertThat(ids, contains(patientId1));
String searchSql = myCaptureQueriesListener.getSelectQueriesForCurrentThread().get(0).getSql(true, true); String searchSql = myCaptureQueriesListener.getSelectQueriesForCurrentThread().get(0).getSql(true, true);
ourLog.info("Search SQL:\n{}", searchSql); ourLog.info("Search SQL:\n{}", searchSql);
@ -1864,7 +2097,7 @@ public class PartitioningSqlR4Test extends BaseJpaR4SystemTest {
map.setLoadSynchronous(true); map.setLoadSynchronous(true);
IBundleProvider results = myPatientDao.search(map); IBundleProvider results = myPatientDao.search(map);
List<IIdType> ids = toUnqualifiedVersionlessIds(results); 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); String searchSql = myCaptureQueriesListener.getSelectQueriesForCurrentThread().get(0).getSql(true, true);
ourLog.info("Search SQL:\n{}", searchSql); ourLog.info("Search SQL:\n{}", searchSql);
@ -1889,7 +2122,7 @@ public class PartitioningSqlR4Test extends BaseJpaR4SystemTest {
map.setLoadSynchronous(true); map.setLoadSynchronous(true);
IBundleProvider results = myPatientDao.search(map); IBundleProvider results = myPatientDao.search(map);
List<IIdType> ids = toUnqualifiedVersionlessIds(results); List<IIdType> ids = toUnqualifiedVersionlessIds(results);
assertThat(ids, Matchers.contains(patientId1)); assertThat(ids, contains(patientId1));
String searchSql = myCaptureQueriesListener.getSelectQueriesForCurrentThread().get(0).getSql(true, true); String searchSql = myCaptureQueriesListener.getSelectQueriesForCurrentThread().get(0).getSql(true, true);
ourLog.info("Search SQL:\n{}", searchSql); ourLog.info("Search SQL:\n{}", searchSql);
@ -1912,7 +2145,7 @@ public class PartitioningSqlR4Test extends BaseJpaR4SystemTest {
IBundleProvider results = myPatientDao.search(map); IBundleProvider results = myPatientDao.search(map);
List<IIdType> ids = toUnqualifiedVersionlessIds(results); List<IIdType> ids = toUnqualifiedVersionlessIds(results);
myCaptureQueriesListener.logSelectQueriesForCurrentThread(); myCaptureQueriesListener.logSelectQueriesForCurrentThread();
assertThat(ids, Matchers.contains(id)); assertThat(ids, contains(id));
String searchSql = myCaptureQueriesListener.getSelectQueriesForCurrentThread().get(0).getSql(true, true); String searchSql = myCaptureQueriesListener.getSelectQueriesForCurrentThread().get(0).getSql(true, true);
ourLog.info("Search SQL:\n{}", searchSql); ourLog.info("Search SQL:\n{}", searchSql);
@ -1935,7 +2168,7 @@ public class PartitioningSqlR4Test extends BaseJpaR4SystemTest {
IBundleProvider results = myPatientDao.search(map); IBundleProvider results = myPatientDao.search(map);
List<IIdType> ids = toUnqualifiedVersionlessIds(results); List<IIdType> ids = toUnqualifiedVersionlessIds(results);
myCaptureQueriesListener.logSelectQueriesForCurrentThread(); myCaptureQueriesListener.logSelectQueriesForCurrentThread();
assertThat(ids, Matchers.contains(id)); assertThat(ids, contains(id));
String searchSql = myCaptureQueriesListener.getSelectQueriesForCurrentThread().get(0).getSql(true, true); String searchSql = myCaptureQueriesListener.getSelectQueriesForCurrentThread().get(0).getSql(true, true);
ourLog.info("Search SQL:\n{}", searchSql); ourLog.info("Search SQL:\n{}", searchSql);
@ -1970,11 +2203,11 @@ public class PartitioningSqlR4Test extends BaseJpaR4SystemTest {
IBundleProvider results = myObservationDao.search(map); IBundleProvider results = myObservationDao.search(map);
List<IIdType> ids = toUnqualifiedVersionlessIds(results); List<IIdType> ids = toUnqualifiedVersionlessIds(results);
myCaptureQueriesListener.logSelectQueriesForCurrentThread(); 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); String searchSql = myCaptureQueriesListener.getSelectQueriesForCurrentThread().get(0).getSql(true, false);
ourLog.info("Search SQL:\n{}", searchSql); assertEquals(1, StringUtils.countMatches(searchSql, "t0.PARTITION_ID IN ('1')"), searchSql);
assertEquals(1, StringUtils.countMatches(searchSql, "t0.PARTITION_ID = '1'"), searchSql);
assertEquals(1, StringUtils.countMatches(searchSql, "t0.SRC_PATH = 'Observation.subject'"), 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, "t0.TARGET_RESOURCE_ID = '" + patientId.getIdPartAsLong() + "'"), searchSql);
assertEquals(1, StringUtils.countMatches(searchSql, "PARTITION_ID"), 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)); IIdType observationId = createObservation(withPartition(null), withSubject(patientId));
addReadDefaultPartition(); addReadDefaultPartition();
;
myCaptureQueriesListener.clear(); myCaptureQueriesListener.clear();
SearchParameterMap map = new SearchParameterMap(); SearchParameterMap map = new SearchParameterMap();
map.add(Observation.SP_SUBJECT, new ReferenceParam(patientId)); map.add(Observation.SP_SUBJECT, new ReferenceParam(patientId));
map.setLoadSynchronous(true); map.setLoadSynchronous(true);
IBundleProvider results = myObservationDao.search(map); IBundleProvider results = myObservationDao.search(map);
List<IIdType> ids = toUnqualifiedVersionlessIds(results); List<IIdType> ids = toUnqualifiedVersionlessIds(results);
assertThat(ids, Matchers.contains(observationId)); assertThat(ids, contains(observationId));
String searchSql = myCaptureQueriesListener.getSelectQueriesForCurrentThread().get(0).getSql(true, false); String searchSql = myCaptureQueriesListener.getSelectQueriesForCurrentThread().get(0).getSql(true, false);
ourLog.info("Search SQL:\n{}", searchSql); ourLog.info("Search SQL:\n{}", searchSql);
@ -2044,11 +2277,11 @@ public class PartitioningSqlR4Test extends BaseJpaR4SystemTest {
IBundleProvider results = myObservationDao.search(map); IBundleProvider results = myObservationDao.search(map);
List<IIdType> ids = toUnqualifiedVersionlessIds(results); List<IIdType> ids = toUnqualifiedVersionlessIds(results);
myCaptureQueriesListener.logSelectQueriesForCurrentThread(); 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{}", myCaptureQueriesListener.getSelectQueriesForCurrentThread().get(0).getSql(true, true));
ourLog.info("Search SQL:\n{}", searchSql); String searchSql = myCaptureQueriesListener.getSelectQueriesForCurrentThread().get(0).getSql(true, false);
assertEquals(1, StringUtils.countMatches(searchSql, "forcedid0_.PARTITION_ID='1'"), searchSql); 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, "and forcedid0_.RESOURCE_TYPE='Patient'"), searchSql);
assertEquals(1, StringUtils.countMatches(searchSql, "PARTITION_ID"), searchSql); assertEquals(1, StringUtils.countMatches(searchSql, "PARTITION_ID"), searchSql);
@ -2080,10 +2313,10 @@ public class PartitioningSqlR4Test extends BaseJpaR4SystemTest {
map.setLoadSynchronous(true); map.setLoadSynchronous(true);
IBundleProvider results = myObservationDao.search(map); IBundleProvider results = myObservationDao.search(map);
List<IIdType> ids = toUnqualifiedVersionlessIds(results); List<IIdType> ids = toUnqualifiedVersionlessIds(results);
assertThat(ids, Matchers.contains(observationId));
String searchSql = myCaptureQueriesListener.getSelectQueriesForCurrentThread().get(0).getSql(true, true); String searchSql = myCaptureQueriesListener.getSelectQueriesForCurrentThread().get(0).getSql(true, true);
ourLog.info("Search SQL:\n{}", searchSql); 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_.PARTITION_ID is null"), searchSql);
assertEquals(1, StringUtils.countMatches(searchSql, "forcedid0_.RESOURCE_TYPE='Patient'"), searchSql); assertEquals(1, StringUtils.countMatches(searchSql, "forcedid0_.RESOURCE_TYPE='Patient'"), searchSql);
assertEquals(1, StringUtils.countMatches(searchSql, "PARTITION_ID"), 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); IBundleProvider results = myPatientDao.history(id, null, null, mySrd);
assertEquals(2, results.sizeOrThrowNpe()); assertEquals(2, results.sizeOrThrowNpe());
List<String> ids = toUnqualifiedIdValues(results); 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()); assertEquals(4, myCaptureQueriesListener.getSelectQueriesForCurrentThread().size());
@ -2192,7 +2425,7 @@ public class PartitioningSqlR4Test extends BaseJpaR4SystemTest {
IBundleProvider results = myPatientDao.history(id, null, null, mySrd); IBundleProvider results = myPatientDao.history(id, null, null, mySrd);
assertEquals(2, results.sizeOrThrowNpe()); assertEquals(2, results.sizeOrThrowNpe());
List<String> ids = toUnqualifiedIdValues(results); 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()); assertEquals(4, myCaptureQueriesListener.getSelectQueriesForCurrentThread().size());
@ -2233,7 +2466,7 @@ public class PartitioningSqlR4Test extends BaseJpaR4SystemTest {
IBundleProvider results = myPatientDao.history(id, null, null, mySrd); IBundleProvider results = myPatientDao.history(id, null, null, mySrd);
assertEquals(2, results.sizeOrThrowNpe()); assertEquals(2, results.sizeOrThrowNpe());
List<String> ids = toUnqualifiedIdValues(results); 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 @Test
@ -2262,26 +2495,27 @@ public class PartitioningSqlR4Test extends BaseJpaR4SystemTest {
IBundleProvider results = mySystemDao.history(null, null, mySrd); IBundleProvider results = mySystemDao.history(null, null, mySrd);
assertEquals(2, results.sizeOrThrowNpe()); assertEquals(2, results.sizeOrThrowNpe());
List<String> ids = toUnqualifiedIdValues(results); 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()); assertEquals(3, myCaptureQueriesListener.getSelectQueriesForCurrentThread().size());
// Count // Count
String searchSql = myCaptureQueriesListener.getSelectQueriesForCurrentThread().get(0).getSql(true, true); ourLog.info("SQL:{}", myCaptureQueriesListener.getSelectQueriesForCurrentThread().get(0).getSql(true, true));
ourLog.info("SQL:{}", searchSql); String sql = myCaptureQueriesListener.getSelectQueriesForCurrentThread().get(0).getSql(true, false).toUpperCase();
assertEquals(1, countMatches(searchSql, "count(")); assertEquals(1, countMatches(sql, "COUNT("), sql);
assertEquals(1, countMatches(searchSql, "PARTITION_ID='1'")); assertEquals(1, countMatches(sql, "PARTITION_ID IN ('1')"), sql);
// Fetch history // Fetch history
searchSql = myCaptureQueriesListener.getSelectQueriesForCurrentThread().get(1).getSql(true, true); sql = myCaptureQueriesListener.getSelectQueriesForCurrentThread().get(1).getSql(true, false).toUpperCase();
ourLog.info("SQL:{}", searchSql); ourLog.info("SQL:{}", sql);
assertEquals(1, countMatches(searchSql, "PARTITION_ID='1'")); assertEquals(1, countMatches(sql, "PARTITION_ID IN ('1')"), sql);
// Fetch history resource // Fetch history resource
searchSql = myCaptureQueriesListener.getSelectQueriesForCurrentThread().get(2).getSql(true, true); sql = myCaptureQueriesListener.getSelectQueriesForCurrentThread().get(2).getSql(true, false);
ourLog.info("SQL:{}", searchSql); sql = sql.replace(" ", "").toUpperCase();
assertEquals(0, countMatches(searchSql, "PARTITION_ID="), searchSql.replace(" ", "").toUpperCase()); ourLog.info("SQL:{}", sql);
assertEquals(0, countMatches(searchSql, "PARTITION_IDIN"), searchSql.replace(" ", "").toUpperCase()); assertEquals(0, countMatches(sql, "PARTITION_ID="), sql);
assertEquals(0, countMatches(sql, "PARTITION_IDIN"), sql);
} }
@Test @Test
@ -2299,7 +2533,7 @@ public class PartitioningSqlR4Test extends BaseJpaR4SystemTest {
IBundleProvider results = mySystemDao.history(null, null, mySrd); IBundleProvider results = mySystemDao.history(null, null, mySrd);
assertEquals(2, results.sizeOrThrowNpe()); assertEquals(2, results.sizeOrThrowNpe());
List<String> ids = toUnqualifiedIdValues(results); 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()); assertEquals(3, myCaptureQueriesListener.getSelectQueriesForCurrentThread().size());
@ -2320,6 +2554,42 @@ public class PartitioningSqlR4Test extends BaseJpaR4SystemTest {
assertEquals(0, countMatches(searchSql, "PARTITION_IDIN"), searchSql.replace(" ", "").toUpperCase()); 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 @Test
public void testHistory_Type_AllPartitions() { public void testHistory_Type_AllPartitions() {
addReadAllPartitions(); addReadAllPartitions();
@ -2346,25 +2616,25 @@ public class PartitioningSqlR4Test extends BaseJpaR4SystemTest {
IBundleProvider results = myPatientDao.history(null, null, mySrd); IBundleProvider results = myPatientDao.history(null, null, mySrd);
assertEquals(2, results.sizeOrThrowNpe()); assertEquals(2, results.sizeOrThrowNpe());
List<String> ids = toUnqualifiedIdValues(results); 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()); assertEquals(3, myCaptureQueriesListener.getSelectQueriesForCurrentThread().size());
// Count // Count
String sql = myCaptureQueriesListener.getSelectQueriesForCurrentThread().get(0).getSql(true, true); String sql = myCaptureQueriesListener.getSelectQueriesForCurrentThread().get(0).getSql(true, false).toUpperCase();
ourLog.info("SQL:{}", sql); ourLog.info("SQL:{}", sql);
assertEquals(1, countMatches(sql, "count(")); assertEquals(1, countMatches(sql, "COUNT("), sql);
assertEquals(1, countMatches(sql, "PARTITION_ID='1'")); assertEquals(1, countMatches(sql, "PARTITION_ID IN ('1')"), sql);
// Fetch history resources // Fetch history resources
sql = myCaptureQueriesListener.getSelectQueriesForCurrentThread().get(1).getSql(true, true); sql = myCaptureQueriesListener.getSelectQueriesForCurrentThread().get(1).getSql(true, false).toUpperCase();
ourLog.info("SQL:{}", sql); ourLog.info("SQL:{}", sql);
assertEquals(1, countMatches(sql, "PARTITION_ID='1'")); assertEquals(1, countMatches(sql, "PARTITION_ID IN ('1')"), sql);
// Resolve forced ID // Resolve forced ID
sql = myCaptureQueriesListener.getSelectQueriesForCurrentThread().get(2).getSql(true, true); sql = myCaptureQueriesListener.getSelectQueriesForCurrentThread().get(2).getSql(true, false).toUpperCase();
ourLog.info("SQL:{}", sql); 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); IBundleProvider results = myPatientDao.history(null, null, mySrd);
assertEquals(2, results.sizeOrThrowNpe()); assertEquals(2, results.sizeOrThrowNpe());
List<String> ids = toUnqualifiedIdValues(results); 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(); myCaptureQueriesListener.logSelectQueriesForCurrentThread();
assertEquals(3, myCaptureQueriesListener.getSelectQueriesForCurrentThread().size()); 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()); verify(interceptor, times(1)).invoke(eq(Pointcut.STORAGE_PARTITION_SELECTED), captor.capture());
RequestPartitionId partitionId = captor.getValue().get(RequestPartitionId.class); RequestPartitionId partitionId = captor.getValue().get(RequestPartitionId.class);
assertEquals(1, partitionId.getPartitionId().intValue()); assertEquals(1, partitionId.getPartitionIds().get(0).intValue());
assertEquals("PART-1", partitionId.getPartitionName()); assertEquals("PART-1", partitionId.getPartitionNames().get(0));
} finally { } finally {
myInterceptorRegistry.unregisterInterceptor(interceptor); myInterceptorRegistry.unregisterInterceptor(interceptor);
@ -2471,9 +2741,15 @@ public class PartitioningSqlR4Test extends BaseJpaR4SystemTest {
myPartitionInterceptor.addCreatePartition(requestPartitionId); myPartitionInterceptor.addCreatePartition(requestPartitionId);
} }
private void addReadPartition(Integer thePartitionId) { private void addReadPartition(Integer... thePartitionId) {
Validate.notNull(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() { private void addReadDefaultPartition() {

View File

@ -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 @Test
public void testUpdatePartition_TryToUseExistingName() { public void testUpdatePartition_TryToUseExistingName() {
@ -92,14 +80,14 @@ public class PartitionSettingsSvcImplTest extends BaseJpaR4Test {
@Test @Test
public void testUpdatePartition_TryToRenameDefault() { public void testUpdatePartition_TryToRenameDefault() {
PartitionEntity partition = new PartitionEntity(); PartitionEntity partition = new PartitionEntity();
partition.setId(0); partition.setId(null);
partition.setName("NAME123"); partition.setName("NAME123");
partition.setDescription("A description"); partition.setDescription("A description");
try { try {
myPartitionConfigSvc.updatePartition(partition); myPartitionConfigSvc.updatePartition(partition);
fail(); fail();
} catch (InvalidRequestException e) { } 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 @Test
public void testUpdatePartition_UnknownPartitionBlocked() { public void testUpdatePartition_UnknownPartitionBlocked() {
PartitionEntity partition = new PartitionEntity(); PartitionEntity partition = new PartitionEntity();

View File

@ -27,7 +27,7 @@ import java.util.List;
import java.util.function.Consumer; import java.util.function.Consumer;
import java.util.function.Supplier; 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 { public abstract class BaseMultitenantResourceProviderR4Test extends BaseResourceProviderR4Test implements ITestDataBuilder {
@ -88,7 +88,7 @@ public abstract class BaseMultitenantResourceProviderR4Test extends BaseResource
private void createTenants() { private void createTenants() {
myTenantClientInterceptor.setTenantId(DEFAULT_PERSISTED_PARTITION_NAME); myTenantClientInterceptor.setTenantId(DEFAULT_PARTITION_NAME);
myClient myClient
.operation() .operation()

View File

@ -3,6 +3,7 @@ package ca.uhn.fhir.jpa.provider.r4;
import ca.uhn.fhir.context.support.IValidationSupport; import ca.uhn.fhir.context.support.IValidationSupport;
import ca.uhn.fhir.jpa.api.dao.DaoRegistry; import ca.uhn.fhir.jpa.api.dao.DaoRegistry;
import ca.uhn.fhir.jpa.api.svc.ISearchCoordinatorSvc; 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.dao.r4.BaseJpaR4Test;
import ca.uhn.fhir.jpa.provider.DiffProvider; import ca.uhn.fhir.jpa.provider.DiffProvider;
import ca.uhn.fhir.jpa.provider.GraphQLProvider; import ca.uhn.fhir.jpa.provider.GraphQLProvider;
@ -65,15 +66,15 @@ public abstract class BaseResourceProviderR4Test extends BaseJpaR4Test {
protected static Server ourServer; protected static Server ourServer;
private static DatabaseBackedPagingProvider ourPagingProvider; private static DatabaseBackedPagingProvider ourPagingProvider;
private static GenericWebApplicationContext ourWebApplicationContext; private static GenericWebApplicationContext ourWebApplicationContext;
private static SubscriptionMatcherInterceptor ourSubscriptionMatcherInterceptor;
protected IGenericClient myClient; protected IGenericClient myClient;
@Autowired @Autowired
protected SubscriptionLoader mySubscriptionLoader; protected SubscriptionLoader mySubscriptionLoader;
@Autowired @Autowired
protected DaoRegistry myDaoRegistry; protected DaoRegistry myDaoRegistry;
@Autowired
protected IPartitionDao myPartitionDao;
ResourceCountCache myResourceCountsCache; ResourceCountCache myResourceCountsCache;
private TerminologyUploaderProvider myTerminologyUploaderProvider; private TerminologyUploaderProvider myTerminologyUploaderProvider;
private boolean ourRestHookSubscriptionInterceptorRequested;
public BaseResourceProviderR4Test() { public BaseResourceProviderR4Test() {
super(); super();
@ -163,7 +164,7 @@ public abstract class BaseResourceProviderR4Test extends BaseJpaR4Test {
WebApplicationContext wac = WebApplicationContextUtils.getWebApplicationContext(subsServletHolder.getServlet().getServletConfig().getServletContext()); WebApplicationContext wac = WebApplicationContextUtils.getWebApplicationContext(subsServletHolder.getServlet().getServletConfig().getServletContext());
myValidationSupport = wac.getBean(IValidationSupport.class); myValidationSupport = wac.getBean(IValidationSupport.class);
mySearchCoordinatorSvc = wac.getBean(ISearchCoordinatorSvc.class); mySearchCoordinatorSvc = wac.getBean(ISearchCoordinatorSvc.class);
ourSubscriptionMatcherInterceptor = wac.getBean(SubscriptionMatcherInterceptor.class); SubscriptionMatcherInterceptor ourSubscriptionMatcherInterceptor = wac.getBean(SubscriptionMatcherInterceptor.class);
confProvider.setSearchParamRegistry(ourSearchParamRegistry); confProvider.setSearchParamRegistry(ourSearchParamRegistry);

View File

@ -1,5 +1,8 @@
package ca.uhn.fhir.jpa.provider.r4; 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.rest.server.exceptions.ResourceNotFoundException;
import ca.uhn.fhir.test.utilities.ITestDataBuilder; import ca.uhn.fhir.test.utilities.ITestDataBuilder;
import org.hl7.fhir.instance.model.api.IIdType; 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.MatcherAssert.assertThat;
import static org.hamcrest.Matchers.containsString; import static org.hamcrest.Matchers.containsString;
import static org.junit.jupiter.api.Assertions.assertEquals; 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.assertTrue;
import static org.junit.jupiter.api.Assertions.fail; import static org.junit.jupiter.api.Assertions.fail;
@ -31,19 +36,34 @@ public class MultitenantServerR4Test extends BaseMultitenantResourceProviderR4Te
} }
@Test @Test
public void testCreateAndRead() { public void testCreateAndRead_NamedTenant() {
// Create patients // Create patients
IIdType idA = createPatient(withTenant(TENANT_A), withActiveTrue()); IIdType idA = createPatient(withTenant(TENANT_A), withActiveTrue());
createPatient(withTenant(TENANT_B), withActiveFalse()); 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 // Now read back
myTenantClientInterceptor.setTenantId(TENANT_A); myTenantClientInterceptor.setTenantId(TENANT_A);
Patient response = myClient.read().resource(Patient.class).withId(idA).execute(); Patient response = myClient.read().resource(Patient.class).withId(idA).execute();
assertTrue(response.getActive()); 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); myTenantClientInterceptor.setTenantId(TENANT_B);
try { try {
myClient.read().resource(Patient.class).withId(idA).execute(); 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 @Test
public void testCreate_InvalidTenant() { public void testCreate_InvalidTenant() {

View File

@ -20,6 +20,7 @@ package ca.uhn.fhir.jpa.migrate.tasks;
* #L% * #L%
*/ */
import ca.uhn.fhir.interceptor.model.RequestPartitionId;
import ca.uhn.fhir.jpa.entity.EmpiLink; import ca.uhn.fhir.jpa.entity.EmpiLink;
import ca.uhn.fhir.jpa.entity.TermCodeSystemVersion; import ca.uhn.fhir.jpa.entity.TermCodeSystemVersion;
import ca.uhn.fhir.jpa.entity.TermConceptMap; import ca.uhn.fhir.jpa.entity.TermConceptMap;
@ -748,7 +749,7 @@ public class HapiFhirJpaMigrationTasks extends BaseMigrationTasks<VersionEnum> {
.withColumns("HASH_IDENTITY", "SP_LATITUDE", "SP_LONGITUDE"); .withColumns("HASH_IDENTITY", "SP_LATITUDE", "SP_LONGITUDE");
spidxCoords spidxCoords
.addTask(new CalculateHashesTask(VersionEnum.V3_5_0, "20180903.5") .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") .setColumnName("HASH_IDENTITY")
); );
} }
@ -771,7 +772,7 @@ public class HapiFhirJpaMigrationTasks extends BaseMigrationTasks<VersionEnum> {
.dropIndex("20180903.9", "IDX_SP_DATE"); .dropIndex("20180903.9", "IDX_SP_DATE");
spidxDate spidxDate
.addTask(new CalculateHashesTask(VersionEnum.V3_5_0, "20180903.10") .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") .setColumnName("HASH_IDENTITY")
); );
} }
@ -792,7 +793,7 @@ public class HapiFhirJpaMigrationTasks extends BaseMigrationTasks<VersionEnum> {
.withColumns("HASH_IDENTITY", "SP_VALUE"); .withColumns("HASH_IDENTITY", "SP_VALUE");
spidxNumber spidxNumber
.addTask(new CalculateHashesTask(VersionEnum.V3_5_0, "20180903.14") .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") .setColumnName("HASH_IDENTITY")
); );
} }
@ -829,9 +830,9 @@ public class HapiFhirJpaMigrationTasks extends BaseMigrationTasks<VersionEnum> {
.withColumns("HASH_IDENTITY_SYS_UNITS", "SP_VALUE"); .withColumns("HASH_IDENTITY_SYS_UNITS", "SP_VALUE");
spidxQuantity spidxQuantity
.addTask(new CalculateHashesTask(VersionEnum.V3_5_0, "20180903.22") .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", t -> BaseResourceIndexedSearchParam.calculateHashIdentity(new PartitionSettings(), RequestPartitionId.defaultPartition(), 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_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(), null, t.getResourceType(), t.getString("SP_NAME"), t.getString("SP_SYSTEM"), 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") .setColumnName("HASH_IDENTITY")
); );
} }
@ -861,8 +862,8 @@ public class HapiFhirJpaMigrationTasks extends BaseMigrationTasks<VersionEnum> {
spidxString spidxString
.addTask(new CalculateHashesTask(VersionEnum.V3_5_0, "20180903.28") .addTask(new CalculateHashesTask(VersionEnum.V3_5_0, "20180903.28")
.setColumnName("HASH_NORM_PREFIX") .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_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(), null, t.getResourceType(), t.getParamName(), t.getString("SP_VALUE_EXACT"))) .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 spidxToken
.addTask(new CalculateHashesTask(VersionEnum.V3_5_0, "20180903.39") .addTask(new CalculateHashesTask(VersionEnum.V3_5_0, "20180903.39")
.setColumnName("HASH_IDENTITY") .setColumnName("HASH_IDENTITY")
.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")))
.addCalculator("HASH_SYS", t -> ResourceIndexedSearchParamToken.calculateHashSystem(new PartitionSettings(), null, t.getResourceType(), t.getParamName(), t.getString("SP_SYSTEM"))) .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(), null, t.getResourceType(), t.getParamName(), t.getString("SP_SYSTEM"), t.getString("SP_VALUE"))) .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(), null, t.getResourceType(), t.getParamName(), 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 spidxUri
.addTask(new CalculateHashesTask(VersionEnum.V3_5_0, "20180903.44") .addTask(new CalculateHashesTask(VersionEnum.V3_5_0, "20180903.44")
.setColumnName("HASH_IDENTITY") .setColumnName("HASH_IDENTITY")
.addCalculator("HASH_IDENTITY", t -> BaseResourceIndexedSearchParam.calculateHashIdentity(new PartitionSettings(), null, t.getResourceType(), t.getString("SP_NAME"))) .addCalculator("HASH_IDENTITY", t -> BaseResourceIndexedSearchParam.calculateHashIdentity(new PartitionSettings(), (RequestPartitionId)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_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")); Boolean present = columnToBoolean(t.get("SP_PRESENT"));
String resType = (String) t.get("RES_TYPE"); String resType = (String) t.get("RES_TYPE");
String paramName = (String) t.get("PARAM_NAME"); 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); consolidateSearchParamPresenceIndexesTask.executeSql("HFJ_RES_PARAM_PRESENT", "update HFJ_RES_PARAM_PRESENT set HASH_PRESENCE = ? where PID = ?", hash, pid);
}); });
version.addTask(consolidateSearchParamPresenceIndexesTask); version.addTask(consolidateSearchParamPresenceIndexesTask);

View File

@ -1,5 +1,6 @@
package ca.uhn.fhir.jpa.migrate.taskdef; 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.migrate.tasks.api.BaseMigrationTasks;
import ca.uhn.fhir.jpa.model.config.PartitionSettings; import ca.uhn.fhir.jpa.model.config.PartitionSettings;
import ca.uhn.fhir.jpa.model.entity.SearchParamPresent; import ca.uhn.fhir.jpa.model.entity.SearchParamPresent;
@ -42,7 +43,7 @@ public class ArbitrarySqlTaskTest extends BaseTest {
Boolean present = (Boolean) t.get("SP_PRESENT"); Boolean present = (Boolean) t.get("SP_PRESENT");
String resType = (String) t.get("RES_TYPE"); String resType = (String) t.get("RES_TYPE");
String paramName = (String) t.get("PARAM_NAME"); 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); task.executeSql("HFJ_RES_PARAM_PRESENT", "update HFJ_RES_PARAM_PRESENT set HASH_PRESENT = ? where PID = ?", hash, pid);
}); });

View File

@ -1,5 +1,6 @@
package ca.uhn.fhir.jpa.migrate.taskdef; 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.config.PartitionSettings;
import ca.uhn.fhir.jpa.model.entity.BaseResourceIndexedSearchParam; import ca.uhn.fhir.jpa.model.entity.BaseResourceIndexedSearchParam;
import ca.uhn.fhir.jpa.model.entity.ResourceIndexedSearchParamToken; 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"); CalculateHashesTask task = new CalculateHashesTask(VersionEnum.V3_5_0, "1");
task.setTableName("HFJ_SPIDX_TOKEN"); task.setTableName("HFJ_SPIDX_TOKEN");
task.setColumnName("HASH_IDENTITY"); task.setColumnName("HASH_IDENTITY");
task.addCalculator("HASH_IDENTITY", t -> BaseResourceIndexedSearchParam.calculateHashIdentity(new PartitionSettings(), null, t.getResourceType(), t.getString("SP_NAME"))); 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(), null, t.getResourceType(), t.getParamName(), t.getString("SP_SYSTEM"))); 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(), null, t.getResourceType(), t.getParamName(), t.getString("SP_SYSTEM"), t.getString("SP_VALUE"))); 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(), null, t.getResourceType(), t.getParamName(), 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); task.setBatchSize(1);
getMigrator().addTask(task); getMigrator().addTask(task);
@ -77,10 +78,10 @@ public class CalculateHashesTest extends BaseTest {
CalculateHashesTask task = new CalculateHashesTask(VersionEnum.V3_5_0, "1"); CalculateHashesTask task = new CalculateHashesTask(VersionEnum.V3_5_0, "1");
task.setTableName("HFJ_SPIDX_TOKEN"); task.setTableName("HFJ_SPIDX_TOKEN");
task.setColumnName("HASH_IDENTITY"); task.setColumnName("HASH_IDENTITY");
task.addCalculator("HASH_IDENTITY", t -> BaseResourceIndexedSearchParam.calculateHashIdentity(new PartitionSettings(), null, t.getResourceType(), t.getString("SP_NAME"))); 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(), null, t.getResourceType(), t.getParamName(), t.getString("SP_SYSTEM"))); 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(), null, t.getResourceType(), t.getParamName(), t.getString("SP_SYSTEM"), t.getString("SP_VALUE"))); 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(), null, t.getResourceType(), t.getParamName(), 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); task.setBatchSize(3);
getMigrator().addTask(task); getMigrator().addTask(task);

View File

@ -22,7 +22,6 @@ package ca.uhn.fhir.jpa.model.entity;
import ca.uhn.fhir.interceptor.model.RequestPartitionId; import ca.uhn.fhir.interceptor.model.RequestPartitionId;
import javax.annotation.Nonnull;
import javax.annotation.Nullable; import javax.annotation.Nullable;
import javax.persistence.Column; import javax.persistence.Column;
import javax.persistence.Embedded; import javax.persistence.Embedded;
@ -42,18 +41,18 @@ public class BasePartitionable implements Serializable {
@Column(name = PartitionablePartitionId.PARTITION_ID, insertable = false, updatable = false, nullable = true) @Column(name = PartitionablePartitionId.PARTITION_ID, insertable = false, updatable = false, nullable = true)
private Integer myPartitionIdValue; private Integer myPartitionIdValue;
@Nonnull @Nullable
public RequestPartitionId getPartitionId() { public PartitionablePartitionId getPartitionId() {
if (myPartitionId != null) { return myPartitionId;
return myPartitionId.toPartitionId(); }
} else {
return RequestPartitionId.defaultPartition(); public void setPartitionId(PartitionablePartitionId thePartitionId) {
} myPartitionId = thePartitionId;
} }
public void setPartitionId(@Nullable RequestPartitionId theRequestPartitionId) { public void setPartitionId(@Nullable RequestPartitionId theRequestPartitionId) {
if (theRequestPartitionId != null) { if (theRequestPartitionId != null) {
myPartitionId = new PartitionablePartitionId(theRequestPartitionId.getPartitionId(), theRequestPartitionId.getPartitionDate()); myPartitionId = new PartitionablePartitionId(theRequestPartitionId.getFirstPartitionIdOrNull(), theRequestPartitionId.getPartitionDate());
} else { } else {
myPartitionId = null; myPartitionId = null;
} }

View File

@ -20,10 +20,12 @@ package ca.uhn.fhir.jpa.model.entity;
* #L% * #L%
*/ */
import ca.uhn.fhir.context.ConfigurationException;
import ca.uhn.fhir.interceptor.model.RequestPartitionId; import ca.uhn.fhir.interceptor.model.RequestPartitionId;
import ca.uhn.fhir.jpa.model.config.PartitionSettings; import ca.uhn.fhir.jpa.model.config.PartitionSettings;
import ca.uhn.fhir.model.api.IQueryParameterType; import ca.uhn.fhir.model.api.IQueryParameterType;
import ca.uhn.fhir.rest.api.Constants; import ca.uhn.fhir.rest.api.Constants;
import ca.uhn.fhir.rest.server.exceptions.InternalErrorException;
import ca.uhn.fhir.util.UrlUtil; import ca.uhn.fhir.util.UrlUtil;
import com.google.common.base.Charsets; import com.google.common.base.Charsets;
import com.google.common.hash.HashCode; import com.google.common.hash.HashCode;
@ -179,6 +181,11 @@ public abstract class BaseResourceIndexedSearchParam extends BaseResourceIndex {
return myModelConfig; 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) { public static long calculateHashIdentity(PartitionSettings thePartitionSettings, RequestPartitionId theRequestPartitionId, String theResourceType, String theParamName) {
return hash(thePartitionSettings, theRequestPartitionId, theResourceType, theParamName); return hash(thePartitionSettings, theRequestPartitionId, theResourceType, theParamName);
} }
@ -190,8 +197,12 @@ public abstract class BaseResourceIndexedSearchParam extends BaseResourceIndex {
Hasher hasher = HASH_FUNCTION.newHasher(); Hasher hasher = HASH_FUNCTION.newHasher();
if (thePartitionSettings.isPartitioningEnabled() && thePartitionSettings.isIncludePartitionInSearchHashes() && theRequestPartitionId != null) { if (thePartitionSettings.isPartitioningEnabled() && thePartitionSettings.isIncludePartitionInSearchHashes() && theRequestPartitionId != null) {
if (theRequestPartitionId.getPartitionId() != null) { if (theRequestPartitionId.getPartitionIds().size() > 1) {
hasher.putInt(theRequestPartitionId.getPartitionId()); 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);
} }
} }

View File

@ -25,6 +25,7 @@ import ca.uhn.fhir.interceptor.model.RequestPartitionId;
import ca.uhn.fhir.model.primitive.IdDt; import ca.uhn.fhir.model.primitive.IdDt;
import ca.uhn.fhir.model.primitive.InstantDt; import ca.uhn.fhir.model.primitive.InstantDt;
import javax.annotation.Nullable;
import java.util.Date; import java.util.Date;
public interface IBaseResourceEntity { public interface IBaseResourceEntity {
@ -51,5 +52,6 @@ public interface IBaseResourceEntity {
boolean isHasTags(); boolean isHasTags();
RequestPartitionId getPartitionId(); @Nullable
PartitionablePartitionId getPartitionId();
} }

View File

@ -22,9 +22,11 @@ package ca.uhn.fhir.jpa.model.entity;
import ca.uhn.fhir.interceptor.model.RequestPartitionId; import ca.uhn.fhir.interceptor.model.RequestPartitionId;
import javax.annotation.Nonnull;
import javax.annotation.Nullable; import javax.annotation.Nullable;
import javax.persistence.Column; import javax.persistence.Column;
import javax.persistence.Embeddable; import javax.persistence.Embeddable;
import javax.validation.constraints.Null;
import java.time.LocalDate; import java.time.LocalDate;
@Embeddable @Embeddable
@ -83,4 +85,13 @@ public class PartitionablePartitionId implements Cloneable {
public RequestPartitionId toPartitionId() { public RequestPartitionId toPartitionId() {
return RequestPartitionId.fromPartitionId(getPartitionId(), getPartitionDate()); return RequestPartitionId.fromPartitionId(getPartitionId(), getPartitionDate());
} }
@Nullable
public static RequestPartitionId toRequestPartitionId(@Nullable PartitionablePartitionId theRequestPartitionId) {
if (theRequestPartitionId != null) {
return theRequestPartitionId.toPartitionId();
} else {
return null;
}
}
} }

View File

@ -104,4 +104,5 @@ public class ResourceHistoryProvenanceEntity extends BasePartitionable {
return myId; return myId;
} }
} }

View File

@ -20,64 +20,53 @@ package ca.uhn.fhir.jpa.model.entity;
* #L% * #L%
*/ */
import ca.uhn.fhir.interceptor.model.RequestPartitionId; import javax.persistence.Column;
import javax.persistence.Embeddable;
import javax.persistence.*; 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; import java.io.Serializable;
@Embeddable @Embeddable
@Entity @Entity
@Table(name = "HFJ_HISTORY_TAG", uniqueConstraints= { @Table(name = "HFJ_HISTORY_TAG", uniqueConstraints = {
@UniqueConstraint(name="IDX_RESHISTTAG_TAGID", columnNames= {"RES_VER_PID","TAG_ID"}) @UniqueConstraint(name = "IDX_RESHISTTAG_TAGID", columnNames = {"RES_VER_PID", "TAG_ID"})
}) })
public class ResourceHistoryTag extends BaseTag implements Serializable { public class ResourceHistoryTag extends BaseTag implements Serializable {
private static final long serialVersionUID = 1L; private static final long serialVersionUID = 1L;
@SequenceGenerator(name = "SEQ_HISTORYTAG_ID", sequenceName = "SEQ_HISTORYTAG_ID") @SequenceGenerator(name = "SEQ_HISTORYTAG_ID", sequenceName = "SEQ_HISTORYTAG_ID")
@GeneratedValue(strategy = GenerationType.AUTO, generator = "SEQ_HISTORYTAG_ID") @GeneratedValue(strategy = GenerationType.AUTO, generator = "SEQ_HISTORYTAG_ID")
@Id @Id
@Column(name = "PID") @Column(name = "PID")
private Long myId; private Long myId;
@ManyToOne() @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; 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; 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; private String myResourceType;
@Column(name="RES_ID", nullable=false) @Column(name = "RES_ID", nullable = false)
private Long myResourceId; 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() {
} }
public ResourceHistoryTag(ResourceHistoryTable theResourceHistoryTable, TagDefinition theTag, RequestPartitionId theRequestPartitionId) {
public ResourceHistoryTag(ResourceHistoryTable theResourceHistoryTable, TagDefinition theTag, PartitionablePartitionId theRequestPartitionId) {
setTag(theTag); setTag(theTag);
setResource(theResourceHistoryTable); setResource(theResourceHistoryTable);
setResourceId(theResourceHistoryTable.getResourceId()); setResourceId(theResourceHistoryTable.getResourceId());
@ -85,6 +74,22 @@ public class ResourceHistoryTag extends BaseTag implements Serializable {
setPartitionId(theRequestPartitionId); 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() { public ResourceHistoryTable getResourceHistory() {
return myResourceHistory; return myResourceHistory;
} }

View File

@ -283,10 +283,20 @@ public class ResourceIndexedSearchParamQuantity extends BaseResourceIndexedSearc
return retval; 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) { public static long calculateHashSystemAndUnits(PartitionSettings thePartitionSettings, RequestPartitionId theRequestPartitionId, String theResourceType, String theParamName, String theSystem, String theUnits) {
return hash(thePartitionSettings, theRequestPartitionId, theResourceType, theParamName, theSystem, 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) { public static long calculateHashUnits(PartitionSettings thePartitionSettings, RequestPartitionId theRequestPartitionId, String theResourceType, String theParamName, String theUnits) {
return hash(thePartitionSettings, theRequestPartitionId, theResourceType, theParamName, theUnits); return hash(thePartitionSettings, theRequestPartitionId, theResourceType, theParamName, theUnits);
} }

View File

@ -270,10 +270,20 @@ public class ResourceIndexedSearchParamString extends BaseResourceIndexedSearchP
return defaultString(getValueNormalized()).startsWith(normalizedString); 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) { public static long calculateHashExact(PartitionSettings thePartitionSettings, RequestPartitionId theRequestPartitionId, String theResourceType, String theParamName, String theValueExact) {
return hash(thePartitionSettings, theRequestPartitionId, theResourceType, theParamName, 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) { 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 * If we're not allowing contained searches, we'll add the first

View File

@ -286,14 +286,29 @@ public class ResourceIndexedSearchParamToken extends BaseResourceIndexedSearchPa
return retVal; 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) { public static long calculateHashSystem(PartitionSettings thePartitionSettings, RequestPartitionId theRequestPartitionId, String theResourceType, String theParamName, String theSystem) {
return hash(thePartitionSettings, theRequestPartitionId, theResourceType, theParamName, trim(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) { 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)); 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) { public static long calculateHashValue(PartitionSettings thePartitionSettings, RequestPartitionId theRequestPartitionId, String theResourceType, String theParamName, String theValue) {
String value = trim(theValue); String value = trim(theValue);
return hash(thePartitionSettings, theRequestPartitionId, theResourceType, theParamName, value); return hash(thePartitionSettings, theRequestPartitionId, theResourceType, theParamName, value);

View File

@ -209,6 +209,11 @@ public class ResourceIndexedSearchParamUri extends BaseResourceIndexedSearchPara
return defaultString(getUri()).equalsIgnoreCase(uri.getValueNotNull()); 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) { public static long calculateHashUri(PartitionSettings thePartitionSettings, RequestPartitionId theRequestPartitionId, String theResourceType, String theParamName, String theUri) {
return hash(thePartitionSettings, theRequestPartitionId, theResourceType, theParamName, theUri); return hash(thePartitionSettings, theRequestPartitionId, theResourceType, theParamName, theUri);
} }

View File

@ -20,13 +20,23 @@ package ca.uhn.fhir.jpa.model.entity;
* #L% * #L%
*/ */
import ca.uhn.fhir.interceptor.model.RequestPartitionId;
import org.apache.commons.lang3.builder.EqualsBuilder; import org.apache.commons.lang3.builder.EqualsBuilder;
import org.apache.commons.lang3.builder.HashCodeBuilder; import org.apache.commons.lang3.builder.HashCodeBuilder;
import org.apache.commons.lang3.builder.ToStringBuilder; import org.apache.commons.lang3.builder.ToStringBuilder;
import org.apache.commons.lang3.builder.ToStringStyle; 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 @Entity
@Table(name = "HFJ_RES_TAG", uniqueConstraints = { @Table(name = "HFJ_RES_TAG", uniqueConstraints = {
@ -52,10 +62,17 @@ public class ResourceTag extends BaseTag {
@Column(name = "RES_ID", insertable = false, updatable = false) @Column(name = "RES_ID", insertable = false, updatable = false)
private Long myResourceId; private Long myResourceId;
/**
* Constructor
*/
public ResourceTag() { public ResourceTag() {
super();
} }
public ResourceTag(ResourceTable theResourceTable, TagDefinition theTag, RequestPartitionId theRequestPartitionId) { /**
* Constructor
*/
public ResourceTag(ResourceTable theResourceTable, TagDefinition theTag, PartitionablePartitionId theRequestPartitionId) {
setTag(theTag); setTag(theTag);
setResource(theResourceTable); setResource(theResourceTable);
setResourceId(theResourceTable.getId()); setResourceId(theResourceTable.getId());

View File

@ -126,6 +126,11 @@ public class SearchParamPresent extends BasePartitionable implements Serializabl
myPartitionSettings = thePartitionSettings; 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) { public static long calculateHashPresence(PartitionSettings thePartitionSettings, RequestPartitionId theRequestPartitionId, String theResourceType, String theParamName, Boolean thePresent) {
String string = thePresent != null ? Boolean.toString(thePresent) : Boolean.toString(false); String string = thePresent != null ? Boolean.toString(thePresent) : Boolean.toString(false);
return BaseResourceIndexedSearchParam.hash(thePartitionSettings, theRequestPartitionId, theResourceType, theParamName, string); return BaseResourceIndexedSearchParam.hash(thePartitionSettings, theRequestPartitionId, theResourceType, theParamName, string);

View File

@ -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 EXT_SEARCHPARAM_PHONETIC_ENCODER = "http://hapifhir.io/fhir/StructureDefinition/searchparameter-phonetic-encoder";
public static final String VALUESET_FILTER_DISPLAY = "display"; public static final String VALUESET_FILTER_DISPLAY = "display";
/**
* The name of the default partition
*/
public static final String DEFAULT_PARTITION_NAME = "DEFAULT";
/** /**
* Non-instantiable * Non-instantiable
*/ */

View File

@ -215,7 +215,8 @@ public class RuleBuilder implements IAuthRuleBuilder {
if (theResource != null) { if (theResource != null) {
RequestPartitionId partitionId = (RequestPartitionId) theResource.getUserData(Constants.RESOURCE_PARTITION_ID); RequestPartitionId partitionId = (RequestPartitionId) theResource.getUserData(Constants.RESOURCE_PARTITION_ID);
if (partitionId != null) { if (partitionId != null) {
if (!myTenantIds.contains(partitionId.getPartitionName())) { String partitionNameOrNull = partitionId.getFirstPartitionNameOrNull();
if (partitionNameOrNull == null || !myTenantIds.contains(partitionNameOrNull)) {
return !myOutcome; return !myOutcome;
} }
} }