001/*-
002 * #%L
003 * HAPI FHIR JPA Server
004 * %%
005 * Copyright (C) 2014 - 2025 Smile CDR, Inc.
006 * %%
007 * Licensed under the Apache License, Version 2.0 (the "License");
008 * you may not use this file except in compliance with the License.
009 * You may obtain a copy of the License at
010 *
011 *      http://www.apache.org/licenses/LICENSE-2.0
012 *
013 * Unless required by applicable law or agreed to in writing, software
014 * distributed under the License is distributed on an "AS IS" BASIS,
015 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
016 * See the License for the specific language governing permissions and
017 * limitations under the License.
018 * #L%
019 */
020package ca.uhn.fhir.jpa.search.builder.predicate;
021
022import ca.uhn.fhir.i18n.Msg;
023import ca.uhn.fhir.jpa.api.config.JpaStorageSettings;
024import ca.uhn.fhir.jpa.search.builder.sql.SearchQueryBuilder;
025import ca.uhn.fhir.jpa.util.QueryParameterUtils;
026import ca.uhn.fhir.model.api.IQueryParameterType;
027import ca.uhn.fhir.rest.param.UriParam;
028import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException;
029import ca.uhn.fhir.rest.server.exceptions.MethodNotAllowedException;
030import ca.uhn.fhir.util.StringUtil;
031import ca.uhn.fhir.util.UrlUtil;
032import com.healthmarketscience.sqlbuilder.BinaryCondition;
033import com.healthmarketscience.sqlbuilder.Condition;
034import com.healthmarketscience.sqlbuilder.FunctionCall;
035import com.healthmarketscience.sqlbuilder.UnaryCondition;
036import com.healthmarketscience.sqlbuilder.dbspec.basic.DbColumn;
037
038import java.util.List;
039
040import static ca.uhn.fhir.jpa.search.builder.predicate.StringPredicateBuilder.createLeftAndRightMatchLikeExpression;
041import static ca.uhn.fhir.jpa.search.builder.predicate.StringPredicateBuilder.createLeftMatchLikeExpression;
042
043public class ResourceHistoryProvenancePredicateBuilder extends BaseJoiningPredicateBuilder
044                implements ISourcePredicateBuilder {
045
046        private final DbColumn myColumnSourceUri;
047        private final DbColumn myColumnRequestId;
048        private final DbColumn myResourceIdColumn;
049
050        /**
051         * Constructor
052         */
053        public ResourceHistoryProvenancePredicateBuilder(SearchQueryBuilder theSearchSqlBuilder) {
054                super(theSearchSqlBuilder, theSearchSqlBuilder.addTable("HFJ_RES_VER_PROV"));
055
056                myResourceIdColumn = getTable().addColumn("RES_PID");
057                myColumnSourceUri = getTable().addColumn("SOURCE_URI");
058                myColumnRequestId = getTable().addColumn("REQUEST_ID");
059        }
060
061        @Override
062        public DbColumn getResourceIdColumn() {
063                return myResourceIdColumn;
064        }
065
066        @Override
067        public Condition createPredicateSourceUri(String theSourceUri) {
068                return BinaryCondition.equalTo(myColumnSourceUri, generatePlaceholder(theSourceUri));
069        }
070
071        @Override
072        public Condition createPredicateMissingSourceUri() {
073                return UnaryCondition.isNull(myColumnSourceUri);
074        }
075
076        @Override
077        public Condition createPredicateSourceUriWithModifiers(
078                        IQueryParameterType theQueryParameter, JpaStorageSettings theStorageSetting, String theSourceUri) {
079                if (theQueryParameter.getMissing() != null && !theQueryParameter.getMissing()) {
080                        return UnaryCondition.isNotNull(myColumnSourceUri);
081                } else if (theQueryParameter instanceof UriParam && theQueryParameter.getQueryParameterQualifier() != null) {
082                        UriParam uriParam = (UriParam) theQueryParameter;
083                        switch (uriParam.getQualifier()) {
084                                case ABOVE:
085                                        return createPredicateSourceAbove(theSourceUri);
086                                case BELOW:
087                                        return createPredicateSourceBelow(theSourceUri);
088                                case CONTAINS:
089                                        return createPredicateSourceContains(theStorageSetting, theSourceUri);
090                                default:
091                                        throw new InvalidRequestException(Msg.code(2569)
092                                                        + String.format(
093                                                                        "Unsupported qualifier specified, qualifier=%s",
094                                                                        theQueryParameter.getQueryParameterQualifier()));
095                        }
096                } else {
097                        return createPredicateSourceUri(theSourceUri);
098                }
099        }
100
101        private Condition createPredicateSourceAbove(String theSourceUri) {
102                List<String> aboveUriCandidates = UrlUtil.getAboveUriCandidates(theSourceUri);
103                List<String> aboveUriPlaceholders = generatePlaceholders(aboveUriCandidates);
104                return QueryParameterUtils.toEqualToOrInPredicate(myColumnSourceUri, aboveUriPlaceholders);
105        }
106
107        private Condition createPredicateSourceBelow(String theSourceUri) {
108                String belowLikeExpression = createLeftMatchLikeExpression(theSourceUri);
109                return BinaryCondition.like(myColumnSourceUri, generatePlaceholder(belowLikeExpression));
110        }
111
112        private Condition createPredicateSourceContains(JpaStorageSettings theStorageSetting, String theSourceUri) {
113                if (theStorageSetting.isAllowContainsSearches()) {
114                        FunctionCall upperFunction = new FunctionCall("UPPER");
115                        upperFunction.addCustomParams(myColumnSourceUri);
116                        String normalizedString = StringUtil.normalizeStringForSearchIndexing(theSourceUri);
117                        String containsLikeExpression = createLeftAndRightMatchLikeExpression(normalizedString);
118                        return BinaryCondition.like(upperFunction, generatePlaceholder(containsLikeExpression));
119                } else {
120                        throw new MethodNotAllowedException(Msg.code(2570) + ":contains modifier is disabled on this server");
121                }
122        }
123
124        @Override
125        public Condition createPredicateRequestId(String theRequestId) {
126                return BinaryCondition.equalTo(myColumnRequestId, generatePlaceholder(theRequestId));
127        }
128}