001/*-
002 * #%L
003 * HAPI FHIR JPA Model
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.model.config;
021
022import ca.uhn.fhir.interceptor.model.IDefaultPartitionSettings;
023import ca.uhn.fhir.jpa.model.entity.StorageSettings;
024
025/**
026 * @since 5.0.0
027 */
028public class PartitionSettings implements IDefaultPartitionSettings {
029
030        private boolean myPartitioningEnabled = false;
031        private CrossPartitionReferenceMode myAllowReferencesAcrossPartitions = CrossPartitionReferenceMode.NOT_ALLOWED;
032        private boolean myIncludePartitionInSearchHashes = false;
033        private boolean myUnnamedPartitionMode;
034        private Integer myDefaultPartitionId;
035        private boolean myAlwaysOpenNewTransactionForDifferentPartition;
036        private boolean myConditionalCreateDuplicateIdentifiersEnabled = false;
037        private boolean myDatabasePartitionMode = false;
038
039        public PartitionSettings() {
040                super();
041        }
042
043        /**
044         * This flag activates partition IDs in PKs mode, which is newly introduced in HAPI FHIR 8.0.0.
045         * This mode causes partition IDs to be included in all primary keys, joins, and emitted
046         * SQL. It also affects the generated schema and migrations. This setting should not be changed
047         * after the database has been initialized, unless you have performed an appropriate migration.
048         *
049         * @since 8.0.0
050         */
051        public boolean isDatabasePartitionMode() {
052                return myDatabasePartitionMode;
053        }
054
055        /**
056         * This flag activates partition IDs in PKs mode, which is newly introduced in HAPI FHIR 8.0.0.
057         * This mode causes partition IDs to be included in all primary keys, joins, and emitted
058         * SQL. It also affects the generated schema and migrations. This setting should not be changed
059         * after the database has been initialized, unless you have performed an appropriate migration.
060         *
061         * @since 8.0.0
062         */
063        public PartitionSettings setDatabasePartitionMode(boolean theDatabasePartitionMode) {
064                myDatabasePartitionMode = theDatabasePartitionMode;
065                return this;
066        }
067
068        /**
069         * Should we always open a new database transaction if the partition context changes
070         *
071         * @since 6.6.0
072         */
073        public boolean isAlwaysOpenNewTransactionForDifferentPartition() {
074                return myAlwaysOpenNewTransactionForDifferentPartition;
075        }
076
077        /**
078         * Should we always open a new database transaction if the partition context changes
079         *
080         * @since 6.6.0
081         */
082        public PartitionSettings setAlwaysOpenNewTransactionForDifferentPartition(
083                        boolean theAlwaysOpenNewTransactionForDifferentPartition) {
084                myAlwaysOpenNewTransactionForDifferentPartition = theAlwaysOpenNewTransactionForDifferentPartition;
085                return this;
086        }
087
088        /**
089         * If set to <code>true</code> (default is <code>false</code>) the <code>PARTITION_ID</code> value will be factored into the
090         * hash values used in the <code>HFJ_SPIDX_xxx</code> tables, removing the need to explicitly add a selector
091         * on this column in queries. If set to <code>false</code>, an additional selector is used instead, which may perform
092         * better when using native database partitioning features.
093         * <p>
094         * This setting has no effect if partitioning is not enabled via {@link #isPartitioningEnabled()}.
095         * </p>
096         * <p>
097         * If {@link StorageSettings#isIndexStorageOptimized()} is enabled this setting should be set to <code>false</code>.
098         * </p>
099         */
100        public boolean isIncludePartitionInSearchHashes() {
101                return myIncludePartitionInSearchHashes;
102        }
103
104        /**
105         * If set to <code>true</code> (default is <code>false</code>) the <code>PARTITION_ID</code> value will be factored into the
106         * hash values used in the <code>HFJ_SPIDX_xxx</code> tables, removing the need to explicitly add a selector
107         * on this column in queries. If set to <code>false</code>, an additional selector is used instead, which may perform
108         * better when using native database partitioning features.
109         * <p>
110         * This setting has no effect if partitioning is not enabled via {@link #isPartitioningEnabled()}.
111         * </p>
112         * <p>
113         * If {@link StorageSettings#isIndexStorageOptimized()} is enabled this setting should be set to <code>false</code>.
114         * </p>
115         */
116        public PartitionSettings setIncludePartitionInSearchHashes(boolean theIncludePartitionInSearchHashes) {
117                myIncludePartitionInSearchHashes = theIncludePartitionInSearchHashes;
118                return this;
119        }
120
121        /**
122         * If enabled (default is <code>false</code>) the JPA server will support data partitioning
123         *
124         * @since 5.0.0
125         */
126        public boolean isPartitioningEnabled() {
127                return myPartitioningEnabled;
128        }
129
130        /**
131         * If enabled (default is <code>false</code>) the JPA server will support data partitioning
132         *
133         * @since 5.0.0
134         */
135        public PartitionSettings setPartitioningEnabled(boolean theMultiTenancyEnabled) {
136                myPartitioningEnabled = theMultiTenancyEnabled;
137                return this;
138        }
139
140        /**
141         * Should resources references be permitted to cross partition boundaries. Default is {@link CrossPartitionReferenceMode#NOT_ALLOWED}.
142         *
143         * @since 5.0.0
144         */
145        public CrossPartitionReferenceMode getAllowReferencesAcrossPartitions() {
146                return myAllowReferencesAcrossPartitions;
147        }
148
149        /**
150         * Should resources references be permitted to cross partition boundaries. Default is {@link CrossPartitionReferenceMode#NOT_ALLOWED}.
151         *
152         * @since 5.0.0
153         */
154        public PartitionSettings setAllowReferencesAcrossPartitions(
155                        CrossPartitionReferenceMode theAllowReferencesAcrossPartitions) {
156                myAllowReferencesAcrossPartitions = theAllowReferencesAcrossPartitions;
157                return this;
158        }
159
160        /**
161         * If set to <code>true</code> (default is <code>false</code>), partitions will be unnamed and all IDs from {@link Integer#MIN_VALUE} through
162         * {@link Integer#MAX_VALUE} will be allowed without needing to be created ahead of time.
163         *
164         * @since 5.5.0
165         */
166        public boolean isUnnamedPartitionMode() {
167                return myUnnamedPartitionMode;
168        }
169
170        /**
171         * If set to <code>true</code> (default is <code>false</code>), partitions will be unnamed and all IDs from {@link Integer#MIN_VALUE} through
172         * {@link Integer#MAX_VALUE} will be allowed without needing to be created ahead of time.
173         *
174         * @since 5.5.0
175         */
176        public PartitionSettings setUnnamedPartitionMode(boolean theUnnamedPartitionMode) {
177                myUnnamedPartitionMode = theUnnamedPartitionMode;
178                return this;
179        }
180
181        /**
182         * If set, the given ID will be used for the default partition. The default is
183         * <code>null</code> which will result in the use of a null value for default
184         * partition items.
185         *
186         * @since 5.5.0
187         */
188        @Override
189        public Integer getDefaultPartitionId() {
190                return myDefaultPartitionId;
191        }
192
193        /**
194         * If set, the given ID will be used for the default partition. The default is
195         * <code>null</code> which will result in the use of a null value for default
196         * partition items.
197         *
198         * @since 5.5.0
199         */
200        public PartitionSettings setDefaultPartitionId(Integer theDefaultPartitionId) {
201                myDefaultPartitionId = theDefaultPartitionId;
202                return this;
203        }
204
205        /**
206         * If enabled the JPA server will allow unqualified cross partition reference
207         */
208        public boolean isAllowUnqualifiedCrossPartitionReference() {
209                return myAllowReferencesAcrossPartitions.equals(
210                                PartitionSettings.CrossPartitionReferenceMode.ALLOWED_UNQUALIFIED);
211        }
212
213        /**
214         * The {@link ca.uhn.fhir.jpa.model.entity.ResourceSearchUrlEntity}
215         * table is used to prevent accidental concurrent conditional create/update operations
216         * from creating duplicate resources by inserting a row in that table as a part
217         * of the database transaction performing the write operation. If this setting
218         * is set to {@literal false} (which is the default), the partition
219         * ID is not written to this table, meaning that duplicates are prevented across
220         * partitions. If this setting is {@literal true}, duplicates will not be
221         * prevented if they appear on different partitions.
222         */
223        public boolean isConditionalCreateDuplicateIdentifiersEnabled() {
224                return myConditionalCreateDuplicateIdentifiersEnabled;
225        }
226
227        /**
228         * The {@link ca.uhn.fhir.jpa.model.entity.ResourceSearchUrlEntity}
229         * table is used to prevent accidental concurrent conditional create/update operations
230         * from creating duplicate resources by inserting a row in that table as a part
231         * of the database transaction performing the write operation. If this setting
232         * is set to {@literal false} (which is the default), the partition
233         * ID is not written to this table, meaning that duplicates are prevented across
234         * partitions. If this setting is {@literal true}, duplicates will not be
235         * prevented if they appear on different partitions.
236         */
237        public PartitionSettings setConditionalCreateDuplicateIdentifiersEnabled(
238                        boolean theConditionalCreateDuplicateIdentifiersEnabled) {
239                myConditionalCreateDuplicateIdentifiersEnabled = theConditionalCreateDuplicateIdentifiersEnabled;
240                return this;
241        }
242
243        public enum CrossPartitionReferenceMode {
244
245                /**
246                 * References between resources are not allowed to cross partition boundaries
247                 */
248                NOT_ALLOWED,
249
250                /**
251                 * References can cross partition boundaries, with an assumption that boundaries
252                 * will be managed by the database.
253                 */
254                ALLOWED_UNQUALIFIED,
255        }
256}