Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

partial merge of SearchBuilder changes #6332

Draft
wants to merge 34 commits into
base: ja_20240718_pk_schema_selector
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
34 commits
Select commit Hold shift + click to select a range
377e44b
attribution and pom change (#6309)
tadgh Sep 25, 2024
3f6d1eb
#5768 Upgrade to latest simple-java-mail (#6261)
Thopap Sep 26, 2024
919e2d2
Attribution for javamail and API bump (#6319)
tadgh Sep 26, 2024
fb75711
Composite unique search parameter with dateTime component does not re…
volodymyr-korzh Sep 26, 2024
55f8e85
Failing test
michaelabuckley Sep 26, 2024
40045fd
cleanup
michaelabuckley Sep 26, 2024
4e240a8
revert partition in group by clause
michaelabuckley Sep 26, 2024
785f218
test update for new joins
michaelabuckley Sep 27, 2024
75f7def
Give up on nullable join
michaelabuckley Sep 27, 2024
40a2a7c
spotless
michaelabuckley Sep 27, 2024
8ecc772
changelog
michaelabuckley Sep 27, 2024
e7fed79
cleanup
michaelabuckley Sep 27, 2024
93d520a
cleanup
michaelabuckley Sep 27, 2024
75b894e
fixmes
michaelabuckley Sep 27, 2024
8e5784f
partitioned sql test
michaelabuckley Sep 27, 2024
6bb72e4
HapiSchemaMigrationTest intermittent failure fix (#6329)
volodymyr-korzh Sep 27, 2024
dad7094
get sort tested
michaelabuckley Sep 27, 2024
afa1c2f
revert tests expecting nullable partition joins
michaelabuckley Sep 27, 2024
4df01b1
Fix result set parsing when coord-sort is present.
michaelabuckley Sep 28, 2024
d05079d
Merge remote-tracking branch 'refs/remotes/origin/master' into mb_202…
michaelabuckley Sep 28, 2024
4cf3af3
Cleanup sql test
michaelabuckley Sep 29, 2024
6713e9b
Change to default select partition_id
michaelabuckley Sep 29, 2024
ddf8ba6
Merge remote-tracking branch 'refs/remotes/origin/mb_20240925_partiti…
michaelabuckley Sep 29, 2024
c9c09a9
Fix everything sql
michaelabuckley Sep 30, 2024
65a657a
Fix everything sql
michaelabuckley Sep 30, 2024
e27445b
Merge remote-tracking branch 'refs/remotes/origin/mb_20240925_partiti…
michaelabuckley Sep 30, 2024
143ad06
fix row extraction
michaelabuckley Sep 30, 2024
5c88f6a
test fix
michaelabuckley Sep 30, 2024
b20225c
Fix group-by on offset path
michaelabuckley Sep 30, 2024
0fbd271
Merge remote-tracking branch 'refs/remotes/origin/mb_20240925_partiti…
michaelabuckley Sep 30, 2024
bbf66d7
More sql test fixups
michaelabuckley Sep 30, 2024
ef7b846
Merge remote-tracking branch 'refs/remotes/origin/mb_20240925_partiti…
michaelabuckley Sep 30, 2024
e6ac211
More sql test fixups
michaelabuckley Sep 30, 2024
a92d875
Merge remote-tracking branch 'refs/remotes/origin/mb_20240925_partiti…
michaelabuckley Sep 30, 2024
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
type: change
issue: 6261
title: "Upgrading to Jakarta had caused a problem with the version of java-simple-mail that was in use. This has been updated to be conformant
with the Jakarta Mail APIs. Thanks to Thomas Papke(@thopap) for the contribution!"
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
type: fix
issue: 6290
title: "Previously, a specific migration task was using the `TRIM()` function, which does not exist in MSSQL 2012. This was causing migrations targeting MSSQL 2012 to fail.
This has been corrected and replaced with usage of a combination of LTRIM() and RTRIM(). Thanks to Primož Delopst at Better for the contribution!"
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
---
type: fix
issue: 6317
title: "Previously, defining a unique combo Search Parameter with the DateTime component and submitting multiple
resources with the same dateTime element (e.g. Observation.effectiveDateTime) resulted in duplicate resource creation.
This has been fixed."

Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
---
type: add
issue: 6325
title: "Add support for including the partitioning column in search query joins."
Original file line number Diff line number Diff line change
Expand Up @@ -361,7 +361,7 @@ public void addSortOnResourceLink(
case STRING:
StringPredicateBuilder stringPredicateBuilder = mySqlBuilder.createStringPredicateBuilder();
addSortCustomJoin(
resourceLinkPredicateBuilder.getColumnTargetResourceId(),
resourceLinkPredicateBuilder.getJoinColumnsForTarget(),
stringPredicateBuilder,
stringPredicateBuilder.createHashIdentityPredicate(targetType, theChain));

Expand All @@ -372,7 +372,7 @@ public void addSortOnResourceLink(
case TOKEN:
TokenPredicateBuilder tokenPredicateBuilder = mySqlBuilder.createTokenPredicateBuilder();
addSortCustomJoin(
resourceLinkPredicateBuilder.getColumnTargetResourceId(),
resourceLinkPredicateBuilder.getJoinColumnsForTarget(),
tokenPredicateBuilder,
tokenPredicateBuilder.createHashIdentityPredicate(targetType, theChain));

Expand All @@ -383,7 +383,7 @@ public void addSortOnResourceLink(
case DATE:
DatePredicateBuilder datePredicateBuilder = mySqlBuilder.createDatePredicateBuilder();
addSortCustomJoin(
resourceLinkPredicateBuilder.getColumnTargetResourceId(),
resourceLinkPredicateBuilder.getJoinColumnsForTarget(),
datePredicateBuilder,
datePredicateBuilder.createHashIdentityPredicate(targetType, theChain));

Expand Down Expand Up @@ -477,24 +477,24 @@ private void addSortCustomJoin(
BaseJoiningPredicateBuilder theFromJoiningPredicateBuilder,
BaseJoiningPredicateBuilder theToJoiningPredicateBuilder,
Condition theCondition) {
addSortCustomJoin(
theFromJoiningPredicateBuilder.getResourceIdColumn(), theToJoiningPredicateBuilder, theCondition);
addSortCustomJoin(theFromJoiningPredicateBuilder.getJoinColumns(), theToJoiningPredicateBuilder, theCondition);
}

private void addSortCustomJoin(
DbColumn theFromDbColumn,
DbColumn theFromDbColumn[],
BaseJoiningPredicateBuilder theToJoiningPredicateBuilder,
Condition theCondition) {

ComboCondition onCondition =
mySqlBuilder.createOnCondition(theFromDbColumn, theToJoiningPredicateBuilder.getResourceIdColumn());
mySqlBuilder.createOnCondition(theFromDbColumn, theToJoiningPredicateBuilder.getJoinColumns());

if (theCondition != null) {
onCondition.addCondition(theCondition);
}

mySqlBuilder.addCustomJoin(
SelectQuery.JoinType.LEFT_OUTER,
theFromDbColumn.getTable(),
theFromDbColumn[0].getTable(),
theToJoiningPredicateBuilder.getTable(),
onCondition);
}
Expand Down Expand Up @@ -1482,7 +1482,11 @@ public Condition createPredicateReference(

public void addGrouping() {
BaseJoiningPredicateBuilder firstPredicateBuilder = mySqlBuilder.getOrCreateFirstPredicateBuilder();
mySqlBuilder.getSelect().addGroupings(firstPredicateBuilder.getJoinColumns());
// mySqlBuilder.getSelect().addGroupings(firstPredicateBuilder.getJoinColumns());
// we always select partition_id, even if not joining.
mySqlBuilder.getSelect().addGroupings(new DbColumn[] {
firstPredicateBuilder.getPartitionIdColumn(), firstPredicateBuilder.getResourceIdColumn()
});
}

public void addOrdering() {
Expand Down Expand Up @@ -1582,7 +1586,12 @@ public Condition createPredicateReferenceForEmbeddedChainedSearchResource(
if (theSourceJoinColumn == null) {
BaseJoiningPredicateBuilder root = mySqlBuilder.getOrCreateFirstPredicateBuilder(false);
DbColumn[] joinColumns = root.getJoinColumns();
Object joinColumnObject = ColumnTupleObject.from(joinColumns);
Object joinColumnObject;
if (joinColumns.length == 1) {
joinColumnObject = joinColumns[0];
} else {
joinColumnObject = ColumnTupleObject.from(joinColumns);
}
retVal = new InCondition(joinColumnObject, union);
} else {
// -- for the resource link, need join with target_resource_id
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -128,6 +128,8 @@
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.transaction.support.TransactionSynchronizationManager;

import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
Expand All @@ -144,7 +146,7 @@
import static ca.uhn.fhir.jpa.model.util.JpaConstants.UNDESIRED_RESOURCE_LINKAGES_FOR_EVERYTHING_ON_PATIENT_INSTANCE;
import static ca.uhn.fhir.jpa.search.builder.QueryStack.LOCATION_POSITION;
import static ca.uhn.fhir.jpa.search.builder.QueryStack.SearchForIdsParams.with;
import static ca.uhn.fhir.jpa.util.InClauseNormalizer.*;
import static ca.uhn.fhir.jpa.util.InClauseNormalizer.normalizeIdListForInClause;
import static java.util.Objects.requireNonNull;
import static org.apache.commons.collections4.CollectionUtils.isNotEmpty;
import static org.apache.commons.lang3.StringUtils.defaultString;
Expand Down Expand Up @@ -2118,7 +2120,7 @@ private void attemptComboUniqueSpProcessing(
* The loop allows us to create multiple combo index joins if there
* are multiple AND expressions for the related parameters.
*/
while (validateParamValuesAreValidForComboParam(theRequest, theParams, comboParamNames)) {
while (validateParamValuesAreValidForComboParam(theRequest, theParams, comboParamNames, comboParam)) {
applyComboSearchParam(theQueryStack, theParams, theRequest, comboParamNames, comboParam);
}
}
Expand Down Expand Up @@ -2214,7 +2216,10 @@ private void applyComboSearchParam(
* (e.g. <code>?date=gt2024-02-01</code>), etc.
*/
private boolean validateParamValuesAreValidForComboParam(
RequestDetails theRequest, @Nonnull SearchParameterMap theParams, List<String> theComboParamNames) {
RequestDetails theRequest,
@Nonnull SearchParameterMap theParams,
List<String> theComboParamNames,
RuntimeSearchParam theComboParam) {
boolean paramValuesAreValidForCombo = true;
List<List<IQueryParameterType>> paramOrValues = new ArrayList<>(theComboParamNames.size());

Expand Down Expand Up @@ -2275,6 +2280,19 @@ private boolean validateParamValuesAreValidForComboParam(
break;
}
}

// Date params are not eligible for using composite unique index
// as index could contain date with different precision (e.g. DAY, SECOND)
if (nextParamDef.getParamType() == RestSearchParameterTypeEnum.DATE
&& theComboParam.getComboSearchParamType() == ComboSearchParamType.UNIQUE) {
ourLog.debug(
"Search with params {} is not a candidate for combo searching - "
+ "Unique combo search parameter '{}' has DATE type",
theComboParamNames,
nextParamName);
paramValuesAreValidForCombo = false;
break;
}
}

if (CartesianProductUtil.calculateCartesianProductSize(paramOrValues) > 500) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -363,10 +363,11 @@ public void addCustomJoin(
mySelect.addCustomJoin(theJoinType, theFromTable, theToTable, theCondition);
}

public ComboCondition createOnCondition(DbColumn theSourceColumn, DbColumn theTargetColumn) {
public ComboCondition createOnCondition(DbColumn[] theSourceColumn, DbColumn[] theTargetColumn) {
ComboCondition onCondition = ComboCondition.and();
onCondition.addCondition(BinaryCondition.equalTo(theSourceColumn, theTargetColumn));

for (int i = 0; i < theSourceColumn.length; i += 1) {
onCondition.addCondition(BinaryCondition.equalTo(theSourceColumn[i], theTargetColumn[i]));
}
return onCondition;
}

Expand Down Expand Up @@ -498,10 +499,6 @@ public void addJoin(
mySelect.addJoins(theJoinType, join);
}

public boolean isSelectPartitionId() {
return mySelectPartitionId;
}

/**
* Generate and return the SQL generated by this builder
*/
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -108,15 +108,14 @@ private void fetchNext() {
* is managed by Spring has been started before this method is called.
*/
HapiTransactionService.requireTransaction();
ourLog.trace("About to execute SQL: {}. Parameters: {}", sql, Arrays.toString(args));

Query nativeQuery = myEntityManager.createNativeQuery(sql);
org.hibernate.query.Query<?> hibernateQuery = (org.hibernate.query.Query<?>) nativeQuery;
for (int i = 1; i <= args.length; i++) {
hibernateQuery.setParameter(i, args[i - 1]);
}

ourLog.trace("About to execute SQL: {}. Parameters: {}", sql, Arrays.toString(args));

/*
* These settings help to ensure that we use a search cursor
* as opposed to loading all search results into memory
Expand Down Expand Up @@ -146,24 +145,7 @@ private void fetchNext() {
if (myResultSet == null || !myResultSet.hasNext()) {
myNext = NO_MORE;
} else {
Object nextRow = Objects.requireNonNull(myResultSet.next());
// We should typically get two columns back, the first is the partition ID and the second
// is the resource ID. But if we're doing a count query, we'll get a single column in an array
// or maybe even just a single non array value depending on how the platform handles it.
if (nextRow instanceof Number) {
long count = ((Number) nextRow).longValue();
myNext = JpaPid.fromId(count, null);
} else {
Object[] nextRowAsArray = (Object[]) nextRow;
if (nextRowAsArray.length == 1) {
Long count = (Long) nextRowAsArray[0];
myNext = JpaPid.fromId(count, null);
} else {
Integer nextPartitionId = (Integer) nextRowAsArray[0];
Long nextResourceId = (Long) nextRowAsArray[1];
myNext = JpaPid.fromId(nextResourceId, nextPartitionId);
}
}
myNext = getNextPid(myResultSet);
}

} catch (Exception e) {
Expand All @@ -174,6 +156,41 @@ private void fetchNext() {
}
}

private JpaPid getNextPid(ScrollableResultsIterator<Object> theResultSet) {
Object nextRow = Objects.requireNonNull(theResultSet.next());
// We should typically get two columns back, the first is the partition ID and the second
// is the resource ID. But if we're doing a count query, we'll get a single column in an array
// or maybe even just a single non array value depending on how the platform handles it.
if (nextRow instanceof Number) {
return JpaPid.fromId(((Number) nextRow).longValue());
} else {
Object[] nextRowAsArray = (Object[]) nextRow;
if (nextRowAsArray.length == 1) {
return JpaPid.fromId((Long) nextRowAsArray[0]);
} else {
// TODO MB add a strategy object to GeneratedSql to describe the result set.
// or make SQE generic
// Comment to reviewer: this will be cleaner with the next
// merge from ja_20240718_pk_schema_selector

// We have some cases to distinguish:
// - res_id
// - count
// - partition_id, res_id
// - res_id, coord-dist
// - partition_id, res_id, coord-dist
// Assume res_id is first Long in row, and is in first two columns
if (nextRowAsArray[0] instanceof Long) {
return JpaPid.fromId((Long) nextRowAsArray[0]);
} else {
Integer nextPartitionId = (Integer) nextRowAsArray[0];
Long nextResourceId = (Long) nextRowAsArray[1];
return JpaPid.fromId(nextResourceId, nextPartitionId);
}
}
}
}

public static SearchQueryExecutor emptyExecutor() {
return NO_VALUE_EXECUTOR;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -37,12 +37,19 @@ public class PartitionSettings {

public PartitionSettings() {}

// FIXME: document
/**
* Is the table partition column (usually PARTITION_ID)
* participating in primary and foreign keys.
* Affects sql joins, sql in() expressions, etc.
*/
public boolean isPartitionIdsInPrimaryKeys() {
return myPartitionIdsInPrimaryKeys;
}

// FIXME: document
/**
* Inform the query engine if the primary and foreign keys
* of the partitioned tables include the partition column.
*/
public void setPartitionIdsInPrimaryKeys(boolean thePartitionIdsInPrimaryKeys) {
myPartitionIdsInPrimaryKeys = thePartitionIdsInPrimaryKeys;
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
package ca.uhn.fhir.jpa.model.dao;

import org.junit.jupiter.api.Test;

import java.util.List;

import static ca.uhn.fhir.jpa.model.dao.JpaPid.fromId;
import static org.junit.jupiter.api.Assertions.*;

class JpaPidTest {

@Test
void testToLongList() {
assertEquals(List.of(), JpaPid.toLongList(List.of()));
assertEquals(List.of(1L, 2L), JpaPid.toLongList(List.of(fromId(1L), fromId(2L))));
}

@Test
void testArrayToLongList() {
assertEquals(List.of(), JpaPid.toLongList(new JpaPid[0]));
assertEquals(List.of(1L, 2L), JpaPid.toLongList(new JpaPid[]{fromId(1L), fromId(2L)}));
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -566,7 +566,8 @@ private Set<String> extractParameterCombinationsForComboParam(
for (BaseResourceIndexedSearchParam nextParam : paramsListForCompositePart) {
IQueryParameterType nextParamAsClientParam = nextParam.toQueryParameterType();

if (nextParamAsClientParam instanceof DateParam) {
if (theParam.getComboSearchParamType() == ComboSearchParamType.NON_UNIQUE
&& nextParamAsClientParam instanceof DateParam) {
DateParam date = (DateParam) nextParamAsClientParam;
if (date.getPrecision() != TemporalPrecisionEnum.DAY) {
continue;
Expand Down
5 changes: 5 additions & 0 deletions hapi-fhir-jpaserver-subscription/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,11 @@
<artifactId>jakarta.servlet-api</artifactId>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>jakarta.mail</groupId>
<artifactId>jakarta.mail-api</artifactId>
<optional>true</optional>
</dependency>

<!-- test dependencies -->
<dependency>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,8 +28,8 @@
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;

import javax.mail.internet.InternetAddress;
import javax.mail.internet.MimeMessage;
import jakarta.mail.internet.InternetAddress;
import jakarta.mail.internet.MimeMessage;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
package ca.uhn.fhir.jpa.provider.dstu3;

import static org.junit.jupiter.api.Assertions.assertEquals;
import ca.uhn.fhir.jpa.searchparam.MatchUrlService;
import ca.uhn.fhir.jpa.searchparam.ResourceSearch;
import ca.uhn.fhir.jpa.searchparam.SearchParameterMap;
Expand All @@ -16,7 +15,7 @@

import java.util.Collections;

import static org.assertj.core.api.Assertions.assertThat;
import static org.junit.jupiter.api.Assertions.assertEquals;

public class ResourceProviderSearchModifierDstu3Test extends BaseResourceProviderDstu3Test{
@Autowired
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,8 @@
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import javax.mail.internet.InternetAddress;
import javax.mail.internet.MimeMessage;
import jakarta.mail.internet.InternetAddress;
import jakarta.mail.internet.MimeMessage;
import java.util.Arrays;

import static org.assertj.core.api.Assertions.assertThat;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,8 +26,8 @@
import org.junit.jupiter.api.extension.RegisterExtension;
import org.springframework.beans.factory.annotation.Autowired;

import javax.mail.internet.InternetAddress;
import javax.mail.internet.MimeMessage;
import jakarta.mail.internet.InternetAddress;
import jakarta.mail.internet.MimeMessage;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
Expand Down
Loading
Loading